簡體   English   中英

將 a.p12 證書存儲在鑰匙串中以供以后使用

[英]Storing a .p12 certificate in keychain to use later

我正在嘗試按照蘋果文檔在此處處理客戶端 p12 證書:

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

我已經從文件系統成功加載了 a.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. 如何存儲和檢索它?

上面的鏈接討論了“獲取和使用持久鑰匙串引用”,這讓我很困惑。

它還談到了“在鑰匙串中查找證書”,但它提到使用證書的名稱來查找它。 我不確定“名稱”的來源。

我想不出將證書存放在鑰匙串中的好理由,雖然我確信可能會有一些。 我只在密鑰鏈中存儲身份(私鑰部分)。 為了更容易在鑰匙串中找到標識,您可以生成對它的持久引用(請參閱鏈接中的清單2-3),然后將該持久性引用保存在應用程序的文件系統中。 持久化ref只是一個CFDataRef,您可以免費橋接到NSData對象,然后輕松保存/加載。 當您想要私有密鑰用於加密/其他時,您可以使用該持久性引用從密鑰鏈加載標識(請參閱鏈接中的清單2-4)。 我會為你發布一些代碼但我現在正在重建我的開發機器並且還沒有安裝Xcode。

  1. 我應該存儲哪個? 證書或身份?

這取決於您正在做什么,以及您是否需要設備上的私鑰進行身份驗證。 SecIdentityRef包含證書和私鑰。 如果您使用.p12文件進行身份驗證,則可能需要存儲並使用完整標識。 如果您只需要證書,那么我首先不會將完整的.p12加載到驅動器上,因為它包含私鑰。

  1. 如何存儲並檢索它?

我建議將您的身份(或證書)存儲在鑰匙串中,並使用kSecAttrLabel作為查詢的唯一參考。

您需要查看的文檔是在鑰匙串中存儲標識,該標識指示您在鑰匙串中存儲證書,並概述存儲標識和證書之間所需的一些細微差別。

這樣做如下(改編自上面的鏈接):

保存到鑰匙串

// 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來實現這一點。

這是一個 Swift 版本,它將 SecIdentity 寫入鑰匙串,並將其讀回:

func addP12ToKeychain(secIdentity:SecIdentity) throws {
        let keychainAddQuery: [String: Any] = [
            // The online example in the Apple doc includes the
            // kSecClass key, but this always failed for me. 
            // When I exclude it, the write works; not sure why.
            // kSecClass as String: kSecClassIdentity,
            kSecValueRef as String: secIdentity,
            kSecAttrLabel as String: "identifierForP12Cert"
        ]
        
        let addResult = SecItemAdd(keychainAddQuery as CFDictionary, nil)
        if addResult != errSecSuccess {
            // You can look up the error # here:
            // https://www.osstatus.com/
            // For ex, errSecInternal = -26276
            throw EtimsError.runtimeError("Err in addP12ToKeychain, error # \(addResult)")
        }
    } // addP12ToKeychain

func getP12FromKeychain() throws -> SecIdentity {
        // Note that the fetch works when you include the kSecClass key,
        // unlike the write.
        let keychainFetchQuery: [String: Any] = [
            kSecClass as String: kSecClassIdentity,
            kSecAttrLabel as String: "identifierForP12Cert",
            kSecReturnRef as String: kCFBooleanTrue!
        ]

        var item: CFTypeRef?
        let fetchResult = SecItemCopyMatching(keychainFetchQuery as CFDictionary, &item)
        guard fetchResult == errSecSuccess else {
            throw EtimsError.runtimeError("Err in getP12FromKeychain, error # \(fetchResult)")
        }

        let secIdentity = item as! SecIdentity
        return secIdentity
    } // getP12FromKeychain

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM