У меня есть система, которая использует HTTPS клиентские сертификаты для аутентификации, но сертификаты сами генерируются, в соответствии со следующим способом:сертификат Android обновления Java и закрытые ключи в Android KeyStore
- Клиентское устройство генерирует сертификат (включая открытый и закрытый ключ)
- Клиентское устройство отправляет открытый ключ на сервер, который подписывает открытый ключ и возвращает его в качестве подписанного сертификата
- Клиент хранит сертификат в безопасном режиме, а затем использует его как сертификат клиента HTTPS
У нас есть эта система, работающая на iOS, и я пытаюсь перейти на Android, но столкнулся с множеством проблем с плохо документированными и запутанными API безопасности Android.
Мой код идет примерно так:
Генерация сертификата
keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);
Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + FORTY_YEARS_IN_MILLISECONDS);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setKeySize(2048)
.setKeyType(KeyProperties.KEY_ALGORITHM_RSA)
.setSubject(new X500Principal("CN=" + alias))
.setSerialNumber(BigInteger.TEN)
.setStartDate(startDate)
.setEndDate(endDate)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE);
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair(); // this will put a certificate and key pair in the keyStore.
dumpKeyStore(keyStore);
byte[] entireKey = keyPair.getPublic().getEncoded();
// chop off first 24 bytes; the java key pair generator puts an object ID of 1.2.840.113549.1.1.1 RSA (RSA_SIGN) before the key which gets mangled when the server signs and sends back the certificate
byte[] publicKeyBytes = Arrays.copyOfRange(entireKey, 24, entireKey.length);
dumpKeyStore
является метод утилита, которая перебирает хранилища ключей, вызывает keyStore.getEntry
, чтобы получить каждую запись и и просто регистрирует вещи. На этом этапе сообщается, что имеется одна запись с заданным псевдонимом, и она имеет тип KeyStore.PrivateKeyEntry
. Он имеет связанный сертификат и открытый ключ, который можно извлечь из PrivateKeyEntry
.
Отправка на сервер
publicKeyBytes
отправляется на сервер, который ставит его в качестве открытого ключа для новой, подписанный сертификат x509, который отправляется обратно в ответ. Я не вставлял код, это просто базовая сеть. Возвращаемый сертификат загружается и отлично выглядит из того, что я могу сказать.
Сохранение и ассоциирования сертификат
Я пытаюсь поставить его в хранилище ключей с тем же псевдонимом, так что (в теории) может быть связано с правильным закрытым ключом из ранее. Мой код до сих пор, как это:
KeyStore keyStore;
try {
keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);
}catch (IOException | NoSuchAlgorithmException | CertificateException e) {
Log.wtf(TAG, e);
throw new FatalError(TAG, e);
}
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
Log.wtf(TAG, e);
throw new FatalError(TAG, e);
}
Certificate cert = certificateFactory.generateCertificate(new ByteArrayInputStream(certificateFromServer));
// find the existing certificate, copy it's private key out, then replace the certificate with the one from the server but keeping the private key
try {
KeyStore.PrivateKeyEntry existingPrivateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
KeyStore.PrivateKeyEntry newEntry = new KeyStore.PrivateKeyEntry(existingPrivateKeyEntry.getPrivateKey(), new Certificate[]{ cert });
keyStore.setEntry(alias, newEntry, null);
} catch (Exception e) {
Log.wtf(TAG, e);
throw new FatalError(TAG, e);
}
dumpKeyStore(keyStore);
На данный момент, окончательное dumpKeyStore указывает, что есть запись с правильным псевдонимом, однако он получает «NoSuchAlgorithmException: Неизвестный ввод ключа» исключение генерируется, когда он пытается звоните keyStore.getEntry
Является ли то, что я пытаюсь сделать (заменить сертификат, но сохранить закрытый ключ) в андроиде? Если да, то как я могу это сделать? Похоже, что это на самом деле не работает
Благодаря
Orion