4

How do I set the private key for signing messages when using ECDSA in OpenSSL programmatically? I have the following code:

static int create_signature(unsigned char* hash)
{
  EC_KEY *eckey=NULL;
  EC_GROUP *ecgroup=NULL;
  EVP_PKEY *evpkey=NULL;
  unsigned char *signature=NULL;
  point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
  int signature_size, block_size;
  unsigned char * block=NULL;

  ecgroup = get_ec_group_192();
  EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE);
  EC_GROUP_set_point_conversion_form(ecgroup, form);
  eckey=EC_KEY_new();
  EC_KEY_set_group(eckey,ecgroup);
  EC_KEY_generate_key(eckey);
  evpkey=EVP_PKEY_new();
  EVP_PKEY_assign_EC_KEY(evpkey,eckey);
  signature=OPENSSL_malloc(EVP_PKEY_size(evpkey));

  ECDSA_sign(0, hash, sizeof(hash), signature, &signature_size, eckey);

  printf("%s", signature);
  return 0;
}

The function get_ec_group_192() is created by running openssl ecparam -C -name secp192k1 -genkey which also generates some EC PARAMETERS and a EC PRIVATE KEY.

What I am trying to do is to encrypt the message contained in hash with my private key so that only public key can decrypt it. Is that possible with the above code, or am I doing this completely wrong?

IanNorton
  • 7,145
  • 2
  • 25
  • 28
Anvar
  • 1,110
  • 2
  • 10
  • 22
  • 2
    The correct terminology is "signing". The word "encryption" is reserved for something else. You haven't mentioned what problem you are having. – President James K. Polk Feb 09 '10 at 13:04
  • Ahh, sorry about that. Well in the above code snippet the private key is not used anywhere? Or is that only required when de-signing the message? – Anvar Feb 09 '10 at 16:09
  • Note, Encrypting using a private key is actually called "signing". Anyone holding the corresponding public half will decrypt the result (which is actually called "verification") – IanNorton Feb 04 '13 at 13:43
  • 1
    Late but: `hash` here should point to a suitable hash value, typically 20 bytes (for SHA1) or 24 to 64 bytes (for SHA2), but in C `sizeof(hash)` is the size of the *pointer*, typically 4 or 8 bytes, not the size of the thing pointed to. Also an ECDSA signature, like most other modern crypto objects, is arbitrary bits (aka 'binary') and can validly include an all-0-bits byte but is not reliably followed by one which makes it invalid to handle it as a null-terminated C string which `printf %s` tries to do, as well as usually including bytes that are not safely printable. ... – dave_thompson_085 Mar 09 '16 at 23:22
  • ... And *RSA* sign and verify are *similar* to but not actually 'encrypt with privatekey' and 'decrypt with publickey', but in ECDSA (and DSA) there is nothing even remotely resembling encryption and decryption, there is *only* sign and verify. – dave_thompson_085 Mar 09 '16 at 23:24

2 Answers2

9

The following verifies successfully for me:

//compiled with gcc -g -lssl -UOPENSSL_NO_EC SO2228860.c -lcrypto
#include <openssl/ec.h>      // for EC_GROUP_new_by_curve_name, EC_GROUP_free, EC_KEY_new, EC_KEY_set_group, EC_KEY_generate_key, EC_KEY_free
#include <openssl/ecdsa.h>   // for ECDSA_do_sign, ECDSA_do_verify
#include <openssl/obj_mac.h> // for NID_secp192k1


static int create_signature(unsigned char* hash)
{
    int function_status = -1;
    EC_KEY *eckey=EC_KEY_new();
    if (NULL == eckey)
    {
        printf("Failed to create new EC Key\n");
        function_status = -1;
    }
    else
    {
        EC_GROUP *ecgroup= EC_GROUP_new_by_curve_name(NID_secp192k1);
        if (NULL == ecgroup)
        {
            printf("Failed to create new EC Group\n");
            function_status = -1;
        }
        else
        {
            int set_group_status = EC_KEY_set_group(eckey,ecgroup);
            const int set_group_success = 1;
            if (set_group_success != set_group_status)
            {
                printf("Failed to set group for EC Key\n");
                function_status = -1;
            }
            else
            {
                const int gen_success = 1;
                int gen_status = EC_KEY_generate_key(eckey);
                if (gen_success != gen_status)
                {
                    printf("Failed to generate EC Key\n");
                    function_status = -1;
                }
                else
                {
                    ECDSA_SIG *signature = ECDSA_do_sign(hash, strlen(hash), eckey);
                    if (NULL == signature)
                    {
                        printf("Failed to generate EC Signature\n");
                        function_status = -1;
                    }
                    else
                    {

                        int verify_status = ECDSA_do_verify(hash, strlen(hash), signature, eckey);
                        const int verify_success = 1;
                        if (verify_success != verify_status)
                        {
                            printf("Failed to verify EC Signature\n");
                            function_status = -1;
                        }
                        else
                        {
                            printf("Verifed EC Signature\n");
                            function_status = 1;
                        }
                    }
                }
            }
            EC_GROUP_free(ecgroup);
        }
        EC_KEY_free(eckey);
    }

  return function_status;
}

int main( int argc , char * argv[] )
{
    unsigned char hash[] = "c7fbca202a95a570285e3d700eb04ca2";
    int status = create_signature(hash);
    return(0) ;
}
Community
  • 1
  • 1
this.josh
  • 663
  • 9
  • 21
  • 6
    How do you verify with just the public part of the eckey? – Anon21 Jan 22 '12 at 01:44
  • 2
    There are a couple of *REALLY* bad bugs in your above code. Firstly, as someone has mentioned, strlen() stops at a NULL byte, which means that if the hash has a zero byte in it anywhere, it's going to give you the wrong size. Secondly and most importantly, you have a bad memory leak. ECDSA_do_sign() returns a ECDSA_SIG* and you should free this returned signature with ECDSA_SIG_free() once you're done with it, or you're going to leak memory. – The Welder Feb 25 '20 at 10:55
  • @AlexandreH.Tremblay here the answer to your question: https://stackoverflow.com/questions/62300337/openssl-sign-and-verify-in-c-with-raw-ec-generated-keys/62310516#62310516 – CipherX Jun 13 '20 at 06:14
  • ECDSA_do_verify() is marked as deprecated in ssl 3.0 – sena Apr 28 '22 at 13:18
2

Their is a small bug in the above code. The hash that is passed is an unsigned char, this hash CAN have 0x00 values in it! Do NOT use the strlen(hash) to calculate the length, as that will possibly pass the incorrect length to the routine IF the hash has a 0x00 in it anywhere. Hashes are fixed length, and should be passed as such. sha256 for example should be of length 64.