使用OpenSSL进行ECDSA消息签名

4
我如何在使用OpenSSL进行ECDSA编程时设置签名消息的私钥?我有以下代码:
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;
}

函数get_ec_group_192()是通过运行openssl ecparam -C -name secp192k1 -genkey创建的,它还生成了一些EC PARAMETERS和一个EC PRIVATE KEY

我现在想做的是使用我的私钥加密hash中包含的消息,以便只有公钥才能解密。通过以上代码是否可能实现这一目标,或者我完全走错了方向?


2
正确的术语是“签名”。单词“加密”保留用于其他用途。您没有提到您遇到了什么问题。 - President James K. Polk
啊,对不起。在上面的代码片段中,私钥没有被使用?或者只有在签名消息时才需要使用它? - Anvar
注意,使用私钥加密实际上称为“签名”。拥有相应公钥的人将解密结果(实际上称为“验证”)。 - IanNorton
1
晚了但:这里的hash应该指向一个合适的哈希值,通常为20字节(对于SHA1)或24到64字节(对于SHA2),但在C语言中,sizeof(hash) 是指针的大小,通常为4或8字节,而不是指向的对象的大小。此外,ECDSA签名与大多数其他现代加密对象一样,都是任意位数(也称为“二进制”),可以有效地包含所有零比特,但不可靠地跟随一个,这使得将其处理为空终止的C字符串无效,而printf %s则试图这样做,通常还包括不安全可打印的字节。... - dave_thompson_085
而RSA签名和验证与“使用私钥加密”和“使用公钥解密”类似,但在ECDSA(和DSA)中根本没有类似加密和解密的东西,只有签名和验证。 - dave_thompson_085
2个回答

9
以下内容对我来说验证成功:
//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) ;
}

6
只使用eckey的公共部分如何进行验证? - Anon21
2
你上面的代码有几个非常严重的错误。首先,正如某人所提到的,strlen()会在遇到NULL字节时停止,这意味着如果哈希值中有任何一个零字节,它都会给出错误的大小。其次,更重要的是,你有一个严重的内存泄漏问题。ECDSA_do_sign()返回一个ECDSA_SIG*,你应该使用ECDSA_SIG_free()释放这个返回的签名,否则你将会泄漏内存。 - The Welder
@AlexandreH.Tremblay,这是你问题的答案:https://stackoverflow.com/questions/62300337/openssl-sign-and-verify-in-c-with-raw-ec-generated-keys/62310516#62310516 - CipherX
ECDSA_do_verify() 在 SSL 3.0 中被标记为已弃用。 - sena

2

以上代码存在一个小错误。传递的哈希值是无符号字符,这个哈希值中可能包含0x00值!不要使用strlen(hash)来计算长度,因为如果哈希值的任何位置都有0x00,则可能会将错误的长度传递给例程。哈希值具有固定长度,并应按此传递。例如,sha256应为长度为64。


1
实际上,SHA256哈希是32个字节。 - poncho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接