KeySize

How to perform asymmetric encryption without user input/hardcoded values with Xamarin iOS

How to perform asymmetric encryption without user input/hardcoded values with Xamarin iOS

I am not repeating all the initial explanations why you should use this way of encryption to secure sensible data in your app(s), as I did this already in the post on how to do that on Android.

iOS KeyChain

The KeyChain API is the most important security element on iOS (and MacOS). The interaction with it is not as difficult as one thinks, after getting the concept for the different using scenarios it supports. In our case, we want to create a public/private key pair for use within our app. Pretty much like on Android, we want no user input and no hardcoded values to get this pair.

Preparing key (pair) creation

On iOS, things are traditionally a little bit more complex than on other platforms. This is also true for things like encryption. The first step would be to prepare the RSA parameters we want to use for encryption. However, that turned out to be a bit challenging because we need to pass in some keys and values that live in the native iOS security library and Xamarin does not fully expose them. Luckily, there is at least a Xamarin API that helps us to extract those values. I found this SO post helpful to understand what is needed for the creation of the key pair. I adapted some of the snippets into my own helper class, this is also true for the IosConstantsclass:

    internal class IosConstants
    {
        private static IosConstants _instance;

        public static IosConstants Instance => _instance ?? (_instance = new IosConstants());

        public readonly NSString KSecAttrKeyType;
        public readonly NSString KSecAttrKeySize;
        public readonly NSString KSecAttrKeyTypeRSA;
        public readonly NSString KSecAttrIsPermanent;
        public readonly NSString KSecAttrApplicationTag;
        public readonly NSString KSecPrivateKeyAttrs;
        public readonly NSString KSecClass;
        public readonly NSString KSecClassKey;
        public readonly NSString KSecPaddingPKCS1;
        public readonly NSString KSecAccessibleWhenUnlocked;
        public readonly NSString KSecAttrAccessible;

        public IosConstants()
        {
            var handle = Dlfcn.dlopen(Constants.SecurityLibrary, 0);

            try
            {
                KSecAttrApplicationTag = Dlfcn.GetStringConstant(handle, "kSecAttrApplicationTag");
                KSecAttrKeyType = Dlfcn.GetStringConstant(handle, "kSecAttrKeyType");
                KSecAttrKeyTypeRSA = Dlfcn.GetStringConstant(handle, "kSecAttrKeyTypeRSA");
                KSecAttrKeySize = Dlfcn.GetStringConstant(handle, "kSecAttrKeySizeInBits");
                KSecAttrIsPermanent = Dlfcn.GetStringConstant(handle, "kSecAttrIsPermanent");
                KSecPrivateKeyAttrs = Dlfcn.GetStringConstant(handle, "kSecPrivateKeyAttrs");
                KSecClass = Dlfcn.GetStringConstant(handle, "kSecClass");
                KSecClassKey = Dlfcn.GetStringConstant(handle, "kSecClassKey");
                KSecPaddingPKCS1 = Dlfcn.GetStringConstant(handle, "kSecPaddingPKCS1");
                KSecAccessibleWhenUnlocked = Dlfcn.GetStringConstant(handle, "kSecAttrAccessibleWhenUnlocked");
                KSecAttrAccessible = Dlfcn.GetStringConstant(handle, "kSecAttrAccessible");

            }
            finally
            {
                Dlfcn.dlclose(handle);
            }
        }
    }

This class picks out the values we need to create the RSA parameters that will be passed to the KeyChain API later. No we have everything in place to create those with this helper method:

private NSDictionary CreateRsaParams()
{
    IList<object> keys = new List<object>();
    IList<object> values = new List<object>();

    //creating the private key params
    keys.Add(IosConstants.Instance.KSecAttrApplicationTag);
    keys.Add(IosConstants.Instance.KSecAttrIsPermanent);
    keys.Add(IosConstants.Instance.KSecAttrAccessible);

    values.Add(NSData.FromString(_keyName, NSStringEncoding.UTF8));
    values.Add(NSNumber.FromBoolean(true));
    values.Add(IosConstants.Instance.KSecAccessibleWhenUnlocked);

    NSDictionary privateKeyAttributes = NSDictionary.FromObjectsAndKeys(values.ToArray(), keys.ToArray());

    keys.Clear();
    values.Clear();

    //creating the keychain entry params
    //no need for public key params, as it will be created from the private key once it is needed
    keys.Add(IosConstants.Instance.KSecAttrKeyType);
    keys.Add(IosConstants.Instance.KSecAttrKeySize);
    keys.Add(IosConstants.Instance.KSecPrivateKeyAttrs);

    values.Add(IosConstants.Instance.KSecAttrKeyTypeRSA);
    values.Add(NSNumber.FromInt32(this.KeySize));
    values.Add(privateKeyAttributes);

    return NSDictionary.FromObjectsAndKeys(values.ToArray(), keys.ToArray());
}

