在钥匙串中存储.p12证书以便以后使用

时间:2015-06-02 14:20:03

标签: ios ssl-certificate keychain client-certificates mutual-authentication

我正在尝试按照Apple docs处理客户端p12证书:

https://developer.apple.com/library/ios/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html#//apple_ref/doc/uid/TP40001358-CH208-SW13

我已成功从文件系统加载.p12证书:

- (SecIdentityRef)getClientCertificate:(NSString *) certificatePath {
    SecIdentityRef identity = nil;
    NSData *PKCS12Data = [NSData dataWithContentsOfFile:certificatePath];

    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    CFStringRef password = CFSTR("password");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
    CFRelease(options);
    CFRelease(password);
    if (securityError == errSecSuccess) {
        NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        identity = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
    } else {
        NSLog(@"Error opening Certificate.");
    }

    return identity;
}

然后我获得该身份的证书:

- (CFArrayRef)getCertificate:(SecIdentityRef) identity {
    SecCertificateRef certificate = nil;

    SecIdentityCopyCertificate(identity, &certificate);
    SecCertificateRef certs[1] = { certificate };



    CFArrayRef array = CFArrayCreate(NULL, (const void **) certs, 1, NULL);

    SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
    SecTrustRef myTrust;

    OSStatus status = SecTrustCreateWithCertificates(array, myPolicy, &myTrust);
    if (status == noErr) {
        NSLog(@"No Err creating certificate");
    } else {
        NSLog(@"Possible Err Creating certificate");
    }
    return array;
}

但我真正想做的是将证书(或身份)存储在我的应用程序钥匙串中,这样我就不会从文件系统中读取它了。

几个问题:

  1. 我应该存储哪个?证书或身份?
  2. 如何存储并检索它?
  3. 上面的链接讨论了“获取和使用持久性钥匙串参考”和“#39;这让我非常困惑。

    它还谈到了“在钥匙串中找到证书”,但它提到使用证书的名称来查找它。我不知道'姓名'来自。

2 个答案:

答案 0 :(得分:2)

我想不出将证书存放在钥匙串中的好理由,虽然我确信可能会有一些。我只在密钥链中存储身份(私钥部分)。为了更容易在钥匙串中找到标识,您可以生成对它的持久引用(请参阅链接中的清单2-3),然后将该持久性引用保存在应用程序的文件系统中。持久化ref只是一个CFDataRef,您可以免费桥接到NSData对象,然后轻松保存/加载。当您想要私有密钥用于加密/其他时,您可以使用该持久性引用从密钥链加载标识(请参阅链接中的清单2-4)。我会为你发布一些代码但是我现在正在重建我的开发机器并且还没有安装Xcode。

答案 1 :(得分:1)

  
      
  1. 我应该存储哪个?证书还是身份?
  2.   

这取决于您正在执行的操作,以及您是否需要设备上的私钥进行身份验证。 SecIdentityRef包含证书和私钥。如果您使用.p12文件进行身份验证,则可能要存储和使用完整身份。如果只需要证书,那么我不会首先将完整的.p12加载到驱动器上,因为它包含私钥。

  
      
  1. 如何存储和检索它?
  2.   

我建议将您的身份(或证书)存储在钥匙串中,并使用kSecAttrLabel作为查询的唯一参考。

您需要查看的文档是Storing an Identity in the Keychain,该文档将您引向Storing a Certificate in the Keychain,并概述了存储身份和证书之间所需的一些细微差别。

操作如下(根据上面的链接改编):

保存到钥匙串

// Create a query (with unique label for reference later)
NSDictionary* addquery = @{ (id)kSecValueRef:   (__bridge id)identity,
                            (id)kSecClass:      (id)kSecClassIdentity,
                            (id)kSecAttrLabel:  @"My Identity",
                           };

// Add the identity to the keychain
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
if (status != errSecSuccess) {
    // Handle the error
}

从钥匙串加载

// Query the keychain for your identity
NSDictionary *getquery = @{ (id)kSecClass:     (id)kSecClassIdentity,
                            (id)kSecAttrLabel: @"My Identity",
                            (id)kSecReturnRef: @YES,
                            };

// Retrieve the identity from the keychain
SecIdentityRef identity = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)getquery,
                                      (CFTypeRef *)&identity);
if (status != errSecSuccess) { <# Handle error #> }
else                         { <# Use identity #> }

if (identity) { CFRelease(identity); } // After you are done with it

正如RyanR所述,您还可以在保存钥匙串项目后创建对它的持久引用,然后将其保存到文件中。我建议将[kSecReturnPersistentRef][3]添加到您的addquery中以实现此目的。