2016-07-11 7 views
1

Я знаю, что есть функция, называемая SecPKCS12Import, которая позволяет вам импортировать данные из файла p12. Тем не менее, я хочу пойти по обратному маршруту. У меня есть SecCertificateRef и общедоступный/частный SecKeyRef, который я хочу использовать для создания файла P12. Кто-нибудь знает, как это сделать на iPhone?Создать файл P12 Xcode?

Благодаря

ответ

2

К сожалению, CommonCrypto не предоставляет никаких средств для экспорта PKCS12 контейнеры не говоря уже о какой-либо другой функции экспорта (хотя его OSX коллега может сделать это). Существуют способы извлечения сырых данных SecKeyRef из ключевой цепи, но вам все равно нужно написать всю упаковку PKCS12.

Мы столкнулись с аналогичной проблемой и пошли с OpenSSL.

Компиляция OpenSSL для прошивки

Интеграции OpenSSL требует немного работы, вам нужно собрать и связать OpenSSL источников самостоятельно. К счастью, есть некоторые скрипты сборки, поэтому вам не нужно делать это самостоятельно, например, https://github.com/x2on/OpenSSL-for-iPhone. Я предлагаю вам использовать их, поскольку вам нужно исправить некоторые из Makefile, которые немного ореховые. Эти скрипты сборки генерируют статические связанные библиотеки для 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

Кроме того, если я хочу, чтобы послать сертификат и закрытый ключ к server, есть ли другой способ сделать это, не используя библиотеку OpenSSL? – hockeybro

+0

Теоретически вы можете экспортировать частные и открытые ключи как NSData из связки ключей, но любое дополнительное форматирование, например, PEM, DER, X.509 или PKCS12, вам необходимо реализовать себя. К сожалению, у нас никогда не было этого, чтобы работать в нашем приложении. Если вас это интересует, вы можете взглянуть на 'SecItemCopyMatching'. Удачи! –

+0

Можно ли получить фрагменты кода для генерации PKCS12 из OpenSSL и просто скопировать его, а не импортировать всю библиотеку? – hockeybro

1

Я согласен, что эта задача может быть выполнена только с использованием OpenSSL. Сложно скомпилировать его для iOS, но с OpenSSL-for-iPhone вполне возможно.

Чтобы решить данную задачу создания PKCS12 хранилищу ключей от SecCertificate и SecKey с Swift 3 просто добавить статические библиотеки libssl.a и libcrypto.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() 
} 
+0

Хороший материал. По соображениям безопасности вы можете поместить комментарий по коду, который печатает закрытый ключ, чтобы не делать этого в производственном приложении? – mbonness

+1

Спасибо. Да, вы правы, ключи не должны печататься на производстве. Я добавил комментарий к соответствующей строке. – sundance

Смежные вопросы