简体   繁体   中英

Create and verify signature in code and from the command line using ECDSA

Problem: Using the written demo code (given below) I can create and verify without problems. However, using the openssl command line tool, verifying a signature (created by the demo code) always seems to fail.

What I have done: The demo code creates a defined public/private key pair, then signs a defined hash and verifies it.

This is the program output:

Public key: 04DFEC134530603832A31F8885EF01888884483D611F87A698213F168534EC06D85D21F7C85795435BC9A7F78190126CC6E52E050CBDFD43E27175FB1DF3E3DEF6 
Hash: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Verified EC Signature
R: 383EB65E4A284CBCBA41A9777CE7C0816C5C23161EA9BC4EF8B1E668C7641A2A 
S: 608F4BAB9DFE6DC8F976C32D72508CD5BD68F9E325ADED69A9883CE487E6698B

It as well outputs the follwing files:

  • privkey.pem (private key in PEM format)
  • pubkey.pem (public Key in PEM format)
  • data.bin (the hash, nothing fancy)
  • data_sig.der (the DER encoded signature: R & S value)

All files seem to be created correctly.

Next, I try to verify data_sig.der with the openssl command line tool.

$ openssl dgst -verify pubkey.pem -signature data_sig.der data.bin
Verification Failure

Why does this fail?

I can only assume, that one of the 4 files has been written incorrectly, but I double-checked everything and cannot spot my error.

Furthermore, using the PEM files created by the demo program, sign & verify works fine from the command line:

$openssl dgst -sign privkey.pem data.bin > data_sig2.der
$openssl dgst -verify pubkey.pem -signature data_sig2.der data.bin
Verified OK

Here is the demo code (compiles with gcc demo_code.c -lcrypto -lssl -o demo_code ):

#include <stdio.h>
#include <stdlib.h>

#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/ecdsa.h>
#include <openssl/pem.h>
#include <openssl/bn.h>

void createSignature()
{
    EC_KEY* eckey = EC_KEY_new();
    EC_GROUP* ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_set_group(eckey,ecgroup);
    EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);

    /* the private key value */
    const char *p_str = "7D22AB6A1FD3FC1F5EBEDCA222151375683B733E9DDC9CA5B2485E202C55D25C";
    BIGNUM* prv = BN_new();
    BN_hex2bn(&prv, p_str);
    EC_POINT* pub = EC_POINT_new(ecgroup);

    /* calculate the public key */
    EC_POINT_mul(ecgroup, pub, prv, NULL, NULL, NULL);

    /* add the private & public key to the EC_KEY structure */
    EC_KEY_set_private_key(eckey, prv);
    EC_KEY_set_public_key(eckey, pub);
    /* output public key in hex format */
    char* hexPKey = EC_POINT_point2hex( ecgroup, pub, POINT_CONVERSION_UNCOMPRESSED, NULL );
    printf("Public key: %s \n", hexPKey); 
    /* create hash */
    printf("Hash: ");
    uint8_t hash[32];
    for(int i=0; i < 32; i++) {
        hash[i] = i;
        printf("%02x",hash[i]);
    }
    printf("\n");
    /* create and verify signature */
    ECDSA_SIG* signature = ECDSA_do_sign(hash, 32, eckey);
    //hash[0] = 0xff; // Uncomment to test if verification fails with a wrong hash
    if (1 != ECDSA_do_verify(hash, 32, signature, eckey)) {
        printf("Failed to verify EC Signature\n");
    } else {
        printf("Verified EC Signature\n");
    }
    /*print R & S value in hex format */
    char* hexR = BN_bn2hex(signature->r);
    char* hexS = BN_bn2hex(signature->s);
    printf("R: %s \nS: %s\n", hexR, hexS);
    /* export raw signature to DER-encoded format */
    int sigSize = i2d_ECDSA_SIG(signature, NULL);
    uint8_t* derSig = (uint8_t*)malloc(sigSize);
    uint8_t* p = derSig;    //memset(sig_bytes, 6, sig_size);
    sigSize= i2d_ECDSA_SIG(signature, &p);

    EVP_PKEY* pkey = EVP_PKEY_new();
    EVP_PKEY_set1_EC_KEY(pkey, eckey);

    /* write files */
    FILE* fp = fopen("pubkey.pem", "w");
    PEM_write_PUBKEY(fp, pkey);
    fclose(fp);
    fp = fopen("privkey.pem", "w");
    PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, 0, NULL);
    fclose(fp);
    fp = fopen("data.bin", "w");
    fwrite(hash, 1, sizeof(hash), fp);
    fclose(fp);
    fp = fopen("data_sig.der", "w");
    fwrite(derSig, 1, sigSize, fp);
    fclose(fp);

    /* free runtime allocated res */
    free(derSig);
    OPENSSL_free(hexPKey); OPENSSL_free(hexR); OPENSSL_free(hexS);
    BN_free(prv);
    EC_POINT_free(pub);
    EC_GROUP_free(ecgroup); 
    EC_KEY_free(eckey);
}

