Getting the IV from SecKeyCreateEncryptedData for Symmetric Encryption?

I am trying to use RSA to encrypt some data on MacOS using SecKeyCreateEncryptedData() and then Decrypt the data on Linux using OpenSSL.
When I call SecKeyCreateEncryptedData I am returned "the RSA encrypted session key, the AES encrypted data, and a 16-byte AES-GCM tag into a block of data" as described in:

https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/using_keys_for_encryption


To decrypt the data using OpenSSL I require those 3 parts plus the IV(AKA, nonce value):

https://shanetully.com/2012/06/openssl-rsa-aes-and-c/

For that example there is a further call to:

```

EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_TAG, 16, ref_TAG);
```

To generate the GCM tag.


My question is how do i get the IV that was used in SecKeyCreateEncryptedData to create the AES encrypted data so that i can pass it to openSSL to decrypt the data?


This is being done on MacOS using Swift 4.2. Below is a short code example demonstrating what I am trying to do:

```

import OpenSSL


// encrypt using security

var response: Unmanaged<CFError>? = nil

let encryptedData = SecKeyCreateEncryptedData(<SecPublicKey>, SecKeyAlgorithm.rsaEncryptionOAEPSHA256AESGCM, <data> as CFData, &response)

if response != nil {

print(response?.takeRetainedValue())

}


// decrypt using OpenSSL

var evp_key = EVP_PKEY_new()

let _ = EVP_PKEY_set1_RSA(evp_key, <PrivateKeyPointer>)
let rsaDecryptCtx = EVP_CIPHER_CTX_new_wrapper()

let encKeyLength = Int(EVP_PKEY_size(evp_key))
// iv is the only missing value for decoding.
// encryptedKey used here

let _ = EVP_OpenInit(rsaDecryptCtx, EVP_aes_128_gcm(), <encryptedKey>, <encryptedKeyLength>, <iv>, evp_key)

// encryptedData used here
let _ = EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, encryptedData, Int32(encryptedData.count)
var decMsgLen = processedLen

// gcm tag used here
EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_TAG, 16, <gcm tag>)
let _EVP_OpenFinal(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen)
decMsgLen += processedLen
let decryptedData = Data(bytes: decrypted, count: Int(decMsgLen))
```
Any help in understanding how I can get the IV value would be greatly appreciated.

Accepted Reply

Generally, the authentication data is required during decryption.
To summarize:

You create an RSA key pair (< 4096 bits for AES128, >= 4096 for AES256) and have some data you want to encrypt.

You pass your data and the public key to the SecKeyCreateEncryptedData function. The output of the function is some data.


Internally, the function uses a 0 IV (as the AES key is randomly generated) and this is regarded secure. Also internally, the random AES key is encrypted with the provided public key using OAEP SHA-256. The hashing function is required for the padding. Also internally, the public key DER data (the output from SecKeyCopyExternalRepresentation) is used as authentication data for the AES GCM operation.


The output data is build up as follows:

[256 or 512 bytes encrypted key data][ 256 or 512 ..< output.count - 16][output.count - 16 ..< output.count]

I'm unsure if the tag is 16 bytes for AES256, but I believe it is (don't quote me on that though).


In order to decrypt, follow these steps:

  1. Decrypt the first portion of the SecKeyCreateEncryptedData (either 256/512 bytes depending on RSA key size) with the private key and OAEP SHA-256 padding. You now have the bytes that make up the key.
  2. Export the public key to DER format.
  3. Create a nonce/IV of 16 '0' bytes.
  4. Use your requested OpenSSL implementation


So something like:


d = OpenSSL(nonce, output[256 or 512 ..< output.count], authentication data)

d = OpenSSL('0' iv, output message without the encrypted key, public key DER data)

This should provide you with the decrypted data. Hope this helps!

Replies

I’m not an expert on GCM, so I don’t have a lot of context here, but the doc comment for

kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM
in
<Security/SecKey.h>
says:

