Search code examples
opensslrsapem

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


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)


Solution

  • 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.