2

我知道有一个函数SecPKCS12Import可以让你从 p12 文件中导入数据。但是,我想走相反的路线。我有一个SecCertificateRef和一个 public/private SecKeyRef,我想用它来创建一个 P12 文件。有谁知道如何在 iPhone 上做到这一点?

谢谢

4

2 回答 2

2

不幸的是,CommonCrypto 没有提供任何方法来导出 PKCS12 容器,更不用说任何其他导出功能(即使它的 OSX 对应物可以做到这一点)。有一些方法可以从密钥链中提取 SecKeyRef 原始数据,但是您仍然需要自己编写所有 PKCS12 包装。

我们遇到了类似的问题并选择了 OpenSSL。

为 iOS 编译 OpenSSL

集成 OpenSSL 需要做一些工作,因为您需要自己编译和链接 OpenSSL 源。幸运的是,有一些构建脚本可用,因此您不必自己执行此操作,例如https://github.com/x2on/OpenSSL-for-iPhone。我建议您使用它们,因为您需要修补一些有点模糊的 Makefile。这些构建脚本为 iOS 和 tvOS 生成静态链接库。您只需要将它们链接到您的项目并相应地设置标题和库搜索路径。

可可豆荚

你也可以使用官方的OpenSSL CocoaPod。这样可以省去配置项目的麻烦。

导出 PKCS12

您可能知道,OpenSSL 是一个C 库。这意味着您可能希望将所有 C 函数封装到 Objective-C 或 Swift 包装器中。有一些支持 im 和导出 PKCS12 容器的开源包装器,但我还没有找到一个具有良好文档的包装器。不过,您应该能够从某些来源中获得相关的片段。

https://github.com/microsec/MscX509Common/blob/master/src/MscPKCS12.m

你也可以看看这个例子http://fm4dd.com/openssl/pkcs12test.htm

希望有帮助!

于 2016-07-12T12:58:45.240 回答
2

我同意这个任务只能使用 OpenSSL 来执行。为 iOS 编译它有点棘手,但使用OpenSSL-for-iPhone是很有可能的。

要解决使用Swift 3从 a和 a创建PKCS12密钥库的给定任务,只需将静态库和添加到您的项目并创建以下桥接头:SecCertificateSecKeylibssl.alibcrypto.a

#import <openssl/err.h>
#import <openssl/pem.h>
#import <openssl/pkcs12.h>
#import <openssl/x509.h>

要创建密钥库,必须将输入数据转换为 OpenSSL 数据结构,这需要一些创造力。SecCertificate可以直接转换为DER格式,然后读入X509结构。SecKey处理起来更糟糕。获取密钥数据的唯一可能解决方案是将其写入钥匙串并获取参考。从参考中我们可以得到base 64编码的字符串,然后可以将其读入EVP_PKEY结构。现在,我们可以创建密钥库并将其保存到文件中。要通过 iOS 函数访问密钥库中的数据,我们必须通过以下方式读取文件let data = FileManager.default.contents(atPath: path)! as NSData

完整的解决方案如下所示:

func createP12(secCertificate: SecCertificate, secPrivateKey: SecKey) {
    // Read certificate
    // Convert sec certificate to DER certificate
    let derCertificate = SecCertificateCopyData(secCertificate)
    // Create strange pointer to read DER certificate with OpenSSL
    // data must be a two-dimensional array containing the pointer to the DER certificate as single element at position [0][0]
    let certificatePointer = CFDataGetBytePtr(derCertificate)
    let certificateLength = CFDataGetLength(derCertificate)
    let certificateData = UnsafeMutablePointer<UnsafePointer<UInt8>?>.allocate(capacity: 1)
    certificateData.pointee = certificatePointer
    // Read DER certificate
    let certificate = d2i_X509(nil, certificateData, certificateLength)
    // Print certificate
    X509_print_fp(stdout, certificate)
    // Read private key
    // Convert sec key to PEM key
    let tempTag = "bundle.temp"
    let tempAttributes = [
        kSecClass: kSecClassKey,
        kSecAttrApplicationTag: tempTag,
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecValueRef: secPrivateKey,
        kSecReturnData: kCFBooleanTrue
        ] as NSDictionary
    var privateKeyRef: AnyObject?
    // Store private key in keychain
    SecItemDelete(tempAttributes)
    guard SecItemAdd(tempAttributes, &privateKeyRef) == noErr else {
        NSLog("Cannot store private key")
        return
    }
    // Get private key data
    guard let privateKeyData = privateKeyRef as? Data else {
        NSLog("Cannot get private key data")
        return
    }
    let pemPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n\(privateKeyData.base64EncodedString())\n-----END RSA PRIVATE KEY-----\n"
    // Delete private key in keychain
    SecItemDelete(tempAttributes)
    let privateKeyBuffer = BIO_new(BIO_s_mem())
    pemPrivateKey.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer<Int8>) -> Void in
        BIO_puts(privateKeyBuffer, bytes)
    })
    let privateKey = PEM_read_bio_PrivateKey(privateKeyBuffer, nil, nil, nil)
    // !!! Remove in production: Print private key
    PEM_write_PrivateKey(stdout, privateKey, nil, nil, 0, nil, nil)
    // Check if private key matches certificate
    guard X509_check_private_key(certificate, privateKey) == 1 else {
        NSLog("Private key does not match certificate")
        return
    }
    // Set OpenSSL parameters
    OPENSSL_add_all_algorithms_noconf()
    ERR_load_crypto_strings()
    // Create P12 keystore
    let passPhrase = UnsafeMutablePointer(mutating: ("" as NSString).utf8String)
    let name = UnsafeMutablePointer(mutating: ("SSL Certificate" as NSString).utf8String)
    guard let p12 = PKCS12_create(passPhrase, name, privateKey, certificate, nil, 0, 0, 0, 0, 0) else {
        NSLog("Cannot create P12 keystore:")
        ERR_print_errors_fp(stderr)
        return
    }
    // Save P12 keystore
    let fileManager = FileManager.default
    let tempDirectory = NSTemporaryDirectory() as NSString
    let path = tempDirectory.appendingPathComponent("ssl.p12")
    fileManager.createFile(atPath: path, contents: nil, attributes: nil)
    guard let fileHandle = FileHandle(forWritingAtPath: path) else {
        NSLog("Cannot open file handle: \(path)")
        return
    }
    let p12File = fdopen(fileHandle.fileDescriptor, "w")
    i2d_PKCS12_fp(p12File, p12)
    fclose(p12File)
    fileHandle.closeFile()
}
于 2017-02-05T14:25:12.683 回答