iOS安全-證書、密鑰及信任服務(wù)(Certificate, Key, and Trust Services Tasks for iOS)

本章描述并演示了如何使用證書、密鑰和信任服務(wù)去導(dǎo)入一個身份(identity),評估證書是否可信,判斷信任失敗的原因,以及信任失敗后的恢復(fù)。
本章按如下順序分別演示了:

  1. 導(dǎo)入一個 identity.
  2. 從導(dǎo)入的數(shù)據(jù)中獲得證書.
  3. 獲得用于證書評估的策略.
  4. 校驗(yàn)證書,根據(jù)指定策略評估證書是否可信.
  5. 測試證書中的可恢復(fù)錯誤.
  6. 判斷證書是否過期.
  7. 改變評估條件,忽略過期證書.
  8. 重新評估證書.
    Certificate, Key, and Trust Services Concepts提供了證書,密鑰,信任服務(wù)的概念和術(shù)語。證書,密鑰,信任服務(wù)函數(shù)的詳細(xì)信息參見Certificate, Key, and Trust Services Reference.

文章中的代碼片段假設(shè)你已經(jīng)引入了以下頭文件。

#import <UIKit/UIKit.h>
#import <Security/Security.h>
#import <CoreFoundation/CoreFoundation.h>

1. 從一個.p12文件中提取、評估身份

如果你需要在iOS設(shè)備上使用加密過的identity(一個密鑰及其關(guān)聯(lián)的證書)進(jìn)行客戶端認(rèn)證,例如:你可以把PKCS#12數(shù)據(jù)以受密碼保護(hù)的文件的方式安全地傳輸?shù)竭@個設(shè)備上。本節(jié)顯示如何從PKCS#12數(shù)據(jù)中提取identity和trust objects(可信任對象),并評估其可信度。

列表2-1 顯示了用SecPKCS12Import函數(shù)從.p12文件中提取identity和可信任對象,以及評估其可信度。
列表 2-2 顯示如何從identity中獲取證書并顯示證書信息。每個列表后都對代碼進(jìn)行了解釋。
在編譯這段代碼時,請確認(rèn)在Xcode工程中加入了Security.framework。

Listing 2-1 Extractingidentity and trust objects from PKCS #12 Data
從PKCS#12數(shù)據(jù)中提取identity和trust對象

OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
                             SecIdentityRef *outIdentity,
                             SecTrustRef *outTrust,
                             CFStringRef keyPassword)
{
OSStatus securityError = errSecSuccess;


const void *keys[] =   { kSecImportExportPassphrase };
const void *values[] = { keyPassword };
CFDictionaryRef optionsDictionary = NULL;

/* Create a dictionary containing the passphrase if one
 was specified.  Otherwise, create an empty dictionary. */
optionsDictionary = CFDictionaryCreate(
                                       NULL, keys,
                                       values, (keyPassword ? 1 : 0),
                                       NULL, NULL);  // 1 創(chuàng)建要傳給 SecPKCS12Import的包含密碼的字典

CFArrayRef items = NULL;
securityError = SecPKCS12Import(inPKCS12Data,
                                optionsDictionary,
                                &items);                    // 2 從PKCS #12數(shù)據(jù)中導(dǎo)出證書、密鑰、信任,放到數(shù)組中。


//
if (securityError == 0) {
    // 3 從數(shù)組中取出第一個字典,并從這個字典中取出身份和信任。
//        SecPKCS12Import方法為PKCS #12數(shù)據(jù)中的每一個條目(身份或證書)返回一個字典。
//        在這個例子中被導(dǎo)出的身份是數(shù)組中的第一個(item #0)。
    CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
    const void *tempIdentity = NULL;
    tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,
                                         kSecImportItemIdentity);
    CFRetain(tempIdentity);
    *outIdentity = (SecIdentityRef)tempIdentity;
    const void *tempTrust = NULL;
    tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
    
    CFRetain(tempTrust);
    *outTrust = (SecTrustRef)tempTrust;
}

if (optionsDictionary)                                      // 4 釋放這些不用的字典和數(shù)組
    CFRelease(optionsDictionary);

if (items)
    CFRelease(items);

return securityError;
}

