-0.4 C
New York
Saturday, February 22, 2025

Creating RSA public key encryption between Android and IOS


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?

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles