问题
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:
- Which am I supposed to store? The certificate or the identity?
- How do I store it and retrieve it?
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.
回答1:
I can't think of a good reason to store the certificate in the keychain, although I'm sure there might be some. I store just the identity (which is the private key portion) in the keychain. To make it easier to find the identity in the keychain you generate a persistent reference to it (See listing 2-3 in the link), and then save that persistent reference in the filesystem for your app. The persistent ref is just a CFDataRef, which you can toll free bridge to an NSData object and then easily save/load. When you want the private key for crypto/whatever, you use that persistent reference to load the identity from the keychain (see listing 2-4 in the link). I'd post some code for you but I'm in the process of rebuilding my development machine right now and don't have Xcode installed just yet.
回答2:
- 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.
来源:https://stackoverflow.com/questions/30598729/storing-a-p12-certificate-in-keychain-to-use-later