openssl decrypt a signed & encrypted message - c

I am trying out a small example program to decrypt a message that has been signed and then encrypted using openSSL. It works well in the command line. However upon trying out the code after modifying the code in the 'demos' folder of OpenSSL, the decryption fails
Here is the decryption code:
int decrypt_smime(){
BIO *in = NULL, *out = NULL, *tbio = NULL;
X509 *rcert = NULL;
EVP_PKEY *rkey = NULL;
//PKCS7 *cms = NULL;
CMS_ContentInfo *cms = NULL;
int ret = 1;
int flags = CMS_STREAM;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
printf("decrypt...\n");
/* Read in recipient certificate and private key */
tbio = BIO_new_file("signer.pem", "r");
if (!tbio)
goto err;
rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL);
BIO_reset(tbio);
rkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL);
if (!rcert || !rkey)
goto err;
printf("decrypt...\n");
/* Open S/MIME message to decrypt */
in = BIO_new_file("smencsign.txt", "r");
if (!in)
goto err;
printf("keys read...\n");
/* Parse message */
cms = SMIME_read_CMS(in, NULL); //here is the problem I think
if (!cms)
goto err;
printf("keys read...\n");
out = BIO_new_file("decout.txt", "w");
if (!out)
goto err;
/* Decrypt S/MIME message */
if (!CMS_decrypt(cms, rkey, rcert, NULL, out, flags))
goto err;
ret = 0;
err:
if (ret)
{
fprintf(stderr, "Error Decrypting Data\n");
ERR_print_errors_fp(stderr);
}
if (cms)
//PKCS7_free(cms);
CMS_ContentInfo_free(cms);
if (rcert)
X509_free(rcert);
if (rkey)
EVP_PKEY_free(rkey);
if (in)
BIO_free(in);
if (out)
BIO_free(out);
if (tbio)
BIO_free(tbio);
return ret;
}
The error I get is:
Error Verifying Data
*3074258568:error:0D0D40D1:asn1 encoding routines:SMIME_read_ASN1:no content type:asn_mime.c:451:*
The commands on openssl that worked:
openssl cms -sign -in encr.txt -signer signer.pem -text | openssl cms -encrypt -out smencsign.txt signer.pem
openssl smime -decrypt -in smencsign.txt -recip signer.pem -inkey signer.pem
So clearly openssl uses 'cms' utility to sign and encrypt but seems to use 'smime' utility for decryption. What is then the code equivalent?

Try to add the following line:
OpenSSL_add_all_ciphers();

Related

Verify public key matches signature of private key

I'm new to openssl API.
my goal is to verify that my public key is "related" to my hidden private key.
solution using openssl cli:
I have a certificate , the private key is hidden (in HSM)
I have a buffer:
echo "hello world!!!!" > sign.txt
using following commands I create sha256 signature of my buffer similar to what my HSM will do :
openssl dgst -sha256 -sign myrootca.key.insecure -out sign.sha256 sign.txt
extract public key:
openssl x509 -pubkey -noout -in myrootca.crt > myrootca.publicKey.pem
verify public key :
openssl dgst -sha256 -verify myrootca.publicKey.pem -signature sign.sha256 sign.txt
I think I know how to represent my public key and signature file correctly
sigkey = load_pubkey(bio_err, keyfile, keyform, 0, NULL,
e, "key file");
sigbio = BIO_new_file(sigfile, "rb");
but can't find a proper API to continue from here
Issue I've encountered was figuring out what is the signature length before hand to allocate place for the signature that must have prior knowledge on RSA key size and type of hash
sha256 + RSA1024 ~ 128byte
sha256 + RSA2048 ~ 256byte
using openssl API based of this guide I've managed to verify signature using following code :
Verifyx509VsPrivKeySig(X509* x509Cert,char* signature,size_t sigLen,char* message,size_t messageLen)
{
int rc;
EVP_PKEY* pPubkey = NULL;
EVP_MD_CTX* ctx = NULL;
rc = getX509Publickey(x509Cert, &pPubkey);
if (rc != SSL_OK)
{
goto err_verify;
}
ctx = EVP_MD_CTX_create();
if(ctx == NULL) {
PRINT_LOG_ERR("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
/* failed */
goto err_verify;
}
rc = EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pPubkey);
if(rc != 1) {
PRINT_LOG_ERR("EVP_DigestVerifyInit failed, error 0x%lx\n", ERR_get_error());
goto err_EVP_XTX_destroy; /* failed */
}
rc = EVP_DigestVerifyUpdate(ctx, message, messageLen);
if(rc != 1) {
PRINT_LOG_ERR("EVP_DigestVerifyUpdate failed, error 0x%lx\n", ERR_get_error());
goto err_EVP_XTX_destroy; /* failed */
}
/* Clear any errors for the call below */
ERR_clear_error();
rc = EVP_DigestVerifyFinal(ctx,(const unsigned char*) signature, sigLen);
if(rc != 1) {
PRINT_LOG_ERR("EVP_DigestVerifyFinal failed, error 0x%lx\n", ERR_get_error());
goto err_EVP_XTX_destroy; /* failed */
}
/*if we got to here , verify sig finished with good result!!*/
if(ctx) {
EVP_MD_CTX_destroy(ctx);
ctx = NULL;
}
return true;
//////////////////// ERROR HANDLING ///////////////////////////
err_EVP_XTX_destroy:
EVP_MD_CTX_destroy(ctx);
err_verify:
return false;
}

