4
votes

As the title says, I have some code that generates a pair of RSA keys. I want to split them apart and use them individually to encrypt/decrypt, rather than use the variable "keypair" to encrypt, and decrypt.

I am working to transfer data across a network, and want to encrypt it using simple RSA encryption. Therefore i want to send the public key over to a different user, so he can use it to encrypt some data, and then send it back to me.

Here is the code that generates the keys:

//Generate key pair
    RSA *keypair = RSA_generate_key(KEY_LENGTH, PUB_EXP, NULL, NULL);

I now want to separate the public from the private key, so i can use them independently to encrypt and decrypt data. How can i do that?

I've got some code that takes the "keypair" and extracts some information into some "BIO" variables, although i am not sure how that would help me:

// To get the C-string PEM form:
BIO *pri = BIO_new(BIO_s_mem());
BIO *pub = BIO_new(BIO_s_mem());

PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
PEM_write_bio_RSAPublicKey(pub, keypair);

pri_len = BIO_pending(pri);
pub_len = BIO_pending(pub);

pri_key = (char*)malloc(pri_len + 1);
pub_key = (char*)malloc(pub_len + 1);

BIO_read(pri, pri_key, pri_len);
BIO_read(pub, pub_key, pub_len);

pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';

#ifdef PRINT_KEYS
    printf("\n%s\n%s\n", pri_key, pub_key);
#endif
printf("done.\n");

This code works, since i've tested it in visual studio 2012. Any ideas on how to separate the keys, then maybe put them back together in a "keypair" or maybe how to use them separately to encrypt/decrypt some string variables?

Thank you

(FULL CODE)

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

#define KEY_LENGTH  2048
#define PUB_EXP     3
#define PRINT_KEYS
#define WRITE_TO_FILE

int main() {
    size_t pri_len;            // Length of private key
    size_t pub_len;            // Length of public key
    char   *pri_key;           // Private key
    char   *pub_key;           // Public key
    char   msg[KEY_LENGTH/8];  // Message to encrypt
    char   *encrypt = NULL;    // Encrypted message
    char   *decrypt = NULL;    // Decrypted message
    char   *err;               // Buffer for any error messages
//Generate key pair
    RSA *keypair = RSA_generate_key(KEY_LENGTH, PUB_EXP, NULL, NULL);

// To get the C-string PEM form:
    BIO *pri = BIO_new(BIO_s_mem());
    BIO *pub = BIO_new(BIO_s_mem());

    PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
    PEM_write_bio_RSAPublicKey(pub, keypair);

    pri_len = BIO_pending(pri);
    pub_len = BIO_pending(pub);

    pri_key = (char*)malloc(pri_len + 1);
    pub_key = (char*)malloc(pub_len + 1);

    BIO_read(pri, pri_key, pri_len);
    BIO_read(pub, pub_key, pub_len);

    pri_key[pri_len] = '\0';
    pub_key[pub_len] = '\0';

    #ifdef PRINT_KEYS
        printf("\n%s\n%s\n", pri_key, pub_key);
    #endif
    printf("done.\n");

// Get the message to encrypt
    printf("Message to encrypt: ");
    fgets(msg, KEY_LENGTH-1, stdin);
    msg[strlen(msg)-1] = '\0';

// Encrypt the message
    encrypt = (char*)malloc(RSA_size(keypair));
    int encrypt_len;
    err = (char*)malloc(130);
    if((encrypt_len = RSA_public_encrypt(strlen(msg)+1, (unsigned char*)msg, (unsigned char*)encrypt, keypair, RSA_PKCS1_OAEP_PADDING)) == -1) {
        ERR_load_crypto_strings();
        ERR_error_string(ERR_get_error(), err);
        fprintf(stderr, "Error encrypting message: %s\n", err);
        goto free_stuff;
    }

// Decrypt it
decrypt = (char*)malloc(encrypt_len);
if(RSA_private_decrypt(encrypt_len, (unsigned char*)encrypt, (unsigned char*)decrypt, keypair, RSA_PKCS1_OAEP_PADDING) == -1) {
    ERR_load_crypto_strings();
    ERR_error_string(ERR_get_error(), err);
    fprintf(stderr, "Error decrypting message: %s\n", err);
    goto free_stuff;
}
printf("Decrypted message: %s\n", decrypt);

getchar();

//printf("%s", pub_key);

free_stuff:
RSA_free(keypair);
BIO_free_all(pub);
BIO_free_all(pri);
free(pri_key);
free(pub_key);
free(encrypt);
free(decrypt);
free(err);
}

