implemented test for fingerprint auth

This commit is contained in:
Philipp Crocoll 2015-12-30 16:08:31 +01:00
parent 5f741f2d5f
commit 6c8afec8b5
6 changed files with 286 additions and 104 deletions

View File

@ -9,8 +9,11 @@ using Android.Hardware.Fingerprints;
using Android.OS;
using Android.Security.Keystore;
using Android.Preferences;
using Android.Util;
using Android.Widget;
using Java.IO;
using Java.Security.Cert;
using Javax.Crypto.Spec;
namespace keepass2android
{
@ -25,7 +28,7 @@ namespace keepass2android
public FingerprintManager FingerprintManager
{
get { return (FingerprintManager) Context.GetSystemService("FingerprintManager"); }
get { return (FingerprintManager) Context.GetSystemService(Context.FingerprintService); }
}
public KeyguardManager KeyguardManager
@ -103,8 +106,9 @@ namespace keepass2android
}
}
public class FingerprintEncryptionModule: FingerprintManager.AuthenticationCallback
public abstract class FingerprintCrypt: FingerprintManager.AuthenticationCallback
{
protected const string FailedToInitCipher = "Failed to init Cipher";
public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString)
{
_callback.OnAuthenticationError(errorCode, errString);
@ -126,37 +130,191 @@ namespace keepass2android
_callback.OnAuthenticationSucceeded(result);
}
private readonly FingerprintModule _fingerprint;
private readonly string _keyId;
private Cipher _cipher;
protected readonly string _keyId;
protected Cipher _cipher;
private bool _selfCancelled;
private CancellationSignal _cancellationSignal;
private FingerprintManager.CryptoObject _cryptoObject;
protected FingerprintManager.CryptoObject _cryptoObject;
private FingerprintManager.AuthenticationCallback _callback;
protected KeyStore _keystore;
private FingerprintManager _fingerprintManager;
public FingerprintEncryptionModule(FingerprintModule fingerprint, string keyId)
public FingerprintCrypt(FingerprintModule fingerprint, string keyId)
{
_fingerprint = fingerprint;
_keyId = keyId;
_cipher = fingerprint.Cipher;
_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
{
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)
{
_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));
}
public string DecryptStored(string prefKey, Context context)
{
string enc = PreferenceManager.GetDefaultSharedPreferences(context).GetString(prefKey, null);
Toast.MakeText(context, "len="+Base64.Decode(enc, 0).Length.ToString(), ToastLength.Long).Show();
return Decrypt(enc);
}
}
public class FingerprintEncryption : FingerprintCrypt
{
private KeyGenerator _keyGen;
public FingerprintEncryption(FingerprintModule fingerprint, string keyId) :
base(fingerprint, keyId)
{
_keyGen = fingerprint.KeyGenerator;
CreateKey();
}
/// <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()
{
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try
{
_fingerprint.Keystore.Load(null);
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
_fingerprint.KeyGenerator.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
_keystore.Load(null);
_keyGen.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeCbc)
// Require the user to authenticate with a fingerprint to authorize every use
@ -164,7 +322,7 @@ namespace keepass2android
.SetUserAuthenticationRequired(true)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7)
.Build());
_fingerprint.KeyGenerator.GenerateKey();
_keyGen.GenerateKey();
}
catch (NoSuchAlgorithmException e)
{
@ -184,13 +342,14 @@ namespace keepass2android
}
}
public bool InitCipher()
public override bool InitCipher()
{
try
{
_fingerprint.Keystore.Load(null);
var key = _fingerprint.Keystore.GetKey(GetAlias(_keyId), null);
_keystore.Load(null);
var key = _keystore.GetKey(GetAlias(_keyId), null);
_cipher.Init(CipherMode.EncryptMode, key);
_cryptoObject = new FingerprintManager.CryptoObject(_cipher);
return true;
}
@ -200,69 +359,28 @@ namespace keepass2android
}
catch (KeyStoreException e)
{
throw new RuntimeException("Failed to init Cipher", e);
throw new RuntimeException(FailedToInitCipher, e);
}
catch (CertificateException e)
{
throw new RuntimeException("Failed to init Cipher", e);
throw new RuntimeException(FailedToInitCipher, e);
}
catch (UnrecoverableKeyException e)
{
throw new RuntimeException("Failed to init Cipher", e);
throw new RuntimeException(FailedToInitCipher, e);
}
catch (IOException e)
{
throw new RuntimeException("Failed to init Cipher", e);
throw new RuntimeException(FailedToInitCipher, e);
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("Failed to init Cipher", e);
throw new RuntimeException(FailedToInitCipher, e);
}
catch (InvalidKeyException e)
{
throw new RuntimeException("Failed to init Cipher", e);
throw new RuntimeException(FailedToInitCipher, e);
}
}
private string GetAlias(string keyId)
{
return "keepass2android." + keyId;
}
public bool IsFingerprintAuthAvailable
{
get
{
return _fingerprint.FingerprintManager.IsHardwareDetected
&& _fingerprint.FingerprintManager.HasEnrolledFingerprints;
}
}
public void StartListening(FingerprintManager.AuthenticationCallback callback)
{
if (!IsFingerprintAuthAvailable)
return;
_cancellationSignal = new CancellationSignal();
_selfCancelled = false;
_callback = callback;
_fingerprint.FingerprintManager.Authenticate(_cryptoObject, _cancellationSignal, 0 /* flags */, this, null);
}
public void StopListening()
{
if (_cancellationSignal != null)
{
_selfCancelled = true;
_cancellationSignal.Cancel();
_cancellationSignal = null;
}
}
public void Encrypt(string textToEncrypt)
{
_cipher.DoFinal(MemUtil)
}
}
}

