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;
}
Hi Berin,
Thanks man..It worked for me..
I am really grateful to you..nice post!!!
Regards,
Syam S
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.
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.
Also,
What is the Base64 class?
Thanks again.
Or if I define publicKeyRef as a SecKeyRef outside the function, that should also contain a pointer to the key, right?
Yup – sounds right!
It’s a class I have that just decodes or encodes base64 text. If you have “raw” data just ignore it.
AS long as you do all the things to pull the key from the keychain using the refString tag it should work, yes.
How did you go with this sahlouls? Did you sort it out?
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
What changes are necessary to do the same for an OpenSSL generated private key?
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.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.
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
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?
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
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
No problems at all – happy for it to be used anywhere with attribution. Glad it helped!
For those interested in a sample project that imports and exports public and private RSA keys. Please let me know if you have suggestions/additions to the project. Here is the github link:
https://github.com/phoganuci/iOS-Certificate–Key–and-Trust-Sample-Project#readme
Thanks Berin,
Patrick
Sorry, the link I posted is incorrect. Here is the correct link to that project: https://github.com/kuapay/iOS-Certificate–Key–and-Trust-Sample-Project
How use SecKeyRawVerify?
First of all, berin thanks for your post, it really helped me out.
Im now able to import my (under java) generated public key to the ios keychain, but the encrypted output could not be decrypted on the java server side.
Maybe you have a minute left and could take a look over my code here:
http://stackoverflow.com/questions/12526452/difference-between-pkcs1-padding-rsa-under-ios-objc-and-java
Thx in advance
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??
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.
Hai Patrick, how to change public key in .pem file?
When I first came across this, I thought this was the only way to import public keys in iOS. Just a comment that on iOS, __the recommended way is to use a signed certificate__ https://devforums.apple.com/message/114555#114555.
In other words __you don’t have import a public key this way__. Creating a self-signed certificate is easy, and you don’t have to pay anything for it, see http://stackoverflow.com/a/16096064/111307
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.
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
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
HI DEAR,
I have RSA public ke in the from exponent and modulus please suggest how to extractedKey,
Thanks
thanks to @berin and @Patrick