在iPhone上使用SecKeyRawSign [英] Using SecKeyRawSign on the iPhone

查看:98
本文介绍了在iPhone上使用SecKeyRawSign的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用SecKeyRawSign签署一些数据,但我一直收到-4 errSecUnimplemented.这似乎很奇怪,因为文档指出它在iPhone OS2.0和更高版本中可用.

I'm trying to sign some data using SecKeyRawSign but I keep getting a -4 errSecUnimplemented. That seems strange since the documentation states that it is available in iPhone OS2.0 and later.

有人可以使用此功能吗?如果是这样,有什么花招吗?

Has anyone been able to use this function? If so, are there any tricks involved?

〜内特

推荐答案

如果遇到此问题,很可能是因为您生成的私钥实际上并未保存到钥匙串中.在停止并重新启动应用程序时,我发现了这一点,并且对消息进行了签名.

If you're having this problem, most likely it is because the private key you generated isn't actually being saved into the keychain. I figured this out when stopping and restarting the application and signing the message wasn't working.

因此,这是我实现此工作的方法.

So here are my methods to make this work.

此密钥生成密钥对

- (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看起来像这样:

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

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 ,所以请告诉我这是否由于某些原因而无效.

I know the parameters are correct, but I haven't myself tested the Secure Enclave yet, so let me know if this doesn't work for some reason.

此外,供参考:256-bit EC键等效于3072-bit RSA键.

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

用于检索以下键的查询也将有所不同:

The query used to retrieve the key below would also be different:

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

由于 Secure Enclave 是安全的,因此您很可能将无法检索私钥位.最有可能的是,您只能生成参考.但是无论如何,您都不需要处理私钥数据.

Because the Secure Enclave is, well, secure, you most likely won't be able to retrieve the private key bits. Most likely, you'll only be able to generate a reference. But you shouldn't need to handle the private key data anyway.

** END EDIT **

此方法从钥匙串而不是引用中检索实际位

This method retrieves the actual bits from the keychain and not just the reference

- (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 saves the bits to 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);
    }
}

然后您像这样签名:

- (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;
}

这篇关于在iPhone上使用SecKeyRawSign的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