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

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 (server, client), we are implementing client authentication (SSL based on X509Certificate). We already have a way to generate a keypair, create a PKCS10 Certificate Signing Request, have this signed by the self-signed CA and create a X509Certificate, send this back. However, to use this certificate in SSL requests, the private key and the X509Certificate have to be exported to a PKCS12 (P12) keystore.

  • Create SSL connection using certificate
  • Allow unverified ssl certificates in WKWebView
  • making CSR certificates in Windows (7)
  • UIWebView + Sharepoint + NTLM Auth - I get Stream is sending an event before being opened
  • iOS 9 ATS SSL error with supporting server
  • Swift: How to Make Https Request Using Server SSL Certificate
  • Does anyone know anything about how to do this, or even if it’s possible? The client has to generate the P12 file (we don’t want to give out the private key), and the client is running iOS, and is a mobile device. The solution worked for Android using BouncyCastle (SpongyCastle), but we found nothing for iOS.

    EDIT: In Java, this export is done by the following:

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        KeyStore ks = KeyStore.getInstance("PKCS12", BouncyCastleProvider.PROVIDER_NAME);
        ks.load(null);
        ks.setKeyEntry("key-alias", (Key) key, password.toCharArray(),
                new java.security.cert.Certificate[] { x509Certificate });
        ks.store(bos, password.toCharArray());
        bos.close();
        return bos.toByteArray();
    

    3 Solutions Collect From Internet About “iOS: How to create PKCS12 (P12) keystore from private key and x509certificate in application programmatically?”

    If you use openssl, you don’t have to copy the full source code into your project, it is enough to add the libs and headers, so the openssl library can be used without any size problem.
    You can generate a key and a cert like that with openssl:

    EVP_PKEY * pkey;
    pkey = EVP_PKEY_new();
    
    RSA * rsa;
    rsa = RSA_generate_key(
            2048,   /* number of bits for the key - 2048 is a sensible value */
            RSA_F4, /* exponent - RSA_F4 is defined as 0x10001L */
            NULL,   /* callback - can be NULL if we aren't displaying progress */
            NULL    /* callback argument - not needed in this case */
    );
    
    EVP_PKEY_assign_RSA(pkey, rsa);
    
    X509 * x509;
    x509 = X509_new();
    
    ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
    
    X509_gmtime_adj(X509_get_notBefore(x509), 0);
    X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
    
    X509_set_pubkey(x509, pkey);
    
    X509_NAME * name;
    name = X509_get_subject_name(x509);
    
    X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC,
            (unsigned char *)"CA", -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC,
            (unsigned char *)"MyCompany Inc.", -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
            (unsigned char *)"localhost", -1, -1, 0);
    
    X509_set_issuer_name(x509, name);
    
    //X509_sign(x509, pkey, EVP_sha1());
    
    const EVP_CIPHER *aConst = EVP_des_ede3_cbc();
    

    And you can write this into pem format with these functions:

    PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL);
    
    
    PEM_write_X509(
                f,   /* write the certificate to the file we've opened */
                x509 /* our certificate */
    );
    

    After that it is possible to write these files into a p12 file, source from here:
    https://github.com/luvit/openssl/blob/master/openssl/demos/pkcs12/pkwrite.c

    /* pkwrite.c */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/pem.h>
    #include <openssl/err.h>
    #include <openssl/pkcs12.h>
    
    /* Simple PKCS#12 file creator */
    int main(int argc, char **argv)
    {
        FILE *fp;
        EVP_PKEY *pkey;
        X509 *cert;
        PKCS12 *p12;
        if (argc != 5) {
            fprintf(stderr, "Usage: pkwrite infile password name p12file\n");
            exit(1);
        }
        SSLeay_add_all_algorithms();
        ERR_load_crypto_strings();
        if (!(fp = fopen(argv[1], "r"))) {
            fprintf(stderr, "Error opening file %s\n", argv[1]);
            exit(1);
        }
        cert = PEM_read_X509(fp, NULL, NULL, NULL);
        rewind(fp);
        pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
        fclose(fp);
        p12 = PKCS12_create(argv[2], argv[3], pkey, cert, NULL, 0,0,0,0,0);
        if(!p12) {
            fprintf(stderr, "Error creating PKCS#12 structure\n");
            ERR_print_errors_fp(stderr);
            exit(1);
        }
        if (!(fp = fopen(argv[4], "wb"))) {
            fprintf(stderr, "Error opening file %s\n", argv[1]);
            ERR_print_errors_fp(stderr);
            exit(1);
        }
        i2d_PKCS12_fp(fp, p12);
        PKCS12_free(p12);
        fclose(fp);
        return 0;
    }
    

    Thank you all very much for this nice solution!

    I translated your code to Swift 3 and built the following function to create a P12 keystore using a signed X509 certificate and a RSA private key, both in PEM format:

    func createP12(pemCertificate: String, pemPrivateKey: String) {
        // Read certificate
        let buffer = BIO_new(BIO_s_mem())
        pemCertificate.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer<Int8>) -> Void in
            BIO_puts(buffer, bytes)
        })
        let certificate = PEM_read_bio_X509(buffer, nil, nil, nil)
        X509_print_fp(stdout, certificate)
        // Read private key
        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)
        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()
    }
    

    EDIT:

    OpenSSL can be used in iOS with the OpenSSL-for-iPhone project:

    1. Check out the repository
    2. Build the static libraries with ./build-libssl.sh
    3. Add $(YOUR_PATH)/OpenSSL-for-iPhone/include to header search paths
    4. Add $(YOUR_PATH)/OpenSSL-for-iPhone/lib to library search paths
    5. Add libcrypto.a and libssl.a to linked frameworks and libraries
    6. Add the following headers to the bridging header:

    Project-Bridging-Header.h:

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

    Problem solved! Thank you, guys.

    The p12 file now is created correctly.

    The code now is:

    NSString *certPem = [certificate pemCertificate];
    [certPem writeToFile:[self certFilePath] atomically:YES encoding:NSUTF8StringEncoding error:nil];
    
    const char *cert_chars = [certPem cStringUsingEncoding:NSUTF8StringEncoding];
    
    BIO *buffer = BIO_new(BIO_s_mem());
    BIO_puts(buffer, cert_chars);
    
    X509 *cert;
    cert = PEM_read_bio_X509(buffer, NULL, 0, NULL);
    if (cert == NULL) {
        NSLog(@"error");
    }
    X509_print_fp(stdout, cert);
    
    if (!X509_check_private_key(cert, [certificate privateKey])) {
        NSLog(@"PK error");
    }
    
    PKCS12 *p12;
    
    SSLeay_add_all_algorithms();
    ERR_load_crypto_strings();
    
    
    p12 = PKCS12_create("passPhrase", "iOSMobileCertificate", [certificate privateKey], cert, NULL, 0,0,0,0,0);
    if(!p12) {
        fprintf(stderr, "Error creating PKCS#12 structure\n");
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    
    [self saveP12File:p12];
    

    saveP12File is:

    //create empty file
    NSString *p12FilePath = [self p12FilePath];
    if (![[NSFileManager defaultManager] createFileAtPath:p12FilePath contents:nil attributes:nil])
    {
        NSLog(@"Error creating file for P12");
        @throw [[NSException alloc] initWithName:@"Fail getP12File" reason:@"Fail Error creating file for P12" userInfo:nil];
    }
    
    //get a FILE struct for the P12 file
    NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:p12FilePath];
    FILE *p12File = fdopen([outputFileHandle fileDescriptor], "w");
    
    i2d_PKCS12_fp(p12File, p12);
    PKCS12_free(p12);
    fclose(p12File);
    

    And p12FilePath is:

    NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    return [documentsFolder stringByAppendingPathComponent:@"CERT.p12"];
    

    Thank you!