Function is not generating proper openssl rsa keys

This is a c function I wrote to generate openssl rsa 4096 bit keys.
bool rsa_gen_keys()
{
int ret = 0;
RSA *rsa = NULL;
BIGNUM *bignum = NULL;
BIO *bio_private = NULL;
BIO *bio_public = NULL;
int bits = 4096;
unsigned long k = RSA_F4;
bignum = BN_new();
ret = BN_set_word(bignum,k);
if(ret != 1){
goto cleanup;
}
rsa = RSA_new();
ret = RSA_generate_key_ex(rsa, bits, bignum, NULL);
if(ret != 1){
goto cleanup;
}
// write rsa private key to file
bio_private = BIO_new_file("private_new.pem", "w+");
ret = PEM_write_bio_RSAPrivateKey(bio_private, rsa, NULL, NULL, 0, NULL, NULL);
BIO_flush(bio_private);
// write rsa public key to file
bio_public = BIO_new_file("public_new.pem", "w+");
ret = PEM_write_bio_RSAPublicKey(bio_public, rsa);
if(ret != 1){
goto cleanup;
}
BIO_flush(bio_public);
cleanup:
BIO_free_all(bio_private);
BIO_free_all(bio_public);
RSA_free(rsa);
BN_free(bignum);
return ret;
}
The keys generated by the above function seem to be missing something. When I try to use the public_new.pem file in another program, I get the following error:
140286309791384:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:701:Expecting: PUBLIC KEY
However, if I use the openssl command to generate the key files, the files work fine.
$openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:4096
I noticed the key sizes generated from the function and from the command line don't match. This is a clue, but what do I need to change in my function to fix this?
-rw-rw-r-- 1 3272 Feb 6 09:19 private_key.pem
-rw-rw-r-- 1 800 Feb 6 09:20 public_key.pem
-rw-rw-r-- 1 3243 Feb 6 10:43 private_new.pem
-rw-rw-r-- 1 775 Feb 6 10:43 public_new.pem
BTW, I tried the above with 2048 bits keys and I get the same result and same size mismatch
openssl genpkey is using PEM_write_bio_PrivateKey (PKCS#8) instead of PEM_write_bio_RSAPrivateKey (PKCS#1): https://github.com/openssl/openssl/blob/master/apps/genpkey.c#L161-L164.
You don't show how you generated public_key.pem, but it was probably written with PEM_write_bio_PUBKEY (X.509 SubjectPublicKeyInfo) vs PEM_write_bio_RSAPublicKey (PKCS#1).
From a PEM armor perspective:
PKCS#1 Public: BEGIN RSA PUBLIC KEY
X.509 SubjectPublicKeyInfo: BEGIN PUBLIC KEY
PKCS#1 Private: BEGIN RSA PRIVATE KEY
PKCS#8: BEGIN PRIVATE KEY
I realized I needed to use the format of the keys for PKCS#8 and X.509. So I switched to EVP functions to generate them. Here is a very simplified version of the code I ended up using (no error checking):
bool rsa_gen_keys() {
int ret = 0;
BIO *bio_private = NULL;
BIO *bio_public = NULL;
int bits = 4096;
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey = NULL;
// Get the context
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx)
goto cleanup;
// init keygen
if (EVP_PKEY_keygen_init(ctx) <= 0)
goto cleanup;
// set the bit size
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0)
goto cleanup;
/* Generate key */
if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
goto cleanup;
// write rsa private key to file
bio_private = BIO_new_file("private_new.pem", "w+");
ret = PEM_write_bio_PrivateKey(bio_private, pkey, NULL, NULL, 0, NULL, NULL);
if (ret != 1) {
goto cleanup;
}
BIO_flush(bio_private);
// write rsa public key to file
bio_public = BIO_new_file("public_new.pem", "w+");
//ret = PEM_write_bio_RSAPublicKey(bio_public, rsa);
ret = PEM_write_bio_PUBKEY(bio_public, pkey);
if (ret != 1) {
goto cleanup;
}
BIO_flush(bio_public);
cleanup:
if(bio_private) BIO_free_all(bio_private);
if(bio_public) BIO_free_all(bio_public);
if(pkey) EVP_PKEY_free(pkey);
return ret;
}

RSA sign in OPENSSL

