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

发布于 2024-07-29 02:38:19 字数 1286 浏览 8 评论 0原文

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

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

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

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

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

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);

I want to feed OpenSSL specific data for use as random seed during the signing of data with an EC key. I'm doing this to compare my application with another reference one (closed source). That utility takes the file with private key, file with data to sign and file with random data as parameters.

I've got the generation of EC keys, and signing of data down, but can't compare the two applications since I have no common ground. OpenSSL generates random data used in signing the data (probably from /dev/random) and thus gives me a different signature every run.

I've tried RAND_clear() in combination with RAND_add(), but keep getting changing signatures. Either I don't understand the whole ECDSA concept, or I'm doing something wrong.

My second option for comparing the applications is to import the public key and verify the signature generated by the reference program. This is the better option, but I'm unable to import the given example public key (83 character hex string). EC_POINT_oct2point() keeps giving me null results.

Any help/pointers/references would be greatly appreciated.

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);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

眼泪淡了忧伤 2024-08-05 02:38:19

这就是您应该如何加载该公钥:

  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");
  }

它似乎验证正常,因此它应该适用于检查签名。

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

This is how you should go about loading that public key:

  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");
  }

It seems to verify OK, so it should work for checking a signature.

I think your bug might have just been passing a NULL (in ecgroup) to EC_POINT_new().

拍不死你 2024-08-05 02:38:19

尽管您正在清除池并重置它,但您得到不同结果的原因是,默认情况下 OpenSSL 的 RAND 实现会将 pid 散列到输出块中(正是为了确保即使使用相同种子的应用程序也不会获得相同的结果) PRNG 输出,因为 99.9% 的情况下发生这种情况是一件坏事)。

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

至于验证:看起来您需要转置以下行:

   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;
   }

The reason you get different results despite the fact that you are clearing the pool and resetting it is that by default OpenSSL's RAND implementation will hash the pid into the output block (precisely to ensure that even applications that use the same seed do not get the same PRNG output, since 99.9% of the time that happening is a Bad Thing).

In addition, even if this was not the case, it is unlikely that your reference application uses the same PRNG that OpenSSL uses to turn the seed file into a series of random bytes. (Unless your reference application actually uses OpenSSL as well, of course). What you would have to do is first figure out what kind of PRNG the reference app uses - this might be a standard PRNG design like the ones from X9.31 or FIPS-186, or might be something totally custom. Then reimplement that design for OpenSSL and plug it in via RAND_set_rand_method.

As to verification: it looks like you need to transpose the lines:

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

Otherwise ecpoint is set to NULL right from the start, and this causes EC_KEY_generate_key to fail, because the group is set to NULL. Quoting from openssl-0.9.8k's crypto/ec/ec_key.c:

if (!eckey || !eckey->group)
   {
   ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
   return 0;
   }
一身仙ぐ女味 2024-08-05 02:38:19

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

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

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

You can control the random data that OpenSSL produces during signing by using the method:

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

where kinv is the random number that is used during signing.

怎会甘心 2024-08-05 02:38:19

签名程序纯粹是为了让其他人确认您已签名,即。 您的私钥用于对消息(或任何数据)进行签名,而无需实际拥有您的私钥。

维基百科上概述了该算法 Elliptic_Curve_DSA
阅读“签名生成算法”后发现,随机数据似乎用于增强签名的强度,并使攻击者更难找出私钥。

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

请参阅“签名验证算法”部分,了解验证步骤是您希望用来确认您的 openssl 版本正在输出 ECDSA 方法和闭源程序的有效签名的验证步骤。

The signing procedure is purely to allow someone else to confirm that you signed it ie. it was your private key that was used to sign the message (or any data) without actually having your private key.

The algorithm is outlined on wikipedia Elliptic_Curve_DSA
Reading "Signature generation algorithm" it appears the random data is used to assist in the strength of the signature and to make it harder to attack to figure out the private key.

Therefore you should expect the signature to be different each time since this is not just simply a hash.

See the section "Signature verification algorithm" to see that the verification steps are the ones you wish to use to confirm that your openssl version is outputing valid signatures w.r.t. the ECDSA method and the closed source program.

想念有你 2024-08-05 02:38:19

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

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

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

int random_number = rand() % n;

而另一个实现可能会这样做:

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

因此,即使您给它们相同的种子数据,您仍然可能会从不同的实现中获得不同的签名。 您所能做的就是验证您是否生成了有效的签名。

I can't find the docs for RAND_clear, so can't comment why it's not resulting in reproducible random numbers.

But even if you get that done, what you want may not be possible. ECDSA signature generation requires choosing a random integer in a particular range. Two different implementations of the algorithm might have completely different ideas about how to generate such an integer from their entropy source. AFAIK the means of transforming bits of entropy into the required number is not part of the ECDSA algorithm.

So for example (very bad examples of course), one implementation might do this:

int random_number = rand() % n;

and another might do this:

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

So even if you give them the same seed data, you might still get different signatures from different implementations. All you can do is validate whether you have generated a valid signature.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文