iOS: How to create PKCS12 (P12) keystore from private key and x509certificate in application programmatically?

后端 未结 4 1621
我在风中等你
我在风中等你 2020-12-05 12:39

This question was apparently similar but had no answers of any kind: Programmatically create a x509 certificate for iPhone without using OpenSSL

In our application (

4条回答
  •  心在旅途
    2020-12-05 12:55

    My solution is similar to sundance's, I reworked it to get around address sanitizer / heap overflow issues encountered in XCode 9.

    func createP12(secCertificate: SecCertificate, secPrivateKeyBase64: String, p12FileName: String, _ p12Password: String = "") throws -> String {
        // 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?>.allocate(capacity: 1)
        certificateData.pointee = certificatePointer
    
        // Read DER certificate
        let certificate = d2i_X509(nil, certificateData, certificateLength)
    
        // Print certificate
        #if DEBUG
            X509_print_fp(stdout, certificate)
        #endif
    
        let pemPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n\(secPrivateKeyBase64)\n-----END RSA PRIVATE KEY-----\n"
    
        let p12Path = try pemPrivateKey.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer) -> String in
            let privateKeyBuffer = BIO_new_mem_buf(bytes, Int32(pemPrivateKey.characters.count))
            let privateKey = PEM_read_bio_PrivateKey(privateKeyBuffer, nil, nil, nil)
            defer {
                BIO_free(privateKeyBuffer)
            }
    
            // Print private key
            #if DEBUG
                PEM_write_PrivateKey(stdout, privateKey, nil, nil, 0, nil, nil)
            #endif
    
            // Check if private key matches certificate
            guard X509_check_private_key(certificate, privateKey) == 1 else {
                throw X509Error.privateKeyDoesNotMatchCertificate
            }
    
            // Set OpenSSL parameters
            OPENSSL_add_all_algorithms_noconf()
            ERR_load_crypto_strings()
    
            // Create P12 keystore
            let passPhrase = UnsafeMutablePointer(mutating: (p12Password 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 {
                ERR_print_errors_fp(stderr)
                throw X509Error.cannotCreateP12Keystore
            }
    
            // Save P12 keystore
            let fileManager = FileManager.default
            let documentsPathURL = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
            let path = documentsPathURL.appendingPathComponent(p12FileName).path
            fileManager.createFile(atPath: path, contents: nil, attributes: nil)
            guard let fileHandle = FileHandle(forWritingAtPath: path) else {
                NSLog("Cannot open file handle: \(path)")
                throw X509Error.cannotOpenFileHandles
            }
            let p12File = fdopen(fileHandle.fileDescriptor, "w")
            i2d_PKCS12_fp(p12File, p12)
            PKCS12_free(p12)
            fclose(p12File)
            fileHandle.closeFile()
    
            NSLog("Wrote P12 keystore to: \(path)")
            return path
        })
    
        return p12Path
    }
    
    enum X509Error: Error {
        case privateKeyDoesNotMatchCertificate
        case cannotCreateP12Keystore
        case cannotOpenFileHandles
    }
    

提交回复
热议问题