21

How to sign and verify some data on iOS with an RSA key (preferably using the system own libcommonCrypto)?

6
  • Where are the RSA keys located (plural because of public and private parts for verify and sign)? Are they in the Keychain, or are they external in PEM or DER format?
    – jww
    Feb 14, 2014 at 10:22
  • As described in the question I would prefer to use libcommonCrypto. This means out of a programmers view the key is available as SecKeyRef (in memory reference which may originate from keychain, PEM or anything else supported by Apple Security Framework). The kind of key shouldn't matter for my question, but at the moment I am storing all the keys (own private key and some public keys) in the device sandboxed keychain.
    – miho
    Feb 14, 2014 at 13:03
  • Are you trying to encrypt/decrypt some data with public/private key using RSA?
    – jailani
    Feb 15, 2014 at 8:35
  • Encrypting and Decrypting data is well documented and I've already done it. And yes I know how signing and verifying works so I would be able to implement my own solution. But I'm wondering that in such an useful framework there isn't anything implemented. (Like java.security.Signature.)
    – miho
    Feb 15, 2014 at 9:03
  • what is the purpose for encryption if you have public and private keys are on your device ? isn't that still vulnearable ?
    – thndrkiss
    Feb 17, 2014 at 8:50

2 Answers 2

31

Since there hasn't been nearly any knowledge about signing and verifying found on StackOverflow and the Apple docs, I had to manually browse around in the iOS header files and found SecKeyRawSign and SecKeyRawVerify. The following lines of code seem to work.


Signing NSData (using SHA256 with RSA):

NSData* PKCSSignBytesSHA256withRSA(NSData* plainData, SecKeyRef privateKey)
{
    size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
    uint8_t* signedHashBytes = malloc(signedHashBytesSize);
    memset(signedHashBytes, 0x0, signedHashBytesSize);

    size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
    uint8_t* hashBytes = malloc(hashBytesSize);
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
        return nil;
    }

    SecKeyRawSign(privateKey,
                  kSecPaddingPKCS1SHA256,
                  hashBytes,
                  hashBytesSize,
                  signedHashBytes,
                  &signedHashBytesSize);

    NSData* signedHash = [NSData dataWithBytes:signedHashBytes
                                        length:(NSUInteger)signedHashBytesSize];

    if (hashBytes)
        free(hashBytes);
    if (signedHashBytes)
        free(signedHashBytes);

    return signedHash;
}

Verification (using SHA256 with RSA):

BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey)
{
    size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
    const void* signedHashBytes = [signature bytes];

    size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
    uint8_t* hashBytes = malloc(hashBytesSize);
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
        return nil;
    }

    OSStatus status = SecKeyRawVerify(publicKey,
                                      kSecPaddingPKCS1SHA256,
                                      hashBytes,
                                      hashBytesSize,
                                      signedHashBytes,
                                      signedHashBytesSize);

    return status == errSecSuccess;
}

Alternatives (OpenSSL):

There is a very good alternative available which utilizes OpenSSL directly instead of libCommonCrypto. MIHCrypto is a well-designed Objective-C wrapper library for OpenSSL which makes working with cryptography very easy. See the example below.

Generating a key is that simple:

MIHAESKeyFactory *factory = [[MIHAESKeyFactory alloc] init];
id<MIHSymmetricKey> aesKey = [factory generateKey];

Or loading a key from file:

NSData *privateKeyData = [[NSFileManager defaultManager] contentsAtPath:"mykey.pem"];
MIHRSAPrivateKey *privateKey = [[MIHRSAPrivateKey alloc] initWithData:privateKeyData];

Now sign something:

NSError *signingError = nil;
NSData *message = // load something to sign from somewhere
NSData *signature = [privateKey signWithSHA256:message error:&signingError]

For more examples browse the MIHCrypto page.

4
  • 1
    Would be great to have a similar example of SecKeyRawSign and SecKeyRawVerify in Swift. If someone succeeded in that, please link to / paste it here. Will do the same when I get it working.
    – stannie
    Apr 10, 2015 at 5:14
  • Any luck? I really need help! stackoverflow.com/questions/32759385/… Sep 29, 2015 at 17:08
  • 2
    Don't forget to import <CommonCrypto/CommonDigest.h> Dec 18, 2015 at 20:03
  • Works great! The only problem is when I verify signature in C#, the result is always false. Is it compatible with RSACryptoServiceProvider in C#?
    – iz25
    Sep 2, 2021 at 12:30
2

It is actually much simpler, no need to create the hash manually

func validateRSASignature(signedData: Data, signature: Data, publicKeyData: Data) -> Bool {

    // Create a SecKey instance from the public key data.
    let publicKey: SecKey! = SecKeyCreateWithData(publicKeyData as NSData, [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPublic
    ] as NSDictionary, nil)

    // Verify the RSA signature.
    return SecKeyVerifySignature(publicKey,
                                 .rsaSignatureMessagePKCS1v15SHA512,
                                 signedData as NSData,
                                 signature as NSData,
                                 nil)
}

This should be possible since iOS 10. You might want to expand on the error handling if your key data and other input varies.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.