How to create certificate request with OpenSSL library - ios

I am trying to create certificate request programmatically in iOS using openSSL. I got testKey.pem(private key) and test.csr finally and the first works well in linux(by openssl command), however test.csr seams strange and cannot be recognized and used properly. here is my code in OC.
- (void)genCertReq {
if (!X509_REQ_set_version(csr.req, csr.ver)) {
LOG(#"set_version failed");
goto error;
}
[self fillDN];
/* subject name */
if (!X509_REQ_set_subject_name(csr.req, csr.subject)) {
LOG(#"subject_name failed");
goto error;
}
rsaPair = RSA_generate_key(bits, e, NULL, NULL);
const char *keyPathChar = [SPFileManager openFile:testKey];
BIO *bp = NULL;
bp = BIO_new_file(keyPathChar, "w");
PEM_write_bio_RSAPrivateKey(bp, rsaPair, NULL, NULL, 0, NULL, NULL);
BIO_free(bp);
/* pub key */
if (1 != EVP_PKEY_assign_RSA(evpKey, rsaPair)) {
LOG(#"assign_RSA failed");
goto error;
}
if (!X509_REQ_set_pubkey(csr.req, evpKey)) {
LOG(#"set_pubkey failed");
goto error;
}
/* attribute */
csr.md = EVP_sha1();
if (!X509_REQ_digest(csr.req, csr.md, (unsigned char *)csr.mdout, (unsigned int *)&csr.mdlen)) {
LOG(#"req_digest failed");
goto error;
}
if (!X509_REQ_sign(csr.req, evpKey, csr.md)) {
LOG(#"req_sign failed");
goto error;
}
const char *csrPathChar = [SPFileManager openFile:csrName];
bp = BIO_new_file(csrPathChar, "w");
PEM_write_bio_X509_REQ(bp, csr.req);
BIO_free(bp);
OpenSSL_add_all_algorithms();
if (X509_REQ_verify(csr.req, evpKey) < 0) {
LOG(#"req_verify failed");
goto error;
}
X509_REQ_free(csr.req);
return;
error:
X509_REQ_free(csr.req);
return;
}
testKey.pem is in PKCS1 format and looks like --BEGIN RSA PRIVATE KEY---, and test.csr looks like ---BEGIN CERTIFICATE REQUEST--- which however I don't think is right.
Any help will be appreciated, thanks.

Related

v4l2src simple pipeline to c-application

My pipeline is like this
gst-launch-1.0 v4l2src ! videoconvert ! xvimagesink
and my code is like this
#include <gst/gst.h>
// easier to pass them as callbacks
typedef struct _CustomData{
GstElement *pipeline;
GstElement *source;
GstElement *convert;
GstElement *sink;
}CustomData;
// callback function
// here src is the v4l2src, newpad is gstpad that has just been added to src element. This is usually the pad to which we want to lnk
// data is the pointer we provided when attaching to the signal.
static void pad_added_handler(GstElement *src, GstPad *new_pad,CustomData *data)
{
GstPad *sink_pad = gst_element_get_static_pad(data->convert, "sink");
GstPadLinkReturn ret;
GstCaps *new_pad_caps = NULL;
GstStructure *new_pad_struct = NULL;
const gchar *new_pad_type = NULL;
if(gst_pad_is_linked(sink_pad))
{
g_print("we are linked. igonring\n");
}
// check the new pad types
// we have previously created a piece of pipeline which deals with videoconvert linked with xvimagesink and we will nto be able to link it to a pad producing video.
//gst-pad_get_current_caps()- retrieves current capabilities of pad
new_pad_caps = gst_pad_get_current_caps(new_pad);
new_pad_struct = gst_caps_get_structure(new_pad_caps, 0);
new_pad_type = gst_structure_get_name(new_pad_struct);
if(!g_str_has_prefix(new_pad_type, "video/x-raw"))
{
g_print("It has new pad type");
}
// gst_pad_link tries to link two pads . the link must be specified from source to sink and both pads must be owned by elements residing in same pipeline
ret = gst_pad_link(new_pad, sink_pad);
if(GST_PAD_LINK_FAILED(ret))
{
g_print("type is new_pad_type");
}
if(new_pad_caps !=NULL)
{
gst_caps_unref(new_pad_caps);
}
gst_object_unref(sink_pad);
}
int main(int argc, char *argv[])
{
GMainLoop *loop;
CustomData data;
GstBus *bus;
GstMessage *msg;
gboolean terminate = FALSE;
gst_init(&argc, &argv);
// loop = g_main_loop_new(NULL, FALSE);
// create the elements
data.source = gst_element_factory_make("v4l2src", "source");
data.convert = gst_element_factory_make("videoconvert", "convert");
data.sink = gst_element_factory_make("xvimagesink", "sink");
data.pipeline = gst_pipeline_new("new-pipeline");
if(!data.pipeline || !data.source || !data.convert || !data.sink)
{
g_printerr("Not all elements could be created\n");
return -1;
}
//we did not link source at this point of time, we will do it later
gst_bin_add_many(GST_BIN(data.pipeline), data.source, data.convert, data.sink, NULL);
// we link convert element to sink, do not link them with source. we dont have source pads here. so we just have videoconvert->sink unlinked
// gst_element_link(data.source, data.convert);
if(!gst_element_link(data.convert,data.sink))
{
g_printerr("elements could not be linked\n");
gst_object_unref(data.pipeline);
return -1;
}
// we set the device source
//g_object_set(source, "device", "/dev/video0", NULL);
//connect to pad added signal.
// we want to attach pad added signal to source element. to do so, we are using g_signal_connect and provide callback function and datapointer.
// when source element has enough information to start producing data, it will create source pads and trigger the pad added signal. at this point, our callback is called
g_signal_connect(G_OBJECT(data.source), "pad-added", G_CALLBACK(pad_added_handler), &data );
//g_signal_connect(G_OBJECT(data.source), "pad-added", G_CALLBACK(handler), &data);
GstStateChangeReturn ret;
ret =gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (data.pipeline);
return -1;
}
// g_main_loop_run(loop);
/* Listen to the bus */
bus = gst_element_get_bus (data.pipeline);
do {
msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
/* Parse message */
if (msg != NULL) {
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
gst_message_parse_error (msg, &err, &debug_info);
g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error (&err);
g_free (debug_info);
terminate = TRUE;
break;
case GST_MESSAGE_EOS:
g_print ("End-Of-Stream reached.\n");
terminate = TRUE;
break;
case GST_MESSAGE_STATE_CHANGED:
/* We are only interested in state-changed messages from the pipeline */
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data.pipeline)) {
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
g_print ("Pipeline state changed from %s to %s:\n",
gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
}
break;
default:
/* We should not reach here */
g_printerr ("Unexpected message received.\n");
break;
}
gst_message_unref (msg);
}
} while (!terminate);
/* Free resources */
gst_object_unref (bus);
gst_element_set_state(data.pipeline, GST_STATE_NULL);
gst_object_unref(data.pipeline);
return 0;
}
and I am getting error like this
Pipeline state changed from NULL to READY:
Pipeline state changed from READY to PAUSED:
Error received from element source: Internal data stream error.
Debugging information: gstbasesrc.c(3055): gst_base_src_loop (): /GstPipeline:new-pipeline/GstV4l2Src:source:
streaming stopped, reason not-linked (-1)
Please let me know what changes should I make to make my pipeline work. Thanks! the above code is based on dynamic pipeline example from gstreamer tutorials. I dont understand where I am going wrong.
The following works though
#include <gst/gst.h>
int main(int argc, char *argv[])
{
GstElement *pipeline, *source,*filter, *convert, *sink;
GstBus *bus;
GstMessage *msg;
GstCaps *caps;
gst_init(&argc, &argv);
source = gst_element_factory_make("v4l2src", "source");
filter = gst_element_factory_make("capsfilter","filter");
convert = gst_element_factory_make("videoconvert", "convert");
sink = gst_element_factory_make("xvimagesink", "sink");\
pipeline = gst_pipeline_new("pipe");
gst_bin_add_many(GST_BIN(pipeline), source, convert,sink, NULL);
gst_element_link_many(source,convert,sink,NULL);
caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "YUY2", NULL);
g_object_set(G_OBJECT(filter), "caps", caps, NULL);
gst_element_set_state(pipeline,GST_STATE_PLAYING);
bus = gst_element_get_bus (pipeline);
msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
/* Parse message */
if (msg != NULL) {
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
gst_message_parse_error (msg, &err, &debug_info);
g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error (&err);
g_free (debug_info);
break;
case GST_MESSAGE_EOS:
g_print ("End-Of-Stream reached.\n");
break;
default:
/* We should not reach here because we only asked for ERRORs and EOS */
g_printerr ("Unexpected message received.\n");
break;
}
gst_message_unref (msg);
}
/* Free resources */
gst_object_unref(bus);
gst_element_set_state(pipeline,GST_STATE_NULL);
gst_object_unref(pipeline);
}
Any ideas why if I add pads, it is not working well??

Verify PKCS7 in DER format using PEM certificate in C using openssl

I have working command line:
openssl cms -inform DER -cmsout -in sod.pkcs7 -verify -CAfile cert.pem
but I'm stragling to make this work in C (under iOS).
I found example which is using PKCS7_verify but with SMIME informat. So I guess I should use CMS_verify but not really sure...
My tests:
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> /* open() */
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/pkcs7.h>
#include <openssl/safestack.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h> /* X509_PURPOSE_ANY */
#include <openssl/x509_vfy.h>
#include <openssl/cms.h>
#include <openssl/pem.h>
int testssl(const char* cert_path, const char* sod_path)
{
X509_STORE *trusted_store;
X509_STORE_CTX *ctx;
STACK_OF(X509) *cert_chain;
X509 *root, *intermediate, *signing;
BIO *in;
int purpose, ret;
X509_VERIFY_PARAM *verify_params;
PKCS7 *p7;
FILE *fp;
int fd;
SSL_library_init();
SSL_load_error_strings();
fd = open(sod_path, O_RDONLY);
in = BIO_new_fd(fd, BIO_NOCLOSE);
p7 = SMIME_read_PKCS7(in, NULL);
cert_chain = sk_X509_new_null();
fp = fopen(cert_path, "r");
root = PEM_read_X509(fp, NULL, NULL, NULL);
sk_X509_push(cert_chain, root);
// <C> not sure what this is good for...
// fp = fopen("intermediate.pem", "r");
fp = fopen(cert_path, "r");
intermediate = PEM_read_X509(fp, NULL, NULL, NULL);
sk_X509_push(cert_chain, intermediate);
trusted_store = X509_STORE_new();
X509_STORE_add_cert(trusted_store, root);
// fp = fopen("signing-ext-no-smimesign.pem", "r");
// signing = PEM_read_X509(fp, NULL, NULL, NULL);
BIO *cont = NULL;
CMS_ContentInfo *cms = NULL;
// cms = d2i_CMS_bio(in, NULL);
// cms = PEM_read_bio_CMS(in, NULL, NULL, NULL);
cms = SMIME_read_CMS(in, &cont);
// <C> all of above return NULL thus CMS_verify() fails
// 1st attempt
//
ret = CMS_verify(cms, cert_chain,trusted_store, NULL, NULL, 0);
printf("CMS_verify: %s\n", ret ? "OK" : "failure");
// 2nd attempt
//
ret = PKCS7_verify(p7, cert_chain, trusted_store, NULL, NULL, 0);
printf("Verification without specifying params: %s\n", ret ? "OK" : "failure");
/* Now set a suitable OpenSSL's "purpose", or disable its checking.
* Note: since OpenSSL 1.1.0, we'd not need `ctx`, but could just use:
* verify_params = X509_STORE_get0_param(trusted_store); */
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, trusted_store, signing, cert_chain);
verify_params = X509_STORE_CTX_get0_param(ctx);
purpose = X509_PURPOSE_get_by_sname("crlsign"); /* Or: purpose = X509_PURPOSE_ANY */
X509_VERIFY_PARAM_set_purpose(verify_params, purpose);
X509_STORE_set1_param(trusted_store, verify_params);
// 3rd attempt
//
ret = PKCS7_verify(p7, cert_chain, trusted_store, NULL, NULL, 0);
printf("Verification with 'crlsign' purpose: %s\n", ret ? "OK" : "failure");
return 0;
}
Figured it out.
int testcms (const char* cert_path, const char* sod_path)
{
BIO *in = NULL, *out = NULL, *tbio = NULL, *cont = NULL;
X509_STORE *st = NULL;
X509 *cacert = NULL;
CMS_ContentInfo *cms = NULL;
int ret = 1;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
st = X509_STORE_new();
tbio = BIO_new_file(cert_path, "r");
if (!tbio) {
fprintf(stderr, "Cert file not opened: path=%s;\n", cert_path);
goto err;
}
cacert = PEM_read_bio_X509(tbio, NULL, 0, NULL);
if (!cacert) {
fprintf(stderr, "PEM_read_bio_X509 FAILED: cert.path=%s;\n", cert_path);
goto err;
}
if (!X509_STORE_add_cert(st, cacert)) {
fprintf(stderr, "X509_STORE_add_cert FAILED: cert.path=%s;\n", cert_path);
goto err;
}
in = BIO_new_file(sod_path, "r");
if (!in) {
fprintf(stderr, "PKCS7 file not opened: path=%s;\n", sod_path);
goto err;
}
//cms = SMIME_read_CMS(in, &cont);
cms = d2i_CMS_bio(in, NULL);
if (!cms) {
fprintf(stderr, "SMIME_read_CMS FAILED: pkcs7.path=%s;\n", sod_path);
goto err;
}
if (!CMS_verify(cms, NULL, st, cont, NULL, 0)) {
fprintf(stderr, "Verification Failure\n");
goto err;
}
fprintf(stderr, "Verification Successful\n");
ret = 0;
err:
if (ret) {
fprintf(stderr, "Error Verifying Data\n");
ERR_print_errors_fp(stderr);
}
CMS_ContentInfo_free(cms);
X509_free(cacert);
BIO_free(in);
BIO_free(out);
BIO_free(tbio);
return ret;
}

S/MIME email encryption library for iOS

I am trying to find a S/MIME email encryption library for an iOS email app I am creating in Swift. I have been having trouble trying to find a library for the encryption, has anyone had any experience with this?
I have tried OpenSSL but have run into issues with importing all the files in need in the bridging header, for example I need to use functions in pem.h but if I try import pem.h the bridging header fails to be imported altogether.
Any help with this would be greatly appreciated.
I had a similar requirement. Eventually I had to import openSSL and write my own code to handle the decrypt of PKCS7. I made a small github repo which should help
https://github.com/zkrige/iOS-pkcs7-decrypt
here is the gist of the code
#include <openssl/bio.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/rand.h>
X509 *getCert(const char *certificate) {
BIO *membuf = BIO_new(BIO_s_mem());
BIO_puts(membuf, certificate);
X509 *x509 = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
return x509;
}
EVP_PKEY *getKey(const char *privateKey) {
BIO *membuf = BIO_new(BIO_s_mem());
BIO_puts(membuf, privateKey);
EVP_PKEY *key = PEM_read_bio_PrivateKey(membuf, NULL, 0, NULL);
return key;
}
PKCS7 *getContainer(const char *encrypted) {
BIO* membuf = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(membuf, 0);
BIO_puts(membuf, encrypted);
PKCS7* pkcs7 = SMIME_read_PKCS7(membuf, NULL);
if (!pkcs7) {
fprintf(stderr, "error: %ld\n", ERR_get_error());
}
return pkcs7;
}
char *decrypt(PKCS7 *pkcs7, EVP_PKEY *pkey, X509 *cert) {
BIO *out = BIO_new(BIO_s_mem());
if (PKCS7_decrypt(pkcs7, pkey, cert, out, 0) != 1) {
X509_free(cert);
EVP_PKEY_free(pkey);
PKCS7_free(pkcs7);
fprintf(stderr, "Error decrypting PKCS#7 object: %ld\n", ERR_get_error());
return NULL;
}
BUF_MEM* mem;
BIO_get_mem_ptr(out, &mem);
char *data = malloc(mem->length + 1);
memcpy(data, mem->data, mem->length + 1);
BIO_flush(out);
BIO_free(out);
return data;
}
char *decrypt_smime(const char *encrypted, const char *privateKey, const char *certificate) {
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
X509 *cert = getCert(certificate);
if (!cert) {
return NULL;
}
EVP_PKEY *pkey = getKey(privateKey);
if (!pkey) {
X509_free(cert);
return NULL;
}
PKCS7 *pkcs7 = getContainer(encrypted);
if (!pkcs7) {
X509_free(cert);
EVP_PKEY_free(pkey);
return NULL;
}
char *data = decrypt(pkcs7, pkey, cert);
X509_free(cert);
EVP_PKEY_free(pkey);
PKCS7_free(pkcs7);
return data;
}

One function gives several results in swift

I have a method in objective-C which I call from swift. It worked pretty well in swift 2, but in swift 3 the behaviour has changed. It gives me 3 different results, even though I send the same parameters.
Sometimes it doesnt find pfile, sometimes it fails on pin checking, sometimes works good and gives me x509.
char* ParsePKCS12(unsigned char* pkcs12_path, unsigned char * pin) {
printf("PARSE PATH: %s\n", pkcs12_path);
printf("PASSWORD: %s\n", pin);
NSString *pfile = [NSString stringWithUTF8String:pkcs12_path];
FILE *fp;
PKCS12 *p12;
EVP_PKEY *pkey;
X509 *cert;
BIO *databio = BIO_new(BIO_s_mem());
STACK_OF(X509) *ca = NULL;
if([[NSFileManager defaultManager] fileExistsAtPath:pfile]) {
NSLog(#"ok, pfile exists!");
} else {
NSLog(#"error, pfile does not exists!");
return "-1";
}
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
fp = fopen([pfile UTF8String], "rb");
p12 = d2i_PKCS12_fp(fp, NULL);
fclose (fp);
if (!p12) {
fprintf(stderr, "Error reading PKCS#12 file\n");
ERR_print_errors_fp(stderr);
return "-1";
}
if (!PKCS12_parse(p12, (const char *)pin, &pkey, &cert, &ca)) { //Error at parsing or pin error
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
ERR_print_errors(databio);
return "-1";
}
BIO *bio = NULL;
char *pem = NULL;
if (NULL == cert) {
//return NULL;
return "-1";
}
bio = BIO_new(BIO_s_mem());
if (NULL == bio) {
return "-1";
}
if (0 == PEM_write_bio_X509(bio, cert)) {
BIO_free(bio);
//return NULL;
}
pem = (char *) malloc(bio->num_write + 1);
if (NULL == pem) {
BIO_free(bio);
return "-1";
}
memset(pem, 0, bio->num_write + 1);
BIO_read(bio, pem, bio->num_write);
BIO_free(bio);
PKCS12_free(p12);
return pem;
}
this code I call in swift like this:
self.x509 = String(cString:ParsePKCS12(UnsafeMutablePointer<UInt8>(mutating: self.path),
UnsafeMutablePointer<UInt8>(mutating: "123456"))!)
Your call
self.x509 = String(cString:ParsePKCS12(UnsafeMutablePointer<UInt8>(mutating: self.path),
UnsafeMutablePointer<UInt8>(mutating: "123456"))!)
does not work reliably because in both
UnsafeMutablePointer<UInt8>(mutating: someSwiftString)
calls, the compiler creates a temporary C string representation of
the Swift string and passes that to the function. But that C string
is only valid until the UnsafeMutablePointer constructor returns, which means that the second
string conversion can overwrite the first, or any other undefined
behaviour.
The simplest solution would be to change the C function to
take constant C strings (and use the default signedness):
char* ParsePKCS12(const char * pkcs12_path, const char * pin)
Then you can simply call it as
self.x509 = String(cString: ParsePKCS12(self.path, "123456"))
and the compiler creates temporary C strings which are valid during
the call of ParsePKCS12().

Import PEM encoded X.509 certificate into iOS KeyChain

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".
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.

Resources