У меня есть открытый ключ, отправленный с сервера Java. Закодированные строки base64 совпадают до того, как я декодирую и вычеркиваю заголовки ASN.1. Я храню открытый ключ в цепочке ключей с помощью SecItemAdd
.
Итак, я пытаюсь зашифровать данные с помощью открытого ключа и расшифровать его с помощью закрытого ключа на Java. Я использую SecKeyEncrypt
на стороне iOS и Cipher
на стороне Java.
То, что я шифрую, является симметричным ключом AES, который шифрует мои фактические данные, поэтому длина ключа составляет 16 байт. Когда просто base64 кодирует ключ, все работает, поэтому я знаю, что что-то не так с этим RSA-шифрованием.
Вот пример моего вызова iOS:
OSStatus sanityCheck = SecKeyEncrypt(publicKey,
kSecPaddingPKCS1,
(const uint8_t *) [incomingData bytes],
keyBufferSize,
cipherBuffer,
&cipherBufferSize
);
Вот пример моего Java-вызова:
public static byte[] decryptMessage (byte[] message, PrivateKey privateKey, String algorithm) {
if (message == null || privateKey == null) {
return null;
}
Cipher cipher = createCipher(Cipher.DECRYPT_MODE, privateKey, algorithm, false);
if (cipher == null) {
return null;
}
try {
return cipher.doFinal(message);
}
catch (IllegalBlockSizeException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
catch (BadPaddingException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
}
private static Cipher createCipher (int mode, Key encryptionKey, String algorithm, boolean useBouncyCastle) {
Cipher cipher;
try {
if (useBouncyCastle) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher = Cipher.getInstance(algorithm, "BC");
}
else {
cipher = Cipher.getInstance(algorithm);
}
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
catch (NoSuchPaddingException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
catch (NoSuchProviderException e) {
e.printStackTrace();
return null;
}
try {
cipher.init(mode, encryptionKey);
}
catch (InvalidKeyException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
return cipher;
}
Я пробовал так много комбинаций и ничего не работал.
- iOS: PKCS1, Java: RSA/ECB/PKCS1Padding
- iOS: PKCS1, Java: RSA
- iOS: PKCS1, Java: RSA/None/PKCS1Padding (бросает
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
) - iOS: OAEP, Java: RSA/ECB/OAEPWithSHA-1AndMGF1Padding
- iOS: OAEP, Java: RSA/ECB/OAEPWithSHA-256AndMGF1Padding
Я также пробовал использовать внутренний поставщик Java, а также поставщик BouncyCastle. javax.crypto.BadPaddingException
получает бросок каждый раз, но сообщение отличается для каждой комбинации. Некоторые показывают Data must start with zero
, а другие - Message is larger than modulus
.
iOS: PKCS1, Java: RSA
не генерирует исключение, но результирующий расшифрованный массив byte[]
должен быть длиной 16, но длина 256, что означает, что заполнение неправильно удалено.
Может кто-нибудь помочь?
*** ИЗМЕНИТЬ ***
Поскольку я делаю больше тестирования, я наткнулся на эту страницу (http://javadoc.iaik.tugraz.at/iaik_jce/current/iaik/pkcs/pkcs1/RSACipher.html), что по сути говорит мне, что RSA == RSA/None/PKCS1Padding
. Расшифровка работает в том смысле, что исключений нет, но я все еще получаю дешифрованный ключ, байт которого [] имеет длину 256, а не длину 16.
Еще одна достопримечательность. Похоже, что если на сервере Java есть открытый ключ, сгенерированный с устройства iOS и зашифрованный с помощью Cipher.getInstance("RSA")
, телефон способен правильно декодировать сообщение с помощью RSA/PKCS1.
*** ИЗМЕНИТЬ 2 ***
Я просмотрел эти руководства и снова просмотрел свой код на стороне iOS:
- http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/
- http://blog.wingsofhermes.org/?p=42
- http://blog.wingsofhermes.org/?p=75
Насколько я могу судить, мой код делает все правильно. Одно существенное различие заключалось в том, как я сохранял ключ, поэтому я попытался сохранить его другим способом:
OSStatus error = noErr;
CFTypeRef persistPeer = NULL;
NSMutableDictionary * keyAttr = [[NSMutableDictionary alloc] init];
keyAttr[(__bridge id) kSecClass] = (__bridge id) kSecClassKey;
keyAttr[(__bridge id) kSecAttrKeyType] = (__bridge id) kSecAttrKeyTypeRSA;
keyAttr[(__bridge id) kSecAttrApplicationTag] = [secKeyWrapper getKeyTag:serverPublicKeyTag];
keyAttr[(__bridge id) kSecValueData] = strippedServerPublicKey;
keyAttr[(__bridge id) kSecReturnPersistentRef] = @YES;
error = SecItemAdd((__bridge CFDictionaryRef) keyAttr, (CFTypeRef *)&persistPeer);
if (persistPeer == nil || ( error != noErr && error != errSecDuplicateItem)) {
NSLog(@"Problem adding public key to keychain");
return;
}
CFRelease(persistPeer);
Это сохранение было успешным, но конечный результат был одинаков: расшифрованный ключ AES по-прежнему оставался 256 байтами вместо 16 байтов.