Я бы хотел использовать ключи с аппаратной поддержкой для взаимной TLS на стороне клиента на моем Android.
Ключ должен быть разблокирован с помощью биометрии.
Я нашел, как генерировать аппаратные пары ключей на Android:
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyGenerator.initialize(
new KeyGenParameterSpec.Builder(myAlias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build());
keyGenerator.generateKeyPair();
и как разблокировать закрытый ключ с аппаратной поддержкой с помощью отпечатка пальца:
FingerprintManager fingerprintManager = (FingerprintManager) this.getSystemService(Context.FINGERPRINT_SERVICE);
PrivateKey key = (PrivateKey) keyStore.getKey(myAlias, null);
Cipher cipher = Cipher.getInstance(cipherAlgorithm, "AndroidKeyStore");
cipher.init(Cipher.DECRYPT_MODE, key);
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, authenticationCallback, null);
Я также могу настроить свой HttpClient для использования клиентских сертификатов:
// I have loaded the PrivateKey privateKey and Certificate certificate from PEM files
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null);
final char pseudoSecretPassword[] = ("##" + System.currentTimeMillis()).toCharArray();
keyStore.setKeyEntry(
PKIModule.DEFAULT_KEYSTORE_ALIAS,
privateKey,
pseudoSecretPassword,
new Certificate[] {certificate}
);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, pseudoSecretPassword);
KeyManager[] keyManagers = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagers, trustManagers, new SecureRandom());
OkHttpClient newClient = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.build();
Однако я не нашел способа напрямую разблокировать закрытый ключ с аппаратной поддержкой для использования в KeyManager, используемом SSLContext, потому что механизм разблокировки работает с криптообъектами, а не с закрытыми ключами.
Как настроить совместную разблокировку биометрического ключа и клиентские сертификаты TLS на Android?
Обновление
После точек @pedrofb я обновил свой код для генерации пары ключей с KeyProperties.PURPOSE_SIGN
и KeyProperties.DIGEST_NONE
. Я подписал пару ключей клиента с помощью CA, который был импортирован в хранилище доверенных сертификатов сервера. А также создание клиентского KeyManager на основе AndroidKeyStore:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
factory.init(keyStore, null);
KeyManager[] keyManagers = factory.getKeyManagers();
sslContext.init(keyManagers, trustManagers, new SecureRandom());
Однако это не с
W/CryptoUpcalls: Preferred provider doesn't support key:
W/System.err: java.security.InvalidKeyException: Keystore operation failed
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1256)
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1281)
at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
at android.security.keystore.AndroidKeyStoreSignatureSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreSignatureSpiBase.java:219)
at android.security.keystore.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:99)
at android.security.keystore.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:77)
at java.security.Signature$Delegate.init(Signature.java:1357)
at java.security.Signature$Delegate.chooseProvider(Signature.java:1310)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1385)
at java.security.Signature.initSign(Signature.java:679)
at com.android.org.conscrypt.CryptoUpcalls.rawSignDigestWithPrivateKey(CryptoUpcalls.java:88)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:383)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:231)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:336)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:300)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:185)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:107)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:87)
at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:221)
at okhttp3.RealCall.execute(RealCall.java:81)
at com.jemmic.secuchat.biometriccrypto.MainActivity.testMutualTLS(MainActivity.java:402)
at com.jemmic.secuchat.biometriccrypto.MainActivity.access$300(MainActivity.java:87)
at com.jemmic.secuchat.biometriccrypto.MainActivity$TestMutualTlsTask.doInBackground(MainActivity.java:315)
at com.jemmic.secuchat.biometriccrypto.MainActivity$TestMutualTlsTask.doInBackground(MainActivity.java:311)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
W/System.err: Caused by: android.security.KeyStoreException: Incompatible padding mode
at android.security.KeyStore.getKeyStoreException(KeyStore.java:1159)
... 43 more
W/CryptoUpcalls: Could not find provider for algorithm: NONEwithRSA