从整数中加载 rsa 私钥并在 openssl 中转换为 PEM 格式或 RSA 结构以签署消息

Load rsa private key from integers and convert to PEM format or RSA structure in openssl to sign message

我有 RSA 密钥的 public exponent(e)、modulus(n) 和 private exponent(d),如何将它们转换为 PEM 格式或 openssl 中定义的 RSA 结构?

我想用带有 openssl 的 RSA 私钥签署文本,在 C 代码或 openssl 实用程序中都是可以接受的。

我在互联网上搜索过,有很多帖子可以从 PEM 密钥中提取参数,但未能找到有关如何使用 [=14= 将它们转换回 PEM 的有用信息].似乎这不是一个常见的场景...

编辑:抱歉,标题有点误导,我的最终目的是用它来签名消息(如描述中所述)

我认为您不具备 RSA 密钥所需的一切。具体来说,您没有生成 RSA 密钥所需的素数 pq

RSA 密钥需要用于计算 ned 的原始质数整数,因此仅有它们不足以生成完整的 RSA 密钥。

有关详细信息,请参阅有关 ASN1 编码的文档 here

编辑:@GregS:nd 对于 public-私钥对来说是不够的,因为 e 实际上是密钥的一部分 (n,e)(d,e)

here可以看出RSA私钥需要prime1, pprime2, q,而Public密钥只需要(n,e)

RSA Private Key file (PKCS#1) The RSA private key PEM file is specific for RSA keys.

RSAPrivateKey ::= SEQUENCE {
      version           Version,
      modulus           INTEGER,  -- n
      publicExponent    INTEGER,  -- e
      privateExponent   INTEGER,  -- d
      prime1            INTEGER,  -- p
      prime2            INTEGER,  -- q
      exponent1         INTEGER,  -- d mod (p-1)
      exponent2         INTEGER,  -- d mod (q-1)
      coefficient       INTEGER,  -- (inverse of q) mod p
      otherPrimeInfos   OtherPrimeInfos OPTIONAL
    }

RSA 数学只需要 nd 来实现签名。此外,OpenSSL 的实现也只需要 nd 进行签名。您可以直接在 RSA 结构中设置这些值。此示例仅显示“...使用 C 代码中的 openssl ... 使用 RSA 私钥签署文本”的示例,并忽略您的 PEM 查询。

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/evp.h>

static void printerrors() {
    char errbuf[1024];
    while (1) {
        unsigned long error = ERR_get_error();
        if (error == 0) {
            break;
        }
        ERR_error_string(error, errbuf);
        fputs(errbuf, stderr);
    }
    fflush(stderr);
}

static void sign(RSA *rsa, const char *message) {
    EVP_PKEY *pkey = EVP_PKEY_new();
    if (!EVP_PKEY_set1_RSA(pkey, rsa)) goto err;
    EVP_MD_CTX *ctx = EVP_MD_CTX_create();
    if (!EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey)) goto err;
    if (!EVP_DigestSignUpdate(ctx, (const void * ) message, strlen(message))) goto err;
    size_t siglen;
    if (!EVP_DigestSignFinal(ctx, NULL, &siglen)) goto err;
    unsigned char *signature = malloc(siglen);
    if (signature == NULL) goto err;

    if (!EVP_DigestSignFinal(ctx, signature, &siglen)) goto err;

    for (int i = 0; i < siglen; i++) {
        printf("%02x", signature[i]);
    }
    printf("\n");
    free(signature);
    EVP_MD_CTX_destroy(ctx);
    EVP_PKEY_free(pkey);

    return;
err:
    printerrors();
    exit(1);
}

int main(int argc, char *argv[]) {
    const int BITS = 1024;
    const int PUBLIC_EXPONENT = 65537;
    OpenSSL_add_all_algorithms();
    RSA *rsa = RSA_generate_key(BITS, PUBLIC_EXPONENT, NULL, NULL);

    RSA *rsa2 = RSA_new();
    rsa2->n = BN_dup(rsa -> n);
    rsa2->e = BN_dup(rsa -> e);
    rsa2->d = BN_dup(rsa -> d);

    RSA_print_fp(stdout, rsa2, 0);
    sign(rsa2, "Sign me, please");
    RSA_free(rsa2);
    RSA_free(rsa);
}

通常与私钥关联的其他值,pq 等并不是绝对必要的。如果存在,它们可用于加速私钥操作,包括利用中国剩余定理进行签名。此外,如果需要,它们可以很容易地从 nde 派生:例如参见 [​​=20=].