這段代碼假設(shè):

  • 你已經(jīng)加載PKCS#12文件為NSData或CFDataRef對象。
  • 已經(jīng)得到了密鑰的密碼。

這里是這段代碼做的內(nèi)容:

  1. 創(chuàng)建要傳給 SecPKCS12Import的包含密碼的字典
  2. 從PKCS #12數(shù)據(jù)中導(dǎo)出證書、密鑰、信任,放到數(shù)組中。
  3. 從數(shù)組中取出第一個字典,并從這個字典中取出身份和信任。SecPKCS12Import方法為PKCS #12數(shù)據(jù)中的每一個條目(身份或證書)返回一個字典。在這個例子中被導(dǎo)出的身份是數(shù)組中的第一個(item #0)。
  4. 釋放這些不用的字典和數(shù)組

完成這些步驟,你應(yīng)該:

  1. 釋放包含新數(shù)據(jù)的CFDataRef對象
  2. 返回的信任對象通過調(diào)用SecTrustEvaluate 或 SecTrustEvaluateAsync評估信任。
  3. 處理信任結(jié)果。
  4. 如果信任結(jié)果是kSecTrustResultInvalid, kSecTrustResultDeny, kSecTrustResultFatalTrustFailure,你不能繼續(xù),以失敗結(jié)束。
    如果信任結(jié)果是kSecTrustResultRecoverableTrustFailure,你應(yīng)該從信任失敗中恢復(fù)。
    下面的代碼清單顯示了如何從身份中獲取證書,如何展示證書的信息。在編譯這段代碼時,請確認(rèn)在Xcode工程中加入了Security.framework。

Listing 2-2 Displaying information from the certificate
顯示證書信息

NSString *copySummaryString(SecIdentityRef identity)
{
// Get the certificate from the identity.
SecCertificateRef myReturnedCertificate = NULL;
OSStatus status = SecIdentityCopyCertificate (identity,
                                              &myReturnedCertificate);  // 1 從證書中提取身份

if (status) {
    NSLog(@"SecIdentityCopyCertificate failed.\n");
    return NULL;
}

CFStringRef certSummary = SecCertificateCopySubjectSummary
(myReturnedCertificate);  // 2 從證書中獲取概要信息。

NSString* summaryString = [[NSString alloc]
                           initWithString:(__bridge NSString *)certSummary];  // 3轉(zhuǎn)換string為NSString對象

CFRelease(certSummary);    //4 釋放NSString對象

return summaryString;
}

2. 獲取和使用持久化的鑰匙串

當(dāng)你在鑰匙串中添加或查找一個條目時,你需要有一個持久化的引用。因?yàn)槌志没媚鼙WC在程序從啟動到能寫入磁盤這段時間內(nèi),始終可用。當(dāng)需要反復(fù)在鑰匙串中查找條目時,使用持久化引用更加容易。以下代碼演示如何獲取一個identity 的持久化引用。

Listing 2-3 Gettinga persistent reference for an identity

CFDataRef persistentRefForIdentity(SecIdentityRef identity)
{
    OSStatus status = errSecSuccess;
    CFTypeRef  persistent_ref = NULL;
    const void *keys[] =   { kSecReturnPersistentRef, kSecValueRef };
    const void *values[] = { kCFBooleanTrue,          identity };
    CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
                                          2, NULL, NULL);
    status = SecItemAdd(dict, &persistent_ref);

    if (dict)
        CFRelease(dict);

    return (CFDataRef)persistent_ref;
}

下面的示例展示了如何從持久化引用的鑰匙串中檢索身份對象。

Listing 2-4 Gettingan identity using a persistent reference

SecIdentityRef identityForPersistentRef(CFDataRef persistent_ref)
{
    CFTypeRef   identity_ref     = NULL;
    const void *keys[] =   { kSecClass, kSecReturnRef,  kSecValuePersistentRef };
    const void *values[] = { kSecClassIdentity, kCFBooleanTrue, persistent_ref };
    CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
                                          3, NULL, NULL);
    SecItemCopyMatching(dict, &identity_ref);

    if (dict)
        CFRelease(dict);
  
    return (SecIdentityRef)identity_ref;
}

3. 從鑰匙串中查找證書