Found this code here : https://shanetully.com/2012/04/simple-public-key-encryption-with-rsa-and-openssl/

4
I've noticed by searching a little that some other people have tried asking the same question with no proper answers so far -> (stackoverflow.com/questions/20002029/…)Dani Marian Morar
How about using command line tools 'rsa' and 'rsautl'?Chiara Hsieh
RSA_public_encrypt seems to expect a public key (according to the API). Have you tried using the public key instead of the key pair? Why would you recreate a key pair? What have you tried that failed?Maarten Bodewes
@ChiaraHsieh To suggest command line utilities on a site about programming is a bit weird.Maarten Bodewes
Yes, that would beat the purpose of encryption. What you are trying to do is pretty basic, I wonder if there is not an example. Otherwise you could take a look at the source of the CLI application. If you need a string instead of bytes then you can simply base 64 encode the PKCS#1 public key. Or add an ASCII armor (PEM).Maarten Bodewes

4 Answers

4
votes

I want to split [the keypair] apart and use them individually to encrypt/decrypt

... Any ideas on how to separate the keys, then maybe put them back together in a "keypair" or maybe how to use them separately to encrypt/decrypt some string variables?

You can use RSAPublicKey_dup and RSAPrivateKey_dup, without the need to round trip them by ASN.1/DER or PEM encoding them. Here, round tripping consist of using a functions like PEM_write_bio_RSAPublicKey and PEM_read_bio_RSAPublicKey.

Below is a sample program that uses them. Its written in C++ (thanks for adding that tag).

While you can separate them, they both use the RSA structure. The public key has some members set to NULL, like the private exponent.


You can also print the keys with RSA_print, RSA_print_fp and friends. See RSA_print (3) or its use below.


// g++ -Wall -Wextra -std=c++11 -stdlib=libc++ -I/usr/local/ssl/macosx-x64/include \
//     t.cpp -o t.exe /usr/local/ssl/macosx-x64/lib/libcrypto.a

#include <memory>
using std::unique_ptr;

#include <openssl/bn.h>
#include <openssl/rsa.h>

#include <cassert>
#define ASSERT assert

using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;

int main(int argc, char* argv[])
{
    int rc;

    RSA_ptr rsa(RSA_new(), ::RSA_free);
    BN_ptr bn(BN_new(), ::BN_free);

    rc = BN_set_word(bn.get(), RSA_F4);
    ASSERT(rc == 1);

    rc = RSA_generate_key_ex(rsa.get(), 2048, bn.get(), NULL);
    ASSERT(rc == 1);

    RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free);
    RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free);

    fprintf(stdout, "\n");
    RSA_print_fp(stdout, rsa_pub.get(), 0);

    fprintf(stdout, "\n");
    RSA_print_fp(stdout, rsa_priv.get(), 0);

    return 0;
}

Here is the output:

$ ./t.exe 

