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;
}