Import PEM encoded X.509 certificate into iOS KeyChain
c, cryptography, encryption, ios, openssl
Solution
The following calls were missing before creating the PKCS12:
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
These solved the problems with the missing cipher and also a subsequent problem of a missing digest.
Problem
I'm receiving a String containing a PEM encoded X.509 certificate from somewhere. I'd like to import this certificate into the KeyChain of iOS. I'm planning to do the following: - convert NSString to openssl X509 - create PKCS12 - convert PKCS12 to NSData - import NSData with SecPKCS12Import So far I came up with the following code: ``` const char *cert_chars = [certStr 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); EVP_PKEY *privateKey; const unsigned char *privateBits = (unsigned char *) [privateKeyData bytes]; int privateLength = [privateKeyData length]; privateKey = d2i_AutoPrivateKey(NULL, &privateBits, privateLength); if (!X509_check_private_key(cert, privateKey)) { NSLog(@"PK error"); } PKCS12 *p12 = PKCS12_create("test", "David's Cert", privateKey, cert, NULL, 0, 0, 0, 0, 0); ``` Unfortunately, p12 is nil even though X509_check_private_key was successful and X509_print_fp(stdout, cert) prints a valid certificate. - is my approach correct - how come PKCS12_create seems to fail? Update: The call PKCS12_create seems to fail in the following method: ``` int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen, ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de) { const EVP_CIPHER *cipher; const EVP_MD *md; int cipher_nid, md_nid; EVP_PBE_KEYGEN *keygen; if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, OBJ_obj2nid(pbe_obj), &cipher_nid, &md_nid, &keygen)) { char obj_tmp[80]; EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_PBE_ALGORITHM); if (!pbe_obj) BUF_strlcpy (obj_tmp, "NULL", sizeof obj_tmp); else i2t_ASN1_OBJECT(obj_tmp, sizeof obj_tmp, pbe_obj); ERR_add_error_data(2, "TYPE=", obj_tmp); return 0; } if(!pass) passlen = 0; else if (passlen == -1) passlen = strlen(pass); if (cipher_nid == -1) cipher = NULL; else { cipher = EVP_get_cipherbynid(cipher_nid); if (!cipher) { EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_CIPHER); return 0; } } if (md_nid == -1) md = NULL; else { md = EVP_get_digestbynid(md_nid); if (!md) { EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_DIGEST); return 0; } } if (!keygen(ctx, pass, passlen, param, cipher, md, en_de)) { EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_KEYGEN_FAILURE); return 0; } return 1; } ``` Retrieving the cipher ``` cipher = EVP_get_cipherbynid(cipher_nid); ``` somehow returns nil for "RC2-40-CBC".