2
votes

I have the public exponent(e), modulus(n) and private exponent(d) of a RSA key, how can I convert them to a RSA key in PEM format or a RSA structure defined in openssl?

I want to sign text with RSA private key with openssl, either in C code or openssl utility is acceptable.

I've searched the internet, there're quite some posts that can extract parameters from the PEM key, but failed to find useful informations on how to convert them back to PEM with C. Seems that this is not a common scenario...

edit: Sorry that the title is kind of misleading, my ultimate purpose is to use it to sign message(as stated in the description)

2
"... or openssl utility is acceptable" - if you want help on using the OpenSSL commands, then you should ask on Super User or Unix & Linux Stack Exchange. Stack Overflow is a programming and development Q&A site. Super User and Unix & Linux Stack Exchange are sites that help you with using commands.jww
@jww , actually I want to implemet it by c program, but if there's an openssl command to work out this, I can make my program by referencing openssl's source code...mosakashaka

2 Answers

2
votes

The RSA math only requires n and d to implement signing. Furthermore, OpenSSL's implementation also only requires n and d to do signing. You can set these values directly in the RSA structure. This example only shows an example of "...sign text with RSA private key with openssl ... in C code" and ignores your PEM query.

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

The other values normally associated with the private key, p, q, etc. are not strictly necessary. If present they can be used to speed up private key operations including signing by taking advantage of the Chinese Remainder Theorem. Also, they can be easily derived from n, d, and e if desired: see for example section 8.2.2 (i) of the Handbook Of Applied Cryptography.

0
votes

I don't think you have everything required for an RSA key. Specifically, you don't have the prime numbers required to generate the RSA key, p and q.

RSA keys require the original prime integers used to calculate n, e, and d, so only having them is not enough to generate an entire RSA key.

See the documentation here on ASN1 encoding for more information.

EDIT: @GregS: n and d are not enough for a public-private keypair, as e is actually a part of the keys (n,e) and (d,e).

From here, you can see that an RSA Private Key requires both prime1, p and prime2, q, while the Public Key only requires (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
    }