I am getting a strange error on OS 10.10 ( and later) when trying to get the Public Key reference from a PEM file.
The SecItemAdd returns the value errSecNoSuchAttr . The specified attribute does not exist. (-25302)
This error seems strange, as if there is some issue with the paramaters passed, which work on iOS. Removing them one by one causes other issues.
I have also created a technical support ticket on this if any internal engineer wants the full code DTS has it.
- (SecKeyRef) getPublicKeyFromPem:(NSString*)name{
NSMutableString *string = [[NSMutableString alloc] init];
NSString *startPublicKey = @"-----BEGIN PUBLIC KEY-----";
NSString *endPublicKey = @"-----END PUBLIC KEY-----";
NSString* path = [[NSBundle mainBundle] pathForResource:@"test"
ofType:@"pem"];
NSString* content = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:NULL];
NSLog(@"Public Key Key: %@", content);
NSError* error = nil;
NSString *publicKeyStr;
NSScanner *scanner = [NSScanner scannerWithString:content];
[scanner scanUpToString:startPublicKey intoString:nil];
[scanner scanString:startPublicKey intoString:nil];
[scanner scanUpToString:endPublicKey intoString:&publicKeyStr];
publicKeyStr = [publicKeyStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *publicTag = [[NSData alloc] initWithBase64EncodedString:publicKeyStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
SecKeyRef publicKey = [self addPeerPublicKey:name keyBits:publicTag error:&error];
[string appendString:[NSString stringWithFormat:@"Public Key Object : %@",publicKey]];
if (error){
[string appendString:[NSString stringWithFormat:@"error is %@", error]];
}
[self.pemResultTextView setString:string];
return publicKey;
}- (SecKeyRef)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKeyData error: (NSError**)outError {
OSStatus sanityCheck = noErr;
SecKeyRef peerKeyRef = NULL;
CFTypeRef persistPeer = NULL;
CFErrorRef cfError = NULL;
SecAccessControlRef accessCtrl;
accessCtrl = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlocked,
kSecAccessControlUserPresence, &cfError);
if(!accessCtrl) {
if (outError)
*outError = CFBridgingRelease(cfError);
return NO;
}
NSData * tagData = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
NSDictionary *peerPublicKeyAttr = @{(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA,
(__bridge id)kSecAttrApplicationTag: tagData,
(__bridge id)kSecValueData: publicKeyData,
(__bridge id)kSecAttrKeySizeInBits: @(publicKeyData.length),
(__bridge id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
(__bridge id)kSecAttrAccessControl: CFBridgingRelease(accessCtrl)};
sanityCheck = SecItemAdd((CFDictionaryRef) CFBridgingRetain(peerPublicKeyAttr), (CFTypeRef *)&persistPeer);
if (checkStatus(sanityCheck, outError) == NO){
return nil;
}
return peerKeyRef;
}
You are, alas, off in the weeds here. If you’re working on macOS you should use macOS’s full-featured API for importing (and exporting) credentials, namely
<Security/SecImportExport.h>. This can import a PEM file directly, without you having to mess around with parsing the text.
Even on iOS you shouldn’t be using
SecItemAdd to import a public key. That (hackish and not well supported) approach is only necessary prior to iOS 10. On iOS 10 and later you should use
SecKeyCreateWithData.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"