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!