In order to use the SecKeyAPI to create a random key for us, we need to pass in a NSDictionarythat holds a list of private key attributes and is attached to a parent NSDictionary that holds it together with some other configuration values for the KeyChain API. If you want, you could also create a NSDictionary for the public key, but that is not needed for my implementation as I request it later from the private key (we’ll have a look on that as well).

Finally, let the OS create a private key

Now we have all our parameters in place, we are able to create a new private key by calling the SecKey.CreateRandomKey() method:

public bool CreatePrivateKey()
{
    Delete();
    var keyParams = CreateRsaParams();

    SecKey.CreateRandomKey(keyParams, out var keyCreationError);

    if (keyCreationError != null)
    {
        Debug.WriteLine($"{keyCreationError.LocalizedFailureReason}\n{keyCreationError.LocalizedDescription}");
    }

    return keyCreationError == null;
}

Like on Android, it is a good idea to call into the Delete() method before creating a new key (I’ll show you that method later). This makes sure your app uses just one key with the specified name. After that, we create a new random key with the help of the OS. Because we specified it to be a private key for RSA before, it will be exactly that. If there is an error, we will return false and print it in the Debug console.

Retrieving the private key

Now we have created the new private key, we are able to retrieve it like this:

public SecKey GetPrivateKey()
{
    var privateKey = SecKeyChain.QueryAsConcreteType(
        new SecRecord(SecKind.Key)
        {
            ApplicationTag = NSData.FromString(_keyName, NSStringEncoding.UTF8),
            KeyType = SecKeyType.RSA,
            Synchronizable = shouldSyncAcrossDevices
        },
        out var code);

    return code == SecStatusCode.Success ? privateKey as SecKey : null;
}

We are using the QueryAsConcreteType method to find our existing key in the Keychain. If the OS does not find the key, we are returning null. In this case, we would need to create a new key.

Retrieving the public key for encryption

Of course, we need a public key if we want to encrypt our data. Here is how to get this public key from the private key:

public SecKey GetPublicKey()
{
    return GetPrivateKey()?.GetPublicKey();
}

Really, that’s it. Even if we are not creating a public key explicitly when we are creating our private key, we are getting a valid public key for encryption from the GetPublicKeymethod, called on the private key instance.

Deleting the key pair

Like I said already earlier, sometimes we need to delete our encryption key(s). This little helper method does the job for us:

public bool Delete()
{
    var findExisting = new SecRecord(SecKind.Key)
    {
        ApplicationTag = NSData.FromString(_keyName, NSStringEncoding.UTF8),
        KeyType = SecKeyType.RSA,
        Synchronizable = _shouldSyncAcrossDevices
    };

    SecStatusCode code = SecKeyChain.Remove(findExisting);

    return code == SecStatusCode.Success;
}

This time, we are searching for a SecRecord with the kind key, and calling the Removemethod of the SecKeyChain API. Based on the status code, we finally return a bool that indicates if we were successful. Note: When we create a new key, (actually) I do not care about the status and just create a new one in my helper class. If we delete the key from another place, we are probably going to work with that status code.

As I did with the Android version, I did not create a demo project, but you can have a look at the full class in this Gist on GitHub.

Usage

Now that we have our helper in place, we are able to encrypt and decrypt data in a very easy way. First, we need to obtain a private and a public key:

var helper = new PlatformEncryptionKeyHelper("testKeyHelper");

if (!helper.KeysExist())
{
    helper.CreateKeyPair();
}

var privKey = helper.GetPrivateKey();
var pubKey = helper.GetPublicKey();

The encryption method needs to be called directly on the public key instance:

var textToCrypt = "this is just a plain test text that will be encrypted and decrypted";
pubKey.Encrypt(SecPadding.PKCS1, Encoding.UTF8.GetBytes(textToCrypt), out var encBytes);

For getting the plain value back, we need to call the decryption method on the private key instance:

privKey.Decrypt(SecPadding.PKCS1, encBytes, out var decBytes);
var decrypted = Encoding.UTF8.GetString(decBytes);

It may make sense to wrap these calls into helper methods, but you could also just use it like I did for demoing purposes. Just remember to use always the same padding method, otherwise you will not get any value back from the encrypted byte array.

Once again, if you need encryption of data in a Xamarin.Forms project, just extract an interface from the class or match it the interface you may already have extracted from the Android version. As I stated already before, every developer should use the right tools to encrypt data really securely in their apps. With that post, you now have also a starting point for your own Xamarin iOS implementation.

Like always, I hope this will be helpful for some of you. In my next post, we will have a look into the OS provided options for encryption and decryption on Windows 10 (UWP).

Until then, happy coding, everyone!

Posted by msicc in Dev Stories, iOS, Xamarin, 1 comment
Xamarin Android: asymmetric encryption without any user input or hardcoded values

