2016-07-11 9 views
1

Ich weiß, dass es eine Funktion namens SecPKCS12Import gibt, mit der Sie Daten aus einer P12-Datei importieren können. Ich möchte jedoch den umgekehrten Weg gehen. Ich habe eine SecCertificateRef und eine öffentliche/private SecKeyRef, die ich verwenden möchte, um eine P12-Datei zu erstellen. Weiß jemand, wie man das auf dem iPhone macht?Generieren Sie eine P12-Datei Xcode?

Dank

Antwort

2

Leider gibt CommonCrypto bietet keine Mittel PKCS12-Container geschweige denn andere Export-Funktionalität (obwohl sein OSX Gegenstück kann das tun) zu exportieren. Es gibt Möglichkeiten, die SecKeyRef-Rohdaten aus der Schlüsselkette zu extrahieren, aber dann müssen Sie noch das gesamte PKCS12-Wrapping selbst schreiben.

Wir standen vor einem ähnlichen Problem und gingen mit OpenSSL.

Kompilieren OpenSSL für iOS

OpenSSL Integration erfordert ein wenig Arbeit, wie Sie die OpenSSL-Quellen selbst kompilieren und verknüpfen müssen. Glücklicherweise sind einige Build-Skripts verfügbar, sodass Sie dies nicht selbst tun müssen, z. B. https://github.com/x2on/OpenSSL-for-iPhone. Ich schlage vor, Sie verwenden sie, wie Sie ein paar der Makefiles patchen müssen, die ein bisschen wie eine Hasel ist. Diese Build-Skripte generieren statische verknüpfte Bibliotheken für iOS und TVOS. Sie müssen sie nur mit Ihrem Projekt verknüpfen und den Suchpfad für Header und Bibliothek entsprechend einstellen.

CocoaPods

Sie auch die offizielle OpenSSL CocoaPod verwenden können. Das erspart Ihnen die Mühe, Ihr Projekt zu konfigurieren.

exportieren PKCS12

Wie Sie vielleicht wissen, ist OpenSSL ein C library. Das bedeutet, dass Sie alle C-Funktionen in einen Objective-C- oder Swift-Wrapper einkapseln möchten. Es gibt einige Open-Source-Wrapper, die Im- und Export von PKCS12-Containern unterstützen, aber ich habe keinen einzigen mit guter Dokumentation gefunden. Sie sollten in der Lage sein, die relevanten Snippets aus einigen Quellen abzuleiten.

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

Sie können an diesem Beispiel auch http://fm4dd.com/openssl/pkcs12test.htm einen Blick.

Hoffe, dass hilft!

+0

Auch, wenn ich ein Zertifikat und einen privaten Schlüssel zu einem schicken möchten Server, gibt es eine andere Möglichkeit, dies zu tun, ohne die OpenSSL-Bibliothek zu verwenden? – hockeybro

+0

Theoretisch können Sie die privaten und öffentlichen Schlüssel als NSData aus dem Schlüsselbund exportieren, aber jede zusätzliche Formatierung, z. B. PEM, DER, X.509 oder PKCS12, die Sie selbst implementieren müssen. Leider haben wir das nie in unserer App funktionieren lassen. Wenn Sie interessiert sind, können Sie sich "SecItemCopyMatching" anschauen. Viel Glück! –

+0

Wäre es möglich, Codeschnipsel zu erhalten, um eine PKCS12 von OpenSSL zu erzeugen, und kopieren Sie diese einfach hinein, anstatt die gesamte Bibliothek zu importieren? – hockeybro

1

Ich stimme zu, dass diese Aufgabe nur mit OpenSSL ausgeführt werden kann. Es ist ein bisschen schwierig, es für iOS zu kompilieren, aber mit OpenSSL-for-iPhone ist es durchaus möglich.

Um die gegebene Aufgabe der Schaffung eines PKCS12 Schlüsselspeicher von einem SecCertificate und SecKey mit Swift 3 fügen Sie einfach den statischen Bibliotheken libssl.a und libcrypto.a zu einem Projekt zu lösen und die folgende Überbrückung Header erstellen:

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

Um den Keystore zu erstellen, müssen die Eingabedaten in OpenSSL-Datenstrukturen konvertiert werden, was etwas Kreativität erfordert.Die SecCertificate kann direkt in das Format DER konvertiert und dann in eine X509 Struktur eingelesen werden. Die SecKey ist noch schlechter zu handhaben. Die einzige mögliche Lösung, um die Daten des Schlüssels zu erhalten, besteht darin, sie in den Schlüsselbund zu schreiben und die Referenz zu erhalten. Aus der Referenz können wir die Base 64 codierte Zeichenfolge erhalten, die dann in eine EVP_PKEY Struktur gelesen werden kann. Jetzt können wir den Keystore erstellen und in einer Datei speichern. Um die Daten in den Schlüsselspeicher über iOS-Funktionen zugreifen, müssen wir die Datei über let data = FileManager.default.contents(atPath: path)! as NSData

Die vollständige Lösung wird gezeigt in der folgenden lesen:

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

Gute Sachen. Aus Sicherheitsgründen möchten Sie möglicherweise einen Kommentar nach dem Code eingeben, der den privaten Schlüssel ausgibt, um dies in einer Produktions-App nicht zu tun? – mbonness

+1

Danke. Ja, Sie haben recht, Schlüssel sollten in der Produktion nicht gedruckt werden. Ich habe der entsprechenden Zeile einen Kommentar hinzugefügt. – sundance