How to sign data using ECDSA algorithm, in C++ with OPENSSL?

Viewed 661

I have a 32bytes SHA256 digest data and need to sign it with ECDSA. The private key is in a .pem file. I've already made it in console using the command: openssl.exe dgst -sha256 -sign ecc.pem -out %sigfile% %data_file%. Now i need to do it in c++ and the results I have been getting isn't the same as in console.

What are wrong in the code below, considering the command I uses in console?

bool sign(unsigned char *digest) {

    BIO *b_pem;
    EVP_PKEY* pkey2 = nullptr;
    const char * fileName = "ecc.pem";
    pkey2 = EVP_PKEY_new();
    b_pem = BIO_new_file(ecFileName, "rb");
    
    PEM_read_bio_PrivateKey(b_pem, &pkey, NULL, NULL))

    unsigned char sign[200] = {0};
    EVP_MD_CTX *mctx;
    const EVP_MD *md = EVP_get_digestbyname("SHA256");
    size_t signlen = 0;

    mctx = EVP_MD_CTX_new();
    
    if (EVP_DigestSignInit(mctx, NULL, md, NULL, pkey)<=0) {
        return false;
    }
    if (EVP_DigestSignUpdate(mctx, digest, 32) <= 0) {
        return false;
    }
    if (EVP_DigestSignFinal(mctx, sign, &signlen) <=0) {
        return false;
    }

    EVP_MD_CTX_free(mctx);
    return true;
}
2 Answers

There is a bug handling the length of the buffer sign. You allocate 200 bytes but never tell EVP_DigestSignFinal about it, because you set signlen = 0. Therefore nothing is written to your buffer.

According to the documentation you need to set signlen to the maximum length available in the buffer and after the call it contains the number of bytes actually used.

I found the solution I've been searched for. Reading some project examples that used ECDSA from OpenSSL, I realised that I needed to convert the ASN1 encoded signature to raw signature bytes. So, here is the approach I used to get the wished result:

After sign the data by EVP_DigestSignFinal() function the code below was included and I could get the bytes in as and ar arrays.

ECDSA_SIG * ec_sig;
const BIGNUM *er = NULL;
const BIGNUM *es = NULL;
unsigned char *ar;
unsigned char *as;
size_t slen = EVP_PKEY_size(pkey);

ec_sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&sign, slen);
ECDSA_SIG_get0(ec_sig, &er, &es);

ar = (unsigned char*)malloc(BN_num_bytes(er));
as = (unsigned char*)malloc(BN_num_bytes(es));

BN_bn2bin(er, ar);
BN_bn2bin(es, as);
Related