Question

I'm trying to encrypt a piece of text with RSA using a public key.

This is the code so far:

//EncViewController.h    
#import <UIKit/UIKit.h>

@interface EncViewController : UIViewController
{
    SecKeyRef publicKey;
    NSData *publicTag;
}

- (void)encryptWithPublicKey:(uint8_t *)plainBuffer cipherBuffer:(uint8_t *)cipherBuffer;
- (SecKeyRef)getPublicKeyRef;

@end

And the implementation file:

#import "EncViewController.h"

UInt8 privateKeyIdentifier[] = "-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEowIBAAKCAQEAqRU6LMhcarK6sgaTgpPEgldUbyBsszsKKpSCDoYqH8fNDpin\n"
"puYhgR/NDurrZ6t1n3AsyACmmEMbkaxbjJ4Ee8U3zXy3dsbhsshaGWdktlfIipUw\n"
"4b+ybi4M3T/AcKHSonWw6NWKPLmA/R5nARQMg4tIlRQF6qQPlBaVxk1kHE5NEKxz\n"
"JaCC01ODsFHEAglDaEMZtGvjGl/hLxI8nqhgLgQsQpU+5ysM4/cmjgqVrU8YzFHp\n"
"9oMGWdcgQhZR3oJxdrWPn7/Vs5gWKuO6369Pc2kMLv8Tba64uAkNYMQXnMNM+TFy\n"
"DzNGOAUHkHS70pntrnufsGb8/hRv5nEcZxfd6QIDAQABAoIBAGdUfKfvjmL6dRPk\n"
"5vLuwTHykrwS8bsawpzBAzZDEa04Wn2oFxTtIN6bg6KxOEmzw/86+3MCyszUfh2p\n"
"Wo116EGHhhHDPQ+OfVHYFQ/fWvIAdaMTh7r+ftnMtLnlgwKSMnpsOEAieAeiSkzl\n"
"7ob/LKKbVTEd+nup5YdXwhJdK2gMB1xFLURYi3/ziNXXAb/MxUkldB0B9ZOdDa6Y\n"
"Ezh1HD2KT3PedZOGuHfXoU2yDi10KeXFjoN6KsOwEJJmicJ1KWevuzYr5zBvZC2N\n"
"xy6CUHp9iDQONdem1C1cMg/ht4QZpR0Rrz2wRvOxjwUfXd0qel3tBMttHiitRS4u\n"
"sCF3YwECgYEA3Jj94d1kb0XM5k6y5eS/DODBW+QRI2JRudoQOCnrMnNNYdv6KHvO\n"
"WfmIDXyw4gi//5NoEDcNf6BPgb487ImXB4AcjMOI0Ox0vNhH300yKbYHF9XgkrvI\n"
"1aCwE/4QvyBb4zLdt/QxYVxJU9qRC5CsSlqjpsqUBvMhH5mb5DdlMckCgYEAxDfP\n"
"pFOYHRYYCYDj+Plldf6Z9wSbfp7mOm7j2VvXMgVAlG1HixIcXW10GG0vq3Obpl4Z\n"
"Li8bik4aX9pN/rRGEgZxNbOc92QkzwkNO00W4L2yHdwD5ONXT8IB8KW7uq3BILld\n"
"nPm3H9lR+eoLYCbGBJVVAux43uFJHmip8dSKWyECgYAyAxV8AX9losODa1Avwp51\n"
"wvbTaG9iQEYbBo8qzIYgF/fxObgLOZZc26+2rxBDDozI2ph7JKAQ0T11QrX5QnBV\n"
"KPxQBXydMR5+OeKBg7TdtAEDrF+PpLcxh46j2bPeQO3UIpIVxGz1j8CoHCNKJfCJ\n"
"oub7R24r7S8TRPYM1WpB0QKBgBgepC+W9wPcS2gfp+ZbAdXXTiXHMzYFYlecefWm\n"
"DHXhn2afaEP4HfR+F99IzKtOsrtopb+/mqsNnsBZnWzJzDM7B3+KbKkcbknk2vOH\n"
"LmDB8Lsq6G+iYoEzX/ms5b60zSCwqIk7SP9pP2JxGqTfH0hA8wpA9zquhsZuJzXK\n"
"4pcBAoGBAKDJhWP7pM66jrSuxX8+dOlQpb+u6oRkcwEFLEXP7Blz45MJVGGAdjCj\n"
"hYgz9bg5pNywBiwE1MbBDw/CZejcQk5CgSY0AfDqsBRipHJ7N7KR8aZfpJi4JSgy\n"
"c5qnAC1sBY/zDjERsOX0CJQfumr5/ae9y637ezvDXVkHcIoNjRxx\n"
"-----END RSA PRIVATE KEY-----";
SecKeyRef privateKey;
NSData *privateTag;

