I try to get public or private key from certificate saved on device.
I'm using this method:
- (SecKeyRef)publicKeyFromFile:(NSString *)path
{
NSData * certificateData = [[NSData alloc] initWithData:[[NSFileManager defaultManager] contentsAtPath:path]];
if (certificateData != nil && certificateData.bytes != 0) {
CFDataRef cfDataPath = CFDataCreate(NULL, [certificateData bytes], [certificateData length]);
SecCertificateRef certificateFromFile = SecCertificateCreateWithData(NULL, cfDataPath);
if (certificateFromFile) {
SecPolicyRef secPolicy = SecPolicyCreateBasicX509();
SecTrustRef trust;
SecTrustCreateWithCertificates( certificateFromFile, secPolicy, &trust);
SecTrustResultType resultType;
SecTrustEvaluate(trust, &resultType);
SecKeyRef publicKeyObj = SecTrustCopyPublicKey(trust);
return publicKeyObj;
}
}
return nil;
}
There is data in cfDataPath, but certificateFromFile is always nil...
Does anyone know where's the problem?
Apple doc refers:
Obtaining a SecKeyRef Object for Public Key Cryptography
Extracting Keys from the Keychain If you are using existing public and private keys from your keychain, read Certificate, Key, and Trust Services Programming Guide to learn how to retrieve a SecKeychainItemRef object for that key.
Once you have obtained a SecKeychainItemRef, you can cast it to a SecKeyRef for use with this API.
Importing Existing Public and Private Keys Importing and exporting public and private key pairs is somewhat more complicated than generating new keys because of the number of different key formats in common use.
This example describes how to import and export a key pair in PEM (Privacy Enhanced Mail) format.
Read more : https://developer.apple.com/library/mac/documentation/Security/Conceptual/SecTransformPG/SigningandVerifying/SigningandVerifying.html and https://developer.apple.com/library/mac/documentation/Security/Conceptual/CertKeyTrustProgGuide/01introduction/introduction.html#//apple_ref/doc/uid/TP40001358
Try with this:
-(BOOL)trustCertFromChallenge:(NSURLAuthenticationChallenge *)challenge
{
SecTrustResultType trustResult;
SecTrustRef trust = challenge.protectionSpace.serverTrust;
OSStatus status = SecTrustEvaluate(trust, &trustResult);
//DLog(#"Failed: %#",error.localizedDescription);
//DLog(#"Status: %li | Trust: %# - %li",(long)status,trust,(long)trustResult);
if (status == 0 && (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed)) {
SecKeyRef serverKey = SecTrustCopyPublicKey(trust);
NSString *certPath = [[NSBundle mainBundle] pathForResource:#"MYCert" ofType:#"der"];
NSData *certData = [NSData dataWithContentsOfFile:certPath];
SecCertificateRef localCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
SecKeyRef localKey = NULL;
SecTrustRef localTrust = NULL;
SecCertificateRef certRefs[1] = {localCertificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, (void *)certRefs, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
OSStatus status = SecTrustCreateWithCertificates(certArray, policy, &localTrust);
if (status == errSecSuccess)
localKey = SecTrustCopyPublicKey(localTrust);
CFRelease(localTrust);
CFRelease(policy);
CFRelease(certArray);
if (serverKey != NULL && localKey != NULL && [(__bridge id)serverKey isEqual:(__bridge id)localKey])
return YES;
else
return NO;
}
//DLog(#"Failed: %#",error.localizedDescription);
return NO;
}
Follow the accepted answer for more details: Objective-C / C pulling private key (modulus) from SecKeyRef
Related
Our app is installing a root CA profile, and I want to verify it is installed and trusted by the user.
Currently this is roughly what we do (trimmed it for the core)
SecPolicyRef policyObj = SecPolicyCreateBasicX509();
SecTrustRef trustObj;
OSStatus error = SecTrustCreateWithCertificates((__bridge CFTypeRef _Nonnull)(fullChain), policyObj, &trustObj);
SecTrustResultType result;
error = SecTrustEvaluate(trustObj, &result);
CFRelease(trustObj);
CFRelease(policyObj);
return (kSecTrustResultUnspecified == result || kSecTrustResultProceed == result);
The problem is this, once the profile is installed the result is either kSecTrustResultUnspecified (iOS 10~) or kSecTrustResultProceed (iOS 11~)
But I want to check if user trusted it (under General->About->Trust Settings)
I dug around apple's docs and found nothing, moreover in the SecTrustEvaluate doc it says return value 'proceed' means user trusted the cert.
proceed— The user explicitly chose to trust a certificate in the chain (usually by clicking a button in a certificate trust panel).
Anyone has idea how this can be done? what am i missing?
So, after digging around i found out that SecPolicyCreateSSL is working as expect, still not 100% why SecPolicyCreateBasicX509 is not.
So for future ref if someone have this issue, this is what we did instead,
SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
SecTrustRef testTrust;
OSStatus status = SecTrustCreateWithCertificates((__bridge CFArrayRef)fullChain, policy, &testTrust);
status = SecTrustEvaluate(testTrust, &trustResult);
CFRelease(testTrust);
CFRelease(policy);
return (status == errSecSuccess) && (kSecTrustResultUnspecified == trustResult || kSecTrustResultProceed == trustResult);;
(basically using SecPolicyCreateSSL instead)
Improoved Objective-C code by #Al Ga, it tested and work on iOS 13/14
SecPolicyRef policyObj = SecPolicyCreateBasicX509();
SecTrustRef trustObj;
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"certName" ofType:#"crt"];
NSData *certData = [NSData dataWithContentsOfFile:filePath];
CFDataRef certCFR = (__bridge CFDataRef)certData;
SecCertificateRef certSCR = SecCertificateCreateWithData(NULL, certCFR);
NSArray* certArray = #[ (__bridge id)certSCR ];
OSStatus error = SecTrustCreateWithCertificates((__bridge CFTypeRef _Nonnull)certArray, policyObj, &trustObj);
SecTrustResultType result;
error = SecTrustEvaluate(trustObj, &result);
SecTrustResultType result will be contain uint32_t :
kSecTrustResultInvalid = 0
kSecTrustResultProceed = 1 //root cert Installed
kSecTrustResultConfirm = 2
kSecTrustResultDeny = 3
kSecTrustResultUnspecified = 4
kSecTrustResultRecoverableTrustFailure = 5 //root cert Doesn't installed
kSecTrustResultFatalTrustFailure = 6
kSecTrustResultOtherError = 7
I am working on an iPhone app that works on GcdAsyncSocket and creates TLS connection, I generate RSA keys and CSR using those and sent CSR to server, server responded with a certificate and some other certificate that is like public key to it. Now I need to make another TLS connection with server and send private key nd 2 certificates back to it. I have gone through many posts but didn't find any way how to achieve this.
If anyone could help and with some code that would be great help.
Thanks.
After spending good amount of time, I was able to resolve the issues using open SSL library. I used following code
+(PKCS12*)convertToP12Certificate:(NSString*)certificate
certificateChain:(NSArray*)certificateChain
publicCertificate:(NSString*) publicCertificate
andPrivateKey:(NSString*)privateKey
{
//we create a x509 from primary certificate which goes as a single entity when creating p12
const char *cert_chars = [certificate cStringUsingEncoding:NSUTF8StringEncoding];
BIO *buffer = BIO_new(BIO_s_mem());
BIO_puts(buffer, cert_chars);
X509 *cert;
cert = PEM_read_bio_X509(buffer, NULL, 0, NULL);
if (cert == NULL) {
NSLog(#"error");
}
X509_print_fp(stdout, cert);
//create a evp from private key which goes as a separate entity while creating p12
const char *privateKey_chars = [privateKey cStringUsingEncoding:NSUTF8StringEncoding];
BIO *privateKeyBuffer = BIO_new(BIO_s_mem());
BIO_puts(privateKeyBuffer, privateKey_chars);
EVP_PKEY *evp;
evp =PEM_read_bio_PrivateKey(privateKeyBuffer, NULL, NULL, "Enc Key");
if (evp == NULL) {
NSLog(#"error");
}
if (!X509_check_private_key(cert, evp))
{
NSLog(#"PK error");
}
PKCS12 *p12;
SSLeay_add_all_algorithms();
ERR_load_crypto_strings();
const char *cert_chars2 = [publicCertificate cStringUsingEncoding:NSUTF8StringEncoding];
BIO *buffer2= BIO_new(BIO_s_mem());
BIO_puts(buffer2, cert_chars2);
X509 *cert2;
cert2 = PEM_read_bio_X509(buffer2, NULL, 0, NULL);
if (cert2 == NULL) {
NSLog(#"error");
}
X509_print_fp(stdout, cert2);
STACK_OF(X509) *sk = sk_X509_new_null();
sk_X509_push(sk, cert2);
for(NSString * tempCertificate in certificateChain)
{
const char *cert_chars3 = [tempCertificate cStringUsingEncoding:NSUTF8StringEncoding];
BIO *buffer3= BIO_new(BIO_s_mem());
BIO_puts(buffer3, cert_chars3);
X509 *cert3;
cert3 = PEM_read_bio_X509(buffer3, NULL, 0, NULL);
if (cert3 == NULL) {
NSLog(#"error");
}
X509_print_fp(stdout, cert3);
sk_X509_push(sk, cert3);
}
p12 = PKCS12_create(P12_Password, P12_Name, evp, cert, sk, 0,0,0,0,0);
return p12;
}
+(NSArray*)getCertificateChainForCertificate:(NSString*)certificate
certificateChain:(NSArray*)certificateChain
publicCertificate:(NSString*) publicCertificate
andPrivateKey:(NSString*)privateKey
{
PKCS12 *p12 = [CryptoHelper convertToP12Certificate:certificate
certificateChain:certificateChain
publicCertificate: publicCertificate
andPrivateKey:privateKey];
NSData *PKCS12Data = [CryptoHelper convertP12ToData:p12];
NSArray *certs = nil;
SecIdentityRef identityRef = nil;
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef password = CFSTR(P12_Password);
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
CFRelease(options);
CFRelease(password);
if (securityError == errSecSuccess)
{
NSLog(#"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
if(certificateChain)
{
CFArrayRef certificates = (CFArrayRef)CFDictionaryGetValue(identityDict,kSecImportItemCertChain);
// There are 3 items in array when we retrieve certChain and for TLS connection cert
SecIdentityRef chainIdentity = (SecIdentityRef)CFArrayGetValueAtIndex(certificates,1);
certs = [[NSArray alloc] initWithObjects:(__bridge id)identityRef,(__bridge id)chainIdentity, nil];
}
else
{
certs = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, nil];
}
} else
{
NSLog(#"Error opening Certificate.");
}
return certs;
}
We can pass this array to TLS connection for key GCDAsyncSocketSSLCertificates.
I am currently stuck at a problem which involves encryption in iOS.
My client has given me the public key,
"-----BEGIN PUBLIC KEY-----
xxxx
-----END PUBLIC KEY-----"
The padding strategy that needs to be used is RSA/ECB/PKCS1Padding.
With android, it seems pretty straight forward
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plain.getBytes());
return encryptedBytes;
I dont see any direct methods to do this in iOS. Any of the common pods used like Commoncrypto doesnt allow me to force PKCS1 padding scheme. Being a pretty inexperienced guy with RSA and encryption, it would be very much appreciated if you could help me understand on how to approach this and guide me through this.
Using standard Security Framework - SecKeyEncrypt with kSecPaddingPKCS1 parameter
My issue was solved using non padding :
kSecPaddingNone
-(SecKeyRef)getPublicKeyForEncryption
{
NSString *thePath = [MAuthBundle pathForResource:#"certificate" ofType:#"der"];
//2. Get the contents of the certificate and load to NSData
NSData *certData = [[NSData alloc]
initWithContentsOfFile:thePath];
//3. Get CFDataRef of the certificate data
CFDataRef myCertData = (__bridge CFDataRef)certData;
SecCertificateRef myCert;
SecKeyRef aPublicKeyRef = NULL;
SecTrustRef aTrustRef = NULL;
//4. Create certificate with the data
myCert = SecCertificateCreateWithData(NULL, myCertData);
//5. Returns a policy object for the default X.509 policy
SecPolicyRef aPolicyRef = SecPolicyCreateBasicX509();
if (aPolicyRef) {
if (SecTrustCreateWithCertificates((CFTypeRef)myCert, aPolicyRef, &aTrustRef) == noErr) {
SecTrustResultType result;
if (SecTrustEvaluate(aTrustRef, &result) == noErr) {
//6. Returns the public key for a leaf certificate after it has been evaluated.
aPublicKeyRef = SecTrustCopyPublicKey(aTrustRef);
}
}
}
return aPublicKeyRef;
}
-(NSString*) rsaEncryptString:(NSString*) string
{
SecKeyRef publicKey = [self getPublicKeyForEncryption];
NSData* strData = [string dataUsingEncoding:NSUTF8StringEncoding];
CFErrorRef err ;
NSData * data = CFBridgingRelease(SecKeyCreateEncryptedData(publicKey, kSecKeyAlgorithmRSAEncryptionPKCS1, ( __bridge CFDataRef)strData, &err));
NSString *base64EncodedString = [data base64EncodedStringWithOptions:0];
return base64EncodedString;
}
i know is christmas but i have a huge problem that i need to solve, and i'm looking for my christmas miracle here...
I have read apples documentation, and there were only guides how to create RSA public and private keys from certificates. In my case, i have only RSA private key in .pem file. So my question is his: how should i sign data, using that key? I dont want to use openssl. i have tried it with no luck, and i think it's possible to sign data with RSA, by using apples API's.
This is how my key looks like:
-----BEGIN RSA PRIVATE KEY-----
..............................
-----END RSA PRIVATE KEY-----
This is what i have done so far:
-(NSString *)signing:(NSString *)dataString {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"PrestaMobilekey" ofType:#"pem"];
NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];
SecKeyRef privateKey = (__bridge SecKeyRef)(data);
uint8_t *signedHashBytes = NULL;
// calculate private key size
size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
// create space to put signature
signedHashBytes = (uint8_t *)malloc(signedHashBytesSize * sizeof(uint8_t));
memset((void *)signedHashBytes, 0x0, signedHashBytesSize);
OSStatus status = NULL;
// sign data
status = SecKeyRawSign(privateKey,
kSecPaddingPKCS1SHA1,
[[[dataString dataUsingEncoding:NSUTF8StringEncoding] SHA1] bytes],
CC_SHA1_DIGEST_LENGTH,
signedHashBytes,
&signedHashBytesSize);
if (privateKey) {
CFRelease(privateKey);
}
// get signature hash
NSData *signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];
// release created space
if (signedHashBytes) {
free(signedHashBytes);
}
if (status != errSecSuccess) {
return #"";
}
// return Base64 encoded signature string
return [Base64 encode:signedHash];
}
I really hope that someone will help me, with some good information and answer.
Thank you.
You don't need to use OpenSSL. You can sign your data using your method with a few tweaks. I don't think you can simply bridge and cast an NSData object to a SecKeyRef. You most likely need to save it to the keychain first.
You can do so with this method:
- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *saveDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecValueData : key,
(__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
};
SecKeyRef savedKey = NULL;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey);
if (sanityCheck != errSecSuccess) {
LOGGING_FACILITY1(sanityCheck != noErr, #"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
}
return savedKey;
}
If you don't want to get the reference immediately, you can change the method type to void and remove the return statement. Change (CFTypeRef *)&savedKey to NULL.
You can then retrieve the saved key like so:
- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
if (privateKeyRef != NULL) {
// already exists in memory, return
return privateKeyRef;
}
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
if (publicKeyRef != NULL) {
// already exists in memory, return
return publicKeyRef;
}
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *queryDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
};
SecKeyRef keyReference = NULL;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
if (sanityCheck != errSecSuccess) {
NSLog(#"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
}
if (isPrivate) {
privateKeyRef = keyReference;
}
else {
publicKeyRef = keyReference;
}
return keyReference;
}
Also, an easier way to return a base64 encoded string is to do this:
NSString *signatureString = [signedHash base64EncodedStringWithOptions:nil];
About privateTag and publicTag
privateTag and publicTag are used to mark the kSecAttrApplicationTag which defines the application that uses this key. You want to have a separate privateTag and publicTag to differentiate between your private key and public key.
It's a bit convoluted because I followed the sample code, but I defined my privateTag and publicTag this way:
SecKeyWrapper.h
#define kPublicKeyTag "com.sample.app.publickey"
#define kPrivateKeyTag "com.sample.app.privatekey"
SecKeyWrapper.m
// just under #implementation or #synthesize lines
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;
- (id)init {
if (self = [super init]) {
// Tag data to search for keys.
privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
}
return self;
}
Then use the privateTag and publicTag as you would in the code samples I provided above.
Ok, o found a solution for this problem my self. I hope that this will help to others... this is what helped me. I thought that i could do this without openssl, but i was wrong. But still, by doing like in that post, you won't need an extra library in you project. Use terminal
I want to recover a public key from a file. Here is the Java code that works:
PublicKey readPubKeyFromFile(AssetFileDescriptor cle) throws IOException {
// read RSA public key
byte[] encodedKey = new byte[(int) cle.getDeclaredLength()];
cle.createInputStream().read(encodedKey);
// create public key
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
PublicKey pk = null;
try {
KeyFactory kf = KeyFactory.getInstance("RSA");
pk = kf.generatePublic(publicKeySpec);
} catch(Exception e) {
Logger.getInstance().logError("KeyUtils", e.toString());
}
return pk;
}
And here is the iOS code that doesn't work:
-(SecKeyRef)readPublicKeyFromFile:(NSString*)filename andExtension:(NSString*)extension {
NSString* filePath = [[NSBundle mainBundle] pathForResource:filename ofType:extension];
NSData* encodedKey = [NSData dataWithContentsOfFile:filePath];
CFDataRef myCertData = (CFDataRef)encodedKey;
SecCertificateRef cert = SecCertificateCreateWithData (kCFAllocatorSystemDefault, myCertData);
CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust;
OSStatus check = SecTrustCreateWithCertificates(certs, policy, &trust);
if (check != noErr)
{
NSLog(#"Problem extracting public key from file: %#", filename);
return nil;
}
SecTrustResultType trustResult;
SecTrustEvaluate(trust, &trustResult);
SecKeyRef pub_key_leaf = SecTrustCopyPublicKey(trust);
return pub_key_leaf;
}
Any idea of what is wrong in my iOS code?
I've tested your code and there is nothing wrong with it. The problem seems to be related with the format of the certificates that you are trying to get the public key.
The function SecCertificateCreateWithData() assumes that the certificate that you are providing is in DER format. Most certificates that you find are encoded in base64 like the famous .pem format. I've tested your code with a correctly formatted DER certificate (the certificate form developer.apple.com converted to DER with openssl) and the public key is correctly extracted.
To convert a .pem certificate to DER simply use openssl in terminal:
openssl x509 -in developer.apple.com.pem -outform der -out cert.der
After that the output certificate file should work with no problems with your code.
But you can convert the certificate on the application itself, you only need to grab de x509 base64 encoded certificate (assuming that you are using .pem encoded certificates) and convert it to binary.
There is an example how you can do it:
This code will assume that the certificate is encoded in the following standard:
-----BEGIN CERTIFICATE-----
< your base64 encoded certificate goes here >
-----END CERTIFICATE-----
The code to convert this certificate to binary DER is:
-(NSData *)getBinaryCertificateFromPemEncodedFile:(NSString *)filename andExtension:(NSString *)extension
{
NSString* filePath = [[NSBundle mainBundle] pathForResource:filename ofType:extension];
NSString *pemCert = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
//The header and footer conforms to .pem specificatio
NSString *header = #"-----BEGIN CERTIFICATE-----";
NSString *footer = #"-----END CERTIFICATE-----";
NSString *base64Cert;
NSScanner *scanner = [NSScanner scannerWithString:pemCert];
//First we ignore the header part
[scanner scanString:header intoString:nil];
//Then we copy the base64 string excluding the footer
[scanner scanUpToString:footer intoString:&base64Cert];
//The reason I'm using NSDataBase64DecodingIgnoreUnknownCharacters is to exclude possible line breaks in the encoding
NSData *binaryCertificate = [[NSData alloc] initWithBase64EncodedString:base64Cert options:NSDataBase64DecodingIgnoreUnknownCharacters];
return binaryCertificate;
}
Then a small adaptation in your perfectly functional code does the trick:
-(SecKeyRef)readPublicKeyFromCertificate:(NSData *)binaryCertificate {
NSData *encodedKey = binaryCertificate;
CFDataRef myCertData = (CFDataRef)CFBridgingRetain(encodedKey);
SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorSystemDefault, myCertData);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust;
//If you only have one certificate you don't need to put it inside an array
OSStatus check = SecTrustCreateWithCertificates(cert, policy, &trust);
if (check != noErr)
{
NSLog(#"Problem extracting public key from certificate");
return nil;
}
SecTrustResultType trustResult;
SecTrustEvaluate(trust, &trustResult);
SecKeyRef pub_key_leaf = SecTrustCopyPublicKey(trust);
return pub_key_leaf;
}
Then just call it:
NSData *data = [self getBinaryCertificateFromPemEncodedFile:#"developer" andExtension:#"pem"];
SecKeyRef key = [self readPublicKeyFromCertificate:data];
NSLog(#"%#", key);
And if your certificate is "valid" you should see:
2014-09-15 21:52:13.275 cert[15813:60b] <SecKeyRef algorithm id: 1,
key type: RSAPublicKey, version: 2, block size: 2048 bits, exponent: {hex: 10001, decimal: 65537},
modulus: BE19E30F47F2D31F27D576CF007B3E615F986D14AFD0D52B825E01E90BA3E1CBB6F3A472E6AECDC28BC13D0B6E58FC497ACF61D80F274E4799602DA4F819E54ADDE2FBFA89FC4EB2172501DDED8DE0FBDDBC5550CC018C73E1FD8152C905DE850862B8D57596025DE1908D8337E95637AF0F52C4A11DA178FF737DCE09471BC0A49DAD7DB39F1BA1B693D3A12F9CA50EF388B50292C73076BF1EEE412A5CFA940E99D4CF07F17FAC87F0D0E2FC8FA3ACDDEEFCCE8AFEC407B94536FCB1E4ACF34773728D189F85EAE4347E0BF868D25C7CE89F8A29B4E6865C68F4F915DFA540549EE9333007145D367FE2852622AAD776F3E5D505A02E5155CC8646A01C1031,
addr: 0x9a48200>
For testing I've used the certificate from developer.apple.com, you can check the public key in the log and compare it.