Given a SecKeyRef
loaded using SecItemImport
from an RSA private key is there a way to obtain or create a SecKeyRef
for only the public key components? In OpenSSL this could be done by copying the modulus and public exponent to a new struct, but SecKeyRef
is opaque and I've been unable to find a function that performs this operation.

- 14,353
- 9
- 63
- 113

- 13,466
- 4
- 40
- 57
2 Answers
As of macOS 10.12, iOS/tvOS 10, and watchOS 3 the function SecKeyCopyPublicKey now exists to do this.

- 13,466
- 4
- 40
- 57
Old question, but because I was fighting with it today, I found a way. Let's say you have base64 encoded public RSA key (it's not certificate, it's not der, pem, ...), created in Java (X509 certificate public key), you can create SecKeyRef
in this way:
- (NSData *)stripPublicKeyHeader2:(NSData *)keyBits {
// Skip ASN.1 public key header
if (keyBits == nil) {
return nil;
}
unsigned int len = [keyBits length];
if (!len) {
return nil;
}
unsigned char *c_key = (unsigned char *)[keyBits bytes];
unsigned int idx = 0;
if (c_key[idx++] != 0x30) {
return nil;
}
if (c_key[idx] > 0x80) {
idx += c_key[idx] - 0x80 + 1;
}
else {
idx++;
}
if (idx >= len) {
return nil;
}
if (c_key[idx] != 0x30) {
return nil;
}
idx += 15;
if (idx >= len - 2) {
return nil;
}
if (c_key[idx++] != 0x03) {
return nil;
}
if (c_key[idx] > 0x80) {
idx += c_key[idx] - 0x80 + 1;
}
else {
idx++;
}
if (idx >= len) {
return nil;
}
if (c_key[idx++] != 0x00) {
return nil;
}
if (idx >= len) {
return nil;
}
// Now make a new NSData from this buffer
return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}
- (SecKeyRef)publicKey:(NSData *)d_key withTag:(NSString *)tag
{
NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
NSDictionary *saveDict = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : d_tag,
(__bridge id) kSecAttrKeyClass : (__bridge id) kSecAttrKeyClassPublic,
(__bridge id) kSecValueData : d_key
};
OSStatus secStatus = SecItemAdd((__bridge CFDictionaryRef)saveDict, NULL );
if (secStatus == errSecDuplicateItem ) {
SecItemDelete((__bridge CFDictionaryRef)saveDict);
secStatus = SecItemAdd((__bridge CFDictionaryRef)saveDict, NULL );
}
if ( secStatus != noErr ) {
return NULL;
}
NSDictionary *queryDict = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPublic,
(__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
};
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
secStatus = SecItemCopyMatching((__bridge CFDictionaryRef)queryDict,
(CFTypeRef *)&keyRef);
if ( secStatus != noErr ) {
return NULL;
}
return keyRef;
}
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:@"base64encoded X509 private key"
options:0];
certificateData = [self stripPublicKeyHeader2:keyData];
SecKeyRef key = [self publicKey:certificateData withTag:[[NSUUID UUID] UUIDString]];
Little bit awkward, not my code, was Googling for the whole day, many pieces glued together. Have to clean it, ... Take it as a hack compiled from the internet.
Tested against server where code is in Java and this is the only way how to do it. The only way I did find. Maybe there are another ways, but only this one does work for me and I can encrypt with this RSA public key (and our server code (Java) can decrypt it. And it does work only without padding (not recommended) or with kSecPaddingOAEP
padding on iOS side and with RSA/NONE/OAEPWithSHA1AndMGF1Padding
on Java side.

- 20,249
- 5
- 47
- 73