2015-12-29 08:18:09 -05:00
|
|
|
|
using System;
|
|
|
|
|
using Android.Content;
|
|
|
|
|
using Javax.Crypto;
|
|
|
|
|
using Java.Security;
|
|
|
|
|
using Java.Lang;
|
|
|
|
|
using Android.Views.InputMethods;
|
|
|
|
|
using Android.App;
|
|
|
|
|
using Android.Hardware.Fingerprints;
|
|
|
|
|
using Android.OS;
|
|
|
|
|
using Android.Security.Keystore;
|
|
|
|
|
using Android.Preferences;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
using Android.Util;
|
|
|
|
|
using Android.Widget;
|
2015-12-29 08:18:09 -05:00
|
|
|
|
using Java.IO;
|
|
|
|
|
using Java.Security.Cert;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
using Javax.Crypto.Spec;
|
2015-12-29 08:18:09 -05:00
|
|
|
|
|
|
|
|
|
namespace keepass2android
|
|
|
|
|
{
|
|
|
|
|
public class FingerprintModule
|
|
|
|
|
{
|
|
|
|
|
public Context Context { get; set; }
|
|
|
|
|
|
|
|
|
|
public FingerprintModule (Context context)
|
|
|
|
|
{
|
|
|
|
|
Context = context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FingerprintManager FingerprintManager
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
get { return (FingerprintManager) Context.GetSystemService(Context.FingerprintService); }
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public KeyguardManager KeyguardManager
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (KeyguardManager) Context.GetSystemService("keyguard");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public KeyStore Keystore
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return KeyStore.GetInstance("AndroidKeyStore");
|
|
|
|
|
}
|
|
|
|
|
catch (KeyStoreException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Failed to get an instance of KeyStore", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public KeyGenerator KeyGenerator
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, "AndroidKeyStore");
|
|
|
|
|
}
|
|
|
|
|
catch (NoSuchAlgorithmException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
|
|
|
|
|
}
|
|
|
|
|
catch (NoSuchProviderException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Cipher Cipher
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return Cipher.GetInstance(KeyProperties.KeyAlgorithmAes + "/"
|
|
|
|
|
+ KeyProperties.BlockModeCbc + "/"
|
|
|
|
|
+ KeyProperties.EncryptionPaddingPkcs7);
|
|
|
|
|
}
|
|
|
|
|
catch (NoSuchAlgorithmException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Failed to get an instance of Cipher", e);
|
|
|
|
|
}
|
|
|
|
|
catch (NoSuchPaddingException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Failed to get an instance of Cipher", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InputMethodManager InputMethodManager
|
|
|
|
|
{
|
|
|
|
|
get { return (InputMethodManager) Context.GetSystemService(Context.InputMethodService); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ISharedPreferences SharedPreferences
|
|
|
|
|
{
|
|
|
|
|
get { return PreferenceManager.GetDefaultSharedPreferences(Context); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-30 10:08:31 -05:00
|
|
|
|
public abstract class FingerprintCrypt: FingerprintManager.AuthenticationCallback
|
2015-12-29 08:18:09 -05:00
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
protected const string FailedToInitCipher = "Failed to init Cipher";
|
2015-12-29 08:18:09 -05:00
|
|
|
|
public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString)
|
|
|
|
|
{
|
|
|
|
|
_callback.OnAuthenticationError(errorCode, errString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnAuthenticationFailed()
|
|
|
|
|
{
|
|
|
|
|
if (!_selfCancelled)
|
|
|
|
|
_callback.OnAuthenticationFailed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnAuthenticationHelp(FingerprintState helpCode, ICharSequence helpString)
|
|
|
|
|
{
|
|
|
|
|
_callback.OnAuthenticationHelp(helpCode, helpString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
|
|
|
|
|
{
|
|
|
|
|
_callback.OnAuthenticationSucceeded(result);
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-30 10:08:31 -05:00
|
|
|
|
protected readonly string _keyId;
|
|
|
|
|
|
|
|
|
|
protected Cipher _cipher;
|
2015-12-29 08:18:09 -05:00
|
|
|
|
private bool _selfCancelled;
|
|
|
|
|
private CancellationSignal _cancellationSignal;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
protected FingerprintManager.CryptoObject _cryptoObject;
|
2015-12-29 08:18:09 -05:00
|
|
|
|
private FingerprintManager.AuthenticationCallback _callback;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
protected KeyStore _keystore;
|
|
|
|
|
|
|
|
|
|
private FingerprintManager _fingerprintManager;
|
2015-12-29 08:18:09 -05:00
|
|
|
|
|
2015-12-30 10:08:31 -05:00
|
|
|
|
public FingerprintCrypt(FingerprintModule fingerprint, string keyId)
|
2015-12-29 08:18:09 -05:00
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
|
2015-12-29 08:18:09 -05:00
|
|
|
|
_keyId = keyId;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
|
2015-12-29 08:18:09 -05:00
|
|
|
|
_cipher = fingerprint.Cipher;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
_keystore = fingerprint.Keystore;
|
|
|
|
|
|
|
|
|
|
_fingerprintManager = fingerprint.FingerprintManager;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public abstract bool InitCipher();
|
|
|
|
|
protected static string GetAlias(string keyId)
|
|
|
|
|
{
|
|
|
|
|
return "keepass2android." + keyId;
|
|
|
|
|
}
|
|
|
|
|
protected static string GetIvPrefKey(string prefKey)
|
|
|
|
|
{
|
|
|
|
|
return prefKey + "_iv";
|
|
|
|
|
}
|
|
|
|
|
public bool IsFingerprintAuthAvailable
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _fingerprintManager.IsHardwareDetected
|
|
|
|
|
&& _fingerprintManager.HasEnrolledFingerprints;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartListening(FingerprintManager.AuthenticationCallback callback)
|
|
|
|
|
{
|
|
|
|
|
if (!IsFingerprintAuthAvailable)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_cancellationSignal = new CancellationSignal();
|
|
|
|
|
_selfCancelled = false;
|
|
|
|
|
_callback = callback;
|
|
|
|
|
_fingerprintManager.Authenticate(_cryptoObject, _cancellationSignal, 0 /* flags */, this, null);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StopListening()
|
|
|
|
|
{
|
|
|
|
|
if (_cancellationSignal != null)
|
|
|
|
|
{
|
|
|
|
|
_selfCancelled = true;
|
|
|
|
|
_cancellationSignal.Cancel();
|
|
|
|
|
_cancellationSignal = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Encrypt(string textToEncrypt)
|
|
|
|
|
{
|
|
|
|
|
return Base64.EncodeToString(_cipher.DoFinal(System.Text.Encoding.UTF8.GetBytes(textToEncrypt)), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void StoreEncrypted(string textToEncrypt, string prefKey, Context context)
|
|
|
|
|
{
|
|
|
|
|
var edit = PreferenceManager.GetDefaultSharedPreferences(context).Edit();
|
|
|
|
|
|
|
|
|
|
edit.PutString(prefKey, Encrypt(textToEncrypt));
|
|
|
|
|
edit.PutString(GetIvPrefKey(prefKey), Base64.EncodeToString(CipherIv,0));
|
|
|
|
|
edit.Commit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private byte[] CipherIv
|
|
|
|
|
{
|
|
|
|
|
get { return _cipher.GetIV(); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class FingerprintDecryption : FingerprintCrypt
|
|
|
|
|
{
|
2015-12-30 10:17:55 -05:00
|
|
|
|
private readonly Context _context;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
private readonly byte[] _iv;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public FingerprintDecryption(FingerprintModule fingerprint, string keyId, byte[] iv) : base(fingerprint, keyId)
|
|
|
|
|
{
|
|
|
|
|
_iv = iv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FingerprintDecryption(FingerprintModule fingerprint, string keyId, Context context, string prefKey)
|
|
|
|
|
: base(fingerprint, keyId)
|
|
|
|
|
{
|
2015-12-30 10:17:55 -05:00
|
|
|
|
_context = context;
|
2015-12-30 10:08:31 -05:00
|
|
|
|
_iv = Base64.Decode(PreferenceManager.GetDefaultSharedPreferences(context).GetString(GetIvPrefKey(prefKey), null), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool InitCipher()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_keystore.Load(null);
|
|
|
|
|
var key = _keystore.GetKey(GetAlias(_keyId), null);
|
|
|
|
|
var ivParams = new IvParameterSpec(_iv);
|
|
|
|
|
_cipher.Init(CipherMode.DecryptMode, key, ivParams);
|
|
|
|
|
|
|
|
|
|
_cryptoObject = new FingerprintManager.CryptoObject(_cipher);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (KeyPermanentlyInvalidatedException)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
catch (KeyStoreException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
|
|
|
|
}
|
|
|
|
|
catch (CertificateException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
|
|
|
|
}
|
|
|
|
|
catch (UnrecoverableKeyException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
|
|
|
|
}
|
|
|
|
|
catch (IOException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
|
|
|
|
}
|
|
|
|
|
catch (NoSuchAlgorithmException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
|
|
|
|
}
|
|
|
|
|
catch (InvalidKeyException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public string Decrypt(string encryted)
|
|
|
|
|
{
|
|
|
|
|
byte[] encryptedBytes = Base64.Decode(encryted, 0);
|
|
|
|
|
return System.Text.Encoding.UTF8.GetString(_cipher.DoFinal(encryptedBytes));
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-30 10:17:55 -05:00
|
|
|
|
public string DecryptStored(string prefKey)
|
2015-12-30 10:08:31 -05:00
|
|
|
|
{
|
2015-12-30 10:17:55 -05:00
|
|
|
|
string enc = PreferenceManager.GetDefaultSharedPreferences(_context).GetString(prefKey, null);
|
2015-12-30 10:08:31 -05:00
|
|
|
|
return Decrypt(enc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class FingerprintEncryption : FingerprintCrypt
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
private KeyGenerator _keyGen;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public FingerprintEncryption(FingerprintModule fingerprint, string keyId) :
|
|
|
|
|
base(fingerprint, keyId)
|
|
|
|
|
{
|
|
|
|
|
_keyGen = fingerprint.KeyGenerator;
|
2015-12-29 08:18:09 -05:00
|
|
|
|
CreateKey();
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-30 10:08:31 -05:00
|
|
|
|
|
2015-12-29 08:18:09 -05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a symmetric key in the Android Key Store which can only be used after the user
|
|
|
|
|
/// has authenticated with fingerprint.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void CreateKey()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
_keystore.Load(null);
|
|
|
|
|
_keyGen.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
|
2015-12-29 08:18:09 -05:00
|
|
|
|
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
|
|
|
|
|
.SetBlockModes(KeyProperties.BlockModeCbc)
|
|
|
|
|
// Require the user to authenticate with a fingerprint to authorize every use
|
|
|
|
|
// of the key
|
|
|
|
|
.SetUserAuthenticationRequired(true)
|
|
|
|
|
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7)
|
|
|
|
|
.Build());
|
2015-12-30 10:08:31 -05:00
|
|
|
|
_keyGen.GenerateKey();
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
catch (NoSuchAlgorithmException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
catch (InvalidAlgorithmParameterException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
catch (CertificateException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
catch (IOException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-30 10:08:31 -05:00
|
|
|
|
public override bool InitCipher()
|
2015-12-29 08:18:09 -05:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
_keystore.Load(null);
|
|
|
|
|
var key = _keystore.GetKey(GetAlias(_keyId), null);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
_cipher.Init(CipherMode.EncryptMode, key);
|
2015-12-30 10:08:31 -05:00
|
|
|
|
|
2015-12-29 08:18:09 -05:00
|
|
|
|
_cryptoObject = new FingerprintManager.CryptoObject(_cipher);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (KeyPermanentlyInvalidatedException)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
catch (KeyStoreException e)
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
catch (CertificateException e)
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
catch (UnrecoverableKeyException e)
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
catch (IOException e)
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
catch (NoSuchAlgorithmException e)
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
catch (InvalidKeyException e)
|
|
|
|
|
{
|
2015-12-30 10:08:31 -05:00
|
|
|
|
throw new RuntimeException(FailedToInitCipher, e);
|
2015-12-29 08:18:09 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|