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 (
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!
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<UnsafePointer<UInt8>?>.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<Int8>) -> 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
}
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:
./build-libssl.sh
$(YOUR_PATH)/OpenSSL-for-iPhone/include
to header search paths$(YOUR_PATH)/OpenSSL-for-iPhone/lib
to library search pathslibcrypto.a
and libssl.a
to linked frameworks and librariesProject-Bridging-Header.h:
#import <openssl/err.h>
#import <openssl/pem.h>
#import <openssl/pkcs12.h>
#import <openssl/x509.h>