Xamarin Android: asymmetric encryption without any user input or hardcoded values

The problem

Android is often said to be one of the most unsecure platforms one can use. This problem is home-made, as there is still a lot of fragmentation. There are thousand of models that do not get the latest updates and security patches, mostly because OEMs (seem to) not care (for different reasons, biggest reason is of course money). On the other side, there are still developers that save user names and passwords in plain text (which is the worst) or have hardcoded values in their code that make it way to easy to compromise encrypted data.

In the past, a lot of us developers made some of those mistakes. Be it because most of the popular samples around the web use hardcoded values (e. g. for the IV) or because of blindly copy & pasting from other websites or by using badly implemented libraries. Everyone should stop using these and use methods that are more secure. One of the most secure ways to do so is to use asymmetric encryption with a private/public key pair. The Android OS is doing a lot to help us generating such a key pair, and I am going to show you how to use it.

AndroidKeyStore

As the name already implies, Android uses the AndroidKeyStoreto keep keys secure. TheAndroidKeyStore is derived from the Java Security implementations and provides:

  • generation of keys and key pairs
  • key material that is maintained out of any application process
  • the key material can be bound to security hardware
  • additional usage limits are implemented in the OS
  • certificate store

Read more on that topic in the official Android documentation.

Encryption with a key pair explained

If you want to handle sensitive data securely in your app (and you should), there are only two ways. Either you are not saving them (which will often keep users not returning to your app or even uninstalling it just because they must type them in over and over again) or encrypt these data before saving it. One of the more secure ways to encrypt data is to use a private/public key pair, also known as asymmetric encryption (because you use one key for encryption and the other for decryption).

The private key is only known to the issuer of the key. In the case of Android, it is the OS or the security hardware that is in built into the device. The private key should always be private, and Android does handle this for us. The public key can be given to external parties (like us developers) to use them for decryption of sensitive data. The OS adds an additional layer and makes sure that only your app(s) are able to use the public key (aka ‘key access validation’).

Of course Google does not make all and everything about that encryption and validation process public (for obvious reasons).

In this post, I will focus on the creation of such a key pair, on how to retrieve a key from the AndroidKeyStoreand in the end, we will of course encrypt some data. My implementation is based on this article series, which provides a whole lot of explanation. If you want to know more about this topic, I absolutely recommend reading it. I will not go to deep into details, if you want to know more, once again, just read the articles linked above.

Let the OS create a key pair for you

The Android OS has two generators – a KeyGenerator and a KeyPairGenerator. The KeyGenerator provides a single key, while we will focus on the KeyPairGenerator, which will give us a brand new private/public key pair.

The first step is to initialize the KeyStore itself, which I am doing in the constructor of my helper class:

       public PlatformEncryptionKeyHelper(Context context, string keyName)
       {
           _context = context;
           _keyName = keyName.ToLowerInvariant();

           _androidKeyStore = KeyStore.GetInstance(KEYSTORE_NAME);
           _androidKeyStore.Load(null);
       }

The essential step here is to load the instance with null, otherwise all other operations will not work. You should also never change the keystore’s name unless you know exactly what you are doing.

Now that we have the KeyStore initialized, let’s go ahead and create a new key pair. As I support Android 5.0 (Lollipop) in my apps, I have also a fallback in place, as the current iteration is only available for device with Android 6 (Marshmallow) and above. Here is the code:

        public void CreateKeyPair()
        {
            DeleteKey();

            KeyPairGenerator keyGenerator =
                KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, KEYSTORE_NAME);

            if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr2 &&
                Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
            {
                var calendar = Calendar.GetInstance(_context.Resources.Configuration.Locale);
                var endDate = Calendar.GetInstance(_context.Resources.Configuration.Locale);
                endDate.Add(CalendarField.Year, 20);

                //this API is obsolete after Android M, but I am supporting Android L
#pragma warning disable 618
                var builder = new KeyPairGeneratorSpec.Builder(_context)
#pragma warning restore 618
                              .SetAlias(_keyName).SetSerialNumber(BigInteger.One)
                              .SetSubject(new X500Principal($"CN={_keyName} CA Certificate"))
                              .SetStartDate(calendar.Time)
                              .SetEndDate(endDate.Time).SetKeySize(KeySize);

                keyGenerator.Initialize(builder.Build());
            }
            else if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                var builder =
                    new KeyGenParameterSpec.Builder(_keyName, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
                        .SetBlockModes(KeyProperties.BlockModeEcb)
                        .SetEncryptionPaddings(KeyProperties.EncryptionPaddingRsaPkcs1)
                        .SetRandomizedEncryptionRequired(false).SetKeySize(KeySize);

                keyGenerator.Initialize(builder.Build());
            }

            keyGenerator.GenerateKeyPair();
        }