const size_t BUFFER_SIZE = 64;
const size_t CIPHER_BUFFER_SIZE = 1024;
size_t FINAL_CIPHER_BUFFER_SIZE;
const uint32_t PADDING = kSecPaddingPKCS1;

UInt8 publicKeyIdentifier[] = "-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqRU6LMhcarK6sgaTgpPE\n"
"gldUbyBsszsKKpSCDoYqH8fNDpinpuYhgR/NDurrZ6t1n3AsyACmmEMbkaxbjJ4E\n"
"e8U3zXy3dsbhsshaGWdktlfIipUw4b+ybi4M3T/AcKHSonWw6NWKPLmA/R5nARQM\n"
"g4tIlRQF6qQPlBaVxk1kHE5NEKxzJaCC01ODsFHEAglDaEMZtGvjGl/hLxI8nqhg\n"
"LgQsQpU+5ysM4/cmjgqVrU8YzFHp9oMGWdcgQhZR3oJxdrWPn7/Vs5gWKuO6369P\n"
"c2kMLv8Tba64uAkNYMQXnMNM+TFyDzNGOAUHkHS70pntrnufsGb8/hRv5nEcZxfd\n"
"6QIDAQAB\n"
"-----END PUBLIC KEY-----\n";

@interface EncViewController ()

@end

@implementation EncViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
 #pragma mark - Navigation

 // In a storyboard-based application, you will often want to do a little preparation before navigation
 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
 {
 // Get the new view controller using [segue destinationViewController].
 // Pass the selected object to the new view controller.
 }
 */

- (void)viewDidLoad
{
    [super viewDidLoad];
    privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
    publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];

    const char *plainBuffer;
    const char *cipherBuffer;

    NSString *textToEncrypt = @"super secret text";



    //const char inputString[] = "How to Encrypt data with public key and Decrypt data with private key";
    int len = strlen(textToEncrypt.UTF8String);
    // TODO: this is a hack since i know inputString length will be less than BUFFER_SIZE
    if (len > BUFFER_SIZE) len = BUFFER_SIZE-1;

    plainBuffer = calloc(BUFFER_SIZE, sizeof(uint8_t));
    cipherBuffer = calloc(CIPHER_BUFFER_SIZE, sizeof(uint8_t));
    plainBuffer = [textToEncrypt UTF8String];
    NSLog(@"init() plainBuffer: %s", plainBuffer);
    [self encryptWithPublicKey:plainBuffer cipherBuffer:cipherBuffer];
    NSLog(@"encrypted data: %s", cipherBuffer);

    const char *d = cipherBuffer;
    int l = strlen(d);
    NSLog(@"final length: %d",l);

    NSData *encodeData = [[NSData alloc] initWithBytes:cipherBuffer length:FINAL_CIPHER_BUFFER_SIZE];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *cachePath = [paths objectAtIndex:0];
    NSLog(@"cache dir: %@",cachePath);

    BOOL written = [encodeData writeToFile:[NSString stringWithFormat:@"%@%@",cachePath,@"/test.txt"] atomically:NO];
    NSLog(@"File was writtend: %d",written);

    NSString *base64String = [encodeData base64EncodedStringWithOptions:0];
    NSLog(@"Encode String Value: %@", base64String);

    free(plainBuffer);
    free(cipherBuffer);
}