以下代碼演示如何在要是串中查找使用名稱識別的證書。在鑰匙串中找到一個持久化引用的條目,參考列表2-4。要用一個id字串查找一個條目,參考“數(shù)據(jù)加密和解密”。

Listing 2-5 Findinga certificate In the Keychain

 void certificateInKeychain(){

OSStatus status = errSecSuccess;
CFTypeRef   certificateRef     = NULL;                      // 1
const char *certLabelString = "Romeo Montague";
CFStringRef certLabel = CFStringCreateWithCString(
                                                  NULL, certLabelString,
                                                  kCFStringEncodingUTF8);         // 2

const void *keys[] =   { kSecClass, kSecAttrLabel, kSecReturnRef };
const void *values[] = { kSecClassCertificate, certLabel, kCFBooleanTrue };
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys,
                                          values, 3,
                                          NULL, NULL);       // 3
status = SecItemCopyMatching(dict, &certificateRef);        // 4

if (status == errSecSuccess) {
    CFRelease(certificateRef);
    certificateRef = NULL;
    }

/* Do something with certificateRef here */

if (dict)
    CFRelease(dict);
}

4. 獲取策略對象并評估可信度

評估證書可信度之前,必需獲取到一個證書對象的引用。你可以從一個identity中提取一個證書對象(列表 2-2),也可以從DER證書數(shù)據(jù)中創(chuàng)建證書對象(使用SecCertificateCreateWithData函數(shù),見列表2-6),或者從鑰匙串中查找證書(列表 2-5)。

評估信任度的標(biāo)準(zhǔn)由信任策略(trust policy)指定。列表3-2 顯示如何獲得用于評估的策略對象。在iOS中有兩種策略可用:Basic X509和SSL(參考AppleX509TP信任策略)。可以用SecPolicyCreateBasicX509或者SecPolicyCreateSSL函數(shù)獲取策略對象。

下列代碼顯示了獲取策略對象并用于評估證書是否可信。
Listing 2-6 Obtaining a policy reference object and evaluating trust

NSString *thePath = [[NSBundle mainBundle]
                     pathForResource:@"Romeo Montague" ofType:@"cer"];
NSData *certData = [[NSData alloc]
                    initWithContentsOfFile:thePath];
CFDataRef myCertData = (__bridge CFDataRef)certData;                 // 1

SecCertificateRef myCert;
myCert = SecCertificateCreateWithData(NULL, myCertData);    // 2

SecPolicyRef myPolicy = SecPolicyCreateBasicX509();         // 3

SecCertificateRef certArray[1] = { myCert };
CFArrayRef myCerts = CFArrayCreate(
                                   NULL, (void *)certArray,
                                   1, NULL);
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(
                                                 myCerts,
                                                 myPolicy,
                                                 &myTrust);  // 4

SecTrustResultType trustResult;
if (status == noErr) {
    status = SecTrustEvaluate(myTrust, &trustResult);       // 5
}
//...                                                             // 6
if (trustResult == kSecTrustResultRecoverableTrustFailure) {
    // ...;
}
// ...
if (myPolicy)
    CFRelease(myPolicy);

在這段代碼中:

  1. 查找證書文件并獲取數(shù)據(jù)。本例中,該文件位于應(yīng)用程序束。但你也可以從網(wǎng)絡(luò)獲取證書。如果證書存在于鑰匙串中,參考“在鑰匙串中查找證書”。
  2. 從證書數(shù)據(jù)中創(chuàng)建certificate引用。
  3. 創(chuàng)建用于評估證書的策略。
  4. 用證書和策略創(chuàng)建信任對象(trust)。如果存在中間證書或者錨證書,應(yīng)把這些證書都包含在certificate數(shù)組中并傳遞給SecTrustCreateWithCertificates函數(shù)。這樣會加快評估的速度。
  5. 評估一個信任對象。
  6. 處理信任結(jié)果(trust result)。如果信任結(jié)果是kSecTrustResultInvalid,kSecTrustResultDeny,kSecTrustResultFatalTrustFailure,你無法進(jìn)行處理。如果信任結(jié)果是kSecTrustResultRecoverableTrustFailure,你可以恢復(fù)這個錯誤。參考“從信任失敗中恢復(fù)”。
  7. 釋放策略對象。

