How to load a PKCS#12 file in OpenSSL programmatically? - c

In an SSL Server Application based on OpenSSL, how can we load a PKCS#12 file programmatically?
Also, can I load a PKCS#12 file having Certificate, Key & CAs in the same file in OpenSSL?

Yes you can load a PKCS#12 file containing certificate, key and CAs in the same file with OpenSSL.
Use d2i_PKCS12_fp() or d2i_PKCS12_bio() to load the PKCS#12 file.
Optionally use PKCS12_verify_mac() to verify the password.
Use PKCS12_parse() which decrypts and extracts key, certificate and CA chain for you.
From openssl-1.0.0d/demos/pkcs12/pkread.c:
#include <stdio.h>
#include <stdlib.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
/* Simple PKCS#12 file reader */
int main(int argc, char **argv)
{
FILE *fp;
EVP_PKEY *pkey;
X509 *cert;
STACK_OF(X509) *ca = NULL;
PKCS12 *p12;
int i;
if (argc != 4) {
fprintf(stderr, "Usage: pkread p12file password opfile\n");
exit (1);
}
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
if (!(fp = fopen(argv[1], "rb"))) {
fprintf(stderr, "Error opening file %s\n", argv[1]);
exit(1);
}
p12 = d2i_PKCS12_fp(fp, NULL);
fclose (fp);
if (!p12) {
fprintf(stderr, "Error reading PKCS#12 file\n");
ERR_print_errors_fp(stderr);
exit (1);
}
if (!PKCS12_parse(p12, argv[2], &pkey, &cert, &ca)) {
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
exit (1);
}
PKCS12_free(p12);
if (!(fp = fopen(argv[3], "w"))) {
fprintf(stderr, "Error opening file %s\n", argv[1]);
exit(1);
}
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));
}
sk_X509_pop_free(ca, X509_free);
X509_free(cert);
EVP_PKEY_free(pkey);
fclose(fp);
return 0;
}

Be warned that the code writes the certs out as trusted certificates (encrypted).
If you want unencrypted certificates, change the calls to PEM_write_X509_AUX() to PEM_write_X509().

Try man SSL, which gives you a list of OpenSSL functions. Something like SSL_load_client_CA_file might suit your needs; it depends if the certificate is in a file on disk or already in memory. There are lots of helper functions, one of them will do the trick. Also check out man PEM for PEM handling routines.
Edit: Hm, maybe a combination of d2i_PKCS12_fp and PKCS12_parse (both available from <openssl/pkcs12.h>) lets you read a certificate from file and parse it.

Related

openssl EVP_CipherFinal_ex failed