Public-Key: (2048 bit)
Modulus:
    00:aa:5a:cc:30:52:f1:e9:49:3d:a6:25:00:33:29:
    a6:fa:f7:53:e0:3c:73:4c:91:41:66:20:ec:62:1f:
    27:2a:2a:6c:0f:90:f8:d9:7e:d5:ec:72:7b:38:8c:
    ca:12:60:f8:d1:fb:f2:65:7c:b1:3a:b6:4e:26:ba:
    5b:86:cc:30:f2:fc:be:c3:a2:00:b9:ea:81:fa:1c:
    22:4e:f7:be:a1:1a:66:90:13:b6:12:66:26:23:6d:
    22:15:7d:3b:a4:99:44:38:fa:1c:70:63:4e:50:6f:
    66:38:6c:f6:1a:13:e1:c7:dc:a6:a1:eb:6f:f9:c9:
    59:c8:30:dc:c2:1b:dc:6c:9d:ea:0c:3d:52:5a:00:
    ea:c9:c9:85:51:21:9f:ec:95:b3:dc:c2:50:21:29:
    c2:64:6c:1e:34:36:d8:61:59:ab:3c:a2:cc:e8:ef:
    57:c3:7f:49:86:be:e3:42:88:1b:39:10:b8:2f:fa:
    81:ef:a0:94:99:0c:71:ae:1e:82:7f:e3:6e:00:6e:
    02:13:66:bb:a9:31:58:ec:90:39:9c:bc:9c:8c:90:
    e9:20:f7:20:8e:d6:a3:a3:df:a2:4a:0f:0f:39:b5:
    57:b9:ef:6a:27:e0:1a:ed:f6:ce:0d:87:cd:43:03:
    bf:67:ef:ff:fd:da:98:cc:22:ab:5e:8d:7b:43:d3:
    90:4d
Exponent: 65537 (0x10001)

Private-Key: (2048 bit)
modulus:
    00:aa:5a:cc:30:52:f1:e9:49:3d:a6:25:00:33:29:
    a6:fa:f7:53:e0:3c:73:4c:91:41:66:20:ec:62:1f:
    27:2a:2a:6c:0f:90:f8:d9:7e:d5:ec:72:7b:38:8c:
    ca:12:60:f8:d1:fb:f2:65:7c:b1:3a:b6:4e:26:ba:
    5b:86:cc:30:f2:fc:be:c3:a2:00:b9:ea:81:fa:1c:
    22:4e:f7:be:a1:1a:66:90:13:b6:12:66:26:23:6d:
    22:15:7d:3b:a4:99:44:38:fa:1c:70:63:4e:50:6f:
    66:38:6c:f6:1a:13:e1:c7:dc:a6:a1:eb:6f:f9:c9:
    59:c8:30:dc:c2:1b:dc:6c:9d:ea:0c:3d:52:5a:00:
    ea:c9:c9:85:51:21:9f:ec:95:b3:dc:c2:50:21:29:
    c2:64:6c:1e:34:36:d8:61:59:ab:3c:a2:cc:e8:ef:
    57:c3:7f:49:86:be:e3:42:88:1b:39:10:b8:2f:fa:
    81:ef:a0:94:99:0c:71:ae:1e:82:7f:e3:6e:00:6e:
    02:13:66:bb:a9:31:58:ec:90:39:9c:bc:9c:8c:90:
    e9:20:f7:20:8e:d6:a3:a3:df:a2:4a:0f:0f:39:b5:
    57:b9:ef:6a:27:e0:1a:ed:f6:ce:0d:87:cd:43:03:
    bf:67:ef:ff:fd:da:98:cc:22:ab:5e:8d:7b:43:d3:
    90:4d
