export and import certificates chain - c++

I try to export pfx file which contain certificates chain and private key from windows certificates store, convert oit into PEM format and save it to file which be read by openssl based application.
I do it by the following steps (capi / openssl commands):
creating memory store - CertOpenStore
Open system store - CertOpenSystemStore
find desired certificate according to freindly name -
CertFindCertificateInStore
Add desired certificate to memory store - CertAddCertificateContextToStore
export memory store - PFXExportCertStoreEx
convert blob into base64 and save into file
Import certificates chain from PEM file- SSL_CTX_use_certificate_chain_file
Import private key from PEM file - SSL_CTX_use_PrivateKey_file
1) I didn't find a command which convert pfx (memory blob / file) to pem format
2) does this scenario export all certificate chain which were originally store in the pfx file
3) does SSL_CTX_use_certificate_chain_file import all certificates chain or I have to use other commands to import all chain into CTX structure
Thanks in advance

1) This worked for me
{
FILE* fp = NULL;
CString errorS = NULL;
PKCS12* p12 = NULL;
EVP_PKEY* pkey = NULL;
X509* cert = NULL;
STACK_OF(X509) *ca = NULL;
int i;
pkey = (EVP_PKEY*)new EVP_PKEY;
cert = (X509*)new X509;
do
{
if (fopen_s(&fp, CT2A(pkcs12File), "rb"))
{
errorS = ("Error opening file %s\n", CT2A(pkcs12File));
break;
}
p12 = d2i_PKCS12_fp(fp, NULL);
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
if (!p12)
{
errorS = ("Error reading PKCS#12 file\n");
break;
}
if (!PKCS12_parse(p12, CT2A(szPassword), &pkey, &cert, &ca))
{
errorS = ("Error parsing PKCS#12 file\n");
break;
}
if (fopen_s(&fp, CT2A(pszNameString + L".pem"), "w"))
{
errorS = ("Error opening file %s\n", CT2A(pemFileName));
break;
}
if (pkey)
{
fprintf(fp, "***Private Key***\n");
PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL);
}
if (cert)
{
fprintf(fp, "***User Certificate***\n");
PEM_write_X509_AUX(fp, cert);
}
if (ca && sk_X509_num(ca))
{
fprintf(fp, "***Other Certificates***\n");
for (i = 0; i < sk_X509_num(ca); i++)
PEM_write_X509_AUX(fp, sk_X509_value(ca, i));
}
} while (0);
PKCS12_free(p12);
sk_X509_pop_free(ca, X509_free);
X509_free(cert);
EVP_PKEY_free(pkey);
if (NULL != fp)
{
fclose(fp);
}
}
2+3) SSL_CTX_use_certificate_chain_file import all certificates chain

Related

How to implement SNI using openssl configuration file?

I have implemented SNI using OpenSSL but not using an OpenSSL config file (cnf). I am loading a server SSL_CTX doing the following:
FILE *fp;
CONF *cnf = NULL;
long eline;
fp = fopen("/somepath/app.cnf", "r");
if (fp == NULL) {
fprintf(stderr, "Error opening configuration file\n");
/* Other missing configuration file behaviour */
} else {
cnf = NCONF_new(NULL);
if (NCONF_load_fp(cnf, fp, &eline) == 0) {
fprintf(stderr, "Error on line %ld of configuration file\n", eline);
ERR_print_errors_fp(stderr);
/* Other malformed configuration file behaviour */
} else if (CONF_modules_load(cnf, "appname", 0) <= 0) {
fprintf(stderr, "Error configuring application\n");
ERR_print_errors_fp(stderr);
/* Other configuration error behaviour */
}
fclose(fp);
NCONF_free(cnf);
}
It works fine, but now I am trying to implement server-side SNI using the OpenSSL config file and I don't know how can I get the required information to do it. I have taken a look to How to implement Server Name Indication (SNI) and this is a good explanation to do it without openssl config file. But that doesn't work using the file.
How can I determine if the server have the certificate requested? Maybe openssl provides the proper certificate by itself?

RSA public encryption using public key from X509 certificate c++

