1

I need to write a C program that generates an RSA key, and saves an X.509 public key in DER format and a PKCS#8 private key in DER format. I've used Google, but haven't really found much. What I have so far is this:

#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>

void main() {
    int ret = 0;
    RSA *r = NULL;
    BIGNUM *bne = NULL;
    BIO *bp_public = NULL, *bp_private = NULL;
    int bits = 2048;
    unsigned long e = RSA_F4;

    // Generate the RSA key
    printf("Generating RSA key...\n");
    bne = BN_new();
    ret = BN_set_word(bne, e);
    if(ret != 1) {
        goto free_all;
    }
    r = RSA_new();
    ret = RSA_generate_key_ex(r, bits, bne, NULL);
    if(ret != 1) {
        goto free_all;
    }

    // Save the public key in PEM format
    printf("Writing key files...\n");
    bp_public = BIO_new_file("public.pem", "w+");
    ret = PEM_write_bio_RSAPublicKey(bp_public, r);
    if(ret != 1) {
        goto free_all;
    }

    // Save the private key in PEM format
    bp_private = BIO_new_file("private.pem", "w+");
    ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);

    // Free everything
    free_all:
    BIO_free_all(bp_public);
    BIO_free_all(bp_private);
    RSA_free(r);
    BN_free(bne);
    printf("Done!\n");
}

This is obviously writing the keys in PEM format. I also need to be able to actually have the data in memory in the code, not just write it directly to a file, as there's some other stuff I need to do with the public key.

Thank you for any help

jww
  • 97,681
  • 90
  • 411
  • 885
atoms118
  • 105
  • 2
  • 11
  • You already have key(r) in memory that you are writing to file – Pras Jan 18 '18 at 15:47
  • I know, but I'm not writing them in the correct format – atoms118 Jan 18 '18 at 15:51
  • 1
    [Look for methods](https://www.openssl.org/docs/manmaster/man3/) that start out with the letters 'i2d', which I think means "internal to der". For example, [this](https://www.openssl.org/docs/manmaster/man3/i2d_PKCS8PrivateKey_bio.html) may be one of the methods you are looking for. – President James K. Polk Jan 18 '18 at 16:02
  • Ok, I've read through the links. Is there any more clear documentation? It's difficult to understand what the various arguments are supposed to be – atoms118 Jan 18 '18 at 16:30

2 Answers2

2

I think you're going to need to convert your key to an EVP_PKEY using EVP_PKEY_assign_RSA. Then you can use i2d_PUBKEY_bio to write out to a bio.

The following modification of your code works for me:

  1. include the header file <openssl/evp.h>
  2. declare BIO for writing out the public_der
  3. create EVP_PKEY structure with EVP_PKEY_new()
  4. convert using EVP_PKEY_assign_RSA
  5. write out to bio with i2d_PUBKEY_bio

In context:

#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>

void main() {
    int ret = 0;
    RSA *r = NULL;
    BIGNUM *bne = NULL;
    BIO *bp_public = NULL, *bp_private = NULL, *bp_public_der = NULL;
    int bits = 2048;
    unsigned long e = RSA_F4;

    // Generate the RSA key
    printf("Generating RSA key...\n");
    bne = BN_new();
    ret = BN_set_word(bne, e);
    if(ret != 1) {
        goto free_all;
    }
    r = RSA_new();
    ret = RSA_generate_key_ex(r, bits, bne, NULL);
    if(ret != 1) {
        goto free_all;
    }

    // Save the public key in PEM format
    printf("Writing key files...\n");
    bp_public = BIO_new_file("public.pem", "w+");
    ret = PEM_write_bio_RSAPublicKey(bp_public, r);
    if(ret != 1) {
        goto free_all;
    }

    // Save the private key in PEM format
    bp_private = BIO_new_file("private.pem", "w+");
    ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);

    // Save in DER
    EVP_PKEY *evp = EVP_PKEY_new();
    ret = EVP_PKEY_assign_RSA(evp, r);
    if(ret != 1){
        printf("failure %i\n", ret);
    }
    bp_public_der = BIO_new_file("public.key", "w+");
    ret = i2d_PUBKEY_bio(bp_public_der, evp);



    // Free everything
    free_all:
    BIO_free_all(bp_public);
    BIO_free_all(bp_public_der);
    BIO_free_all(bp_private);
    RSA_free(r);
    BN_free(bne);
    printf("Done!\n");
}

You can now find the DER of the public key in public.key. And you should be able to do the same thing for the private.

Hope this helps.

JawguyChooser
  • 1,816
  • 1
  • 18
  • 32
2

Your question is a little ambiguous in what you actually mean by "saves an X.509 public key in DER format". Assuming you actually mean "save it as a SubjectPublicKeyInfo structure" (which is the bit of an X.509 certificate that holds public keys) then you should use i2d_RSA_PUBKEY (or i2d_RSA_PUBKEY_fp or i2d_RSA_PUBKEY_bio) to write it out (no need to convert it to an EVP_PKEY first).

For the PKCS#8 private key in DER format, your current method is incorrect for PEM format. The PEM_write_bio_RSAPrivateKey() function will write this out in traditional format (not PKCS#8).

I assume you don't want to do anything complicated like encrypting the key first. For this one you will need to convert it to an EVP_PKEY (using EVP_PKEY_assign_RSA() as mentioned by @JawguyChooser). Next you obtain a PKCS8_PRIV_KEY_INFO structure using the (sadly undocumented) function EVP_PKEY2PKCS8.

PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey);

You need to free this structure when your done using PKCS8_PRIV_KEY_INFO_free(). Next write out the PKCS8 DER using i2d_PKCS8_PRIV_KEY_INFO() (or i2d_PKCS8_PRIV_KEY_INFO_fp() or i2d_PKCS8_PRIV_KEY_INFO_bio).

See the man page for info on various of these functions:

https://www.openssl.org/docs/man1.1.0/crypto/i2d_RSAPublicKey.html

Matt Caswell
  • 8,167
  • 25
  • 28