publicExponent: 65537 (0x10001)
privateExponent:
    66:a4:ce:e3:4f:16:f3:b9:6d:ab:ee:1f:70:b4:68:
    28:4f:5d:fa:7e:71:fa:70:8b:37:3e:1f:30:00:15:
    59:12:b6:89:aa:90:46:7c:65:e9:52:11:6c:c1:68:
    00:2a:ed:c1:98:4d:35:59:2c:70:73:e8:22:ed:a6:
    b8:51:d0:2c:98:9d:58:c3:04:2d:01:5f:cf:93:a4:
    18:70:ae:2b:e3:fc:68:53:78:21:1d:eb:5c:ed:24:
    dc:4d:d8:e2:14:77:46:dd:6c:c5:4b:10:a4:e6:7a:
    71:05:36:44:00:36:ca:75:e8:f1:27:2b:11:16:81:
    42:5e:2e:a5:c6:a3:c9:cd:60:59:ce:72:71:76:c8:
    ca:ba:f0:45:c3:86:07:7b:22:20:c4:74:c6:a8:ab:
    7c:2c:f8:de:ea:25:95:81:79:33:54:67:7b:61:91:
    80:a8:1f:4c:38:32:d4:4d:2e:a8:7d:9b:d4:1a:3e:
    6b:ca:50:3c:a0:61:0e:00:ad:f4:5c:0f:26:1a:59:
    00:3c:bd:ee:c3:e8:d0:b8:9b:0e:44:89:49:d1:24:
    a4:39:15:dc:0e:c5:d5:41:a2:4a:f4:e5:e3:23:c7:
    98:8a:87:f7:18:a6:e2:7b:27:83:f6:fb:62:42:46:
    ae:de:ba:48:ad:07:39:40:da:65:17:d1:d2:ed:df:
    01
prime1:
    00:dd:dc:70:b5:70:ea:10:20:28:40:a0:c3:b8:70:
    6d:3d:84:c0:57:2d:69:fc:e9:d4:55:ed:4f:ac:3d:
    c2:e9:19:49:f0:ab:c6:bd:99:9e:0f:e5:a4:61:d4:
    b3:c5:c2:b1:e4:3a:10:ff:e6:cd:ce:6e:2d:93:bc:
    87:12:92:87:7c:d3:dd:bc:32:54:9e:fa:67:b1:9d:
    e2:27:53:e6:03:a7:22:17:45:63:0d:42:f3:96:5d:
    a3:e0:9c:93:f0:42:8b:bb:95:34:e6:f6:0b:f7:b6:
    c5:59:a0:b5:2a:71:59:c0:f2:7e:bf:95:2d:dd:6d:
    94:23:2a:95:4a:4f:f1:d0:93
prime2:
    00:c4:91:6a:33:1b:db:24:eb:fd:d3:69:e9:3c:e2:
    a2:2d:23:7a:92:65:a8:a0:50:1d:0a:2b:b4:f0:64:
    e4:40:57:f3:dc:f7:65:18:7d:51:75:73:b9:d6:67:
    9b:0e:94:5f:37:02:6c:7f:eb:b9:13:4b:bf:8e:65:
    22:0b:2c:c6:8d:2a:a2:88:ec:21:e3:f9:0b:78:b4:
    1d:d0:44:e6:36:0d:ec:3c:8f:0a:c7:3b:0d:91:65:
    b7:de:a3:c9:a3:2a:8c:7f:1f:a1:d2:6e:9b:ee:23:
    78:c1:30:76:87:af:a8:11:a4:15:b4:54:16:d8:94:
    71:5c:64:30:43:58:b5:07:9f
exponent1:
    2f:91:e8:88:be:e1:30:fb:f4:25:87:52:ef:e5:0b:
    47:39:83:94:2d:a4:a0:19:f2:f1:49:a4:df:a5:8e:
    79:34:76:ea:27:aa:c1:54:82:d3:9d:c5:95:44:6a:
    17:69:1b:83:77:ff:d5:1e:c3:da:13:3d:aa:83:ad:
    e2:89:90:8b:6f:52:07:dc:32:d0:b3:98:30:39:4e:
    18:68:a0:d4:ff:ad:0b:98:51:18:b2:d6:4f:d3:5c:
    23:f8:ee:af:81:55:3c:af:4d:5c:88:3d:20:ac:0b:
    bc:9f:fc:b8:50:fd:91:a5:6d:0f:df:08:aa:85:a8:
    51:b1:fb:b8:a7:53:8e:09