5. 從信任失敗中恢復(fù)

信任評估的結(jié)果有多個,這取決于:是否證書鏈中的所有證書都能找到并全都有效,以及用戶對這些證書的信任設(shè)置是什么。信任結(jié)果怎么處理則由你的程序來決定。例如,如果信任結(jié)果是kSecTrustResultConfirm,你可以顯示一個對話框,詢問用戶是否允許繼續(xù)。

信任結(jié)果kSecTrustResultRecoverableTrustFailure的意思是:信任被否決,但可以通過改變設(shè)置獲得不同結(jié)果。例如,如果證書簽發(fā)過期,你可以改變評估日期以判斷是否證書是有效的同時文檔是已簽名的。列表2-7 演示如何改變評估日期。注意 CFDateCreate函數(shù)使用絕對時間(從2001年1月1日以來的秒數(shù))。你可以用CFGregorianDateGetAbsoluteTime函數(shù)把日歷時間轉(zhuǎn)換為絕對時間。

 void recoverFromTrustFailure(SecTrustRef myTrust)
{
SecTrustResultType trustResult;
OSStatus status = SecTrustEvaluate(myTrust, &trustResult);  // 1

//Get time used to verify trust
CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
CFDateRef newDate;
if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
    trustTime = SecTrustGetVerifyTime(myTrust);             // 3
    timeIncrement = 31536000;                               // 4
    currentTime = CFAbsoluteTimeGetCurrent();               // 5
    newTime = currentTime - timeIncrement;                  // 6
    if (trustTime - newTime){                               // 7
        newDate = CFDateCreate(NULL, newTime);              // 8
        SecTrustSetVerifyDate(myTrust, newDate);            // 9
        status = SecTrustEvaluate(myTrust, &trustResult);   // 10
    }
}
if (trustResult != kSecTrustResultProceed) {                // 11
    //...
}
}

在這段代碼中:

  1. 評估證書可信度。參考“獲取策略對象并評估可信度”。

  2. 檢查信任評估結(jié)果是否是可恢復(fù)的失敗( kSecTrustResultRecoverableTrustFailure)。

  3. 取得證書的評估時間(絕對時間)。如果證書在評估時已經(jīng)過期了,則被認(rèn)為無效。

  4. 設(shè)置時間的遞增量為1年(以秒計算)。

  5. 取得當(dāng)前時間的絕對時間。

  6. 設(shè)置新時間(第2次評估的時間)為當(dāng)前時間減一年。

  7. 檢查評估時間是否大于1年前(最近一次評估是否1年前進(jìn)行的)。如果是,使用新時間(1年前的時間)進(jìn)行評估,看證書是否在1年前就已經(jīng)過期。

  8. 把新時間轉(zhuǎn)換為CFDateRef。也可以用NSDate,二者是完全互通的,方法中的NSDate*參數(shù),可以用CFDateRef進(jìn)行傳遞;反之亦可。

  9. 設(shè)置信任評估時間為新時間(1年前)。

  10. 再次進(jìn)行信任評估。如果證書是因?yàn)檫^期(到期時間在1年內(nèi))導(dǎo)致前次評估失敗,那么這次評估應(yīng)該成功。

  11. 再次檢查評估結(jié)果。如果仍不成功,則需要做更進(jìn)一步的操作,比如提示用戶安裝中間證書,或則友好地告知用戶證書校驗(yàn)失敗。

6.加密和解密數(shù)據(jù)(Encrypting and Decrypting Data)

