Using SecKeyRawSign on iPhone

I am trying to sign some data using SecKeyRawSign, but I keep getting -4 errSecUnimplemented. This seems odd because the documentation states that it is available on iPhone OS2.0 and later.

Could anyone use this feature? If so, are there any tricks?

~ Nate

+4
source share
2 answers

If you have this problem, most likely this is because the private key you created is not actually stored in the keychain. I realized this when stopping and restarting the application and signing the message did not work.

So, here are my methods to make this work.

This generates a key pair

- (void)generateKeyPair:(NSUInteger)keySize { OSStatus sanityCheck = noErr; publicKeyRef = NULL; privateKeyRef = NULL; LOGGING_FACILITY1( keySize == 512 || keySize == 1024 || keySize == 2048, @"%d is an invalid and unsupported key size.", keySize ); // First delete current keys. [self deleteAsymmetricKeys]; // Container dictionaries. // See SecKey.h for other values NSDictionary *privateKeyDict = @{ (__bridge id) kSecAttrIsPermanent : [NSNumber numberWithBool:YES], (__bridge id) kSecAttrApplicationTag : privateTag }; // See SecKey.h for other values NSDictionary *publicKeyDict = @{ (__bridge id) kSecAttrIsPermanent : [NSNumber numberWithBool:YES], (__bridge id) kSecAttrApplicationTag : publicTag }; NSDictionary *keyPairDict = @{ (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA, (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize], (__bridge id) kSecPrivateKeyAttrs : privateKeyDict, (__bridge id) kSecPublicKeyAttrs : publicKeyDict }; // SecKeyGeneratePair returns the SecKeyRefs sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef) keyPairDict, &publicKeyRef, &privateKeyRef); LOGGING_FACILITY( sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL, @"Something really bad went wrong with generating the key pair." ); // retrieve the actual bits for the keys, not just the references NSData *publicKeyBits = [self getKeyBitsFromKey:publicKeyRef]; NSData *privateKeyBits = [self getKeyBitsFromKey:privateKeyRef]; // save the keys to the keychain [self saveKeyToKeychain:publicKeyBits keySize:keySize private:NO]; [self saveKeyToKeychain:privateKeyBits keySize:keySize private:YES]; } 

** EDIT **

IOS 9 introduces a new feature called Secure Enclave. If you want to generate a key that will be stored there and only there, you will need to use the 256-bit EC key, as this is the only type supported by the enclave. Instead, keyPairDict will look like this:

 NSDictionary *keyPairDict = @{ (__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC, // we can use keySize here if we want // but since 256 is the only available size // we can just hardcode it for now (__bridge id) kSecAttrKeySizeInBits : @256], (__bridge id) kSecPrivateKeyAttrs : privateKeyDict, (__bridge id) kSecPublicKeyAttrs : publicKeyDict }; 

I know the parameters are correct, but I have not tested Secure Enclave yet, so let me know if this does not work for some reason.

Also, for your reference: the 256-bit EC key is equivalent to the 3072-bit RSA key.

The query used to retrieve the key below will also differ:

 NSDictionary *queryKey = @{ (__bridge id) kSecClass : (__bridge id) kSecClassKey, (__bridge id) kSecAttrApplicationTag : tempTag, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC }; 

Since Secure Enclave is secure, you most likely will not be able to get the private key bit. Most likely, you can create a link. But you still don't need to process the secret key data.

** END EDIT **

This method extracts the actual bits from the keychain, not just the link

 - (NSData *)getKeyBitsFromKey:(SecKeyRef)givenKey { static const uint8_t publicKeyIdentifier[] = "com.sample.temp"; NSData *tempTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)]; NSDictionary *queryKey = @{ (__bridge id) kSecClass : (__bridge id) kSecClassKey, (__bridge id) kSecAttrApplicationTag : tempTag, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA }; // Temporarily add key to the Keychain, return as data: NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithDictionary:queryKey copyItems:YES]; [attributes setObject:(__bridge id) givenKey forKey:(__bridge id) kSecValueRef]; [attributes setObject:@YES forKey:(__bridge id) kSecReturnData]; // result codes: https://developer.apple.com/library/ios/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html#//apple_ref/doc/uid/TP30000157-CH4g-339030 OSStatus sanityCheck = noErr; NSData *keyBits = nil; CFTypeRef result; sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result); if (sanityCheck == errSecSuccess) { keyBits = CFBridgingRelease(result); // Remove from Keychain again: (void) SecItemDelete((__bridge CFDictionaryRef) queryKey); return keyBits; } else if (sanityCheck == errSecDuplicateItem) { // Remove from Keychain again: (void) SecItemDelete((__bridge CFDictionaryRef) queryKey); return [self getKeyBitsFromKey:givenKey]; } return nil; } 

This method stores a bit in the keychain.

 - (void)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate { OSStatus sanityCheck = noErr; NSData *tag; id keyClass; if (isPrivate) { tag = privateTag; keyClass = (__bridge id) kSecAttrKeyClassPrivate; } else { tag = publicTag; keyClass = (__bridge id) kSecAttrKeyClassPublic; } NSDictionary *saveDict = @{ (__bridge id) kSecClass : (__bridge id) kSecClassKey, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA, (__bridge id) kSecAttrApplicationTag : tag, (__bridge id) kSecAttrKeyClass : keyClass, (__bridge id) kSecValueData : key, (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize], (__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize], (__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse, (__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue, (__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse, (__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue, (__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse, (__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue, (__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse }; SecKeyRef savedKey = NULL; sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey); if (sanityCheck != errSecSuccess) { LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck); } } 

And then you subscribe like this:

 - (NSData *)getSignatureBytes:(NSData *)plainText { OSStatus sanityCheck = noErr; NSData *signedHash = nil; uint8_t *signedHashBytes = NULL; size_t signedHashBytesSize = 0; SecKeyRef privateKey = NULL; privateKey = [self getKeyRef:YES]; signedHashBytesSize = SecKeyGetBlockSize(privateKey); // Malloc a buffer to hold signature. signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t)); memset((void *) signedHashBytes, 0x0, signedHashBytesSize); // Sign the SHA1 hash. sanityCheck = SecKeyRawSign(privateKey, kTypeOfSigPadding, (const uint8_t *) [[self getHashBytes:plainText] bytes], kChosenDigestLength, signedHashBytes, &signedHashBytesSize ); LOGGING_FACILITY1( sanityCheck == noErr, @"Problem signing the SHA1 hash, OSStatus == %d.", sanityCheck ); // Build up signed SHA1 blob. signedHash = [NSData dataWithBytes:(const void *) signedHashBytes length:(NSUInteger) signedHashBytesSize]; if (signedHashBytes) { free(signedHashBytes); } return signedHash; } 
+4
source

Error -4 errSecUnimplemented was caused by a bad reference to the private key used to sign the data. Error in this situation. An errSecParam error would be better.

~ Nate

+1
source

Source: https://habr.com/ru/post/1310248/


All Articles