exponent2:
    7d:46:0b:7f:ad:06:19:de:c8:b2:7e:f2:25:5a:6e:
    6f:04:08:6e:da:99:00:2a:6e:87:77:d9:65:c7:76:
    ec:46:e1:64:f6:ca:18:34:6d:c0:c3:d3:31:00:70:
    82:77:2e:c3:59:29:1a:d1:78:ef:02:3c:7f:9c:96:
    78:b6:bd:87:64:1f:97:d1:9d:bb:b3:91:8b:08:87:
    63:9f:35:74:47:a5:41:e7:0b:c0:73:33:2f:71:bb:
    20:0a:14:4c:87:a6:68:b2:19:28:8a:53:98:0e:45:
    3c:22:0d:b8:65:cb:60:0a:c9:c6:56:3d:05:24:7d:
    a6:9b:37:63:04:5a:c3:13
coefficient:
    00:cc:d7:5c:e6:0e:7b:79:d4:cb:4f:6d:82:a7:45:
    90:67:90:dc:d3:83:62:f1:4b:17:43:5c:4a:ea:bf:
    38:25:c3:6f:34:e2:05:91:5e:60:d6:de:6d:07:1a:
    73:71:b3:1d:73:f2:3c:60:ed:ec:42:d4:39:f8:a4:
    ae:d5:aa:40:1e:90:b1:eb:b1:05:a3:2f:03:5f:c6:
    b7:07:4c:df:0f:c4:a9:80:8c:31:f5:e2:01:00:73:
    8a:25:03:84:4e:48:7a:31:8e:e6:b8:04:4c:44:61:
    7d:e4:87:1c:57:4f:45:44:33:bb:f3:ae:1c:d2:e1:
    99:ed:78:29:76:4d:8c:6d:91

Related, you never ask how to encrypt or decrypt with a RSA key, yet you claim the answer to the encryption and decryption problems is shown in the code in your answer.

You seem to have made the question a moving target :) You should probably avoid that on Stack Overflow (I think its OK to do in those user thread forums). On Stack Overflow, you should ask a separate question.

3
votes

You can extract the RSA public key from RSA keypair using d2i_RSAPublicKey and i2d_RSAPublicKey (link). Use i2d_RSAPublicKey to encode your keypair to PKCS#1 RSAPublicKey stucture, store it in a bytestring, then use d2i_RSAPublicKey to decode it back to RSA key struct.

3
votes

I've found a solution to my question among other Stack-Overflow posts and namely Reading Public/Private Key from Memory with OpenSSL

The answer i was looking for is answered by @SquareRootOfTwentyThree is his last line of code,

After extracting the Public key into a BIO variable called pub:

PEM_write_bio_RSAPublicKey(pub, keypair);

i can send the variable pub across the network, and after it reaches the other side create a RSA variable and put pub inside it:

SOLUTION:

RSA *keypair2 = NULL; 
PEM_read_bio_RSAPublicKey( pub, &keypair2, NULL, NULL);

After i've done this i can successfully encrypt the message as usual, using keypair2:

ENCRYPTION:

encrypt = (char*)malloc(RSA_size(keypair));
int encrypt_len;
err = (char*)malloc(130);
if((encrypt_len = RSA_public_encrypt(strlen(msg)+1, (unsigned char*)msg,
        (unsigned char*)encrypt, keypair2 ,RSA_PKCS1_OAEP_PADDING)) == -1) {
    ERR_load_crypto_strings();
    ERR_error_string(ERR_get_error(), err);
    fprintf(stderr, "Error encrypting message: %s\n", err);
}

I can then send this encrypt variable back to the first machine, and decrypt it as usual, using my original keypair, without having to send it over the network.

DECRYPTION:

decrypt = (char*)malloc(encrypt_len);
if(RSA_private_decrypt(encrypt_len, (unsigned char*)encrypt, (unsigned char*)decrypt,
        keypair, RSA_PKCS1_OAEP_PADDING) == -1) {
    ERR_load_crypto_strings();
    ERR_error_string(ERR_get_error(), err);
    fprintf(stderr, "Error decrypting message: %s\n", err);
}

Thank you everyone for contributing to this post!!!

0
votes

Reviewing your code, it appears that you successfully separated the public and private keys into the strings pub_key and pri_key, but then used printf to output them which pasted them back together. To just print the public key change the printf statement to:

printf("\n%s\n", pub_key);