如何提供OpenSSL随机数据以用于ECDSA签名?

我想在使用EC密钥签名数据期间提供OpenSSL特定数据以用作随机种子。 我这样做是为了将我的应用程序与另一个引用程序(闭源)进行比较。 该实用程序将文件与私钥,带有数据的文件进行签名,并将随机数据作为参数进行归档。

我已经获得了EC密钥的生成和数据签名,但由于我没有共同点,因此无法比较这两个应用程序。 OpenSSL生成用于签名数据的随机数据(可能来自/ dev / random),因此每次运行都会给我一个不同的签名。

我已经尝试将RAND_clear()RAND_add()结合使用,但不断改变签名。 要么我不了解整个ECDSA概念,要么我做错了。

我比较应用程序的第二个选项是导入公钥并validation参考程序生成的签名。 这是更好的选择,但我无法导入给定的示例公钥(83个字符的hex字符串)。 EC_POINT_oct2point()不断给我null结果。

任何帮助/指针/参考将不胜感激。

 char * key_as_binary_data; //369368AF243193D001E39CE76BB1D5DA08A9BC0A63307AB352338E5EA5C0E05A0C2531866F3E3C2702 int data_size; //Size of the key buffer EC_POINT * ecpoint = NULL; EC_GROUP * ecgroup = NULL; EC_KEY * eckey = NULL; point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED; int asn1_flag = OPENSSL_EC_NAMED_CURVE; eckey = EC_KEY_new(); ecpoint = EC_POINT_new(ecgroup); ecgroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("sect163k1")); EC_GROUP_set_asn1_flag(ecgroup, asn1_flag); EC_GROUP_set_point_conversion_form(ecgroup, form); EC_KEY_set_group(eckey,ecgroup); EC_KEY_generate_key(eckey); //This gives me a null ecpoint EC_POINT_oct2point(ecgroup,ecpoint,key_as_binary_data,data_size-1,ctx); EC_KEY_set_public_key(eckey,ecpoint); 

这是你应该如何加载该公钥:

  EC_KEY *key = NULL; EC_POINT *pub_key; const EC_GROUP *group; SSL_library_init(); SSL_load_error_strings(); key = EC_KEY_new_by_curve_name(NID_sect163k1); group = EC_KEY_get0_group(key); pub_key = EC_POINT_new(group); EC_POINT_hex2point(group, "369368AF243193D001E39CE76BB1D5DA08A9BC0A63307AB352338E5EA5C0E05A0C2531866F3E3C2702", pub_key, NULL); EC_KEY_set_public_key(key, pub_key); if (!EC_KEY_check_key(key)) { printf("EC_KEY_check_key failed:\n"); printf("%s\n",ERR_error_string(ERR_get_error(),NULL)); } else { printf("Public key verified OK\n"); } 

它似乎validation好,所以它应该用于检查签名。

我认为您的错误可能只是将NULL(在ecgroup中)传递给EC_POINT_new()。

尽管你正在清理池并重置它,但是你得到不同结果的原因是默认情况下OpenSSL的RAND实现会将pid哈希到输出块中(确切地说,即使使用相同种子的应用程序也不会得到相同的结果) PRNG输出,因为发生的99.9%的时间是坏事。

此外,即使不是这种情况,您的参考应用程序也不太可能使用OpenSSL用于将种子文件转换为一系列随机字节的相同PRNG。 (当然,除非您的参考应用程序实际上也使用OpenSSL)。 您需要做的是首先弄清楚参考应用程序使用的PRNG类型 – 这可能是标准的PRNG设计,如X9.31或FIPS-186中的设计,或者可能是完全自定义的。 然后重新实现OpenSSL的设计并通过RAND_set_rand_method将其RAND_set_rand_method

至于validation:看起来你需要调换线条:

  ecpoint = EC_POINT_new(ecgroup); ecgroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("sect163k1")); 

否则,ecpoint从开始就设置为NULL,这会导致EC_KEY_generate_key失败,因为该组设置为NULL。 引用openssl-0.9.8k的crypto / ec / ec_key.c:

 if (!eckey || !eckey->group) { ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER); return 0; } 

您可以使用以下方法控制OpenSSL在签名期间生成的随机数据:

ECDSA_SIG* ECDSA_do_sign_ex(const unsigned char *dgst, int dgstlen, const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *eckey);

其中kinv是签名期间使用的随机数。

签署程序纯粹是为了让其他人确认您签署了它,即。 这是您的私钥,用于签署邮件(或任何数据)而不实际拥有您的私钥。

该算法在维基百科上概述Elliptic_Curve_DSA阅读“签名生成算法”,似乎随机数据用于协助签名的强度并使得更难以攻击以找出私钥。

因此,您应该期望签名每次都不同,因为这不仅仅是一个哈希。

请参阅“签名validation算法”部分,以确定validation步骤是您希望用于确认您的openssl版本是否通过ECDSA方法和封闭源程序输出有效签名的步骤。

我找不到RAND_clear的文档,因此无法评论为什么它不会产生可重现的随机数。

但即使你完成了,你想要的也许是不可能的。 ECDSA签名生成需要选择特定范围内的随机整数。 算法的两种不同实现可能对如何从其熵源生成这样的整数有完全不同的想法。 AFAIK将熵比特转换为所需数量的方法不是ECDSA算法的一部分。

例如(当然非常糟糕的例子),一个实现可能会这样做:

 int random_number = rand() % n; 

而另一个可能会这样做:

 int random_number = (rand() * n) / RAND_MAX; 

因此,即使您为它们提供相同的种子数据,您仍可能从不同的实现中获得不同的签名。 您所能做的就是validation您是否已生成有效签名。