I got this below function file_encrypt_decrypt for encryption and decryption of a file using AES256 CBC from here.
If I'm doing encryption and decryption both from same program, (main function given at the end) encryption and decryption is working properly. Though both the time same function is called and ctx is initiated again.
If I'm commenting the encryption part, passing the above created encrypted_file, decryption is failing with error:
ERROR: EVP_CipherFinal_ex failed. OpenSSL error: error:06065064:lib(6):func(101):reason(100)
[[meaningful]] OpenSSL error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
Somewhere people are talking about some padding length issue. But I can't figure it out properly.
Also how the same function is working properly if encryption is done at the same program but separately, it is failing?
Some guidance will be appreciated.
PS: Instead of a common function, I've tried separate functions for encryption and decryption with EVP_DecryptInit_ex(), EVP_DecryptUpdate(), EVP_DecryptFinal_ex() and similar for encryption but of no effect.
Full Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#define ERR_EVP_CIPHER_INIT -1
#define ERR_EVP_CIPHER_UPDATE -2
#define ERR_EVP_CIPHER_FINAL -3
#define ERR_EVP_CTX_NEW -4
#define AES_256_KEY_SIZE 32
#define AES_BLOCK_SIZE 16
#define BUFSIZE 1024
typedef struct _cipher_params_t{
unsigned char *key;
unsigned char *iv;
unsigned int encrypt;
const EVP_CIPHER *cipher_type;
}cipher_params_t;
void cleanup(cipher_params_t *params, FILE *ifp, FILE *ofp, int rc){
free(params);
fclose(ifp);
fclose(ofp);
exit(rc);
}
void file_encrypt_decrypt(cipher_params_t *params, FILE *ifp, FILE *ofp){
// Allow enough space in output buffer for additional block
int cipher_block_size = EVP_CIPHER_block_size(params->cipher_type);
unsigned char in_buf[BUFSIZE], out_buf[BUFSIZE + cipher_block_size];
int num_bytes_read, out_len;
EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();
if(ctx == NULL){
fprintf(stderr, "ERROR: EVP_CIPHER_CTX_new failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
cleanup(params, ifp, ofp, ERR_EVP_CTX_NEW);
}
// Don't set key or IV right away; we want to check lengths
if(!EVP_CipherInit_ex(ctx, params->cipher_type, NULL, NULL, NULL, params->encrypt)){
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
cleanup(params, ifp, ofp, ERR_EVP_CIPHER_INIT);
}
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == AES_256_KEY_SIZE);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == AES_BLOCK_SIZE);
// Now we can set key and IV
if(!EVP_CipherInit_ex(ctx, NULL, NULL, params->key, params->iv, params->encrypt)){
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(ctx);
cleanup(params, ifp, ofp, ERR_EVP_CIPHER_INIT);
}
while(1){
// Read in data in blocks until EOF. Update the ciphering with each read.
num_bytes_read = fread(in_buf, sizeof(unsigned char), BUFSIZE, ifp);
if (ferror(ifp)){
fprintf(stderr, "ERROR: fread error: %s\n", strerror(errno));
EVP_CIPHER_CTX_cleanup(ctx);
cleanup(params, ifp, ofp, errno);
}
if(!EVP_CipherUpdate(ctx, out_buf, &out_len, in_buf, num_bytes_read)){
fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(ctx);
cleanup(params, ifp, ofp, ERR_EVP_CIPHER_UPDATE);
}
fwrite(out_buf, sizeof(unsigned char), out_len, ofp);
if (ferror(ofp)) {
fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
EVP_CIPHER_CTX_cleanup(ctx);
cleanup(params, ifp, ofp, errno);
}
if (num_bytes_read < BUFSIZE) {
// Reached End of file
break;
}
}
// Now cipher the final block and write it out to file
if(!EVP_CipherFinal_ex(ctx, out_buf, &out_len)){
fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(ctx);
cleanup(params, ifp, ofp, ERR_EVP_CIPHER_FINAL);
}
fwrite(out_buf, sizeof(unsigned char), out_len, ofp);
if (ferror(ofp)) {
fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
EVP_CIPHER_CTX_cleanup(ctx);
cleanup(params, ifp, ofp, errno);
}
EVP_CIPHER_CTX_cleanup(ctx);
}
int main(int argc, char *argv[]) {
FILE *f_input, *f_enc, *f_dec;
// Make sure user provides the input file
if (argc != 2) {
printf("Usage: %s /path/to/file\n", argv[0]);
return -1;
}
cipher_params_t *params = (cipher_params_t *)malloc(sizeof(cipher_params_t));
if (!params) {
// Unable to allocate memory on heap
fprintf(stderr, "ERROR: malloc error: %s\n", strerror(errno));
return errno;
}
// Key to use for encrpytion and decryption
unsigned char key[AES_256_KEY_SIZE];
// Initialization Vector
unsigned char iv[AES_BLOCK_SIZE];
// Generate cryptographically strong pseudo-random bytes for key and IV
if (!RAND_bytes(key, sizeof(key)) || !RAND_bytes(iv, sizeof(iv))) {
// OpenSSL reports a failure, act accordingly
fprintf(stderr, "ERROR: RAND_bytes error: %s\n", strerror(errno));
return errno;
}
params->key = key;
params->iv = iv;
// Indicate that we want to encrypt
params->encrypt = 1;
// Set the cipher type you want for encryption-decryption
params->cipher_type = EVP_aes_256_cbc();
// Open the input file for reading in binary ("rb" mode)
f_input = fopen(argv[1], "rb");
if (!f_input) {
// Unable to open file for reading
fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
return errno;
}
// Open and truncate file to zero length or create ciphertext file for writing
f_enc = fopen("encrypted_file", "wb");
if (!f_enc) {
// Unable to open file for writing
fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
return errno;
}
// Encrypt the given file
file_encrypt_decrypt(params, f_input, f_enc);
// Encryption done, close the file descriptors
fclose(f_input);
fclose(f_enc);
// Decrypt the file
// Indicate that we want to decrypt
params->encrypt = 0;
// Open the encrypted file for reading in binary ("rb" mode)
f_input = fopen("encrypted_file", "rb");
if (!f_input) {
// Unable to open file for reading
fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
return errno;
}
// Open and truncate file to zero length or create decrypted file for writing
f_dec = fopen("decrypted_file", "wb");
if (!f_dec) {
// Unable to open file for writing
fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
return errno;
}
// Decrypt the given file
file_encrypt_decrypt(params, f_input, f_dec);
// Close the open file descriptors
fclose(f_input);
fclose(f_dec);
// Free the memory allocated to our structure
free(params);
return 0;
}
The code generates a new key and a new IV with each run. So if only the encryption part is commented out, then two different key / IV pairs are generated and used for encryption and decryption, which leads to the observed error message. If for testing purposes a fixed key / IV pair is used instead of the each time freshly generated pair, the code works as expected.
In general, the key / IV pair used for encryption must also be used for decryption. Regarding the IV, in practice a random IV is usually generated during encryption. After its use, it's simply added in front of the ciphertext (since the IV isn't secret), so that it can be reconstructed and used during decryption.

