本文介紹如何在 iOS/macOS 下將私鑰加載為 SecKeyRef
,涵蓋 PEM 格式的 ECC 密鑰讀取、X9.63 數據構建、以及與 Keychain 的集成。
1. 使用 SecKeyCreateWithData 加載私鑰
Apple 提供的 SecKeyCreateWithData
方法可以直接將密鑰數據加載為 SecKeyRef
對象。
SecKeyRef SecKeyCreateWithData(CFDataRef keyData, CFDictionaryRef attributes, CFErrorRef _Nullable *error);
文檔:SecKeyCreateWithData(::_😃 | Apple Developer Documentation
attributes 必須包含如下 key
kSecAttrKeyType
:密鑰類型,如 ECC、RSAkSecAttrKeyClass
:密鑰類(公鑰/私鑰)
keyData 的數據格式
- ECC 私鑰數據應采用 X9.63 格式。
- RSA 私鑰應為 PKCS #1 格式。
詳見:SecKeyCopyExternalRepresentation(:😃 | Apple Developer Documentation
X9.63 格式說明
- ECC 公鑰:
04 || X || Y
- ECC 私鑰:
04 || X || Y || K
其中 X、Y 為公鑰坐標,K 為大端序編碼的私鑰值,所有字段均為定長,必要時補零。
構建 X9.63 可參考:將加密工具包密鑰存儲在鑰匙串 | 蘋果開發者文檔
2. PEM 格式轉換與讀取
2.1 PEM 轉 P256
如果你有 PEM 編碼的 P256 私鑰,可用 CryptoKit 直接解析為 P256 私鑰對象:
文檔:init(pemRepresentation:) | Apple Developer Documentation
2.2 讀取 ECC PEM 密鑰為 SecKey
示例代碼(Swift):
/// 加載 PEM 證書為 SecKey
/// - Parameter name: PEM 文件名(不含擴展名)
/// - Returns: SecKey 類型值
class func loadECCSecKeyFromPem(_ name: String) -> SecKey? {guard let pemURL = Bundle.main.url(forResource: name, withExtension: "pem") else {return nil}do {let pemStr = try String(contentsOf: pemURL)let p256 = try P256.Signing.PrivateKey(pemRepresentation: pemStr)let attributes: [String: Any] = [kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,kSecAttrKeyClass as String: kSecAttrKeyClassPrivate]// kSecAttrIsPermanent 是否需要存儲,默認不存儲guard let secKey = SecKeyCreateWithData(p256.x963Representation as CFData,attributes as CFDictionary,nil)else {return nil}return secKey} catch {print(error)return nil}
}
如果需要將 PEM 轉換為 X9.63,可先用 CryptoKit 讀取后再取其
x963Representation
字節。
3. iOS 端創建 ECC 私鑰并存儲到 Keychain
如需新建私鑰并寫入鑰匙串,可參考如下 Objective-C 代碼:
+ (NSDictionary *)privateKeyParams {return @{(__bridge NSString *) kSecClass : (__bridge NSString *) kSecClassKey,(__bridge NSString *) kSecAttrApplicationLabel : MTRCAKeyChainLabel,// 存入鑰匙串時,按需設置屬性(__bridge NSString *) kSecAttrKeyClass : (__bridge NSString *) kSecAttrKeyClassSymmetric,};
}
+ (NSDictionary *)privateKeyCreationParams {// 按需設置密鑰長度const size_t keySizeInBits = 256;return @{(__bridge NSString *) kSecAttrKeyClass : (__bridge NSString *) kSecAttrKeyClassPrivate,(__bridge NSString *) kSecAttrKeyType : (__bridge NSNumber *) kSecAttrKeyTypeECSECPrimeRandom,(__bridge NSString *) kSecAttrKeySizeInBits : @(keySizeInBits),(__bridge NSString *) kSecAttrIsPermanent : @(NO), // 是否永久保存};
}
/// 創建并存儲 ECC 私鑰
+ (SecKeyRef)generateCAPrivateKey {NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithDictionary:[MSMatterFabricKeys privateKeyParams]];// 先刪除舊密鑰,防止添加失敗SecItemDelete((__bridge CFDictionaryRef) query);CFErrorRef error = NULL;SecKeyRef key = SecKeyCreateRandomKey((__bridge CFDictionaryRef)[MSMatterFabricKeys privateKeyCreationParams], &error);if (error) {NSLog(@"Could not generate private key: %@", (__bridge NSError *) error);return NULL;}NSData *keyData = (__bridge_transfer NSData *) SecKeyCopyExternalRepresentation(key, &error);if (error) {NSLog(@"Could not get key external representation: %@", (__bridge NSError *) error);CFRelease(key);return NULL;}query[(__bridge NSString *) kSecValueData] = [keyData base64EncodedDataWithOptions:0];OSStatus status = SecItemAdd((__bridge CFDictionaryRef) query, NULL);if (status != errSecSuccess) {NSLog(@"Failed to store private key : %d", status);CFRelease(key);return NULL;}return key;
}
4. 證書直接讀取
如需直接讀取證書內容,可參考:證書讀取方法(CSDN)
5. 相關資源
- SecKeyCreateWithData | Apple Developer Documentation
- SecKeyCopyExternalRepresentation | Apple Developer Documentation
- 將加密工具包密鑰存儲在鑰匙串 | Apple Developer Documentation
- PEM 轉 P256 | Apple Developer Documentation
- 證書讀取方法(CSDN)