从OpenSSL 3中提取RSA密钥对的编码公钥

huangapple go评论58阅读模式
英文:

Extracting the encoded public key from RSA key pair in openssl 3

问题

使用以下代码,我试图生成一对RSA密钥,然后尝试提取公钥。根据输出,密钥生成过程成功。尽管根据openssl手册<https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_get1_encoded_public_key.html>,调用EVP_PKEY_get1_encoded_public_key函数似乎是正确的,但该函数始终无法提取公钥。有人可以指出我哪里出错吗?

#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <stdio.h>

void print_hex(const char *label, const unsigned char *data, size_t length) {
  printf("%s:\n", label);
  for (size_t i = 0; i < length; ++i) {
    printf("%02X", data[i]);
  }
  printf("\n");
}

int main() {

  // 生成RSA密钥对
  EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
  if (!ctx) {
    printf("无法创建EVP_PKEY_CTX。\n");
    return 1;
  }

  if (EVP_PKEY_keygen_init(ctx) <= 0) {
    printf("无法初始化EVP_PKEY_CTX。\n");
    return 1;
  }

  if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0) {
    printf("无法设置RSA密钥大小。\n");
    return 1;
  }

  EVP_PKEY *keypair = NULL;
  if (EVP_PKEY_keygen(ctx, &keypair) <= 0) {
    printf("无法生成RSA密钥对。\n");
    return 1;
  }

  // 提取RSA公钥模数
  unsigned char *pubkey;
  size_t pubkey_len;

  pubkey_len = EVP_PKEY_get1_encoded_public_key(keypair, &pubkey);

  if (pubkey_len == 0) {
    printf("无法获取编码的公钥长度。\n");
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(keypair);
    return 1;
  }

  // 打印原始密钥组件
  print_hex("编码的公钥", pubkey, pubkey_len);

  // 清理
  free(pubkey);
  EVP_PKEY_CTX_free(ctx);
  EVP_PKEY_free(keypair);

  return 0;
}
英文:

Using the following code I am trying to generate a pair of RSA key and then trying to extract the public key. Based on the output the key generation process is successful.
Although calling the EVP_PKEY_get1_encoded_public_key based on the openssl man page
<https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_get1_encoded_public_key.html>
seems to be correct But the function always fails to extract the public key. Can any one point it out where I am making mistake.


#include &lt;openssl/bn.h&gt;
#include &lt;openssl/err.h&gt;
#include &lt;openssl/evp.h&gt;
#include &lt;openssl/rsa.h&gt;
#include &lt;stdio.h&gt;

void print_hex(const char *label, const unsigned char *data, size_t length) {
  printf(&quot;%s:\n&quot;, label);
  for (size_t i = 0; i &lt; length; ++i) {
    printf(&quot;%02X&quot;, data[i]);
  }
  printf(&quot;\n&quot;);
}

int main() {

  // Generate RSA key pair
  EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
  if (!ctx) {
    printf(&quot;Failed to create EVP_PKEY_CTX.\n&quot;);
    return 1;
  }

  if (EVP_PKEY_keygen_init(ctx) &lt;= 0) {
    printf(&quot;Failed to initialize EVP_PKEY_CTX.\n&quot;);
    return 1;
  }

  if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) &lt;= 0) {
    printf(&quot;Failed to set RSA key size.\n&quot;);
    return 1;
  }

  EVP_PKEY *keypair = NULL;
  if (EVP_PKEY_keygen(ctx, &amp;keypair) &lt;= 0) {
    printf(&quot;Failed to generate RSA key pair.\n&quot;);
    return 1;
  }

  // Extract RSA public key modulus
  unsigned char *pubkey;
  size_t pubkey_len;

  pubkey_len = EVP_PKEY_get1_encoded_public_key(keypair, &amp;pubkey);

  if (pubkey_len == 0) {
    printf(&quot;Failed to get encoded public key length.\n&quot;);
    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(keypair);
    return 1;
  }

  // Print the raw key component
  print_hex(&quot;Encoded Public Key&quot;, pubkey, pubkey_len);

  // Cleanup
  free(pubkey);
  EVP_PKEY_CTX_free(ctx);
  EVP_PKEY_free(keypair);

  return 0;
}

