SecKeyDecrypt succeeds but UTF-8 decoding returns nil for one specific RSA ciphertext (1024-bit, kSecPaddingNone)

Question

I'm encountering a very strange RSA issue on iOS.

I'm using a 1024-bit RSA key pair generated by SecKeyGeneratePair.

Public Key MIGJAoGBAK2wYDpTcGxFDe+0SwUj4twWOQWfWE+pvbgoUFsXZ/XBplEv7qlruq8ZutG+q7qW2M1983V5HzWGmtDrz2l8vLHYNKaBqEGYAtlYIePBq6+lfsUj2qmFE5Uui8R44KLz8i8QqMxYJUQoeUY3JxHeOe4DnUMWK3+X2nWWv4YWv9qxAgMBAAE= Private Key MIICWwIBAAKBgQCtsGA6U3BsRQ3vtEsFI+LcFjkFn1hPqb24KFBbF2f1waZRL+6pa7qvGbrRvqu6ltjNffN1eR81hprQ689pfLyx2DSmgahBmALZWCHjwauvpX7FI9qphROVLovEeOCi8/IvEKjMWCVEKHlGNycR3jnuA51DFit/l9p1lr+GFr/asQIDAQABAoGAAe2GUpITWkmgQw6Z0uZdfIAM+nm/YcT6di/mXRh4VC3l0hq0LV0PfvrhYWDFKFvyb0IhthEAg8LTzD6tzZfaQqIOMEu77BkuDnpu3g7xTnZP2n2nmBU+4kz7F9l/pBr0KL6RopnagcwfuAtPXxGKv2kfWWbX3szHJXwjMo2KjgECQQDhN/i/yDjhzu8psTHvGptiHrOqi5H1CdmSg7u7EbBbZIPp/FsoxBsxcY8FPmMAkD0bR0JIpwBmYkPLFm+rITeBAkEAxW13UsOstGN8rRJ34SklhlYUDUQKN2R8OLyES5w7rRPcOUfCdKR45SJ2OWrLHATqlPRN+9jo5WmsQWArxlm7MQJAF/2DVMJ6NKt7VGCYphSaeqA0mTUWzhhHgdDgfXPL6mBRoOoNt9Dz6JdlGbTkjNnQeIhlqiGH7GAJQqfYK011gQJATWDIAaI7kD/l35d7cL5FvM8D8OcX8fnqTfplB5VmNkGwcKrVZg4IbBYDrCFVFdg/cL4HyouLTE8MelsNDSKiIQJAEwj2CoK+uupS2T1gJNd57wqOkZp+LbkVX92oSUbJQfez1P7Ldtbn4uCMyoOL34I9CTDfCzlxWqbZnwFmSq3eaQ== Test Data

Plaintext:

SO202605140735073974

Encrypted ciphertext (Base64):

AAKhsQxaEVqqZbFLUzC1+8uzdyCDVxzkTbq37pea6cgLlCTOniYeZQVwaOIRdioQmZ/M/0q33l0votNRWZje8bj4Km0vQF2NjvX+8EjPg6T6OsJKEt/KZf+bX6+PoNooSh1tC9H/HVFhhgNJdpKj9s/Fdl3eLH4qEsmQ25FS53M= After calling SecKeyDecrypt, I get the following decrypted bytes:

4d48734341514177457759484b6f5a497a6a3043415159494b6f5a497a6a304441514545595442664167454242426835707a4b493973744f4e42416b4a4757646e5035465842546d386e555272565367436759494b6f5a497a6a3044415147684e414d794141545a542b4a49746b4a4e647554424e70624d336f324d7a654e5565306f6e4e617a623767645975303370712f2f4271505861397747504e4575462b7145786a4c633d However:

NSString *result = [[NSString alloc] initWithData:plainData encoding:NSUTF8StringEncoding];

returns nil.

Additional Information This RSA key pair works correctly for many other plaintext values. The failure only occurs when encrypting and decrypting this specific string: SO202605140735073974 The RSA key pair is generated using: SecKeyGeneratePair(...)

with a 1024-bit key size.

Encryption and decryption use the Security framework APIs: SecKeyEncrypt SecKeyDecrypt Decryption returns errSecSuccess (status == noErr).

There are no Security framework errors.

Decryption Code

status = SecKeyDecrypt(
    key,
    kSecPaddingNone,
    dataSegment.bytes,
    dataSegmentRealSize,
    cipherBuffer,
    &cipherBufferSize
);

status is noErr.

However, converting the decrypted data to UTF-8 always returns nil.

My Questions Why can SecKeyDecrypt return noErr, yet the resulting data is not valid UTF-8? Is it possible that the decrypted bytes are actually an ASN.1/DER structure rather than the original plaintext? Could this be related to using kSecPaddingNone instead of kSecPaddingPKCS1? Is there anything special about this plaintext (SO202605140735073974) that could trigger this behavior, even though other plaintexts work correctly with the same key pair?

Any insights would be greatly appreciated. Thanks!

You posted your plaintext as a string, so there’s some sort of text encoding involved in order to get plaintext bytes, which is what SecKey works with. I’m kinda assuming that it’s UTF-8. If so, the plaintext becomes:

53 4F 32 30 32 36 30 35 31 34 30 37 33 35 30 37 
33 39 37 34

That is, 20 bytes. That’s 160 bits, but you’re using RSA with a 1024 bit key and no padding, and thus the input to the encryption process must be 1024 bits.

Looking at the other data you included:

  • The “Encrypted ciphertext” value looks reasonable, because when I undo the Base64 encoding I end up with 128 bytes, so 1024 bits.
  • The “decrypted bytes” value is wonky because, assuming you posted a hex dump, that’s 168 bytes, and then if I undo the Base64 encoding I get only 125 bytes, neither of which is the required 128.
  • And I can’t test the decryption for myself because you only posted the public key.

It would help if you posted an example that shows all the necessary bits:

  • A hex dump of the public key, so that I can import that and encrypt
  • A hex dump of the private key, so I can import that and decrypt
  • A hex dump of the plaintext
  • A hex dump of the cyphertext, that is, that plaintext encrypted by the public key
  • A hex dump of the cyphertext as decrypted by the private key
  • A code snippet of your encryption code
  • A code snippet of your decryption code

Finally, are you building your own cryptosystem here? Or trying to implement someone else’s? Because if you’re building your own cryptosystem then… well… you really don’t want to be working at this level. Rather, you should look at Apple CryptoKit, which is an API specifically designed to guide you to building stuff that’s actually secure.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

SecKeyDecrypt succeeds but UTF-8 decoding returns nil for one specific RSA ciphertext (1024-bit, kSecPaddingNone)
 
 
Q