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.OS;
using Android.Security.Keystore; using Android.Security.Keystore;
using Android.Preferences; using Android.Preferences;
using Android.Util;
using Android.Widget;
using Java.IO; using Java.IO;
using Java.Security.Cert; using Java.Security.Cert;
using Javax.Crypto.Spec;
namespace keepass2android namespace keepass2android
{ {
@@ -25,7 +28,7 @@ namespace keepass2android
public FingerprintManager FingerprintManager public FingerprintManager FingerprintManager
{ {
get { return (FingerprintManager) Context.GetSystemService("FingerprintManager"); } get { return (FingerprintManager) Context.GetSystemService(Context.FingerprintService); }
} }
public KeyguardManager KeyguardManager 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) public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString)
{ {
_callback.OnAuthenticationError(errorCode, errString); _callback.OnAuthenticationError(errorCode, errString);
@@ -126,37 +130,191 @@ namespace keepass2android
_callback.OnAuthenticationSucceeded(result); _callback.OnAuthenticationSucceeded(result);
} }
private readonly FingerprintModule _fingerprint; protected readonly string _keyId;
private readonly string _keyId;
private Cipher _cipher; protected Cipher _cipher;
private bool _selfCancelled; private bool _selfCancelled;
private CancellationSignal _cancellationSignal; private CancellationSignal _cancellationSignal;
private FingerprintManager.CryptoObject _cryptoObject; protected FingerprintManager.CryptoObject _cryptoObject;
private FingerprintManager.AuthenticationCallback _callback; 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; _keyId = keyId;
_cipher = fingerprint.Cipher; _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(); CreateKey();
} }
/// <summary> /// <summary>
/// Creates a symmetric key in the Android Key Store which can only be used after the user /// Creates a symmetric key in the Android Key Store which can only be used after the user
/// has authenticated with fingerprint. /// has authenticated with fingerprint.
/// </summary> /// </summary>
private void CreateKey() 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 try
{ {
_fingerprint.Keystore.Load(null); _keystore.Load(null);
// Set the alias of the entry in Android KeyStore where the key will appear _keyGen.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
// and the constrains (purposes) in the constructor of the Builder
_fingerprint.KeyGenerator.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt) KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeCbc) .SetBlockModes(KeyProperties.BlockModeCbc)
// Require the user to authenticate with a fingerprint to authorize every use // Require the user to authenticate with a fingerprint to authorize every use
@@ -164,7 +322,7 @@ namespace keepass2android
.SetUserAuthenticationRequired(true) .SetUserAuthenticationRequired(true)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7) .SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7)
.Build()); .Build());
_fingerprint.KeyGenerator.GenerateKey(); _keyGen.GenerateKey();
} }
catch (NoSuchAlgorithmException e) catch (NoSuchAlgorithmException e)
{ {
@@ -184,13 +342,14 @@ namespace keepass2android
} }
} }
public bool InitCipher() public override bool InitCipher()
{ {
try try
{ {
_fingerprint.Keystore.Load(null); _keystore.Load(null);
var key = _fingerprint.Keystore.GetKey(GetAlias(_keyId), null); var key = _keystore.GetKey(GetAlias(_keyId), null);
_cipher.Init(CipherMode.EncryptMode, key); _cipher.Init(CipherMode.EncryptMode, key);
_cryptoObject = new FingerprintManager.CryptoObject(_cipher); _cryptoObject = new FingerprintManager.CryptoObject(_cipher);
return true; return true;
} }
@@ -200,69 +359,28 @@ namespace keepass2android
} }
catch (KeyStoreException e) catch (KeyStoreException e)
{ {
throw new RuntimeException("Failed to init Cipher", e); throw new RuntimeException(FailedToInitCipher, e);
} }
catch (CertificateException e) catch (CertificateException e)
{ {
throw new RuntimeException("Failed to init Cipher", e); throw new RuntimeException(FailedToInitCipher, e);
} }
catch (UnrecoverableKeyException e) catch (UnrecoverableKeyException e)
{ {
throw new RuntimeException("Failed to init Cipher", e); throw new RuntimeException(FailedToInitCipher, e);
} }
catch (IOException e) catch (IOException e)
{ {
throw new RuntimeException("Failed to init Cipher", e); throw new RuntimeException(FailedToInitCipher, e);
} }
catch (NoSuchAlgorithmException e) catch (NoSuchAlgorithmException e)
{ {
throw new RuntimeException("Failed to init Cipher", e); throw new RuntimeException(FailedToInitCipher, e);
} }
catch (InvalidKeyException 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> <ItemGroup>
<XamarinComponentReference Include="xamandroidsupportv7appcompat"> <XamarinComponentReference Include="xamandroidsupportv7appcompat">
<Visible>False</Visible> <Visible>False</Visible>
<Version>21.0.3.0</Version> <Version>22.2.0.0</Version>
</XamarinComponentReference> </XamarinComponentReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />

View File

@@ -47,14 +47,22 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" /> <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>
<ItemGroup> <ItemGroup>
<Compile Include="FingerprintModule.cs" />
<Compile Include="MainActivity.cs" /> <Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" /> <Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="GettingStarted.Xamarin" /> <None Include="GettingStarted.Xamarin" />
<None Include="packages.config" />
<None Include="Resources\AboutResources.txt" /> <None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" /> <None Include="Assets\AboutAssets.txt" />
</ItemGroup> </ItemGroup>
@@ -70,6 +78,12 @@
<ItemGroup> <ItemGroup>
<None Include="Properties\AndroidManifest.xml" /> <None Include="Properties\AndroidManifest.xml" />
</ItemGroup> </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" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -14,10 +14,11 @@ using keepass2android;
namespace FingerprintTest 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 public class MainActivity : AppCompatActivity
{ {
int count = 1; int count = 1;
private string _keyId = "mykeyid";
const int FINGERPRINT_PERMISSION_REQUEST_CODE = 0; const int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
protected override void OnCreate(Bundle bundle) protected override void OnCreate(Bundle bundle)
@@ -32,26 +33,61 @@ namespace FingerprintTest
Button button = FindViewById<Button>(Resource.Id.MyButton); Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Visibility = ViewStates.Gone; button.Visibility = ViewStates.Gone;
RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FINGERPRINT_PERMISSION_REQUEST_CODE); RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FINGERPRINT_PERMISSION_REQUEST_CODE);
} }
string prefKey = "enc_pref_key";
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{ {
if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && grantResults[0] == Android.Content.PM.Permission.Granted) if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && grantResults[0] == Android.Content.PM.Permission.Granted)
{ {
Button button = FindViewById<Button>(Resource.Id.MyButton); Button encButton = FindViewById<Button>(Resource.Id.MyButton);
button.Visibility = ViewStates.Visible; Button decButton = FindViewById<Button>(Resource.Id.Decrypt);
button.Enabled = true; encButton.Visibility = ViewStates.Visible;
encButton.Enabled = true;
var fingerprint = new keepass2android.FingerprintModule(this); 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) 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. // 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" Toast.MakeText(this, "Secure lock screen hasn't set up.\n"
+ "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint", ToastLength.Long).Show(); + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint", ToastLength.Long).Show();
@@ -61,45 +97,55 @@ namespace FingerprintTest
if (!fingerprint.FingerprintManager.HasEnrolledFingerprints) if (!fingerprint.FingerprintManager.HasEnrolledFingerprints)
{ {
button.Enabled = false; encButton.Enabled = false;
// This happens when no fingerprints are registered. // This happens when no fingerprints are registered.
Toast.MakeText(this, "Go to 'Settings -> Security -> Fingerprint' " + Toast.MakeText(this, "Go to 'Settings -> Security -> Fingerprint' " +
"and register at least one fingerprint", ToastLength.Long).Show(); "and register at least one fingerprint", ToastLength.Long).Show();
return; 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) public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{ {
_fingerprintEnc.Encrypt("abc"); var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
var edit = PreferenceManager.GetDefaultSharedPreferences(Application.Context).Edit(); Toast.MakeText(_context, _fingerprintDec.DecryptStored(_prefKey, _context), ToastLength.Long).Show();
edit.PutString("encrypted", );
} }
} }
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"?> <?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"> <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-sdk android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application android:label="FingerprintTest" android:icon="@drawable/Icon"></application> <application android:label="FingerprintTest" android:icon="@drawable/Icon"></application>
</manifest> </manifest>

View File

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