The task is to encrypt some message with RSA public key which lies in .crt file - X509 certificate file.
With issue below I succesfully get sertificate information, but can not find any issue how to use public key from x to encrypt message using RSA algorythm.
When I trying to create BIO buffer with key data and to use PEM_read_bio_RSA_PUBKEY function to write key into RSA struct it returns an error "error:0906D06C:PEM routines:PEM_read_bio:no start line" which is clear why appear but not clear how to correctly solve. Are any issue how I can do encryption without creating another buffer and adding manualy lines with "-----BEGIN RSA PUBLIC KEY-----" and "-----END RSA PUBLIC KEY-----"?
Here are my code for this job:
unsigned char message[] = "Hello cryptographic world!";
unsigned char *encrypted_text;
int CyferWithHostKeyPub()
{
ERR_load_crypto_strings();
int res_length = 0;
RSA *public_key;
X509 *x;
BIO *fp = BIO_new_file("cert.crt", "r");
if (!fp)
{
printf("\nCan't open file.\n");
return -1;
}
x = PEM_read_bio_X509(fp, 0, 0, NULL);
if (!x) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return 1;
}
BIO *membuf = BIO_new_mem_buf((void*)x->cert_info->key->public_key->data, x->cert_info->key->public_key->length);
public_key = PEM_read_bio_RSA_PUBKEY(membuf, &public_key, NULL, NULL);
if (!public_key) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return 1;
}
int encrypted_length = RSA_size(public_key);
encrypted_text = new unsigned char[ encrypted_length ];
RSA_public_encrypt(sizeof(message), message, encrypted_text, public_key, RSA_PKCS1_PADDING);
RSA_free(public_key);
X509_free(x);
BIO_free(membuf);
BIO_free(fp);
return encrypted_length;
}
So, how can I do this correctly? Thanks!
Use the X509_get_pubkey() function to obtain a reference to the EVP_PKEY structure contained within the X509 structure. Then you can use EVP_PKEY_get1_RSA() to get a reference to the RSA structure.
See these man pages for further info:
https://www.openssl.org/docs/man1.1.0/crypto/X509_get_pubkey.html
https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_RSA.html

PKCS#12 Invalid Password