答案1

得分: 2

从手册中得知:"目前只支持支持密钥交换的[算法]",实际上是指密钥协商,即(FF)DH EC(DH) XDH。RSA不支持密钥协商,因此不支持此API。

要获取以"SPKI"编码的RSA公钥(通常称为X.509,尽管实际上有歧义,大多数使用公钥对象的OpenSSL命令行操作的默认值,以及许多基于、衍生自或设计为与OpenSSL兼容的东西,以及Java加密所使用的),请使用i2d_PUBKEY,对于更简单但很少使用的PKCS1编码,请使用i2d_PublicKey,类似于以下方式:

#if SPKI
  pubkey_len = i2d_PUBKEY (keypair, NULL);
  pubkey = malloc(pubkey_len); unsigned char *ptr = pubkey;
  i2d_PUBKEY (keypair, &amp;ptr);
#else
  pubkey_len = i2d_PublicKey (keypair, NULL);
  pubkey = malloc(pubkey_len); unsigned char *ptr = pubkey;
  i2d_PublicKey (keypair, &amp;ptr);
#endif

尽管ASN.1(DER)编码的长度可能会略有不同,但只要知道以位为单位的键大小,就可以预测这些编码的大小,所以您可以使用静态分配,比如声明unsigned char pubkey [256+16]用于PKCS1或[256+48]用于SPKI。我没有费心去计算。

但无论哪种情况,您的注释"提取RSA模数"都是错误的;公钥至少包括模数和公共指数。PKCS1形式实际上是这两个值在ASN.1中作为两个INTEGER的SEQUENCE;SPKI形式添加了一个"AlgorithmIdentifier",对于这种情况(经典RSA),只不过是一个略多于ASN.1对象标识符(OID)的OID,指定它是RSA,再加上一个包装PKCS1编码的OCTETSTRING,在第二个SEQUENCE中。

尽管您没有询问,但这些编码例程(以及用于输入的类似解码例程)在所有OpenSSL版本中都是相同的,从0.9.early到1.0.0之前的版本,尽管在1.0.0之前的版本中,密钥对生成API是不同的。

英文:

From the man page: "this currently only works for [algorithms] that support key exchange" by which it apparently actually means key agreeement, namely (FF)DH EC(DH) XDH. RSA does not support key agreement and thus not this API.

To get the RSA public key in "SPKI" encoding (often called X.509 although that's actually ambiguous, and the default for most OpenSSL commandline operations that use a publickey object, as well as a lot of things built on, derived from, or designed to be compatible with OpenSSL, and also used by Java crypto) use i2d_PUBKEY and for
the simpler but rarely used PKCS1 encoding use i2d_PublicKey, something like:

#if SPKI
  pubkey_len = i2d_PUBKEY (keypair, NULL);
  pubkey = malloc(pubkey_len); unsigned char *ptr = pubkey;
  i2d_PUBKEY (keypair, &amp;ptr);
#else
  pubkey_len = i2d_PublicKey (keypair, NULL);
  pubkey = malloc(pubkey_len); unsigned char *ptr = pubkey;
  i2d_PublicKey (keypair, &amp;ptr);
#endif

Although ASN.1 (DER) encodings can vary in length a little, it is possible to predict the size of these encodings within a few bytes just from knowing the key 'size' in bits which you hardcoded, so instead of dynamic allocation you can just declare say unsigned char pubkey [256+16] for PKCS1 or [256+48] for SPKI. I didn't bother.

But in either case your comment 'Extract the RSA modulus' is wrong; the publickey does not consist solely of the modulus but at minimum the modulus and public exponent. The PKCS1 form is actually just those two values in ASN.1 as a SEQUENCE of two INTEGERs; the SPKI form adds an 'AlgorithmIdentifier' which for this case (classic RSA) is only a little more than an ASN.1 Object Identifier (OID) specifying that it is RSA, plus an OCTETSTRING wrapping around the PKCS1 encoding, in a second SEQUENCE.

And, although you didn't ask, these encoding routines (and similar decoding routines for input) were the same on all OpenSSL versions back to 0.9.early, although the keypair generation API was different before 1.0.0.

huangapple
  • 本文由 发表于 2023年7月14日 05:47:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76683427.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定