View File

@ -81,7 +81,7 @@
<ItemGroup>
<XamarinComponentReference Include="xamandroidsupportv7appcompat">
<Visible>False</Visible>
<Version>21.0.3.0</Version>
<Version>22.2.0.0</Version>
</XamarinComponentReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />

View File

@ -47,14 +47,22 @@
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
<Reference Include="Xamarin.Android.Support.v4">
<HintPath>..\packages\Xamarin.Android.Support.v4.23.1.1.0\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Android.Support.v7.AppCompat">
<HintPath>..\packages\Xamarin.Android.Support.v7.AppCompat.23.1.1.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="FingerprintModule.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="GettingStarted.Xamarin" />
<None Include="packages.config" />
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
@ -70,6 +78,12 @@
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<XamarinComponentReference Include="xamandroidsupportv7appcompat">
<Visible>False</Visible>
<Version>21.0.3.0</Version>
</XamarinComponentReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -14,10 +14,11 @@ using keepass2android;
namespace FingerprintTest
{
[Activity(Label = "FingerprintTest", MainLauncher = true, Icon = "@drawable/icon")]
[Activity(Label = "FingerprintTest", MainLauncher = true, Icon = "@drawable/icon", Theme = "@style/Theme.AppCompat")]
public class MainActivity : AppCompatActivity
{
int count = 1;
private string _keyId = "mykeyid";
const int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
protected override void OnCreate(Bundle bundle)
@ -32,26 +33,61 @@ namespace FingerprintTest
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Visibility = ViewStates.Gone;
RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FINGERPRINT_PERMISSION_REQUEST_CODE);
}
string prefKey = "enc_pref_key";
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && grantResults[0] == Android.Content.PM.Permission.Granted)
{
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Visibility = ViewStates.Visible;
button.Enabled = true;
Button encButton = FindViewById<Button>(Resource.Id.MyButton);
Button decButton = FindViewById<Button>(Resource.Id.Decrypt);
encButton.Visibility = ViewStates.Visible;
encButton.Enabled = true;
var fingerprint = new keepass2android.FingerprintModule(this);
button.Click += (sender, args) =>
encButton.Click += (sender, args) =>
{
var fingerprintEnc = new FingerprintEncryption(fingerprint, _keyId);
if (fingerprintEnc.InitCipher())
{
fingerprintEnc.StartListening(new EncryptionCallback(this, fingerprintEnc, prefKey));
}
else
{
Toast.MakeText(this, "Error initiating cipher", ToastLength.Long).Show();
}
};
decButton.Click += (sender, args) =>
{
var fingerprintDec = new FingerprintDecryption(fingerprint, _keyId, this, prefKey);
if (fingerprintDec.InitCipher())
{
fingerprintDec.StartListening(new DecryptionCallback(this, fingerprintDec,prefKey));
}
else
{
Toast.MakeText(this, "Error initiating cipher", ToastLength.Long).Show();
}
};
if (!fingerprint.KeyguardManager.IsKeyguardSecure)
{
button.Enabled = false;
encButton.Enabled = false;
// Show a message that the user hasn't set up a fingerprint or lock screen.
Toast.MakeText(this, "Secure lock screen hasn't set up.\n"
+ "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint", ToastLength.Long).Show();
@ -61,45 +97,55 @@ namespace FingerprintTest
if (!fingerprint.FingerprintManager.HasEnrolledFingerprints)
{
button.Enabled = false;
encButton.Enabled = false;
// This happens when no fingerprints are registered.
Toast.MakeText(this, "Go to 'Settings -> Security -> Fingerprint' " +
"and register at least one fingerprint", ToastLength.Long).Show();
return;
}
var fingerprintEnc = new FingerprintEncryptionModule(fingerprint, "abc");
if (fingerprintEnc.InitCipher())
{
fingerprintEnc.StartListening(new EncryptionCallback(this, fingerprintEnc));
}
else
{
Toast.MakeText(this, "Error initiating cipher", ToastLength.Long).Show();
}
}
}
}
public class EncryptionCallback : FingerprintManager.AuthenticationCallback
public class DecryptionCallback : FingerprintManager.AuthenticationCallback
{
private readonly FingerprintEncryptionModule _fingerprintEnc;
private readonly Context _context;
private readonly FingerprintDecryption _fingerprintDec;
private readonly string _prefKey;
public EncryptionCallback(Context context, FingerprintEncryptionModule fingerprintEnc)
public DecryptionCallback(Context context, FingerprintDecryption fingerprintDec, string prefKey)
{
_fingerprintEnc = fingerprintEnc;
_context = context;
_fingerprintDec = fingerprintDec;
_prefKey = prefKey;
}
public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{
_fingerprintEnc.Encrypt("abc");
var edit = PreferenceManager.GetDefaultSharedPreferences(Application.Context).Edit();
edit.PutString("encrypted", );
var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
Toast.MakeText(_context, _fingerprintDec.DecryptStored(_prefKey, _context), ToastLength.Long).Show();
}
}
public class EncryptionCallback : FingerprintManager.AuthenticationCallback
{
private readonly FingerprintCrypt _fingerprintEnc;
private readonly string _prefKey;
public EncryptionCallback(Context context, FingerprintCrypt fingerprintEnc, string prefKey)
{
_fingerprintEnc = fingerprintEnc;
_prefKey = prefKey;
}
public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{
_fingerprintEnc.StoreEncrypted("some töst data", _prefKey, Application.Context);
}
}
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="FingerprintTest.FingerprintTest" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application android:label="FingerprintTest" android:icon="@drawable/Icon"></application>
</manifest>

View File

@ -2,12 +2,15 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:id="@+id/MyButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/Hello"
/>
</LinearLayout>
android:layout_height="fill_parent">
<Button
android:id="@+id/MyButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/Hello" />
<Button
android:id="@+id/Decrypt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Decrypt" />
</LinearLayout>