2016-07-11 120 views
1

我知道有一個函數叫做SecPKCS12Import,它允許你從p12文件導入數據。但是,我想要走相反的路線。我有一個SecCertificateRef和一個公共/私人SecKeyRef,我想用它來創建一個P12文件。有誰知道如何在iPhone上做到這一點?生成P12文件Xcode?

感謝

回答

2

不幸的是,有CommonCrypto不提供任何手段來導出PKCS12容器更不用說任何其他導出功能(即使其OSX對應可以做到這一點)。有許多方法可以從關鍵鏈中提取SecKeyRef原始數據,但是仍然需要編寫所有包裝自己的PKCS12。

我們遇到了類似的問題,並使用OpenSSL。

編譯OpenSSL的爲iOS

集成的OpenSSL需要一點點的工作,因爲你需要編譯和鏈接OpenSSL的源自己。幸運的是,有一些構建腳本可供使用,因此您無需親自操作,例如https://github.com/x2on/OpenSSL-for-iPhone。我建議你使用它們,因爲你需要修補一些具有淡褐色的Makefiles。這些構建腳本爲iOS和tvOS生成靜態鏈接庫。您只需將它們與您的項目相關聯,並相應地設置標題和庫搜索路徑。

的CocoaPods

您也可以使用官方OpenSSL CocoaPod。這爲您節省配置項目的麻煩。

導出PKCS12

正如你可能知道,OpenSSL是一個C library。這意味着您可能想要將所有C函數封裝到Objective-C或Swift包裝器中。有一些開源的包裝器可以支持導入和導出PKCS12容器,但我還沒有找到一個包含良好文檔的包裝器。儘管如此,你應該能夠從一些來源獲得相關的片段。

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

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

希望有幫助!

+0

,如果我要發送的證書和私鑰到:要訪問通過iOS的功能密鑰庫中的數據,我們必須讀通過let data = FileManager.default.contents(atPath: path)! as NSData

文件的完整解決方案表現在以下服務器,有沒有其他的方式來做到這一點,而不使用OpenSSL庫? – hockeybro

+0

理論上,您可以將私鑰和公鑰作爲NSData從鑰匙串中導出,但也可以使用任何其他格式(例如PEM,DER,X.509或PKCS12)來實現自己。不幸的是,我們從來沒有在我們的應用中使用它。如果你有興趣,你可以看看'SecItemCopyMatching'。祝你好運! –

+0

是否有可能獲得代碼片段來從OpenSSL生成PKCS12,並將其複製,而不是導入整個庫? – hockeybro

1

我同意這個任務只能用OpenSSL來完成。對於iOS編譯它有點棘手,但是它很有可能是OpenSSL-for-iPhone

爲了解決創建從一個SecCertificate一個PKCS12密鑰庫,並與斯威夫特3個只需添加靜態庫libssl.a一個SecKeylibcrypto.a到項目的既定任務,並創建以下橋接報:

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

要創建密鑰庫,必須將輸入數據轉換爲OpenSSL數據結構,這需要一些創造性。SecCertificate可以直接轉換爲DER格式,然後讀入X509結構。 SecKey更難以處理。獲得密鑰數據的唯一可能的解決方案是將其寫入鑰匙鏈並獲取參考。從參考資料中我們可以得到64位編碼的字符串,然後可以將其讀入EVP_PKEY結構。現在,我們可以創建密鑰庫並將其保存到文件中。此外

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() 
} 
+0

好東西。出於安全考慮,您可能希望通過打印出私鑰的代碼進行評論,以免在生產應用中執行此操作? – mbonness

+1

謝謝。是的,你是對的,鑰匙不應該在生產中打印。我在相應的行中添加了一條評論。 – sundance