iOS SecKeyRef到NSData

时间:2013-05-25 11:18:17

标签: ios objective-c rsa

我有两个密钥,public和private,都存储在SecKeyRef变量中。为简单起见,让我们从公众开始吧。我想做的是将它导出到NSData对象。为此,Apple提供了几乎着名的代码片段,其中包括:

- (NSData *)getPublicKeyBits {
    OSStatus sanityCheck = noErr;
    NSData * publicKeyBits = nil;

    NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];

    // Set the public key query dictionary.
    [queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];

    // Get the key bits.
    sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);

    if (sanityCheck != noErr)
    {
        publicKeyBits = nil;
    }

    [queryPublicKey release];

    return publicKeyBits;
}

然而,我有Xcode 4.6.2,并且代码显示错误(在每次转换为id之前添加“__bridge”)。新版本如下所示:

- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
    OSStatus sanityCheck = noErr;
    NSData * publicKeyBits = nil;

    NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];

    // Set the public key query dictionary.
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnData];

    // Get the key bits.
    sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);

    if (sanityCheck != noErr)
    {
        publicKeyBits = nil;
    }

    return publicKeyBits;
}

但仍有两个错误:

  • 使用未声明的标识符'publicTag'
  • ARC不允许使用指向'CFTypeRef '的Objective-C指针的间接指针(又名'const void * ')

现在,我希望在你的帮助之后,第一个问题将不再是一个问题,因为我不想构建一个查询或什么来从钥匙串中提取密钥。 我在变量中有它,我希望从那里提取它。变量的名称是givenPublicKey,这是我希望转换为NSData的密钥。

那么,我将如何解决这个ARC问题呢?

跟进:如何将私有密钥导出到NSData,因为我已经多次读过我正在尝试使用的函数只适用于公钥。

1 个答案:

答案 0 :(得分:14)

  • 使用未声明的标识符' publicTag'

publicTag只是添加到Keychain项目的一些唯一标识符。在CryptoExercise示例项目中,它被定义为

#define kPublicKeyTag "com.apple.sample.publickey"
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
  • 将指向Objective-C指针的间接指针强制转换为' CFTypeRef' (又称' const void *')不允许使用ARC

这可以通过使用临时CFTypeRef变量来解决:

CFTypeRef result;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, &result);
if (sanityCheck == errSecSuccess) {
    publicKeyBits = CFBridgingRelease(result);
}
  • 我不想构建查询或诸如此类来从密钥链中提取密钥。我把它放在变量中,我希望从那里提取它......

据我所知,您必须暂时将SecKeyRef存储到Keychain中。 SecItemAdd 可以选择将添加的项目作为数据返回。来自文档:

  

要获取添加项目的数据作为CFDataRef类型的对象,   指定返回类型键kSecReturnData,其值为   kCFBooleanTrue

将所有这些放在一起,以下代码应该做你想要的:

- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {

    static const uint8_t publicKeyIdentifier[] = "com.your.company.publickey";
    NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];

    OSStatus sanityCheck = noErr;
    NSData * publicKeyBits = nil;

    NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

    // Temporarily add key to the Keychain, return as data:
    NSMutableDictionary * attributes = [queryPublicKey mutableCopy];
    [attributes setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
    [attributes setObject:@YES forKey:(__bridge id)kSecReturnData];
    CFTypeRef result;
    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result);
    if (sanityCheck == errSecSuccess) {
        publicKeyBits = CFBridgingRelease(result);

        // Remove from Keychain again:
        (void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
    }

    return publicKeyBits;
}

我希望这有效,我现在无法测试。

  • 跟进:如何将私钥导出到NSData,因为我已多次读过我尝试使用的功能仅适用于公钥。

我不知道。