Hello, my main project uses Objective-C, so my code here is in Objective-C as well, but feel free to provide examples in Swift—I can translate them into Objective-C myself.
I am able to obtain both SecCertificateRef and SecKeyRef without any issues. However, my problem lies in the next step, where I need to use them to generate a SecIdentityRef. I attempted to follow the suggestions found online to store SecCertificateRef and SecKeyRef in the keychain and then retrieve the SecIdentityRef, but this approach failed, as shown in the demo in my email, or in the example below.
SecCertificateRef certificateRef = [self certificateRef]; // get it not null
SecKeyRef privateKeyRef = [self privateKeyRef]; // get it not null too
NSString *privateTag = @"yc_clinet_pri_tag";
NSString *certTag = @"yc_clinet_cert_tag";
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
[privateKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[privateKeyAttr setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrLabel];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
[privateKeyAttr setObject:(__bridge id _Nonnull)(privateKeyRef) forKey:(id)kSecValueRef];
[privateKeyAttr setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass];
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef];
[privateKeyAttr setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible];
OSStatus privateKeyAttrCheck = SecItemAdd((CFDictionaryRef) privateKeyAttr, nil);
NSString *priKeyMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(privateKeyAttrCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)privateKeyAttrCheck];
NSLog(@"query privateKey: %@", priKeyMsg);
if ((privateKeyAttrCheck != noErr) && (privateKeyAttrCheck != errSecDuplicateItem)){
return nil;
}
NSMutableDictionary * queryCertificate = [[NSMutableDictionary alloc] init];
[queryCertificate setObject:(id)kSecClassCertificate forKey:(id)kSecClass];
[queryCertificate setObject:certTag forKey:(id)kSecAttrLabel];
[queryCertificate setObject:(__bridge id)certificateRef forKey:(id)kSecValueRef];
[queryCertificate setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible];
OSStatus certCheck = SecItemAdd((CFDictionaryRef)queryCertificate, nil);
NSString *certMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(certCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)certCheck];
NSLog(@"query certificate: %@", certMsg);
if ((certCheck != noErr) && (certCheck != errSecDuplicateItem)) {
return nil;
}
SecIdentityRef identityRef = NULL;
NSMutableDictionary * queryIdentityRef = [[NSMutableDictionary alloc] init];
[queryIdentityRef setObject:(id)kSecClassIdentity forKey:(id)kSecClass];
[queryIdentityRef setObject:privateTag forKey:(id)kSecAttrApplicationTag];
[queryIdentityRef setObject:certTag forKey:(id)kSecAttrLabel];
[queryIdentityRef setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[queryIdentityRef setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
OSStatus identityCheck = SecItemCopyMatching((CFDictionaryRef)queryIdentityRef, (CFTypeRef *)&identityRef);
NSString *identityMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(identityCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)identityCheck];
NSLog(@"query identity: %@", identityMsg); // here print null
if (identityCheck != noErr) {
return nil;
}
return identityRef;
please help
Post
Replies
Boosts
Views
Activity
My ultimate goal is to perform client-side authentication when making requests, so I need to generate an NSURLCredential. To create the NSURLCredential, I need a SecIdentityRef. From my research, I found that storing the private key and certificate in the keychain allows me to retrieve the SecIdentityRef. Below is my code, which includes the private key and certificate. I would appreciate any help.
NSString *certString = @"-----BEGIN CERTIFICATE-----MIICJjCCAcugAwIBAgIQbR5jIkUPfd6lID5G3+vSIDAKBggqhkjOPQQDAjBOMQswCQYDVQQGEwJVUzELMAkGA1UEBxMCR1oxDjAMBgNVBAoTBUNUWVVOMRAwDgYDVQQLEwdBRFYtREVWMRAwDgYDVQQDEwdyb290LWNhMB4XDTI0MDMyODA3MjA1NVoXDTM0MDMyNjA3MjE1NVowTjELMAkGA1UEBhMCVVMxCzAJBgNVBAcTAkdaMQ4wDAYDVQQKEwVDVFlVTjEQMA4GA1UECxMHQURWLURFVjEQMA4GA1UEAxMHcm9vdC1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPa/ZkLJSjZI/0KWZA54iGmfkkXcMh00vjmK1k+ZDdKepDDsa8gkHPI7I67qSARMk/Aq3+mZHx1rpf63w/sxpKCjgYowgYcwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE8Dk3jsnNaJ3gLz/caeaDU4YaE9MB8GA1UdIwQYMBaAFE8Dk3jsnNaJ3gLz/caeaDU4YaE9MCQGA1UdEQQdMBuGGXNwaWZmZTovL2MxLmVjaGEuY3R5dW4uY24wCgYIKoZIzj0EAwIDSQAwRgIhAP6K83hDw8MQVftyTzsiEiqavndUELQV2JZpYGDhS3XGAiEA0R9ShXvK/0qs+rJyKdzttmNTBQmcfq1S/nbTm983Igc=-----END CERTIFICATE-----";
NSData *certData = [self dataFromPEMString:certString replacing:@"CERTIFICATE"];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
return certificate;
}
NSString *priKeyString = @"-----BEGIN EC PRIVATE KEY-----MHcCAQEEIDS6kb3mrIXtqtDS4Fuh6eYB3XK0lq7waNA6UR6Q8hY5oAoGCCqGSM49AwEHoUQDQgAE9r9mQslKNkj/QpZkDniIaZ+SRdwyHTS+OYrWT5kN0p6kMOxryCQc8jsjrupIBEyT8Crf6ZkfHWul/rfD+zGkoA==-----END EC PRIVATE KEY-----";
NSData *privateKeyData = [self dataFromPEMString:priKeyString replacing:@"EC PRIVATE KEY"];
NSMutableDictionary *privateKeyOptions = [@{
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeEC,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
(id)kSecAttrKeySizeInBits: @256
} mutableCopy];
#if !TARGET_OS_SIMULATOR
[privateKeyOptions setObject:(id)kSecAttrTokenIDSecureEnclave forKey:(id)kSecAttrTokenID];
#endif
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateWithData((__bridge CFDataRef)privateKeyData,
(__bridge CFDictionaryRef)privateKeyOptions,
&error);
if (error) {
CFStringRef errorDescription = CFErrorCopyDescription(error);
NSLog(@"Error creating private key: %@", errorDescription);
CFRelease(errorDescription);
return NULL;
}
return privateKey;
}
Here is the code client authentication
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
// get identity
SecIdentityRef identity = [[YCCertificateManager instance] clientIdentity];
SecCertificateRef certificate = [[YCCertificateManager instance] certificateRef];
if (identity) {
*credential = [NSURLCredential credentialWithIdentity:identity certificates:@[] persistence:NSURLCredentialPersistencePermanent];
return NSURLSessionAuthChallengeUseCredential;
} else {
return NSURLSessionAuthChallengePerformDefaultHandling;
}
}
Therefore, I need a SecIdentityRef parameter, so I am looking for a method to obtain SecIdentityRef.
- (SecIdentityRef)clientIdentity {
SecCertificateRef certificateRef = [self certificateRef];
SecKeyRef privateKeyRef = [self privateKeyRef];
SecKeyRef publicKeyRef = SecCertificateCopyKey(certificateRef);
NSString *privateTag = @"yc_clinet_pri_tag";
NSString *publicTag = @"yc_clinet_pub_tag";
NSString *certTag = @"yc_clinet_cert_tag";
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
[privateKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[privateKeyAttr setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrLabel];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
[privateKeyAttr setObject:(__bridge id _Nonnull)(privateKeyRef) forKey:(id)kSecValueRef];
[privateKeyAttr setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass];
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef];
[privateKeyAttr setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible];
OSStatus privateKeyAttrCheck = SecItemAdd((CFDictionaryRef) privateKeyAttr, nil);
NSString *priKeyMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(privateKeyAttrCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)privateKeyAttrCheck];
NSLog(@"query privateKey: %@", priKeyMsg);
if ((privateKeyAttrCheck != noErr) && (privateKeyAttrCheck != errSecDuplicateItem)){
return nil;
}
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
[publicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[publicKeyAttr setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag];
// [publicKeyAttr setObject:publicTag forKey:(id)kSecAttrPublicKeyHash];
[publicKeyAttr setObject:(__bridge id _Nonnull)(publicKeyRef) forKey:(id)kSecValueRef];
[publicKeyAttr setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass];
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef];
[publicKeyAttr setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible];
OSStatus pubKeyCheck = SecItemAdd((CFDictionaryRef) publicKeyAttr, nil);
NSString *pubKeyMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(pubKeyCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)pubKeyCheck];
NSLog(@"query publicKey: %@", pubKeyMsg);
if ((pubKeyCheck != noErr) && (pubKeyCheck != errSecDuplicateItem)){
return nil;
}
NSMutableDictionary * queryCertificate = [[NSMutableDictionary alloc] init];
[queryCertificate setObject:(id)kSecClassCertificate forKey:(id)kSecClass];
[queryCertificate setObject:certTag forKey:(id)kSecAttrLabel];
[queryCertificate setObject:(__bridge id)certificateRef forKey:(id)kSecValueRef];
[queryCertificate setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible];
OSStatus certCheck = SecItemAdd((CFDictionaryRef)queryCertificate, nil);
NSString *certMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(certCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)certCheck];
NSLog(@"query certificate: %@", certMsg);
if ((certCheck != noErr) && (certCheck != errSecDuplicateItem)) {
return nil;
}
SecIdentityRef identityRef = NULL;
NSMutableDictionary * queryIdentityRef = [[NSMutableDictionary alloc] init];
[queryIdentityRef setObject:(id)kSecClassIdentity forKey:(id)kSecClass];
[queryIdentityRef setObject:privateTag forKey:(id)kSecAttrApplicationTag];
[queryIdentityRef setObject:certTag forKey:(id)kSecAttrLabel];
[queryIdentityRef setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[queryIdentityRef setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
OSStatus identityCheck = SecItemCopyMatching((CFDictionaryRef)queryIdentityRef, (CFTypeRef *)&identityRef);
NSString *identityMsg = (__bridge_transfer NSString *)SecCopyErrorMessageString(identityCheck, NULL) ?: [NSString stringWithFormat:@"%d", (int)identityCheck];
NSLog(@"query identity: %@", identityMsg);
if (identityCheck != noErr) {
return nil;
}
return identityRef;
}
query identity: The specified item could not be found in the keychain.
Everything works fine except for the part where obtaining SecIdentityRef throws an error: "query identity: The specified item could not be found in the keychain."
please help