SecItemAdd errors extracting from a Pem file

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;
}
Answered by DTS Engineer in 249016022

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"
Accepted Answer

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"

Hi Eskimo


There is literally no examples of SecKeychainItemExport/Import anywhere, Apple or anywhere else. Nice to know though, thanks.


No, there is an example in the CryptoCompatibility code. My bad.


People have gotten SetItemAdd to work on Mac OS ( it works for me for a certificate). As I said we need to use something compatible with older versions of the OS - from 10.10 onwards. My response from DTS was there was a bug in SecItemAdd - but having deprecated it I suppose you know that.


I will retry with that API. I may return with questions about it if needed. Thanks

.Can extract the Public Key using SecItemImport on all OSes. Thanks.

SecItemAdd errors extracting from a Pem file
 
 
Q