證書,密鑰和信任API包含了生產(chǎn)不對稱密鑰對并用于數(shù)據(jù)加密和解密的函數(shù)。例如,您可能想要使用此功能來對你不想在備份數(shù)據(jù)中訪問的數(shù)據(jù)進(jìn)行加密?;蛘撸憧赡芟胧褂霉€/私鑰在你的iOS應(yīng)用和桌面應(yīng)用間通過網(wǎng)絡(luò)發(fā)送加密數(shù)據(jù)。列表2-8 顯示如何生成可用于手機(jī)的公/私鑰對。列表2-9 顯示如何用公鑰加密數(shù)據(jù),列表2-10 顯示如何用私鑰解密數(shù)據(jù)。注意,這幾個示例都使用了cocoa對象(如NSMutableDictionary),而不是像本章其他示例那樣使用了core foundation對象(如CFMutableDictionaryRef),Cocoa對象和對應(yīng)的Core Foundation完全相同,免費(fèi)橋接。例如:在方法中有個NSMutableDictionary *參數(shù),你可以轉(zhuǎn)化為CFMutableDictionaryRef,方法中的CFMutableDictionaryRef參數(shù),你可以轉(zhuǎn)換為NSMutableDictionary實(shí)例。

Listing 2-8 Generatinga key pair

生成密鑰對

 //1 定義獨(dú)特的字符串作為屬性添加到私鑰和公鑰密鑰鏈項(xiàng),來讓他們以后更容易找到。
static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";



- (void)generateKeyPairPlease
{
OSStatus status = noErr;
NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
// 2 為SecKeyGeneratePair方法中的屬性分配字典

NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
                                    length:strlen((const char *)publicKeyIdentifier)];
NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
                                     length:strlen((const char *)privateKeyIdentifier)];
// 3 創(chuàng)建包含步驟1中定義的標(biāo)識符字符串的NSData對象

SecKeyRef publicKey = NULL;
SecKeyRef privateKey = NULL;                                // 4 為公鑰和私鑰創(chuàng)建SecKeyRef對象

[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA
                forKey:(__bridge id)kSecAttrKeyType]; // 5 設(shè)置密鑰對類型屬性為RSA
[keyPairAttr setObject:[NSNumber numberWithInt:1024]
                forKey:(__bridge id)kSecAttrKeySizeInBits]; // 6 設(shè)置密鑰對長度為1024字節(jié)

[privateKeyAttr setObject:[NSNumber numberWithBool:YES]
                   forKey:(__bridge id)kSecAttrIsPermanent]; // 7 設(shè)置私鑰的持久化屬性(即是否存入鑰匙串)為YES
[privateKeyAttr setObject:privateTag
                   forKey:(__bridge id)kSecAttrApplicationTag]; // 8 把1-3步中的identifier放到私鑰的dictionary中

[publicKeyAttr setObject:[NSNumber numberWithBool:YES]
                  forKey:(__bridge id)kSecAttrIsPermanent]; // 9 設(shè)置公鑰的持久化屬性(即是否存入鑰匙串)為YES
[publicKeyAttr setObject:publicTag
                  forKey:(__bridge id)kSecAttrApplicationTag]; // 10 把1-3步中的identifier放到公鑰的dictionary中

[keyPairAttr setObject:privateKeyAttr
                forKey:(__bridge id)kSecPrivateKeyAttrs]; // 11 把私鑰的屬性集(dictionary)加到密鑰對的屬性集(dictionary)中
[keyPairAttr setObject:publicKeyAttr
                forKey:(__bridge id)kSecPublicKeyAttrs]; // 12 把公鑰的屬性集(dictionary)加到密鑰對的屬性集(dictionary)中

status = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr,
                            &publicKey, &privateKey); // 13 產(chǎn)生密鑰對
//    error handling...


if(publicKey) CFRelease(publicKey);
if(privateKey) CFRelease(privateKey);                       // 14 釋放不用的對象內(nèi)存
}

你可以將您的公鑰發(fā)送給任何人,誰可以使用它來加密數(shù)據(jù)。如果你保持你的私鑰安全,那么只有你能夠解密數(shù)據(jù)。以下代碼示例展示了如何使用公鑰加密數(shù)據(jù)。這可以在設(shè)備上生成一個公鑰(參見前面的代碼示例)或者從發(fā)送給你或鑰匙鏈中的證書中提取公鑰。您可以使用SecTrustCopyPublicKey函數(shù)來從證書中提取公鑰。在下面的代碼示例中,假定密鑰已經(jīng)在設(shè)備上生成并放置在鑰匙串中。

Listing 2-9 Encryptingdata with a public key

