Skip to content

Instantly share code, notes, and snippets.

@MSiccDev
Last active January 19, 2022 05:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MSiccDev/6c3b8bc532192cd3abb41982198e2373 to your computer and use it in GitHub Desktop.
Save MSiccDev/6c3b8bc532192cd3abb41982198e2373 to your computer and use it in GitHub Desktop.
Helper class to securely create encryption keys without the need of user interaction on Android. There are way to much samples that use some hardcoded values which could be compromised. The keys of this class are only available for usage within the app that created them. Plus, if the app gets uninstalled, the keys get deleted.
using Android.Content;
using Android.OS;
using Android.Security;
using Android.Security.Keystore;
using Java.Math;
using Java.Security;
using Javax.Security.Auth.X500;
using Calendar = Android.Icu.Util.Calendar;
using CalendarField = Android.Icu.Util.CalendarField;
namespace [YOURNAMESPACEHERE]
{
public class PlatformEncryptionKeyHelper
{
//do not change this name, this is where the keys have to be stored on Android
static readonly string KEYSTORE_NAME = "AndroidKeyStore";
private readonly KeyStore _androidKeyStore;
private readonly Context _context;
private readonly string _keyName;
//Supported sizes: 512, 768, 1024, 2048, 3072, 4096
//default is 2048
//Higher value means longer processing time!
public int KeySize { get; set; } = 2048;
public PlatformEncryptionKeyHelper(Context context, string keyName)
{
_context = context;
_keyName = keyName.ToLowerInvariant();
_androidKeyStore = KeyStore.GetInstance(KEYSTORE_NAME);
_androidKeyStore.Load(null);
}
public bool DeleteKey()
{
if (!_androidKeyStore.ContainsAlias(_keyName))
return false;
_androidKeyStore.DeleteEntry(_keyName);
return true;
}
public bool KeysExist()
{
return _androidKeyStore.ContainsAlias(_keyName);
}
public IKey GetPrivateKey()
{
if (!_androidKeyStore.ContainsAlias(_keyName))
return null;
return _androidKeyStore.GetKey(_keyName, null);
}
public IKey GetPublicKey()
{
if (!_androidKeyStore.ContainsAlias(_keyName))
return null;
return _androidKeyStore.GetCertificate(_keyName)?.PublicKey;
}
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, so I need this
#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();
}
}
}
@tblack2200
Copy link

You mentioned you could use DependencyService to use this in Xamarin Forms. I can create the interface and the class for Android\IOS. However, how would you handle the interface for GetPrivateKey() and GetPublicKey() since Android returns IKey and IOS returns SecKey?

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment