Converting RSA public key Modulus and Exponent into PEM file

Some days ago, I battled reading a raw RSA public key on iOS and using it to encrypt some data with on an iPad. Part of that battle was taking a “modulus” and “exponent” value from an RSA public key, and converting it into a PEM encoded public key file. I created this little C program to do this. It is using “openssl” and “libcrypto” to do its work.

In the case others, or I, some day need this, I created a gist for it here:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
 
// cheating, .. ignoring deprecation warnings
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 
unsigned char *base64_decode(const char* base64data, int* len) {
BIO *b64, *bmem;
size_t length = strlen(base64data);
unsigned char *buffer = (unsigned char *)malloc(length);
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf((void*)base64data, length);
bmem = BIO_push(b64, bmem);
*len = BIO_read(bmem, buffer, length);
BIO_free_all(bmem);
return buffer;
}
 
BIGNUM* bignum_base64_decode(const char* base64bignum) {
BIGNUM* bn = NULL;
int len;
unsigned char* data = base64_decode(base64bignum, &len);
if (len) {
bn = BN_bin2bn(data, len, NULL);
}
free(data);
return bn;
}
 
EVP_PKEY* RSA_fromBase64(const char* modulus_b64, const char* exp_b64) {
BIGNUM *n = bignum_base64_decode(modulus_b64);
BIGNUM *e = bignum_base64_decode(exp_b64);
 
if (!n) printf("Invalid encoding for modulus\n");
if (!e) printf("Invalid encoding for public exponent\n");
 
if (e && n) {
EVP_PKEY* pRsaKey = EVP_PKEY_new();
RSA* rsa = RSA_new();
rsa->e = e;
rsa->n = n;
EVP_PKEY_assign_RSA(pRsaKey, rsa);
return pRsaKey;
} else {
if (n) BN_free(n);
if (e) BN_free(e);
return NULL;
}
}
 
void assert_syntax(int argc, char** argv) {
if (argc != 4) {
fprintf(stderr, "Description: %s takes a RSA public key modulus and exponent in base64 encoding and produces a public key file in PEM format.\n", argv[0]);
fprintf(stderr, "syntax: %s <modulus_base64> <exp_base64> <output_file>\n", argv[0]);
exit(1);
}
}
 
int main(int argc, char** argv) {
assert_syntax(argc, argv);
 
const char* modulus = argv[1];
const char* exp = argv[2];
const char* filename = argv[3];
 
EVP_PKEY* pkey = RSA_fromBase64(modulus, exp);
 
if (pkey == NULL) {
fprintf(stderr, "an error occurred :(\n");
return 2;
} else {
printf("success decoded into RSA public key\n");
FILE* file = fopen(filename, "w");
PEM_write_PUBKEY(file, pkey);
fflush(file);
fclose(file);
printf("written to file: %s\n", filename);
}
 
return 0;
}

I’ve developed and tested this on OS X 10.7.4 with Xcode 4.3.2 installed. It can be built using the makefile which is also part of the gist:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
CC = clang
CFLAGS =
DEPS =
OBJ = modexp2pubkey.o
LIBS = -lssl -lcrypto
 
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
 
modexp2pubkey: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
.PHONY: clean
 
clean:
rm -f *.o
view raw Makefile This Gist brought to you by GitHub.

I have no idea if this works properly on other platforms.

June 1, 2012 В· polesen В· One Comment
Tags: , , , ,  В· Posted in: Programming

One Response

  1. Carlos - March 16, 2013

    Hi,

    I’ve spent 3 days searching the web until i found you post. I’m not certainly a genius in RSA matters. If i understood correctly your post you are generating the key from a base64 modulus and exponent. My question is what if i have my modulus in hex format like n=”C696034213D7D8546984579D1D0F0EA519CFF8DEFFC429354CF3A871A6F7183F1228DA5C7470C055387100CB935A712C4E2864DF5D64BA93FE7E63E71F25B1E5F5298575EBE1C63AA617706917911DC2A75AC28B251C7EF40F2365912490B939BCA2124A30A28F54402C34AECA331AB67E1E79B285DD5771B5D9FF79EA630B75″ and e=”03″

    Do i have to convert first to base64? or i can handle them in hex format? I tried to convert to BIGNUM using BN_hex2bn but i’m not sure that it is working:

    Thanks
    Carlos

Leave a Reply