用公鑰加密數(shù)據(jù)
- (NSData *)encryptWithPublicKey
{
OSStatus status = noErr;

size_t cipherBufferSize;
uint8_t *cipherBuffer;                     // 1 為加密文本分配緩沖區(qū)

// [cipherBufferSize]
const uint8_t dataToEncrypt[] = "the quick brown fox jumps "
"over the lazy dog\0"; // 2 指定要加密的文本
size_t dataLength = sizeof(dataToEncrypt)/sizeof(dataToEncrypt[0]);

SecKeyRef publicKey = NULL;                                 // 3 定義SecKeyRef,用于公鑰。

NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
                                    length:strlen((const char *)publicKeyIdentifier)]; // 4 定義NSData對象,存儲公鑰的identifier(見列表 2-8 的第1、3、8步),該id在鑰匙串中唯一。

NSMutableDictionary *queryPublicKey =
[[NSMutableDictionary alloc] init]; // 5 定義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)kSecReturnRef];
// 6 設(shè)置dictionary的鍵-值屬性。屬性中指定,鑰匙串條目類型為“密鑰”,條目identifier為第4步中指定的字符串,密鑰類型為RSA,函數(shù)調(diào)用結(jié)束返回查找到的條目引用。

status = SecItemCopyMatching
((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey); // 7 調(diào)用SecItemCopyMatching函數(shù)進(jìn)行查找。

//  Allocate a buffer

cipherBufferSize = SecKeyGetBlockSize(publicKey);
cipherBuffer = malloc(cipherBufferSize);

//  Error handling

if (cipherBufferSize < sizeof(dataToEncrypt)) {
    // Ordinarily, you would split the data up into blocks
    // equal to cipherBufferSize, with the last block being
    // shorter. For simplicity, this example assumes that
    // the data is short enough to fit.
    printf("Could not decrypt.  Packet too large.\n");
    return NULL;
}

// Encrypt using the public.
status = SecKeyEncrypt(    publicKey,
                       kSecPaddingPKCS1,
                       dataToEncrypt,
                       (size_t) dataLength,
                       cipherBuffer,
                       &cipherBufferSize
                       );                              // 8 加密數(shù)據(jù), 返回結(jié)果用PKCS1格式對齊。

//  Error handling
//  Store or transmit the encrypted text

if (publicKey) CFRelease(publicKey);

NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];

free(cipherBuffer);

return encryptedData;
}

下面的代碼示例顯示了如何解密數(shù)據(jù)。這個示例使用用來加密數(shù)據(jù)的公鑰對應(yīng)的私鑰,并假設(shè)您已經(jīng)在前面的示例中創(chuàng)建的密文。從鑰匙串中獲取私鑰的技術(shù)跟前面的示例中獲取公鑰的技術(shù)相同。

Listing 2-10 Decryptingwith a private key

私鑰解密
- (void)decryptWithPrivateKey: (NSData *)dataToDecrypt
{
OSStatus status = noErr;

size_t cipherBufferSize = [dataToDecrypt length];
uint8_t *cipherBuffer = (uint8_t *)[dataToDecrypt bytes];

size_t plainBufferSize;
uint8_t *plainBuffer;

SecKeyRef privateKey = NULL;

NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
                                     length:strlen((const char *)privateKeyIdentifier)];

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

// 1 設(shè)置放鑰匙串中私鑰的字典
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];

status = SecItemCopyMatching
((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKey); // 2 從鑰匙串中找到私鑰

//  Allocate the buffer
plainBufferSize = SecKeyGetBlockSize(privateKey);
plainBuffer = malloc(plainBufferSize);

if (plainBufferSize < cipherBufferSize) {
    // Ordinarily, you would split the data up into blocks
    // equal to plainBufferSize, with the last block being
    // shorter. For simplicity, this example assumes that
    // the data is short enough to fit.
    printf("Could not decrypt.  Packet too large.\n");
    return;
}

//  Error handling

status = SecKeyDecrypt(    privateKey,
                       kSecPaddingPKCS1,
                       cipherBuffer,
                       cipherBufferSize,
                       plainBuffer,
                       &plainBufferSize
                       );                              // 3 解密數(shù)據(jù)

//  Error handling
//  Store or display the decrypted text

if(privateKey) CFRelease(privateKey);       // 4 釋放內(nèi)存
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容