I am using this OpenSSL code on the iPhone to generate a PKCS#12 file given some data of a certificate/private key. I am able to verify that this PKCS#12 is parseable on OpenSSL, since it doesn't print out an error when I check for it in the code. However, when I transfer it to my server, it says: Error: PKCS#12 MAC could not be verified. Invalid password? Does anyone know why this is? I am using the same password, which is 'password'
- (NSData *)generateP12AsNSData:(NSData *)keyData certificate:(NSData *)certificateData {
//put the certificateData into a X509 certificate
const unsigned char *certificateDataBytes = (const unsigned char*)[certificateData bytes];
X509 *certificateX509 = d2i_X509(NULL, &certificateDataBytes, [certificateData length]);
EVP_PKEY *privateKey;
PKCS12 *p12;
//cast the key data as an unsigned char so that we can convert it to the OpenSSL key format
const unsigned char *bits = (unsigned char *) [keyData bytes];
int length = (int)[keyData length];
privateKey = EVP_PKEY_new();
//convert the unsigned char to OpenSSL Key format
RSA *rsa = NULL;
d2i_RSAPrivateKey(&rsa, &bits, &length);
EVP_PKEY_assign_RSA(privateKey, rsa);
//create the PKCS#12
OpenSSL_add_all_algorithms();
p12 = PKCS12_create("password", "ExtraDeviceP12", privateKey, certificateX509, NULL, 0,0,0,0,0);
//make sure the p12 exists
if(!p12) {
fprintf(stderr, "Error creating PKCS#12 ");
ERR_print_errors_fp(stderr);
return nil;
}
//error checking to make sure we generated the CSR correctly
STACK_OF(X509) *ca = NULL;
EVP_PKEY *parseKey;
X509 *parseCert;
if (!PKCS12_parse(p12, "password", &parseKey, &parseCert, &ca)) {
printf("error parsing PKCS#12 file");
return nil;
}
//convert the PKCS#12 to binary data
//create a new memory BIO. A BIO is used for basic I/O abstraction.
BIO *bio;
bio = BIO_new(BIO_s_mem());
//i2d_PKCS12_bio is used to export a PKCS12 object
i2d_PKCS12_bio(bio, p12);
BUF_MEM *buffer;
BIO_get_mem_ptr(bio, &buffer);
//int bioLen = BIO_pending(&buffer);
char *buff = (char*)malloc(buffer->length);
memcpy(buff, buffer->data, buffer->length - 1);
buff[buffer->length - 1] = 0;
NSData *data = [NSData dataWithBytes:buff length:buffer->length];
NSString *string = [data base64EncodedStringWithOptions:0];
NSLog(#"Base64 PKCS#12: %#", string);
BIO_free_all(bio);
return data;
}
EDIT:
Here is the code on my server side written in Javascript. In this case req.body is the NSData sent from the iPhone. I get the invalid password error.
var p12b64 = req.body.toString('base64');
var p12Der = forge.util.decode64(pk12b64);
var p12Asn1 = forge.asn1.fromDer(p12Der);
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
Try the following. It shows you where some key return values should be checked, and it avoids extra copies of the data:
BIO *bio = BIO_new(BIO_s_mem());
ASSERT(bio != NULL);
int ret = i2d_PKCS12_bio(bio, p12);
ASSERT(ret == 1);
BUF_MEM *buffer;
BIO_get_mem_ptr(bio, &buffer);
ASSERT(buffer != NULL);
NSData *data = [NSData dataWithBytes:buffer->data length:buffer->length];
BIO_free_all(bio);
You can find the docs for i2d_PKCS12_bio at i2d_PKCS12_bio man pages.
If you want, you can Base64 encode the binary data from i2d_PKCS12_bio. See Non-printable character after generating random n-byte Base64 string for using a BIO chain and ensuring the Base64 string is NULL terminated.

openssl SSL_get_verify_result returns error 20

I am writing a C++ program that connects using SSL. The certificate chain checks out using:
openssl verify -CAfile test.pem private.pem
where test.pem contains the intermediate and root certificate. My test program does not verify the certificate chain.
if ( !SSL_CTX_load_verify_locations( ctx, "c:/Certs/test.pem", NULL ) ) {
// Failure message and cleanup goes here.
}
SSL* ssl;
BIO* bio = BIO_new_ssl_connect( ctx );
BIO_get_ssl( bio, &ssl );
SSL_set_mode( ssl, SSL_MODE_AUTO_RETRY );
BIO_set_conn_hostname( bio, "url.com:https" );
if ( BIO_do_connect( bio ) <= 0 ) {
// Failure message and cleanup goes here.
}
if ( SSL_get_verify_result( ssl ) != X509_V_OK ){
// Here is where I get the error 20...
// Free all resources and exit.
}
OpenSSL documentation describes error 20 as:
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate.
The issuer certificate could not be found: this occurs if the issuer certificate of an
untrusted certificate cannot be found.
I need help identifying the problem and how to solve it. I am certain the certificates I have are correct.
It seems the certificate or certificate chain is not trusted.
You can load your own from a pem file before trying to connect by using:
int rc = SSL_CTX_load_verify_locations(ssl_context, file_name, NULL);
if (rc != 1) { // verify authentication result
g_warning("Load of certificates failed!: %s", X509_verify_cert_error_string(ERR_get_error()));
return FALSE;
}
Additionally you can load from memory directly.
With something like this:
char *chain_certs = "------- BEGIN CERTIFICAT...."; /// <<< YOUR CERTIFICATE CHAIN
// Load chain of certs
X509 *cacert=NULL;
BIO *mem = BIO_new_mem_buf(chain_certs,strlen(chain_certs));
X509_STORE *cert_store = SSL_CTX_get_cert_store(ssl_context);
if(cert_store!=NULL){
int index = 0;
while ((cacert = PEM_read_bio_X509(mem, NULL, 0, NULL))!=NULL) {
if(cacert) {
g_debug("Our certificate name is %s", cacert->name);
X509_STORE_add_cert(cert_store, cacert);
X509_free(cacert);
cacert=NULL;
} /* Free immediately */
index++;
}
}
BIO_free(mem);

Failing to validate server certificate with OpenSSL

I have written a SOAP client using OpenSSL (written in C++ on Ubuntu 12.04) but it currently works without checking the server security certificate. This is the function I am using to set up the connection and checking the certificate
bool bInitialiseSSL(SSL_CTX* &ctx, SSL* &ssl, BIO* &bio)
{
ctx = SSL_CTX_new(SSLv23_client_method());
bio = BIO_new_ssl_connect(ctx);
if (bio == NULL) {
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return false;
}
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
char target[] = "api.betfair.com:https";
BIO_set_conn_hostname(bio, target);
BIO_set_nbio(bio,1);
while (1) {
if (BIO_do_connect(bio) <= 0) {
if (!BIO_should_retry(bio)) {
cout << "Connect failed." << endl;
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
} else {
break;
}
}
if (BIO_do_handshake(bio) <= 0) {
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
X509 *cert;
bool bValid = false;
cert = SSL_get_peer_certificate(ssl);
if ( cert != NULL ) {
long res = SSL_get_verify_result(ssl);
if (res == X509_V_OK) {
bValid = true;
} else {
cout << "Error in security validation: " << res << endl;
}
X509_free(cert);
}
return bValid;
}
This works fine but the return value of SSL_get_verify_result is 20 which corresponds to
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local
issuer certificate
I have read some of the OpenSSL documentation for their functions but it is not particularly user friendly. I have looked at a number of web tutorials and I cannot see what I am doing wrong. My software worked perfectly before I tried to implement the certificate checking but I cannot see what I need to do. Do I need to configure settings on my machine? The server is betfair which is supposedly very secure and I find it hard to believe that they do not have valid SSL certificates. If anyone can tell me what I am doing wrong I would be very grateful.
It depends on the certificates of the server.
If it is a public valid certificate, you can include the CA certs file into SSL_CTX.
code:
ctx = SSL_CTX_new(SSLv23_client_method());
// You can load CA certs into SSL_CTX
SSL_CTX_load_verify_locations(ctx, cafile, NULL); // cafile: CA PEM certs file
You can download the public CA certs file from cURL website CA Certs from mozilla.org
If it is a private certs, and you have the certificate file, you can use SSL_CTX_use_certificate_file instead of SSL_CTX_load_verify_locations.