unable to open file which is in d: drive

How to read and write a file which is not in the bin directory that is it is out of the C drive.
I wrote this code
fs=fopen("d:/source.txt","w");
if(fs==NULL)
{
puts("Unable to open file");
}
And it is outputting "Unable to open file". Can someone please help me out.
FILE *fs= fopen("d:/source.txt","w");
if(fs==NULL)
{
printf("can't open");
}
if (fs!=NULL)
{
fputs ("Opened successfully",fs);
fclose (fs);
}
Make sure the source.txt file exists and it is not read only. I tried above code didn't get any error.
There are several possible reasons why the file cannot be opened via fopen().
To get information on details of the error print out errno and/or call perror() and/or strerror() like for example so:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
char filename[] = "d:/source.txt";
FILE * fs = fopen(filename,"w");
if (NULL == fs)
{
perror("fopen() failed");
fprintf(stderr, "Error #%d occurred when trying to open file '%s': %s.\n",
errno,
filename,
strerror(errno));
}
...
return 0;
}

Creating a self signed certificate with distinguished name fields

I am trying to write an application which will create a self signed certificate with supplied distinguished names.
I have the following application which creates a .key and a .csr file.
#include <stdio.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#define _RAND_FILENAME "/dev/random"
#define _RAND_LOADSIZE 128
#define _RSA_KEYSIZE 1024
int build_and_save_csr(int dn_entries, char *dn_fields[], char *dn_values[], FILE *req_f, FILE *priv_f)
{
X509_REQ *req_p;
EVP_PKEY *key_p;
RSA *rsa_p;
X509_NAME *name_p = NULL;
int priv_len;
unsigned char *priv_p;
int i;
if (!(req_p = X509_REQ_new())) {
fprintf(stderr, "X509_REQ_new() failed\n");
return -1;
}
if (!(key_p = EVP_PKEY_new())) {
X509_REQ_free(req_p);
fprintf(stderr, "EVP_PKEY_new() failed\n");
return -1;
}
#if 0
if (RAND_load_file(_RAND_FILENAME, _RAND_LOADSIZE) != _RAND_LOADSIZE) {
X509_REQ_free(req_p);
EVP_PKEY_free(key_p);
fprintf(stderr, "RAND_load_file() failed\n");
return -1;
}
#endif
if (!(rsa_p = RSA_generate_key(_RSA_KEYSIZE, RSA_F4, NULL, NULL))) {
X509_REQ_free(req_p);
EVP_PKEY_free(key_p);
fprintf(stderr, "RSA key generatione failed\n");
return -1;
}
if (!EVP_PKEY_assign_RSA(key_p, rsa_p)) {
X509_REQ_free(req_p);
RSA_free(rsa_p);
EVP_PKEY_free(key_p);
return -1;
}
/*!!! the memory for rsa_p is now managed by key_p - don't free! */
if (!X509_REQ_set_version(req_p, 0L) /* Version 1 */
|| !X509_REQ_set_pubkey(req_p, key_p)) {
X509_REQ_free(req_p);
EVP_PKEY_free(key_p);
fprintf(stderr, "Failed to set stuff on X509_REQ\n");
return -1;
}
if (!(name_p = X509_REQ_get_subject_name(req_p))) {
X509_REQ_free(req_p);
EVP_PKEY_free(key_p);
fprintf(stderr, "Failed to make get X509_REQ_get_subject_name\n");
return -1;
}
/* Build the DN */
for (i = 0; i < dn_entries; i++) {
if (!X509_NAME_add_entry_by_txt(name_p, dn_fields[i], MBSTRING_ASC,
dn_values[i], -1, -1, 0)) {
X509_REQ_free(req_p);
EVP_PKEY_free(key_p);
fprintf(stderr, "Failed to make the X509_NAME\n");
return -1;
}
}
if (!X509_REQ_sign(req_p, key_p, EVP_sha1())) {
X509_REQ_free(req_p);
EVP_PKEY_free(key_p);
fprintf(stderr, "X509_REQ_sign() failed\n");
return -1;
}
/* save the private key to file (PKCS#1) */
priv_p = NULL;
priv_len = i2d_RSAPrivateKey(rsa_p, &priv_p);
fwrite(priv_p, priv_len, 1, priv_f);
OPENSSL_free(priv_p);
EVP_PKEY_free(key_p); /* nolonger need the EVP structure */
/* save the CSR to file (PKCS#10) */
i2d_X509_REQ_fp(req_f, req_p);
X509_REQ_free(req_p);
return 0;
}
int main(int argc, char *argv[])
{
char *dn_fields[] = { "O", "CN" };
char *dn_values[] = { "My Application", "tomcelic#selleck.com" };
FILE *req_f, *priv_f;
req_f = fopen("selleck.csr", "w");
priv_f = fopen("selleck.key", "w");
if (build_and_save_csr(2, dn_fields, dn_values, req_f, priv_f) != 0) {
fprintf(stderr, "Oh bother...\n");
fclose(req_f);
fclose(priv_f);
exit(1);
}
fclose(req_f);
fclose(priv_f);
return 0;
}
I presume this line could be swapped for one which creates a self signed cert instead of a sign request??
/* save the CSR to file (PKCS#10) */
i2d_X509_REQ_fp(req_f, req_p);
This is all totally new to me so I would appreciate any advice!
I presume this line could be swapped for one which creates a self signed cert instead of a sign request??
No, I'm sorry it's no so easy.
The X509_REQ is not the correct structure to create a self signed certificate. It's just for CSR (Certificate Sign Request) management, so the main structure and the functions (with the _REQ suffix) are not valid for your pourpose.
But, the process of a self signed certificate creation it's really close to this one (the CSR it's self signed as you can notice in the X509_REQ_sign call.).
Here you can find a very complete and exhaustive answer of the entire process, and here an example of a self-signed creation.
As explanation i2d_X509_REQ_fp it's used to save the X509 CSR to a PKCS#10 format, Info.

