TL; DR: открытый ключ RSA, сгенерированный в iOS и сохраненный в цепочке для ключей, экспортированный как base64 и отправленный в серверную часть Java, не распознается.
Я реализуюФункция шифрования чата в приложении iOS, и я использую симметричные + асимметричные ключи для ее обработки.
Не вдаваясь в подробности, в бэкэнде я использую открытый ключ пользователя для шифрования симметричного ключа, используемого для шифрования.и расшифровывать сообщения.
Я создал две платформы, соответственно, в Swift и в Java (бэкэнд) для обработки генерации ключей, шифрования, дешифрования и т. д. У меня также есть тесты для них, так что я на 100% все работаеткак и ожидалось.
Однако похоже, что серверная часть не может распознать формат открытого ключа, переданного из iOS.Используя RSA с обеих сторон, этот код я использую в Swift для генерации ключа:
// private key parameters
static let privateKeyParams: [String : Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "..." // I have a proper unique tag here
]
// public key parameters
static let publicKeyParams: [String : Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "..." // I have a proper unique tag here
]
// global parameters for our key generation
static let keyCreationParameters: [String : Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPublicKeyAttrs as String: publicKeyParams,
kSecPrivateKeyAttrs as String: privateKeyParams
]
...
var publicKey, privateKey: SecKey?
let status = SecKeyGeneratePair(Constants.keyCreationParameters as CFDictionary, &publicKey, &privateKey)
Я использую зеркальный код для чтения ключей из цепочки для ключей.
Это частькод, который я использую для экспорта открытого ключа в виде строки base64:
extension SecKey {
func asBase64() throws -> String {
var dataPtr: CFTypeRef?
let query: [String:Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: "...", // Same unique tag here
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecReturnData as String: kCFBooleanTrue
]
let result = SecItemCopyMatching(query as CFDictionary, &dataPtr)
switch (result, dataPtr) {
case (errSecSuccess, .some(let data)):
// convert to Base64 string
let base64PublicKey = data.base64EncodedString(options: [])
return base64PublicKey
default:
throw CryptoError.keyConversionError
}
}
}
На внутреннем уровне я использую этот код Java для преобразования строки base64 в открытый ключ:
public PublicKey publicKeyFrom(String data) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] publicBytes = Base64.decodeBase64(data);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
Нов последней строке происходит сбой, за этим исключением:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
Выполняя некоторую ручную отладку, я заметил, что формат открытого ключа отличается - когда я генерирую ключ в iOS, а затем экспортирую как базу 64, это выглядит так:
MIIBCgKCAQEA4M/bRDdH0f6qFIXxOg13RHka+g4Yv8u9PpPp1IR6pSwrM1aq8B6cyKRwnLe/MOkvODvDfJzvGXGQ01zSTxYWAW1B4uc/NCEemCmZqMosSB/VUJdNxxWtt2hJxpz06hAawqV+6HmweAB2dUn9tDEsQLsNHdwYouOKpyRZGimcF9qRFn1RjR0Q54sUh1tQAj/EwmgY2S2bI5TqtZnZw7X7Waji7wWi6Gz88IkuzLAzB9VBNDeV1cfJFiWsZ/MIixSvhpW3dMNCrJShvBouIG8nS+vykBlbFVRGy3gJr8+OcmIq5vuHVhqrWwHNOs+WR87K/qTFO/CB7MiyiIV1b1x5DQIDAQAB
для 360 символов, в то время как в Java (по-прежнему используется RSA) это выглядит так:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAAnWO4BXUGP0qM3Op36YXkWNxb4I2pPZuZ7jJtfUO7v+IO1mq43WzNaxLqqLPkTnMrv2ACRDK55vin+leQlL1z0LzVxjtZ9F6pajQo1r7PqBlL5N8bzBFKpagEf0QfyHPw0/0kG9DMnvQ+Im881QyN2zdl33wp5Fi+jRT7cunFQIDAQAB
длиной 216иероглифы.
Я не могу понять, в чем дело - очевидно, я не удивлюсь, если iOS будет обрабатывать ключи в другом ключе, и потребует специальной обработки для общения с другими людьми.
Есть идеи?