- (NSString *)encryptText:(NSString *)textToEncrypt
{
    uint8_t *plainBuffer;
    uint8_t *cipherBuffer;
    const char *input = [textToEncrypt UTF8String];

    int len = strlen(input);

    plainBuffer = (uint8_t *)calloc(len, sizeof(uint8_t));
    cipherBuffer = (uint8_t *)calloc(CIPHER_BUFFER_SIZE, sizeof(uint8_t));

    strncpy( (char *)plainBuffer, input, len);

    NSLog(@"init() plainBuffer: %s", plainBuffer);
    //NSLog(@"init(): sizeof(plainBuffer): %d", sizeof(plainBuffer));
    [self encryptWithPublicKey:(UInt8 *)plainBuffer cipherBuffer:cipherBuffer];
    NSLog(@"encrypted data: %s", cipherBuffer);
    NSString *s = [NSString stringWithUTF8String:(char *)cipherBuffer];
    NSLog(@"encrypted nsstring %@",s);
    NSData *data = [[NSData alloc] initWithBytes:cipherBuffer length:sizeof(cipherBuffer)];
    NSLog(@"====== /second test =======================================");

    free(plainBuffer);
    //free(cipherBuffer);
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

/* Borrowed from:
 * https://developer.apple.com/library/mac/#documentation/security/conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html
 */
- (void)encryptWithPublicKey:(uint8_t *)plainBuffer cipherBuffer:(uint8_t *)cipherBuffer
{

    NSLog(@"== encryptWithPublicKey()");

    OSStatus status = noErr;

    NSLog(@"** original plain text 0: %s", plainBuffer);

    size_t plainBufferSize = strlen((char *)plainBuffer);

    FINAL_CIPHER_BUFFER_SIZE = CIPHER_BUFFER_SIZE;

    NSLog(@"SecKeyGetBlockSize() public = %lu", SecKeyGetBlockSize([self getPublicKeyRef]));
    //  Error handling
    // Encrypt using the public.
    status = SecKeyEncrypt([self getPublicKeyRef],
                           PADDING,
                           plainBuffer,
                           plainBufferSize,
                           &cipherBuffer[0],
                           &FINAL_CIPHER_BUFFER_SIZE
                           );
    NSLog(@"encryption result code: %ld (size: %lu)", status, FINAL_CIPHER_BUFFER_SIZE);
    NSLog(@"encrypted text: %s", cipherBuffer);
}

-(SecKeyRef)getPublicKeyRef {

    OSStatus sanityCheck = noErr;
    SecKeyRef publicKeyReference = NULL;

    if (publicKeyReference == NULL) {
        [self generateKeyPair:512];
        NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];

        // Set the public key query dictionary.
        [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
        [queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
        [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];


        // Get the key.
        sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference);


        if (sanityCheck != noErr)
        {
            publicKeyReference = NULL;
        }


        //        [queryPublicKey release];

    } else {
        publicKeyReference = publicKey;
    }

    return publicKeyReference;
}

- (void)generateKeyPair:(NSUInteger)keySize {
    OSStatus sanityCheck = noErr;
    publicKey = NULL;
    privateKey = NULL;

    //  LOGGING_FACILITY1( keySize == 512 || keySize == 1024 || keySize == 2048, @"%d is an invalid and unsupported key size.", keySize );

    // First delete current keys.
    //  [self deleteAsymmetricKeys];

    // Container dictionaries.
    NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];

    // Set top level dictionary for the keypair.
    [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];

    // Set the private key dictionary.
    [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [privateKeyAttr setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    // See SecKey.h to set other flag values.

    // Set the public key dictionary.
    [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [publicKeyAttr setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    // See SecKey.h to set other flag values.

    // Set attributes to top level dictionary.
    [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
    [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];

    // SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
    sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKey, &privateKey);
    //  LOGGING_FACILITY( sanityCheck == noErr && publicKey != NULL && privateKey != NULL, @"Something really bad went wrong with generating the key pair." );
    if(sanityCheck == noErr  && publicKey != NULL && privateKey != NULL)
    {
        NSLog(@"Successful");
    }
}

@end

Afterwards I'm trying to decrypt the generated encrypted file (test.txt) using the following command from terminal:

cat test.txt | openssl rsautl -pkcs -decrypt -inkey private.pem which should output my decrypted message but I get instead the following error:

RSA operation error 15328:error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02:/SourceCache/OpenSSL098/OpenSSL098-50/src/crypto/rsa/rsa_pk1.c:190: 15328:error:04065072:rsa routines:RSA_EAY_PRIVATE_DECRYPT:padding check failed:/SourceCache/OpenSSL098/OpenSSL098-50/src/crypto/rsa/rsa_eay.c:614:

I don't get why since I use the same padding both for encryption in xcode and for decryption using the openssl command...

Was it helpful?

Solution

It turned out that's quite tricky to handle keys using chars.

It's much easier to use .DER format of the public key/certificate, as CommonCrypto supports it already. I solved my problem using the .DER directly. You can load it like this:

SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, ( __bridge CFDataRef)publicKeyFileContent);

where publicKeyFileContent is an NSData instance containing your .DER content.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top