Cannot read X509 object into a temporary file using openssl for windows C programming

int _tmain(int argc, _TCHAR* argv[])
{
X509 *x;
EVP_PKEY *pkey;
PKCS12 *p12;
STACK_OF(X509) *ca=NULL;
FILE *fp;
int code;
CRYPTO_malloc_init();
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
x = X509_new();
code = fopen_s(&fp,PRIVATE_CERTIFICATE, "rb");
//fp = fopen(PRIVATE_CERTIFICATE, "rb");
p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (!PKCS12_parse(p12, KEYPASS, &pkey, &x, &ca)) {
printf(" Error while parsing\n");
}
PKCS12_free(p12);
code = fopen_s(&fp,TEMP_STORE_CERTIFICATE, "w");
PEM_write_X509(fp, x);
fclose(fp);
//RSA Private Certificate
fp = fopen(TEMP_STORE_KEY_CERTIFICATE,"w");
PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL);
fclose(fp);
return 0;
}
When the execution gets up to PEM_write_X509, it gets stuck and does not proceed. I guess, it enters into the infinite loop, and the execution doesnt goes beyond that. A console simply opens up which doesnt goes away. Someone please help
File pointed by fp is opened into "rb" mode where PEM_write_fp will try to write on a file which is already opened into read mode.
Close the file and open it into write mode before writing.
Moreover, it does not seems good to overwrite the content of the file.

