iosrsapkcs#7commoncrypto

Signing and Verifying on iOS using RSA


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


Solution

  • 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.