iOS证书导入-.p12与NSData
我在iOS中使用客户端证书时遇到问题 当我在应用程序中存储.p12文件并按如下方式导入时:iOS证书导入-.p12与NSData,ios,objective-c,ssl,ssl-certificate,Ios,Objective C,Ssl,Ssl Certificate,我在iOS中使用客户端证书时遇到问题 当我在应用程序中存储.p12文件并按如下方式导入时: -(void)importCertificateToKeychain:(NSURL *)url withPassword:(NSString *)password name:(NSString *)name { importedItems = NULL; NSD
-(void)importCertificateToKeychain:(NSURL *)url
withPassword:(NSString *)password
name:(NSString *)name {
importedItems = NULL;
NSData* data = [url isFileURL] ? [NSData dataWithContentsOfFile:url.path] : [NSData dataWithContentsOfURL:url];
err = SecPKCS12Import(
(__bridge CFDataRef) data,
(__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
password, kSecImportExportPassphrase,
nil
],
&importedItems
);
if (err == noErr) {
for (NSDictionary * itemDict in (__bridge id) importedItems) {
SecIdentityRef identity;
identity = (__bridge SecIdentityRef) [itemDict objectForKey:(__bridge NSString *) kSecImportItemIdentity];
NSMutableDictionary *addItemDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)identity, kSecValueRef,
nil
];
[addItemDictionary setValue:name forKey:(__bridge NSString *)kSecAttrLabel];
err = SecItemAdd((__bridge CFDictionaryRef)addItemDictionary, NULL);
}
这工作正常,我可以加载以下内容:
-(NSURLCredential *)loadCertificateFromKeychain:(NSString *)name {
OSStatus err;
CFArrayRef latestIdentities;
NSMutableDictionary *filterDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassIdentity, kSecClass,
kSecMatchLimitAll, kSecMatchLimit,
kCFBooleanTrue, kSecReturnRef,
nil];
[filterDictionary setValue:name forKey:(__bridge NSString *)kSecAttrLabel];
err = SecItemCopyMatching((__bridge CFDictionaryRef)(filterDictionary),
(CFTypeRef *) &latestIdentities
);
SecIdentityRef identityRef = (SecIdentityRef)CFArrayGetValueAtIndex(latestIdentities, 0);
id certificates = nil;
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:identityRef certificates:certificates persistence:NSURLCredentialPersistenceNone];
return credential;
}
然后,我在中使用凭据
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
但是,当我从服务器获得相同的证书(不是以.p12文件存储,而是以NSData格式存储)时,这就不再有效了。我试图将收到的NSData放入SecPKCS12Import,但它返回-26275,因此我猜这不仅仅是p12的NSData,而是“提取”的证书。(我不控制服务器端)
这就是为什么我也尝试使用
SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(certificateDataFromServer));
并将结果保存为:
CFTypeRef cert = nil;
CFStringRef certLabel = CFStringCreateWithCString(
NULL, certLabelString,
kCFStringEncodingUTF8);
OSStatus err =
SecItemAdd((__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)
kSecClassCertificate, kSecClass,
kCFBooleanTrue, kSecReturnRef,
deviceCertificate, kSecValueRef,
certLabel,kSecAttrLabel,
nil],
&cert);
然后在authenticateForChallenge中,我使用身份和证书
- (BOOL)authenticateForChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] > 0) {
return NO;
}
NSURLCredential *newCredential = nil;
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
SecIdentityRef identity = [self clientIdentity];
NSArray *certs = [self clientCertificates];
if (identity) {
newCredential = [NSURLCredential credentialWithIdentity:identity
certificates:certs
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
return YES;
}
return NO;
}
}
但它不起作用。在调试中,我可以看到有正确的标识和一个正确证书的数组,但服务器上没有使用客户端证书,身份验证不起作用。
服务器日志:
TLSv1.2 - "POST /my_service/v1 HTTP/1.1" - --- - (certificate should be here instead of ---)
有人能看出我的方法有什么问题吗?我真的认为这应该特别适用于使用Credential:forAuthenticationChallenge是正确的证书。它怎么可能消失
thx
m
注意:在不同的应用程序(与第一个共享密钥链)中,我可以看到密钥链中存储了两个相同的身份和一个证书。(但两种身份都不起作用)也许我的代码的某些部分可以帮助您解决问题。请随时提供一些反馈 我也有同样的问题。最后,我通过仔细查看身份,解决了SSL握手问题。我意识到从我的密钥链中提取了两个身份(这是我保存DER编码的客户端证书进行身份验证的地方) 这是我现在的工作代码:
致意
-(void) addCertToKeychain:(NSData*)certInDer
{
/*certInDer a Base64 encoded NSData from a String which I received from server ([[NSData alloc] initWithBase64Encoding:resultString];)*/
SecCertificateRef cert;
cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)(certInDer));
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:(__bridge id)kSecClassCertificate forKey:(__bridge id)kSecClass];
[dictionary setObject:(__bridge id)(cert) forKey:(__bridge id<NSCopying>)(kSecValueRef)];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
assert(status == noErr || status == errSecDuplicateItem);
}
- (NSURLCredential*)getClientCertFromKeychain {
OSStatus err;
CFArrayRef latestIdentities;
NSURLCredential *credential=nil;
NSMutableDictionary *filterDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassIdentity, kSecClass,
kSecMatchLimitAll, kSecMatchLimit,
kCFBooleanTrue, kSecReturnRef,
nil];
err = SecItemCopyMatching((__bridge CFDictionaryRef)(filterDictionary),
(CFTypeRef *) &latestIdentities
);
/*This is the interesting part: The query might return more than one identity!!! */
//NSLog(@"Array Count %@",latestIdentities);
// Identity to obtain a certificate.
if(err == errSecSuccess) {
/*Here you can choose the identity to use, maybe you have to try according to your certificate structure*/
SecIdentityRef identityRef = (SecIdentityRef)CFArrayGetValueAtIndex(latestIdentities, 1);
SecCertificateRef certificate = nil;
//Create a new CertificateRef from identity
OSStatus status = SecIdentityCopyCertificate(identityRef, &certificate);
if(status == errSecSuccess){
const void *certs[] = { certificate };
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);
NSArray *certificatesForCredential = (__bridge NSArray *)certsArray;
//Fill the credential information
credential = [NSURLCredential credentialWithIdentity:identityRef
certificates:certificatesForCredential
persistence:NSURLCredentialPersistenceNone];
CFRelease(certsArray);
}
CFRelease(certificate);
CFRelease(identityRef);
}
return credential;
}
NSURLCredential *certData= [self getClientCertFromKeychain];
if(certData!=nil){
[[challenge sender] useCredential:certData forAuthenticationChallenge:challenge];
} else {
[challenge.sender cancelAuthenticationChallenge:challenge];
}