How to verify the password of a pkcs#12 certificate (.PXF) with openssl C API?

I have an .pxf (AFAIK PKCS#12) certificate. How can I confirm a given password for this certificate using the openssl C API?
One approach to finding answers like this is to find an OpenSSL utility that performs the same functionality as what you are trying to do. In this case, you can use the pkcs12 utility that comes with OpenSSL to verify the password.
The command to verify a pfx file is the following:
openssl pkcs12 -in mypfx.pfx -noout
With that information, you can then look at its source code ({openssl_src}/apps/pkcs12.c) to see how they do it.
The source code shows that it calls PKCS12_verify_mac to verify the password. First to verify that there is no password:
if( PKCS12_verify_mac(p12, NULL, 0) )
{
printf("PKCS12 has no password.\n");
}
And then if there is a password, verify it by passing it as an argument:
if( PKCS12_verify_mac(p12, password, -1) )
{
printf("PKCS12 password matches.\n");
}
OpenSSL also has demos for working with PKCS12 in openssl/demos/pkcs12. The pkread.c demo provides an example for parsing a pfx file with a password.
EVP_PKEY *pkey;
X509 *cert;
STACK_OF(X509) *ca = NULL;
if (!PKCS12_parse(p12, password, &pkey, &cert, &ca)) {
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
exit(1);
}
Full example, compiled with gcc -std=c99 verifypfx.c -o verifypfx -lcrypto:
#include <stdio.h>
#include <errno.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
int main(int argc, char *argv[])
{
const char *password = "mypassword";
PKCS12 *p12;
// Load the pfx file.
FILE *fp = fopen("mypfx.pfx", "rb");
if( fp == NULL ) { perror("fopen"); return 1; }
p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
OpenSSL_add_all_algorithms();
ERR_load_PKCS12_strings();
if( p12 == NULL ) { ERR_print_errors_fp(stderr); exit(1); }
// Note: No password is not the same as zero-length password. Check for both.
if( PKCS12_verify_mac(p12, NULL, 0) )
{
printf("PKCS12 has no password.\n");
}
else if( PKCS12_verify_mac(p12, password, -1) )
{
printf("PKCS12 password matches.\n");
}
else
{
printf("Password not correct.\n");
}
return 0;
}
Use PKCS12_verify_mac(). eg.
FILE* f = fopen("myfile.pfx", "rb");
PKCS12* p12 = d2i_PKCS12_fp(f, NULL);
fclose(f);
if (!PKCS12_verify_mac(p12, (char*)"mypassword", strlen("mypassword")))
{
// handle failure
}

Resources