int main(int argc, char** argv) {
    createSignature();
    return (EXIT_SUCCESS);
}

It seems you sign data rather than result of hashing data. But when you run:

$ openssl dgst -verify pubkey.pem -signature data_sig.der data.bin

openssl utility calculate sha256 hash from data.bin and try to verify signature on this hash.

So you need to calc sha256 from 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f and sign result:

void createSignature()
{
    EC_KEY* eckey = EC_KEY_new();
    EC_GROUP* ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_set_group(eckey,ecgroup);
    EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);

    /* the private key value */
    const char *p_str = "7D22AB6A1FD3FC1F5EBEDCA222151375683B733E9DDC9CA5B2485E202C55D25C";
    BIGNUM* prv = BN_new();
    BN_hex2bn(&prv, p_str);
    EC_POINT* pub = EC_POINT_new(ecgroup);

    /* calculate the public key */
    EC_POINT_mul(ecgroup, pub, prv, NULL, NULL, NULL);

    /* add the private & public key to the EC_KEY structure */
    EC_KEY_set_private_key(eckey, prv);
    EC_KEY_set_public_key(eckey, pub);
    /* output public key in hex format */
    char* hexPKey = EC_POINT_point2hex( ecgroup, pub, POINT_CONVERSION_UNCOMPRESSED, NULL );
    printf("Public key: %s \n", hexPKey); 
    /* create hash */
    printf("Data: ");
    uint8_t data[32];
    for(int i=0; i < 32; i++) {
        data[i] = i;
        printf("%02x",data[i]);
    }
    printf("\n");

    uint8_t hash[32];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, data, sizeof(data));
    SHA256_Final(hash, &sha256);

    printf("Hash: ");
    for(int i=0; i < 32; i++) {
        data[i] = i;
        printf("%02x",hash[i]);
    }
    printf("\n");

    /* create and verify signature */
    ECDSA_SIG* signature = ECDSA_do_sign(hash, 32, eckey);
    /* hash[0] = 0xff; // Uncomment to test if verification fails with a wrong hash */
    if (1 != ECDSA_do_verify(hash, 32, signature, eckey)) {
        printf("Failed to verify EC Signature\n");
    } else {
        printf("Verified EC Signature\n");
    }
    /*print R & S value in hex format */
    char* hexR = BN_bn2hex(signature->r);
    char* hexS = BN_bn2hex(signature->s);
    printf("R: %s \nS: %s\n", hexR, hexS);
    /* export raw signature to DER-encoded format */
    int sigSize = i2d_ECDSA_SIG(signature, NULL);
    uint8_t* derSig = (uint8_t*)malloc(sigSize);
    uint8_t* p = derSig;    //memset(sig_bytes, 6, sig_size);
    sigSize= i2d_ECDSA_SIG(signature, &p);

    EVP_PKEY* pkey = EVP_PKEY_new();
    EVP_PKEY_set1_EC_KEY(pkey, eckey);

    /* write files */
    FILE* fp = fopen("pubkey.pem", "w");
    PEM_write_PUBKEY(fp, pkey);
    fclose(fp);
    fp = fopen("privkey.pem", "w");
    PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, 0, NULL);
    fclose(fp);
    fp = fopen("data.bin", "w");
    fwrite(data, 1, sizeof(data), fp);
    fclose(fp);
    fp = fopen("data_sig.der", "w");
    fwrite(derSig, 1, sigSize, fp);
    fclose(fp);

    /* free runtime allocated res */
    free(derSig);
    OPENSSL_free(hexPKey);
    OPENSSL_free(hexR); OPENSSL_free(hexS);
    BN_free(prv);
    EC_POINT_free(pub);
    EC_GROUP_free(ecgroup); 
    EC_KEY_free(eckey);
}

Now openssl check works:

$ openssl dgst -verify pubkey.pem -signature data_sig.der data.bin
Verified OK

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM