This repository was archived by the owner on May 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
release 1.4 Co-authored-by: cbeets <87814389+cbeets@users.noreply.github.com>
- Loading branch information
1 parent
65329d4
commit 895026c
Showing
41 changed files
with
1,225 additions
and
324 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions
128
src/IDWallet.Android/SecurityChecks/HardwareKeyServiceAndroid.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using Android.Content.PM; | ||
using Android.Security.Keystore; | ||
using IDWallet.Droid.SecurityChecks; | ||
using IDWallet.Interfaces; | ||
using Java.Security; | ||
using Java.Security.Cert; | ||
using Java.Security.Spec; | ||
using Java.Util; | ||
using Newtonsoft.Json; | ||
using Xamarin.Essentials; | ||
|
||
[assembly: Xamarin.Forms.Dependency(typeof(HardwareKeyServiceAndroid))] | ||
namespace IDWallet.Droid.SecurityChecks | ||
{ | ||
public class HardwareKeyServiceAndroid : IHardwareKeyService | ||
{ | ||
private const string ALIAS = "BaseIdHWKey"; | ||
private const string TRANSFORMATION = "SHA256withECDSA"; | ||
private const string KEYALGORITHM = KeyProperties.KeyAlgorithmEc; | ||
private const string KEYSTORE_TYPE = "AndroidKeyStore"; | ||
private const int AUTHENTICATION_LEVEL = 32768; | ||
|
||
public string GetPublicKeyAsBase64(byte[] nonce) | ||
{ | ||
// Check if PPK pair is allready generated | ||
bool strongBoxBacked = StrongBoxFeatureAvailable(); | ||
bool isPhysicalDevice = IsPhysicalDevice(); | ||
|
||
GenerateNewKeyPair(nonce, strongBoxBacked); | ||
|
||
KeyStore keyStore = KeyStore.GetInstance(KEYSTORE_TYPE); | ||
keyStore.Load(null); | ||
Certificate[] certificates = keyStore.GetCertificateChain(ALIAS); | ||
string[] certArr = CreateCertificateChainArray(certificates); | ||
|
||
return JsonConvert.SerializeObject(certArr); | ||
} | ||
|
||
public string Sign(byte[] nonce) | ||
{ | ||
KeyStore.PrivateKeyEntry entry = null; | ||
bool exists = TryGetPrivateKey(out entry); | ||
if (exists) | ||
{ | ||
IPrivateKey privateKey = entry.PrivateKey; | ||
|
||
Java.Security.Signature signature = Java.Security.Signature.GetInstance(TRANSFORMATION); | ||
signature.InitSign(privateKey); | ||
|
||
signature.Update(nonce); | ||
|
||
try | ||
{ | ||
byte[] signed = signature.Sign(); | ||
|
||
Base64.Encoder encoder = Base64.GetEncoder(); | ||
return encoder.EncodeToString(signed); | ||
} | ||
catch (SignatureException) | ||
{ | ||
return null; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private bool TryGetPrivateKey(out KeyStore.PrivateKeyEntry entry) | ||
{ | ||
KeyStore keyStore = KeyStore.GetInstance(KEYSTORE_TYPE); | ||
keyStore.Load(null); | ||
entry = (KeyStore.PrivateKeyEntry)keyStore.GetEntry(ALIAS, null); | ||
|
||
// Check if PPK pair is allready generated | ||
if (entry == null) | ||
{ | ||
return false; | ||
} | ||
else | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
private static string[] CreateCertificateChainArray(Certificate[] certificates) | ||
{ | ||
string[] certArr = new string[certificates.Length]; | ||
|
||
for (int i = 0; i < certificates.Length; i++) | ||
{ | ||
certArr[i] = $"-----BEGIN CERTIFICATE-----{EncodeToString(certificates[i].GetEncoded())}-----END CERTIFICATE-----"; | ||
} | ||
|
||
return certArr; | ||
} | ||
|
||
private static string EncodeToString(byte[] bytesToEncde) | ||
{ | ||
Base64.Encoder encoder = Base64.GetEncoder(); | ||
return encoder.EncodeToString(bytesToEncde); | ||
} | ||
|
||
private static void GenerateNewKeyPair(byte[] nonce, bool strongBoxBacked) | ||
{ | ||
KeyPairGenerator kpGenerator = KeyPairGenerator.GetInstance(KEYALGORITHM, KEYSTORE_TYPE); | ||
// This failes, when the user does not have a biometric enrollment | ||
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KeyStorePurpose.Sign) | ||
.SetDigests(KeyProperties.DigestSha256) | ||
.SetAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) | ||
.SetRandomizedEncryptionRequired(false) | ||
.SetAttestationChallenge(nonce) | ||
.SetIsStrongBoxBacked(strongBoxBacked) | ||
.Build(); | ||
|
||
kpGenerator.Initialize(spec); | ||
kpGenerator.GenerateKeyPair(); | ||
} | ||
|
||
private bool StrongBoxFeatureAvailable() | ||
{ | ||
return Android.App.Application.Context.PackageManager.HasSystemFeature(PackageManager.FeatureStrongboxKeystore); | ||
} | ||
|
||
private bool IsPhysicalDevice() | ||
{ | ||
return DeviceInfo.DeviceType == DeviceType.Physical; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
src/IDWallet.iOS/SecurityChecks/HardwareKeyServiceIOS.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
using IDWallet.Interfaces; | ||
using IDWallet.iOS.SecurityChecks; | ||
using IDWallet.Utils; | ||
using Foundation; | ||
using Security; | ||
using System.Diagnostics; | ||
|
||
[assembly: Xamarin.Forms.Dependency(typeof(HardwareKeyServiceIOS))] | ||
namespace IDWallet.iOS.SecurityChecks | ||
{ | ||
public class HardwareKeyServiceIOS : IHardwareKeyService | ||
{ | ||
private const string ALIAS = "BaseIdHWKey"; | ||
|
||
public string GetPublicKeyAsBase64(byte[] nonce) | ||
{ | ||
SecKey privKey = GetPrivateKey(); | ||
while (privKey != null) | ||
{ | ||
Debug.WriteLine("Key found"); | ||
var deleted = SecKeyChain.Remove(new SecRecord(SecKind.Key) | ||
{ | ||
ApplicationTag = NSData.FromString(ALIAS), | ||
KeyType = SecKeyType.ECSecPrimeRandom, | ||
}); | ||
|
||
Debug.WriteLine($"Key deleted: {deleted}"); | ||
|
||
privKey = GetPrivateKey(); | ||
} | ||
|
||
CreateKey(nonce); | ||
|
||
privKey = GetPrivateKey(); | ||
|
||
SecKey publKey = privKey.GetPublicKey(); | ||
return publKey.GetExternalRepresentation().GetBase64EncodedString(NSDataBase64EncodingOptions.None); | ||
} | ||
|
||
public string Sign(byte[] nonce) | ||
{ | ||
SecKey key = GetPrivateKey(); | ||
if (key != null) | ||
{ | ||
NSError nSError; | ||
NSData encrypt = key.CreateSignature(SecKeyAlgorithm.EcdsaSignatureMessageX962Sha256, NSData.FromArray(nonce), out nSError); | ||
|
||
return encrypt.GetBase64EncodedString(NSDataBase64EncodingOptions.None); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public void CreateKey(byte[] nonce) | ||
{ | ||
using (SecAccessControl access = new SecAccessControl(SecAccessible.WhenUnlockedThisDeviceOnly, SecAccessControlCreateFlags.PrivateKeyUsage)) | ||
{ | ||
SecKeyGenerationParameters keyParameters = new SecKeyGenerationParameters | ||
{ | ||
KeyType = SecKeyType.ECSecPrimeRandom, | ||
KeySizeInBits = 256, | ||
Label = ALIAS, | ||
ApplicationTag = NSData.FromString(ALIAS), | ||
// CanSign = true, | ||
PrivateKeyAttrs = new SecKeyParameters | ||
{ | ||
//IsPermanent = true, | ||
ApplicationTag = NSData.FromString(ALIAS), | ||
AccessControl = access | ||
}, | ||
PublicKeyAttrs = new SecKeyParameters | ||
{ | ||
ApplicationTag = NSData.FromArray(Sha256.sha256(nonce)) | ||
} | ||
}; | ||
|
||
NSError error; | ||
SecKey genKey = SecKey.CreateRandomKey(keyParameters, out error); | ||
if (genKey == null) | ||
{ | ||
throw new NSErrorException(error); | ||
} | ||
|
||
SecRecord sr = new SecRecord(SecKind.Key) | ||
{ | ||
ApplicationTag = NSData.FromString(ALIAS), | ||
KeyType = SecKeyType.ECSecPrimeRandom, | ||
}; | ||
sr.SetKey(genKey); | ||
|
||
SecStatusCode ssc = SecKeyChain.Add(sr); | ||
} | ||
} | ||
|
||
private SecKey GetPrivateKey() | ||
{ | ||
object privateKey = SecKeyChain.QueryAsConcreteType( | ||
new SecRecord(SecKind.Key) | ||
{ | ||
ApplicationTag = NSData.FromString(ALIAS), | ||
KeyType = SecKeyType.ECSecPrimeRandom, | ||
}, | ||
out SecStatusCode code); | ||
return code == SecStatusCode.Success ? privateKey as SecKey : null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.