I am trying to sign a message with a RSA private key. I read a private key to pkey and then sign a string as what says on openssl wiki, but failed in the final step. It always returns 0 on the line commented in program, which means sign failed. Could anyone helps me find out what’s wrong?
void main() {
EVP_MD_CTX * mdctx ;
EVP_PKEY * pkey ;
char dmessage[20] = "The messages";
int ret = 0;
FILE * fp;
unsigned char * sig = NULL;
size_t * slen = malloc(sizeof(size_t));
fp = fopen ("privkey.pem", "r");
if (fp == NULL) exit (1);
pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
fclose (fp);
if (pkey == NULL) {
ERR_print_errors_fp (stderr);
exit (1);
}
if(!(mdctx = EVP_MD_CTX_create())) goto err;
if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey)) goto err;
if(1 != EVP_DigestSignUpdate(mdctx, dmessage, 12)) goto err;
if(1 != EVP_DigestSignFinal(mdctx, NULL, slen)) goto err;
if(!(sig = OPENSSL_malloc(sizeof(unsigned char) * (int)(*slen)))) goto err;
if(1 != (ret = EVP_DigestSignFinal(mdctx, sig, slen))) goto err;//*****it return 0 here,which means sign failed
ret = 1;
err:
if(ret != 1)
{
printf("%d somthing wrong\n",ret);
}
/* Clean up */
if(sig && !ret) OPENSSL_free(sig);
if(mdctx) EVP_MD_CTX_destroy(mdctx);
return;
}
Thanks a lot!
I’m using openssl 1.0.1j on linux mint 17, and the private key is generated by
openssl genrsa -out privkey.pem 256
Your key is way too small, that's bits not bytes. Try again with a good secure key size that can hold the hash and PKCS#1 padding. I would recommend at least 2048 bits instead of the 256 bits that you generated using the OpenSSL command line.
See keylength.com for more information about key sizes. Note that RSA requires a key size a lot larger than those required for symmetric algorithms such as AES.

how to decrypt ASN.1 DER encoded CMS file using the openssl API?

I have a DER encoded CMS file that I would like to decrypt using the openssl API.
I have found the API for decrypting:
CMS_decrypt(cms_content, pkey, cert, NULL, out, NULL);
I have found examples for reading the pkey and cert PEM files, and setting up the output BIO, but I can't find out how to read the cms file.
Question: how can I read an ASN.1 DER encoded file into the cms_content variable that has the type CMS_ContentInfo?
EDIT: Thanks to the Camille's answer, I managed to get it working with:
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/cms.h>
int main (int argc, char **argv)
{
char pkeypath[] = "recipient_prvkey.pem";
char certpath[] = "sender_cert.pem";
char cmspath[] = "encrypted.der";
char decpath[] = "decrypted.zip";
BIO *in = NULL, *out = NULL, *tbio = NULL;
CMS_ContentInfo *cms = NULL;
EVP_PKEY *pkey;
X509 *cert;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
tbio = BIO_new_file(pkeypath, "r");
pkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL);
if (pkey == NULL) {
printf("error reading private key");
return EXIT_FAILURE;
}
BIO_free(tbio);
tbio = BIO_new_file(certpath, "r");
cert = PEM_read_bio_X509(tbio, NULL, 0, NULL);
if (cert == NULL) {
printf("error reading private key");
return EXIT_FAILURE;
}
in = BIO_new_file(cmspath, "r");
cms = d2i_CMS_bio(in, NULL);
BIO_free(in);
out = BIO_new_file(decpath, "w");
if (!CMS_decrypt_set1_pkey(cms, pkey, cert))
{
fprintf(stderr, "set1_pkey error\n");
return EXIT_FAILURE;
}
if (!CMS_decrypt(cms, NULL, NULL, NULL, out, CMS_BINARY))
{
int error = ERR_get_error();
fprintf(stderr, "error: %s :: %s :: %s\n",
ERR_reason_error_string(error),
ERR_func_error_string(error),
ERR_lib_error_string(error)
);
}
BIO_free(out);
return 0;
}
I just copied the sample of OpenSSL cms_dec which works with PEM and adapted it for DER Encoded CMS File
BIO *in = NULL, *out = NULL, *tbio = NULL;
X509 *cert= NULL;
EVP_PKEY *rkey = NULL;
CMS_ContentInfo *cms = NULL;
int ret = 1;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
/* Read in recipient certificate and private key */
tbio = BIO_new_file("yourCMS.der", "r");
rcert = i2d_x509_bio(tbio, NULL);
BIO_reset(tbio);
i2d_PrivateKey_bio(tbio, rkey);
/* Open S/MIME message to decrypt */
in = BIO_new_file("smencr.txt", "r");
/* Parse message */
cms = SMIME_read_CMS(in, NULL);
out = BIO_new_file("decout.txt", "w");
/* Decrypt S/MIME message */
CMS_decrypt(cms, rkey, rcert, out, NULL, CMS_BINARY) //Edit : Added Flags for DER.
...

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