Import Standard RSA key to iPhone key store

So following from my post on exporting a public key from an iPhone into java, here is some sample code for going the other way.

It follows the same logic as exporting the key but in reverse. (It starts with decoding the Base64 piece as the app I am working on always passes data in encoded format.)

Once it’s base 64 decoded it strips the ASN.1 encoding associated with the OID and sequence encoding that generally prepends the RSA key data. That leaves it with just the large numbers that make up the public key.

The key loading parts are heavily based on the Crypto example provided by Apple. It’s not been extensively tested, but it works with the few keys I’ve thrown at it. (refString is just a string constant I define elsewhere that identifies the key in the key store.)

Let me know what you find that’s wrong! I’d also be interested in knowing how to do this without saving the key as a persistent reference. I didn’t have time to work out how to do that part (assuming it’s possible).

- (BOOL) setPublicKey: (NSString *) keyAsBase64 {

    /* First decode the Base64 string */
    Base64 * b64 = [[Base64 alloc] init];
    NSData * rawFormattedKey = [b64 base64decode:[keyAsBase64 UTF8String] length:[keyAsBase64 length]];
    
    /* Now strip the uncessary ASN encoding guff at the start */
    unsigned char * bytes = (unsigned char *)[rawFormattedKey bytes];
    size_t bytesLen = [rawFormattedKey length];
    
    /* Strip the initial stuff */
    size_t i = 0;
    if (bytes[i++] != 0x30)
        return FALSE;
    
    /* Skip size bytes */
    if (bytes[i] > 0x80)
        i += bytes[i] - 0x80 + 1;
    else 
        i++;
    
    if (i >= bytesLen)
        return FALSE;
    
    if (bytes[i] != 0x30)
        return FALSE;

    /* Skip OID */
    i += 15;
    
    if (i >= bytesLen - 2)
        return FALSE;

    if (bytes[i++] != 0x03)
        return FALSE;
    
    /* Skip length and null */
    if (bytes[i] > 0x80)
        i += bytes[i] - 0x80 + 1;
    else 
        i++;

    if (i >= bytesLen)
        return FALSE;

    if (bytes[i++] != 0x00)
        return FALSE;

    if (i >= bytesLen)
        return FALSE;
    
    /* Here we go! */
    NSData * extractedKey = [NSData dataWithBytes:&bytes[i] length:bytesLen - i];

    /* Load as a key ref */
    OSStatus error = noErr;
    CFTypeRef persistPeer = NULL;
    
    NSData * refTag = [[NSData alloc] initWithBytes:refString length:strlen(refString)];
    NSMutableDictionary * keyAttr = [[NSMutableDictionary alloc] init];
    
    [keyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [keyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [keyAttr setObject:refTag forKey:(id)kSecAttrApplicationTag]; 
    
    /* First we delete any current keys */
    error = SecItemDelete((CFDictionaryRef) keyAttr);
    
    [keyAttr setObject:extractedKey forKey:(id)kSecValueData];
    [keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef];
    
    error = SecItemAdd((CFDictionaryRef) keyAttr, (CFTypeRef *)&persistPeer);
    
    if (persistPeer == nil || ( error != noErr && error != errSecDuplicateItem)) {
        NSLog(@"Problem adding public key to keychain");
        return FALSE;
    }
    
    CFRelease(persistPeer);
    
    publicKeyRef = nil;
    
    /* Now we extract the real ref */
    [keyAttr removeAllObjects];
    /*
    [keyAttr setObject:(id)persistPeer forKey:(id)kSecValuePersistentRef];
    [keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
    */
    [keyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [keyAttr setObject:refTag forKey:(id)kSecAttrApplicationTag];
    [keyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
    
    // Get the persistent key reference.
    error = SecItemCopyMatching((CFDictionaryRef)keyAttr, (CFTypeRef *)&publicKeyRef);    

    if (publicKeyRef == nil || ( error != noErr && error != errSecDuplicateItem)) {
        NSLog(@"Error retrieving public key reference from chain");
        return FALSE;
    }
    

    return TRUE;
}


Leave a Reply

35 thoughts on “Import Standard RSA key to iPhone key store”

  1. Hi Berin,
    I’m new to RSA and I just found your post, GREAT !!!
    Maybe you can help me with my case.
    May app receive a TLV format to have the modulo and the public key in hex
    For example
    Tag1=\x01
    Length=\x02
    Value=\x01 \x00 \x01
    Tag2=\x02
    Length=\x01 \x00
    Value= \x01 \x7F \x79 \x79 \x9E \x6B \x59 \x4E …..(256 Bytes) which is 2048 bytes
    Can I create keyAttr from that extractedKey like above ?
    And how I use the modulo and the public key to encode data (in hex) ??
    I’m stuck with no ideas and the documentation is very poor and complicated.
    Any help is more than welcome.

  2. Great post!
    Just to clarify, when this is finished, the key should be in the key chain, accessible by the tag with value refString?
    Thanks.

  3. Or if I define publicKeyRef as a SecKeyRef outside the function, that should also contain a pointer to the key, right?

  4. Hi, When I tried to add RSA key from server to keychain
    with your code , I got the following error.
    “errSecInteractionNotAllowed”.. Any idea? Regards, Tipton

  5. What changes are necessary to do the same for an OpenSSL generated private key?

  6. Should be roughly the same. Use openssl rsa -pubout ..... to give you the public key component and then same approach. I did a quick dump of a generated rsa public key and it is indeed in this format.

  7. Hi Berin,

    There is just one piece of the puzzle left, what are you exporting from java, to send to the iphone to get hold of your NSString. I’m trying to do this with a key from c# (modulus and exponent in xml) but I can find no info online other than your blog, and so I am considering creating a Java service to do this to fit with your model.

    Thanks in advance.

  8. Hi Sean,

    It’s just a PKCS#1 encoded public key. So an ASN.1 sequence of modulus and exponent but with some guff at the start to give the OID (which is the bit I strip to give to the iPhone). To be honest, if you are starting with those two, it’s probably easier just to build up the data exactly the way the iPhone expects it in C#. I.e. ASN.1 sequence of Modulus and Exponent.

    Cheers,
    Berin

  9. Berin,

    Wonderful post. Is there a way to do this for a private key as well. I am working on a host proof storage concept that spans multiple devices where the user stores both keys encrypted with AES-CCM 256. The user can access this data on multiple devices by decryption with AES password. I would like to use this tool for private keys too. Is this possible?

  10. Hi Patrick,

    I started looking at this and struggled to find information in the apple docs. At the time I didn’t really need to do it so didn’t go any further. I think you can do it, using a similar approach, but I wouldn’t be surprised if this was one of the APIs that wasn’t available on the iPhone.

    If I find anything on it or make it work I’ll report back 🙂

    Cheers,
    Berin

  11. Berin,

    Thanks for the enthusiasm in helping to try to solve this problem. It turns out that the RSA private key format required by the keychain is exactly the same as generated by the terminal openssl command: openssl genrsa -out testkeypair.pem 1024 without the text header and footer. There is no ASN.1 preamble (or similar obstacle to contend with). Simply strip the first and last line of the key (the non-base 64 part), convert this to NSData and place this in the field for kSecValueData as you have done in the above example. I have now succeeded in importing both private and public key pairs thanks to your wonderful post. I would like to ask if you would have any objections to me posting my project up on Github, including the functions you have given with proper attribution, for the benefit of the rest of the community. Let me know if this would be alright. Thanks,

    Patrick

  12. all the methods which are explained here is about exporting and importing private and public keys. is it possible to generate public and private key on iphone using exponent and modulus values? if yes please suggest me the methods used to generate??

  13. Hi Patrick,
    I tried saving a private key (generated in Java side) in IOS using the same steps as what you mentioned. I don’t know the “secitemcopymatching” method cannot retrieve the private key at all. It always returns Null. Please find below the steps i used:
    1. Get the private key (base 64 encoded ) in NSString
    2. Decode it to NSData
    3. Create the required keypairs for “KeyAttr” object
    4. Delete it in case if present already. Add it to key chain
    5. Retrieve the same from key chain

    Please let me know where it might have got wrong.

  14. Yup. Saw that when I first wrote the code. I have a system that uses public keys only in a protocol and I don’t want the overhead of adding things into certificates all the time. If you have it in a certificate or are happy to wrap it then knock yourself out.

    Personally I’ve always thought the overhead of being forced to use certificates for RSA encryption is just plain silly. They serve a very useful purpose – use them for that.

  15. Hello berin, I want to import public.key (KEY extension) from java server and using that key i want to do encrypt my message and resend it ti the server… so how can i do that ? i am not able to read .key file in ios

  16. Hi Berin,
    Thank you so much for your post.
    I have a public key, generated else where.
    I can not convert it to SecKeyRef.
    Because when I create Cert from public key data. It return NULL.
    So I can not get the SecKeyRef for RSA encryption
    Could you help me
    Thanks a lot

  17. HI DEAR,
    I have RSA public ke in the from exponent and modulus please suggest how to extractedKey,

    Thanks