Randomly generated AES session key is encrypted by RSA with OAEP padding. User data are encrypted using session key in GCM mode with all-zero 16 bytes long IV (initialization vector).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for your reply.
I think you are right about using an All-zero 16 byte IV. (I believe this is ok to do since the AES keys are only used once)
I have changed the OpenSSL implementation to use an all zero IV, However I am still unable to decode the security encrypted data with openSSL and vice versa.
I have created a demo repo with code testing the desired functionality which runs on swift 4.2 on MacOS:
https://github.com/Andrew-Lees11/MacLinuxRSA

you previously replied to a post from someone who seemed to be attempting the same thing as me:
https://forums.developer.apple.com/thread/89722
This mentions authentication data and I was curious if I would need to add this for the gcm encryption/decryption.
The other point I am unsure on is what the SHA256 in "rsaEncryptionOAEPSHA256AESGCM" refers to since OpenSSL doesn't appear to be hashing and Security uses a 128 byte key if the RSA key is less than 4096.
Thanks again

The other point I am unsure on is what the SHA256 in "rsaEncryptionOAEPSHA256AESGCM" refers to …

The

SHA256
refers to the hash function being use by OAEP. If you look at the doc comment for the related
kSecKeyAlgorithmRSAEncryptionOAEPSHA256
algorithm, it says:

RSA encryption or decryption, data is padded using OAEP padding scheme internally using SHA256.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Generally, the authentication data is required during decryption.
To summarize:

You create an RSA key pair (< 4096 bits for AES128, >= 4096 for AES256) and have some data you want to encrypt.

You pass your data and the public key to the SecKeyCreateEncryptedData function. The output of the function is some data.


Internally, the function uses a 0 IV (as the AES key is randomly generated) and this is regarded secure. Also internally, the random AES key is encrypted with the provided public key using OAEP SHA-256. The hashing function is required for the padding. Also internally, the public key DER data (the output from SecKeyCopyExternalRepresentation) is used as authentication data for the AES GCM operation.


The output data is build up as follows:

[256 or 512 bytes encrypted key data][ 256 or 512 ..< output.count - 16][output.count - 16 ..< output.count]

I'm unsure if the tag is 16 bytes for AES256, but I believe it is (don't quote me on that though).


In order to decrypt, follow these steps:

  1. Decrypt the first portion of the SecKeyCreateEncryptedData (either 256/512 bytes depending on RSA key size) with the private key and OAEP SHA-256 padding. You now have the bytes that make up the key.
  2. Export the public key to DER format.
  3. Create a nonce/IV of 16 '0' bytes.
  4. Use your requested OpenSSL implementation


So something like:


d = OpenSSL(nonce, output[256 or 512 ..< output.count], authentication data)

d = OpenSSL('0' iv, output message without the encrypted key, public key DER data)

This should provide you with the decrypted data. Hope this helps!

Thank you very much to both Craz1k0ek and eskimo. I have got my implementation working now.
For others I will try and summerise the key steps and information:
For Apple Security Encrypting and decrypting, use `SecKeyCreateEncryptedData` with `rsaEncryptionOAEPSHA1AESGCM`
For OpenSSL:
- Generate a 16 byte all 0 Initialization vector (IV)
- Use either the `EVP_aes_128_gcm`/`EVP_aes_256_gcm` (RSA key size < 4096 bits for AES128, >= 4096 for AES256)
- Set the OpenSSL context IV length to be 16 (It defaults to 12)
- Use a randomly generated 16 byte AES key
- RSA Encrypt the AES key using `RSA_PKCS1_OAEP_PADDING` and your RSA public key
- Set the additional authenticated data (aad) as the RSA key modulus and publicExponent in an ASN1 sequence
- Encrypt your plaintext using the symmetic key and AES-GCM
- Get the 16 byte GCM tag
Then you construct the "envelope" which is the 256/512 bytes of encrypted AES key, X bytes of encrypted data, 16 bytes GCM tag.
Decrypt using the same setting but in reverse.
You use the same 16 byte all 0 IV and can extract the public key data aad from the private key.
This has allowed me to encrypt the data on one platform and then decrypt it on the other.
Thanks again!