As you can see, the creation of such a key pair is way easier with Android 6 (Marshmallow) and above. I will focus on this part, details for the fallback solution can be found in the articles I linked above. I am requesting a RSA key pair for encryption and decryption, which needs to be specified explicitly. We are using the so called ‘ Electronic Codebook’ encryption mode, which will cut the data to encrypt into blocks that will be encrypted. Also important: the key’s size. A bigger key means more security, but also more time for operations done with it. Android defaults to a key size of 2048 bits, which provides a good average of security and execution time. With this method in place, we are already able to create a brand new key pair.

Note: The DeleteKey()method call beforehand just makes sure we have only one valid key pair with that name available. I am also following Google’s recommendations by calling it before creating a new key.

Retrieving the public key for encryption

Now that the AndroidKeyStoreholds a key pair for us, let us have a look on how to retrieve the public key, which is used for encryption:

public IKey GetPublicKey()
{
    if (!_androidKeyStore.ContainsAlias(_keyName))
        return null;

    return _androidKeyStore.GetCertificate(_keyName)?.PublicKey;
}

Android internally creates a self signed certificate for the key pair (that’s why we had to perform this action manually before Android 6 (Marshmallow). The API makes this visible to us in the case of the retrieval of the public key. Xamarin provides the IKey interface, which is once again inherited from the Java Security APIs.

Retrieving the private key for decryption

Of course, we want to decrypt the data we encrypted at some point. That is as easy as getting the public key:

public IKey GetPrivateKey()
{
    if (!_androidKeyStore.ContainsAlias(_keyName))
        return null;

    return _androidKeyStore.GetKey(_keyName, null);
}

As we did not set a password during the key pair creation, we are passing null in here to get our private key.

Deleting a key pair

There may be situations where you want to delete a key. The AndroidKeyStore has an API available for that as well. You may guess it, it is also very easy to use:

public bool DeleteKey()
{
    if (!_androidKeyStore.ContainsAlias(_keyName))
        return false;

    _androidKeyStore.DeleteEntry(_keyName);
    return true;
}

Usage

As you probably remember, I created a helper class for handling all things related to the AndroidKeyStore. Let’s have a look on how to encrypt and decrypt a string with the help of this class.

_encryptionKeyHelper = new PlatformEncryptionKeyHelper(Application.Context, KeyStoreName);
_encryptionKeyHelper.CreateKeyPair();

_privateKey = _encryptionKeyHelper.GetPrivateKey();
_publicKey = _encryptionKeyHelper.GetPublicKey();

After instantiating the helper class, we use the CreateKeyPair()method to get a key pair. In the full class I will share later in this post, I have another helper that will check if the key already exists. You can use this class to step over the creation part if there is already a key pair.

Now let’s see how encryption works:

//we used these values to create the keys
//now we need to tell the OS to use the same values during encryption/decryption
var transformation = "RSA/ECB/PKCS1Padding";

var stringToEncrypt = "This is a simple string for demo purposes only. Nothing special here.";

var cipher = Cipher.GetInstance(transformation);
cipher.Init(CipherMode.EncryptMode, _publicKey);

var encryptedData = cipher.DoFinal(Encoding.UTF8.GetBytes(stringToEncrypt));

We are using the Cipher class provided by Xamarin, which inherits from the Java Crypto API. The transformation string consists of “algorithm/mode/padding” and needs to be passed to the cipher instance. After specifying that we want to encrypt with the public key, the DoFinalmethod encrypts the string and returns it as a byte array, which can be saved pretty easy.

Decryption works in a similar way:

var transformation = "RSA/ECB/PKCS1Padding"; 

var cipher = Cipher.GetInstance(transformation);
cipher.Init(CipherMode.DecryptMode, _privateKey);

var decryptedBytes = cipher.DoFinal(encyrptedData);
var finalString = Encoding.UTF8.GetString(decryptedBytes);

Once again, we are using the Cipherclass. Remember to initialize the cipher instance once again, because we are using now the decryption mode. The DoFinalmethod will decrypt the encrypted byte array, which can be turned into a string once again.

I did not create a sample project this time. However, the full helper class is available here on my GitHub account as Gist.

Xamarin.Forms tipp: You can make this class available by extracting an interface from it and use the DependencyService to get access from your forms project if necessary.

Conclusion

The security of your user’s data should always be something you are concerned about. With this little helper, we are using the OS (and in some cases also the device) to secure data in your Xamarin.Android app. Sadly, a lot of samples require user interaction or even use some hardcoded values. This should not be used in a production app. Feel free to use my helper class as a starting point.

As always, I hope this post is helpful for some of you. In the next post, I will show you how to use a similar mechanism in your Xamarin.iOS app.

Until then, happy coding, everyone!

 

Posted by msicc in Android, Dev Stories, Xamarin, 9 comments