5

i know is christmas but i have a huge problem that i need to solve, and i'm looking for my christmas miracle here...

I have read apples documentation, and there were only guides how to create RSA public and private keys from certificates. In my case, i have only RSA private key in .pem file. So my question is his: how should i sign data, using that key? I dont want to use openssl. i have tried it with no luck, and i think it's possible to sign data with RSA, by using apples API's.

This is how my key looks like:

-----BEGIN RSA PRIVATE KEY-----
..............................
-----END RSA PRIVATE KEY-----

This is what i have done so far:

-(NSString *)signing:(NSString *)dataString {
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"PrestaMobilekey" ofType:@"pem"];
    NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];

    SecKeyRef privateKey = (__bridge SecKeyRef)(data);

    uint8_t *signedHashBytes = NULL;
    // calculate private key size
    size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);

    // create space to put signature
    signedHashBytes = (uint8_t *)malloc(signedHashBytesSize * sizeof(uint8_t));
    memset((void *)signedHashBytes, 0x0, signedHashBytesSize);

    OSStatus status = NULL;

    // sign data
    status = SecKeyRawSign(privateKey,
                           kSecPaddingPKCS1SHA1,
                           [[[dataString dataUsingEncoding:NSUTF8StringEncoding] SHA1] bytes],
                           CC_SHA1_DIGEST_LENGTH,
                           signedHashBytes,
                           &signedHashBytesSize);

    if (privateKey) {
        CFRelease(privateKey);
    }

    // get signature hash
    NSData *signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];

    // release created space
    if (signedHashBytes) {
        free(signedHashBytes);
    }

    if (status != errSecSuccess) {
        return @"";
    }

    // return Base64 encoded signature string
    return [Base64 encode:signedHash];
}

I really hope that someone will help me, with some good information and answer.

Thank you.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
user3130777
  • 61
  • 1
  • 6
  • It appears that you've already asked this question, as "[how to get rsa private key from pem file and sign data/string with it](http://stackoverflow.com/questions/20751338/how-to-get-rsa-private-key-from-pem-file-and-sign-data-string-with-it)". –  Dec 25 '13 at 18:46
  • Nobody answered that, and if you are trying to be helpful, answer my question that is all i need. I dont need guys saying that this is a duplicate, because i know it's not. – user3130777 Dec 25 '13 at 18:56
  • Asking the same question multiple times is not going to get you an answer faster. Your sample code appears to be the exact same (other than a few added comments), so it's evident you haven't done any work on your own since you last asked. –  Dec 25 '13 at 18:59
  • Thank you for your criticism, but i do everything i can to do my tasks. Your words have truth, but i have been struggling with this, for two weeks now, and i'm desperate. Please understand me. – user3130777 Dec 25 '13 at 19:25
  • This helped me http://stackoverflow.com/questions/10579985/how-can-i-get-seckeyref-from-der-pem-file – Michal Zaborowski Sep 18 '14 at 21:49

2 Answers2

3

You don't need to use OpenSSL. You can sign your data using your method with a few tweaks. I don't think you can simply bridge and cast an NSData object to a SecKeyRef. You most likely need to save it to the keychain first.

You can do so with this method:

- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
    OSStatus sanityCheck = noErr;
    NSData *tag;
    id keyClass;
    if (isPrivate) {
            tag = privateTag;
            keyClass = (__bridge id) kSecAttrKeyClassPrivate;
    }
    else {
            tag = publicTag;
            keyClass = (__bridge id) kSecAttrKeyClassPublic;
    }

    NSDictionary *saveDict = @{
                    (__bridge id) kSecClass : (__bridge id) kSecClassKey,
                    (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
                    (__bridge id) kSecAttrApplicationTag : tag,
                    (__bridge id) kSecAttrKeyClass : keyClass,
                    (__bridge id) kSecValueData : key,
                    (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
                    (__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
                    (__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
                    (__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
                    (__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
                    (__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
                    (__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
                    (__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
                    (__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
    };

    SecKeyRef savedKey = NULL;
    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey);
    if (sanityCheck != errSecSuccess) {
            LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
    }

    return savedKey;
}

If you don't want to get the reference immediately, you can change the method type to void and remove the return statement. Change (CFTypeRef *)&savedKey to NULL.

You can then retrieve the saved key like so:

- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
    OSStatus sanityCheck = noErr;
    NSData *tag;
    id keyClass;
    if (isPrivate) {
        if (privateKeyRef != NULL) {
            // already exists in memory, return
            return privateKeyRef;
        }
        tag = privateTag;
        keyClass = (__bridge id) kSecAttrKeyClassPrivate;
    }
    else {
        if (publicKeyRef != NULL) {
            // already exists in memory, return
            return publicKeyRef;
        }
        tag = publicTag;
        keyClass = (__bridge id) kSecAttrKeyClassPublic;
    }

    NSDictionary *queryDict = @{
            (__bridge id) kSecClass : (__bridge id) kSecClassKey,
            (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
            (__bridge id) kSecAttrApplicationTag : tag,
            (__bridge id) kSecAttrKeyClass : keyClass,
            (__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
    };

    SecKeyRef keyReference = NULL;
    sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
    if (sanityCheck != errSecSuccess) {
        NSLog(@"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
    }

    if (isPrivate) {
        privateKeyRef = keyReference;
    }
    else {
        publicKeyRef = keyReference;
    }
    return keyReference;
}

Also, an easier way to return a base64 encoded string is to do this:

NSString *signatureString = [signedHash base64EncodedStringWithOptions:nil];

About privateTag and publicTag

privateTag and publicTag are used to mark the kSecAttrApplicationTag which defines the application that uses this key. You want to have a separate privateTag and publicTag to differentiate between your private key and public key.

It's a bit convoluted because I followed the sample code, but I defined my privateTag and publicTag this way:

SecKeyWrapper.h

#define kPublicKeyTag           "com.sample.app.publickey"
#define kPrivateKeyTag          "com.sample.app.privatekey"

SecKeyWrapper.m

// just under @implementation or @synthesize lines
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;

- (id)init {
    if (self = [super init]) {
        // Tag data to search for keys.
        privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
        publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
    }

    return self;
}

Then use the privateTag and publicTag as you would in the code samples I provided above.

mikeho
  • 6,790
  • 3
  • 34
  • 46
  • Can you explain what privateTag and publicTag are? – knarf Aug 05 '14 at 21:27
  • @knarf74: I added a section that explained more about private and public tags. Let me know if have any more questions. Thanks! – mikeho Aug 05 '14 at 22:08
  • Nice! Thanks. I was looking at the crypto exercise and even the SecKeyWrapper class, but got lost in it. Appreciate the help. For anyone else wondering, check out the CryptoExercise in the iOS Developer Library: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008019-Intro-DontLinkElementID_2 – knarf Aug 06 '14 at 17:07
  • @knarf74: You're welcome! Let me know if I can help in any way else. Don't forget to upvote! :) – mikeho Aug 06 '14 at 21:27
1

Ok, o found a solution for this problem my self. I hope that this will help to others... this is what helped me. I thought that i could do this without openssl, but i was wrong. But still, by doing like in that post, you won't need an extra library in you project. Use terminal

Community
  • 1
  • 1
user3130777
  • 61
  • 1
  • 6