我正在尝试使用SecKeyRawSign签署一些数据但我仍然得到-4 errSecUnimplemented。这似乎很奇怪,因为文档声明它可以在iPhone OS2.0及更高版本中使用。
有没有人能够使用此功能?如果是这样,是否涉及任何技巧?
〜内特
答案 0 :(得分:4)
如果您遇到此问题,很可能是因为您生成的私钥实际上并未保存到钥匙串中。我在停止并重新启动应用程序并签署消息时无法正常工作。
所以这是我的方法来完成这项工作。
这个生成密钥对
- (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];
}
** 编辑 **
iOS 9引入了一项名为 Secure Enclave 的新功能。如果要生成将存储在那里的密钥,那么您将需要使用256-bit EC
密钥,因为这是该区域唯一支持的类型。 keyPairDict
将会是这样的:
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
};
我知道参数是正确的,但我还没有自己测试 Secure Enclave ,所以如果由于某种原因这不起作用,请告诉我。
此外,供参考:256-bit EC
密钥相当于3072-bit RSA
密钥。
用于检索以下密钥的查询也会有所不同:
NSDictionary *queryKey = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrApplicationTag : tempTag,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC
};
由于 Secure Enclave 非常安全,因此您很可能无法检索私钥位。最有可能的是,您只能生成参考。但是你无论如何都不需要处理私钥数据。
** 结束编辑 **
此方法从钥匙串中检索实际位,而不仅仅是参考
- (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;
}
此方法将位保存到钥匙串
- (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);
}
}
然后你这样签名:
- (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;
}
答案 1 :(得分:1)
-4 errSecUnimplemented 错误是由对用于签名数据的私钥的错误引用引起的。这种情况令人困惑的错误。 errSecParam 会更好。
〜NATE