X.509 RSA Encryption/Decryption iOS

encryption, ios, objective-c, rsa, x509

Solution

Where you are stuck

It seems your private key is encrypted (openssl asked you for a password on the command line), yet you do not decrypt it when you try to open it. Besides, private_key.pem is an RSA key, not a certificate, so you should use PEM_read_RSAPrivateKey.

The following decoding code should work:

int pass_cb(char *buf, int size, int rwflag, void* password) {
    snprintf(buf, size, "%s", (char*) password);
    return strlen(buf);
}

+(void)readTest{
    FILE *fp;
    RSA *x;
    NSString *path =[[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"pem"];
    fp=fopen([path UTF8String],"r");

    x = PEM_read_RSAPrivateKey(fp,&x,pass_cb,"key password");

    if (x == NULL) {
        NSLog(@"Cant Read File"); // This ALWAYS fires
    }

    fclose(fp);
    X509_free(x);
}

Alternatively, you could generate a non-encrypted key. Pass `-nodes` to openssl when creating the keys and the certificate.

Please note that you might need to make sure OpenSSL is properly initialized with:

SSL_library_init();
OpenSSL_add_all_algorithms();

Besides, OpenSSL generates error messages that could help you through development. You load the error strings with:

SSL_load_error_strings();

And you could call:

ERR_print_errors_fp(stderr);

RSA encryption and decryption on iOS

OpenSSL is not the only solution as Security framework on iOS contains everything you need. I guess you turned to OpenSSL because you did not know how to convert your private key file to valid parameters for SecKeyDecrypt.

The trick is to produce a PKCS#12 file and to call `SecPKCS12Import`.

You can produce this file with OpenSSL:

openssl x509 -inform der -outform pem -in public_key.der -out public_key.pem
openssl pkcs12 -export -in public_key.pem -inkey private_key.pem -out private_key.p12

This will ask you for an export password. This password should be passed to SecPKCS12Import (`"key password"` below).

NSString *privateKeyPath = [[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"];
NSData *pkcs12key = [NSData dataWithContentsOfFile:privateKeyPath];
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys: @"key password", kSecImportExportPassphrase, nil];
CFArrayRef              importedItems = NULL;
OSStatus returnCode = SecPKCS12Import(
                      (__bridge CFDataRef) pkcs12key,
                      (__bridge CFDictionaryRef) options,
                      &importedItems
                      );

`importedItems` is an array containing all imported PKCS12 items, and basically, the "identity" (private key + certificate).

NSDictionary* item = (NSDictionary*) CFArrayGetValueAtIndex(importedItems, 0);
SecIdentityRef  identity = (__bridge SecIdentityRef) [item objectForKey:(__bridge NSString *) kSecImportItemIdentity];
SecKeyRef privateKeyRef;
SecIdentityCopyPrivateKey(identity, &privateKeyRef);

Then you can use privateKeyRef to perform the decryption with `SecKeyDecrypt`. To match your encryption routine:

size_t cipherLen = [content length];
void *cipher = malloc(cipherLen);
[content getBytes:cipher length:cipherLen];
size_t plainLen = SecKeyGetBlockSize(privateKeyRef) - 12;
void *plain = malloc(plainLen);

OSStatus decryptReturnCode = SecKeyDecrypt(privateKeyRef, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen);

Problem

I need to implement encryption / decryption using a X.509 RSA public/private key pair. So far, I have something which I think will work for encryption, but I have no way of decrypting to check. Everything I try has issues reading the private key. Generating the key pairs (returns a `.der` and a `.pem`) ``` openssl req -x509 -out public_key.der -outform der -new -newkey rsa:1024 -keyout private_key.pem -days 3650 ``` Encryption (Not sure if this works, but looks like it does) ``` + (NSData *) RSAEncryptData:(NSData *)content { SecKeyRef publicKey; SecCertificateRef certificate; SecPolicyRef policy; SecTrustRef trust; size_t maxPlainLen; NSString *publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"]; NSData *base64KeyData = [NSData dataWithContentsOfFile:publicKeyPath]; certificate = SecCertificateCreateWithData(kCFAllocatorDefault, ( __bridge CFDataRef) base64KeyData); if (certificate == nil) { NSLog(@"Can not read certificate from data"); return nil; } policy = SecPolicyCreateBasicX509(); OSStatus returnCode = SecTrustCreateWithCertificates(certificate, policy, &trust); if (returnCode != 0) { NSLog(@"SecTrustCreateWithCertificates fail. Error Code: %d", (int)returnCode); return nil; } SecTrustResultType trustResultType; returnCode = SecTrustEvaluate(trust, &trustResultType); if (returnCode != 0) { return nil; } publicKey = SecTrustCopyPublicKey(trust); if (publicKey == nil) { NSLog(@"SecTrustCopyPublicKey fail"); return nil; } maxPlainLen = SecKeyGetBlockSize(publicKey) - 12; size_t plainLen = [content length]; if (plainLen > maxPlainLen) { NSLog(@"content(%ld) is too long, must < %ld", plainLen, maxPlainLen); return nil; } void *plain = malloc(plainLen); [content getBytes:plain length:plainLen]; size_t cipherLen = 128; // currently RSA key length is set to 128 bytes void *cipher = malloc(cipherLen); OSStatus encryptReturnCode = SecKeyEncrypt(publicKey, kSecPaddingPKCS1, plain, plainLen, cipher, &cipherLen); NSData *result = nil; if (encryptReturnCode != 0) { NSLog(@"SecKeyEncrypt fail. Error Code: %d", (int)returnCode); } else { result = [NSData dataWithBytes:cipher length:cipherLen]; } free(plain); free(cipher); return result; } ``` Decryption I have tried using OpenSSL's `PEM_read_X509` also `PEM_read_RSAPrivateKey`, but both fail to read the cert. I have not even gotten past that. If I could do this without having a dependency on the OpenSSL library, that would be even better. ``` +(void)readTest{ FILE *fp; X509 *x; NSString *path =[[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"pem"]; fp=fopen([path UTF8String],"r"); x=NULL; PEM_read_X509(fp,&x,NULL,NULL); // I have also tried PEM_read_RSAPrivateKey if (x == NULL) { NSLog(@"Cant Read File"); // This ALWAYS fires } fclose(fp); X509_free(x); } ``` If someone could help me out with encryption/decryption using X.509 RSA pairs, I'd appreciate it. Thanks.

Original source