Hello.

I'm trying to use high level API provided by OpenSSL for cryptography work.

Here it works pretty well.

/**
 * Instructions for generating private key file and self signed certificate
file.
 *
 * openssl genrsa -des3 -out keys.pem 2048
 * openssl rsa -in keys.pem -out rsa.pem
 * openssl req -new -x509 -key rsa.pem -out rsa-cert.pem -days 9999
 *
 * Compile
 * g++ -ggdb -o exe.bex src.cpp -lcrypto
 */

//c standard library
#include <stdio.h>
#include <string.h>

//openssl library
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/x509.h>

//c++ standard library
#include <iostream>
#include <string>

int main(int argc, char* argv[]) {
    FILE* fd = fopen("rsa.pem", "r");
    X509* x509;
    STACK_OF(X509)* x509_stack = sk_X509_new_null();
    EVP_PKEY* key;
    if (NULL != fd)
    {
        key = PEM_read_PrivateKey(fd, NULL, NULL, NULL);
    }
    fd = fopen("rsa-cert.pem", "r");
    if (NULL != fd)
    {
        while (NULL != (x509 = PEM_read_X509(fd, NULL, NULL, NULL)))
        {
            sk_X509_push(x509_stack, x509);
        }
        fclose(fd);
    }
    OpenSSL_add_all_algorithms();

    while (!std::cin.eof()) {
        std::string msg;

        //get input text
        printf("Message to PKCS7 encrypt: ");
        fflush(stdout);
        std::getline(std::cin, msg);
        if (223 < msg.length())
        {
            // SMIME_read_PKCS7 bug, need to be fixed.
            //
            // bt:
            //#0  asn1_d2i_read_bio (in=0x60bc40, pb=0x7fffffffe238) at
a_d2i_fp.c:286
            //#1  0x00007ffff7aeb762 in ASN1_item_d2i_bio
(it=0x7ffff7dc2da0, in=0x60bc40, x=0x0) at a_d2i_fp.c:113
            //#2  0x00007ffff7b0201c in b64_read_asn1 (bio=0x60bc40,
it=0x7ffff7dc2da0) at asn_mime.c:191
            //#3  0x00007ffff7b02dd0 in SMIME_read_ASN1 (bio=0x60af00,
bcont=0x7fffffffe350, it=0x7ffff7dc2da0) at asn_mime.c:527
            //#4  0x00007ffff7b39db2 in SMIME_read_PKCS7 (bio=0x60af00,
bcont=0x7fffffffe350) at pk7_mime.c:96
            //#5  0x0000000000401668 in main (argc=1, argv=0x7fffffffe4b8)
at ./crypto-pkcs.cpp:99
            //
            std::cout << "string too long " << msg.length() << std::endl;
            continue;
        }
        if (msg.empty())
        {
            std::cout << "string too short" << std::endl;
            continue;
        }


        //save input
        BIO* bio_input_plain_text = BIO_new(BIO_s_mem());
        BIO_write(bio_input_plain_text, msg.c_str(), msg.length());
        BIO_flush(bio_input_plain_text);

        //create PKCS7 object in the way of PKCS7_encrypt.
        PKCS7* pkcs7_encrypt = PKCS7_encrypt(x509_stack,
bio_input_plain_text, EVP_aes_256_cbc(), 0);
        if (NULL == pkcs7_encrypt) {
            std::cout << "PKCS7_encrypt returns NULL" <<  std::endl; return
-1;}

        //dump encryped info.
        BIO* bio_encrypted_smime = BIO_new(BIO_s_mem());
        if (SMIME_write_PKCS7(bio_encrypted_smime, pkcs7_encrypt,
bio_input_plain_text, 0) != 1){ std::cout << "SMIME_write_PKCS7 failed" <<
std::endl; return -1;}
        BIO_flush(bio_encrypted_smime);

        //get internal data address
        const char* encrypted = NULL;
        BIO_get_mem_data(bio_encrypted_smime, &encrypted);  //encrypted has
no new resource, only the reflection of the internal BIO data.
        //char encrypted[8 * 1024] = "";
        //copy BIO to char array
        //BIO_read(bio_encrypted_smime, encrypted, sizeof encrypted - 1);
//if we read the data out of BIO, later we need to write it back, BIO_read
deletes the internal data inside BIO
        std::cout << "PKCS7_encrypt length:" << strlen(encrypted) <<
std::endl << encrypted << std::endl;
        //please be careful while taking care of BIO object.
        //if we call BIO_read against BIO to get out data, the operation
will cause the data deleted in the BIO at the same time.
        //here we recover BIO data.
        //BIO_write(bio_encrypted_smime, encrypted, strlen(encrypted));
        //BIO_flush(bio_encrypted_smime);

        BIO* bio_pkcs7 = BIO_new(BIO_s_mem());
        //read&load PKCS7 object from SMIME format.
        PKCS7* pkcs7_smime = SMIME_read_PKCS7(bio_encrypted_smime,
&bio_pkcs7);
        if (NULL == pkcs7_smime) {std::cout << "SMIME_read_PKCS7 returns
NULL" << std::endl;return -1;}
        BIO_flush(bio_pkcs7);
        BIO* bio_pkcs7_decrypt = BIO_new(BIO_s_mem());
        //decrypt in the way of PKCS7_decrypt
        if (0 == PKCS7_decrypt(pkcs7_smime, key, x509, bio_pkcs7_decrypt,
0)){ std::cout << "PKCS7_decrypt failed" << std::endl;return -1;}
        BIO_flush(bio_pkcs7_decrypt);
        //char decrypted[8 * 1024] = "";
        //dump decrypted data.
        //BIO_read(bio_pkcs7_decrypt, decrypted, sizeof decrypted - 1);
        const char* decrypted = NULL;
        //get internal data address
        BIO_get_mem_data(bio_pkcs7_decrypt, &decrypted);
        std::cout << "PKCS7_decrypt length: " << strlen(decrypted) <<
std::endl << decrypted << std::endl;

        //cleanup, idiot! donot forget to release resource you piece of
shit!
        BIO_free(bio_pkcs7_decrypt);
        BIO_free(bio_pkcs7);
        BIO_free(bio_encrypted_smime);
        PKCS7_free(pkcs7_smime);
        PKCS7_free(pkcs7_encrypt);
    }
    X509_free(x509);
    sk_X509_pop_free(x509_stack, X509_free);

    return 0;
}

This piece of code works fine with openssl-1.0.1g. I tried both threads and
no-threads config options.

But if we input one 224 length plain text WITHOUT the if-continue stuff,
encryption is still working, it fails to decrypt in the method of
SMIME_read_PKCS7, check backtrace comment above.

The length of encrypted S/MIME is 1200 in case of the length of input is
223.

In the case of 224 length input, the encrypted result which is in S/MIME
format has length beyond 1200, SMIME_read_PKCS7 can not handle that length,
check bt above for internal calls, but I have not find any document or page
for explanation.

What is the secret of SMIME_read_PKCS7?

Reply via email to