4

I need to get the raw public key bytes (with openssl this is 'der' format) from a public key SecKeyRef to implement public key pinning with. The actual pinning function is already written, cannot change, and takes a unsigned char *pubkey, size_t pubkeylen with which to compare it's pins against.

This is what I have tried so far:

SecTrustRef trust;
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
SecKeyRef keyRef = SecTrustCopyPublicKey(trust);

CFDataRef publicKeyBits;

OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, &publicKeyBits) == errSecSuccess;
//OSStatus success = SecItemExport(keyRef, kSecFormatPEMSequence, 0, NULL, &publicKeyBits) == errSecSuccess;

size_t pubkeylen = CFDataGetLength(publicKeyBits);
printf("pubkeylen: %d\n", pubkeylen);
unsigned char *pubkey = CFDataGetBytePtr(publicKeyBits);

The problem is this does not seem to be DER format at all, I expect pubkeylen to be 550 for this particular key, but it prints 526, and the data isn't correct either. I also tried kSecFormatPEMSequence and then base64 decoding it with the same results (it does change into the identical 526 length array as before, so at least it's consistent).

edit: It looks like when exported to kSecFormatPEMSequence the PEM contains: -----BEGIN RSA PUBLIC KEY----- instead of the expected -----BEGIN PUBLIC KEY----- Which is a different format that does not base64-decode to DER, maybe it could be parsed with more API calls back into a DER format someway?

What am I missing? Thanks for any help!

edit: I have also tried the following:

CFDataRef d_tag = CFDataCreate(NULL, "com.bob", 7);

const void *cKeys[] = {kSecClass, kSecAttrKeyType,
     kSecAttrKeyClass, kSecValueData,
    kSecReturnData
};
const void *cValues[] = {kSecClassKey, kSecAttrKeyTypeRSA,
     kSecAttrKeyClassPublic, keyRef,
    kCFBooleanTrue
};

CFDictionaryRef saveDict = CFDictionaryCreate(NULL, cKeys, cValues,
  5L, NULL, NULL);

OSStatus secStatus = SecItemCopyMatching(saveDict, (CFTypeRef)&publicKeyBits);
if (secStatus == errSecDuplicateItem ) {
    printf("errSecDuplicateItem\n");
    SecItemDelete(saveDict);
    secStatus = SecItemAdd(saveDict, (CFTypeRef)&publicKeyBits );
}

if ( secStatus != noErr ) {
    printf("return NULL\n");
    return NULL;
}

size_t pubkeylen = CFDataGetLength(publicKeyBits);
printf("pubkeylen: %d\n", pubkeylen);
unsigned char *pubkey = CFDataGetBytePtr(publicKeyBits);

And the results are even worse, pubkeylen is consistently 96, but the data in pubkey is always different!

I have also tried converting https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/public_key_utils.m#L65 from objective-c, I get:

OSStatus resultAdd, resultDel = noErr;
CFDataRef d_tag = CFDataCreate(NULL, "com.bobe", 9);

const void *peerPublicKeyAddcKeys[] = {kSecClass, kSecAttrApplicationTag,
     kSecValueRef, kSecAttrAccessible, kSecReturnData
};
const void *peerPublicKeyAddcValues[] = {kSecClassKey, d_tag,
     keyRef, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
    kCFBooleanTrue
};

CFDictionaryRef peerPublicKeyAdd = CFDictionaryCreate(NULL, peerPublicKeyAddcKeys, peerPublicKeyAddcValues,
  5L, NULL, NULL);

const void *publicKeyGetcKeys[] = {kSecClass, kSecAttrApplicationTag,
     kSecReturnData
};
const void *publicKeyGetcValues[] = {kSecClassKey, d_tag,
    kCFBooleanTrue
};

CFDictionaryRef publicKeyGet = CFDictionaryCreate(NULL, publicKeyGetcKeys, publicKeyGetcValues,
  3L, NULL, NULL);

resultAdd = SecItemAdd(peerPublicKeyAdd, &publicKeyBits);
resultDel = SecItemDelete(publicKeyGet);

if ((resultAdd != errSecSuccess) || (resultDel != errSecSuccess))
{
    // Something went wrong with the Keychain we won't know if we did get the right key data
    printf("return NULL\n");
    return NULL;
}

And this time publicKeyBits is always NULL, I'm starting to tear my hair out here!

  • On iOS I was able to accomplish this. Essentially, you save the keyref in the keychain and read it as data. I used this question as a guide: https://stackoverflow.com/questions/16748993/ios-seckeyref-to-nsdata – thelaws Apr 25 '16 at 14:51
  • Thanks thelaws, I tried that and didn't get anywhere with it, I've updated my question. – moparisthebest Apr 25 '16 at 15:15
  • I remember it was a little tricky, and might be different on mac anyway. Here's a gist with our complete solution: https://gist.github.com/aleclaws/e1eb680081f05dfa6bb5b70d89bbd69c – thelaws Apr 25 '16 at 16:05

0 Answers0