I am trying to follow the apple docs for dealing with client p12 certificates here:
https://developer.apple.com/library/ios/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html#//apple_ref/doc/uid/TP40001358-CH208-SW13
I have successfully loaded a .p12 cert from the file system:
- (SecIdentityRef)getClientCertificate:(NSString *) certificatePath {
SecIdentityRef identity = nil;
NSData *PKCS12Data = [NSData dataWithContentsOfFile:certificatePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef password = CFSTR("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);
identity = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
} else {
NSLog(@"Error opening Certificate.");
}
return identity;
}
I then get the certificate for that identity:
- (CFArrayRef)getCertificate:(SecIdentityRef) identity {
SecCertificateRef certificate = nil;
SecIdentityCopyCertificate(identity, &certificate);
SecCertificateRef certs[1] = { certificate };
CFArrayRef array = CFArrayCreate(NULL, (const void **) certs, 1, NULL);
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(array, myPolicy, &myTrust);
if (status == noErr) {
NSLog(@"No Err creating certificate");
} else {
NSLog(@"Possible Err Creating certificate");
}
return array;
}
But what I really want to do is store the certificate (or the identity) in my apps keychain so I am not reading it from the file system.
A couple of questions:
The link above talks about 'Getting and Using Persistent Keychain References' which is very confusing to me.
It also talks about 'Finding a Certificate In the Keychain', but it mentions using the name of the certificate to find it. I am not sure where the 'name' comes from.
- Which am I supposed to store? The certificate or the identity?
It depends on what you are doing, and whether you need the private key on your device for authentication. A SecIdentityRef
contains the certificate and private key. If you are using the .p12 file for authentication, then you likely want to store and use the full identity. If you only need the certificate, then I wouldn't be loading the full .p12 onto the drive in the first place, as it contains the private key.
- How do I store it and retrieve it?
I would recommend storing your identity (or certificate) in the keychain, and using kSecAttrLabel
as a unique reference for querying.
The documentation you need to look at is Storing an Identity in the Keychain, which directs you to Storing a Certificate in the Keychain and outlines some minor differences required between storing an identity and certificate.
This is done as follows (adapted from the links above):
Save to Keychain
// Create a query (with unique label for reference later)
NSDictionary* addquery = @{ (id)kSecValueRef: (__bridge id)identity,
(id)kSecClass: (id)kSecClassIdentity,
(id)kSecAttrLabel: @"My Identity",
};
// Add the identity to the keychain
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
if (status != errSecSuccess) {
// Handle the error
}
Load from Keychain
// Query the keychain for your identity
NSDictionary *getquery = @{ (id)kSecClass: (id)kSecClassIdentity,
(id)kSecAttrLabel: @"My Identity",
(id)kSecReturnRef: @YES,
};
// Retrieve the identity from the keychain
SecIdentityRef identity = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)getquery,
(CFTypeRef *)&identity);
if (status != errSecSuccess) { <# Handle error #> }
else { <# Use identity #> }
if (identity) { CFRelease(identity); } // After you are done with it
As RyanR mentioned, you can also create a persistent reference to the keychain item once it has been saved, and then save that to file. I would recommend adding [kSecReturnPersistentRef][3]
to your addquery
to achieve this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With