如何加载私钥为 SecKeyRef
本文介绍如何在 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)