I’m making an attempt to setup RSA pubic key encryption between Android and IOS.
TLDR – Android is ready to encrypt the information utilizing the IOS public key, and sends the encrypted knowledge by way of tcp connection. The decrypt perform on IOS runs however the string conversion on the finish returns nil
RSA key pair technology strategies for IOS
static func generateKeyPair(for identifier: String) -> SecKey? {
if let existingKey = getPrivateKey(for: identifier) {
log(
"RSAKeyPairHelper",
"Key already exists. Returning present key.")
return existingKey
}
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: getTag(for: identifier),
]
var error: Unmanaged?
guard
let privateKey = SecKeyCreateRandomKey(
attributes as CFDictionary, &error)
else { return nil }
return privateKey
}
static func getPrivateKey(for identifier: String) -> SecKey? {
let question: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: getTag(for: identifier),
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecReturnRef as String: true,
]
var key: AnyObject?
let standing = SecItemCopyMatching(question as CFDictionary, &key)
return (standing == errSecSuccess) ? (key as! SecKey?) : nil
}
static func getPublicKey(for identifier: String) -> SecKey? {
guard let privateKey = getPrivateKey(for: identifier) else {
return nil
}
return SecKeyCopyPublicKey(privateKey)
}
static func exportPublicKey(for identifier: String) -> String? {
guard let publicKey = getPublicKey(for: identifier) else { return nil }
var error: Unmanaged?
guard
let publicKeyData = SecKeyCopyExternalRepresentation(
publicKey, &error) as Information?
else { return nil }
// since android wants x509 format and we're getting PKCS#1 format
let x509Header: [UInt8] = [
0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00
]
let x509PublicKey = Information(x509Header) + publicKeyData
let x509PublicKeyString = x509PublicKey.base64EncodedString()
log("exported public Key", x509PublicKeyString)
return x509PublicKeyString
}
static func decrypt(encryptedString: String, for identifier: String) -> String? {
log("incoming encrypted string", encryptedString)
guard let encryptedData = Information(base64Encoded: encryptedString) else {
elog("","unable to transform to knowledge")
return nil
}
guard let privateKey = getPrivateKey(for: identifier) else {
elog("","unable to get personal key")
return nil
}
var error: Unmanaged?
guard let decryptedData = SecKeyCreateDecryptedData(
privateKey, .rsaEncryptionPKCS1, encryptedData as CFData, &error
) as Information? else {
elog("","Decryption failed: (error!.takeRetainedValue().localizedDescription)")
return nil
}
let textual content = String(knowledge: decryptedData, encoding: .utf8)
log("textual content", "(textual content), (decryptedData)")
return textual content
}
I ship the general public key from exportPublicKey perform by way of a tcp connection then use it like this
enjoyable encryptWithPublicKey(publicKeyString: String, plainText: String): String? {
return attempt {
log("incoming public key", publicKeyString)
log("incoming knowledge", plainText)
val publicKey =
convertBase64ToPublicKey(publicKeyString)
?: throw Exception("Invalid public key string")
val cipher = Cipher.getInstance(RSA_MODE)
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val encryptedBytes = cipher.doFinal(plainText.toByteArray())
val encryptedData = Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
log("encrypted knowledge", encryptedData)
return encryptedData
} catch (e: Exception) {
logE("RSAKeyPairHelper", "Error whereas encrypting textual content: ${e.message}")
null
}
}
personal enjoyable convertBase64ToPublicKey(base64String: String): PublicKey? {
return attempt {
log("convertBase64ToPublicKey", "Public Key: $base64String")
// Decode the Base64 public key from iOS
val keyBytes = Base64.decode(
base64String,
Base64.NO_WRAP
)
val keySpec = X509EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(keySpec)
} catch (e: Exception) {
logE("RSAKeyPairHelper", "Error whereas changing string to public key: ${e.message}")
null
}
}
Android is ready to encrypt the information utilizing the IOS public key, and sends the encrypted knowledge by way of tcp connection. The decrypt perform on IOS runs however the string conversion on the finish returns nil
IOS logs
[exported public Key]: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0p5O+rnE3WNlZwWuWgT54UmqCPKidO7740RF0utjdlh/GVX7QaqMdx7iDBavqbRrbjnpFvqCuNMd7RbTBgAMohG5Mudc5Ereyj+25p/Y0Tbnc24Y70xKWn3OEs5eZ+SpzJEPxkOjwXfSMijtSC5oeEkTkmwFfjkgM0FCsx5suad5hM3vkCh8w7pP8zrg3xu8w/KJ4opsYzfTyIYeFZHxdQ9Jx1+d8UNg0MoFqfIGiifMOeCEnNDUgobJ1+W0RutWoVjo9xjW8rMdx/Nllsn4ggIAeWCMVAY1TAe4RxMDchCTVqq2CUn5QmVzDGfML+usLDXAnwZz6uT1GyGBnWyaYwIDAQAB
[incoming encrypted string]: Wf+yRn/HtZh4A6GerLP2T5gmw+6O6AsGGfgyid8IFGa+kypri7zsHQOU45HdgdLaBHzxKl+GhQPurjjZvY6hIygy0W/qXJrBnNLnxCVMWXRulk1yRS4KyEwGV0DLchK611/KL6GrSZJxDRKPN3SyBT003kHMWmw4pddJDufDXgsJInHzBbgc4F7dsPRqFRUT6yMMGXk6zyFP/xbDKwgwyoj1mf1kB3zxY3znAVmcv5JNYsQHjp5vdV20JvBdjMbaGpyUJ0XSuRquh8uG3HbZITYrofptjen2iKkqbMhc5UGsX0yKJF8UPXx/0yrg7gps3kqLDJR+zpVfNFbRrXeRYA==
[text]: nil, 92 bytes
Android logs
[incoming public key]: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0p5O+rnE3WNlZwWuWgT54UmqCPKidO7740RF0utjdlh/GVX7QaqMdx7iDBavqbRrbjnpFvqCuNMd7RbTBgAMohG5Mudc5Ereyj+25p/Y0Tbnc24Y70xKWn3OEs5eZ+SpzJEPxkOjwXfSMijtSC5oeEkTkmwFfjkgM0FCsx5suad5hM3vkCh8w7pP8zrg3xu8w/KJ4opsYzfTyIYeFZHxdQ9Jx1+d8UNg0MoFqfIGiifMOeCEnNDUgobJ1+W0RutWoVjo9xjW8rMdx/Nllsn4ggIAeWCMVAY1TAe4RxMDchCTVqq2CUn5QmVzDGfML+usLDXAnwZz6uT1GyGBnWyaYwIDAQAB
[incoming data]: Check Token
[encrypted data]: Wf+yRn/HtZh4A6GerLP2T5gmw+6O6AsGGfgyid8IFGa+kypri7zsHQOU45HdgdLaBHzxKl+GhQPurjjZvY6hIygy0W/qXJrBnNLnxCVMWXRulk1yRS4KyEwGV0DLchK611/KL6GrSZJxDRKPN3SyBT003kHMWmw4pddJDufDXgsJInHzBbgc4F7dsPRqFRUT6yMMGXk6zyFP/xbDKwgwyoj1mf1kB3zxY3znAVmcv5JNYsQHjp5vdV20JvBdjMbaGpyUJ0XSuRquh8uG3HbZITYrofptjen2iKkqbMhc5UGsX0yKJF8UPXx/0yrg7gps3kqLDJR+zpVfNFbRrXeRYA==
The information on each ends (public key and encrypted base64 string) is similar.
I attempted with out the x509 header however then X509EncodedKeySpec
will generate an error.
What am I doing incorrect?