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.
.der and a .pem)openssl req -x509 -out public_key.der -outform der -new -newkey rsa:1024 -keyout private_key.pem -days 3650
+ (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;
}
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.
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);
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);
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With