I have a certificate in 509* format and I want to serialize it into a char buffer and then later desserialize it in other to recover the certificate 509* again.
I am doing it like this to serialize:
int size_cert = 0;
unsigned char* data;
BIO* bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio,certificate);
size_cert = BIO_get_mem_data(bio, &data);
BIO_free(bio);
where data should have the certificate data!
To reconstruct the X509* certificate back from the data buffer I am doing this:
BIO* bio;
X509* cert;
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, data);
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
Where cert should get the certificate. This is not working properly, can some one give me a good example for this?
I have done it with the below code,
1 Load certificate to BIO using BIO_read_filename
2 convert it to x509 using PEM_read_bio_X509_AUX
3 convert it to unsigned char* using i2d_X509
4 reconstruct the x509 from unsigned char* using d2i_X509
int main()
{
X509 *x509,*x509ser;
BIO *certBio = BIO_new(BIO_s_file());
char * path = "E:\\share\TempCert.pem"; // certificate path
int len;
unsigned char *buf;
buf = NULL;
BIO_read_filename(certBio, path); // reading certificate to bio
x509 = PEM_read_bio_X509_AUX(certBio, NULL, 0, NULL); //converting to x509
len = i2d_X509(x509, &buf); // converting to unsigned char*
x509ser = d2i_X509(NULL, &buf, len); // converting back to x509 from unsigned char*
BIO_free_all(certBio);
return 0;
}
Related
I'm porting an AES encryption procedure from Javascript to C.
I'm using OpenSSL.
For the encryption I'm using ECB 256 mode.
The encryption goes successfully but the final Base64 does not correspond to the result I get in JS.
By testing with several online tools, the output I was getting from the C version was actually correct but different from the one from JS.
Then I realized the problem thanks to this tool https://gchq.github.io/CyberChef.
The key I'm providing is a MD5 hash like this:
aa8744256a89cba0d77f0686916f8b2e
If I pass the key as an HEX string in the CyberChef tool, the output corresponds to the JS version.
The C version is reading the key as an UTF string.
How can I force it to read the key as an HEX string?
Here's the code:
//generating MD5 of key
char keyHash[33];
unsigned char digest[MD5_DIGEST_LENGTH];
MD5((unsigned char*)keyString, strlen(keyString), (unsigned char*)&digest);
for (int i = 0; i < 16; i++)
sprintf(&keyHash[i * 2], "%02x", (unsigned int)digest[i]);
char out[256];
int outlen = 0;
//initializing context
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
//inizializing encryption with 256 ECB Cipher and MD5 Hash as Key
EVP_EncryptInit(ctx, EVP_aes_256_ecb(), keyHash, 0);
//setting padding
EVP_CIPHER_CTX_set_padding(ctx, EVP_PADDING_PKCS7);
//encrypting
EVP_EncryptUpdate(ctx, out, &outlen, user, strlen(user));
EVP_EncryptFinal(ctx, out, &outlen);
EVP_CIPHER_CTX_free(ctx);
//parsing the result to Base64
BIO *mbio = BIO_new(BIO_s_mem());
BIO *b64bio = BIO_new(BIO_f_base64());
BIO_set_flags(b64bio, BIO_FLAGS_BASE64_NO_NL);
BIO *bio = BIO_push(b64bio, mbio);
BIO_write(bio, out, outlen);
BIO_flush(bio);
char* data = NULL;
size_t datalen = 0;
datalen = BIO_get_mem_data(mbio, &data);
data[datalen] = '\0';
//printing the result
printf("encrypted data: [%s]\n", data);
I am trying to encrypt some text using OpenSSL's RSA encryption functions. My main issue is that the length of the encrypted RSA text varies between 0 and 256.
My RSA encryption function is:
/* Encrypt data using RSA */
char* rsa_encrypt(char* pub_key_filename, const unsigned char *data)
{
int padding = RSA_PKCS1_PADDING;
FILE *fp_pub;
fp_pub = fopen(pub_key_filename, "rb");
if (fp_pub == NULL)
{
printf("There was an error opening the public key file. Exiting!\n");
exit(EXIT_FAILURE);
}
RSA *pub_key = PEM_read_RSA_PUBKEY(fp_pub, NULL, NULL, NULL);
char *encrypted = malloc(2048);
int i;
for (i = 0; i < (2048); i++)
{
encrypted[i] = '\0';
}
int result = RSA_public_encrypt(strlen(data), data, encrypted, pub_key, padding);
if (result == -1)
{
printf("There was an error during RSA encryption.\n");
return "ERROR_RSA_ENCRYPTION";
}
fclose(fp_pub);
return encrypted;
}
The following code involves trying to encrypt some text:
const unsigned char *key = (unsigned char *)"abcdefghijklmnopqrstuvwxyzabcdef";
unsigned char *encrypted_aes_key = rsa_encrypt("public.pem", key);
I know that RSA with no padding is primitive RSA encryption and the resulting length is between 0 and n (RSA bit size) as seen here but my code is using RSA_PKCS1_PADDING so I am not sure why I am still getting variable length output.
The length of the encrypted data is returned in result from:
int result = RSA_public_encrypt(strlen(data), data, encrypted, pub_key, padding);
The encrypted data returned in encrypted is binary data. You can't do a strlen on it. The data is not 0 terminated and might contain some random 0 in it.
I am using the EVP library found here: https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html
Here are my two encryption and decryption functions:
I am trying to encrypt a string using AES 128 CBC.
The string is usually of the format word1 word2 word3
char* encrypt(char *s, char *key) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if (EVP_EncryptUpdate(&ctx, outbuf, &outlen1, s, strlen(s)) == 1) {
if (EVP_EncryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
char* decrypt(char *s, char *key) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if (EVP_DecryptUpdate(&ctx, outbuf, &outlen1, s, strlen(s)) == 1) {
printf("After decrypt update\n");
if (EVP_DecryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
printf("After decrypt final\n");
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
The problem is the decryption final function works on some strings but not on others.
If the string before it is encrypted is something like cat dog cow, the decryption works.
But if it is like bat dog cow, the decryption fails in particular at the EVP_DecryptFinal_ex() function.
For some strings, the decryption always fails at the EVP_DecryptFinal_ex() function. It does not return 1.
Any idea what the problem could be? Padding maybe? I just can't seem to figure it out.
You probably miss that the encrypted string may contain zero-bytes, so the strlen(s) in DecryptUpdate has a too low value. You have to remember from encrypt how long the encrypted data is and use that value for decrypting.
I am trying to encrypt some plain text with a public key using RSA_public_encrypt(), this data would then be sent to a remote server for validation. I believe I have the encryption/decryption working, since the output of RSA_public_encrypt can be passed to RSA_private_decrypt and it works. The problem I am having now is I need to base64 encode the data in order to send it over HTTP.
As a test (before sending it to the server) I am encoding to base64 the output of RSA_public_encrypt(), then decoding it and passing it back into RSA_private_decrypt(). This appears to work some of the time, and fails with an error like this:
error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error
When I use memcmp to compare the original data (pre-base64) to the output of my base64 decode function I receive a -1 despite the contents appearing to match (in Visual Studio by viewing the contents of the memory as hex). I have also checked the base64 encoded version with various online tools and they appear to decode to the expected value.
I have double checked that the input/output from the base64/unbase64 functions are null terminated which appears to make little difference.
I've been going round in circles with this problem for a couple of days but I believe it must be something with the base64 encode/decode process because when that is not involved everything works. If anyone has any advice on how this could be happening it would be appreciated.
Openssl version: 1.0.1c
Platform: Windows/MSVC
Base64 Code:
char *base64(const unsigned char *input, int length)
{
BIO *bmem, *b64;
BUF_MEM *bptr;
char *buff = NULL;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, length);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
buff = (char *)malloc(bptr->length+1);
memcpy(buff, bptr->data, bptr->length);
buff[bptr->length] = '\0';
BIO_free_all(b64);
return buff;
}
Unbase64 Code:
char *unbase64(unsigned char *input, int length)
{
BIO *b64, *bmem;
char *buffer = (char *)malloc(length+1);
memset(buffer, 0, length+1);
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(input, length);
bmem = BIO_push(b64, bmem);
BIO_read(bmem, buffer, length);
buffer[length] = '\0';
BIO_free_all(bmem);
return buffer;
}
Encrypted "hello world" example:
š:Œ¼JŒ"ÌïëŸÔè#¢Oo‚À– œê\çrú¿±a/8ƒòÌ¢Q\T¹]nío
Base64 version (using the above code):
G5qdOgWMvEqMIswZ7+uf1OgPI6JPb4LAlgmc6lzncvq/sWEvOIPyzByiUVwMjYFUuV0Vbu1v
Thanks for any help!
You are passing in the correct size in the unbase64 function? It should be the size of the base64 buffer returned, not the size of the destination buffer i.e. using an example main function:
int main(void)
{
unsigned char bufron[2000];
int i;
char *chab;
unsigned char *chac;
for (i = 0; i < 2000; i++) {
bufron[i] = i % 255;
}
chab = base64(bufron, 2000);
printf("%s\n", chab);
chac = unbase64(chab, strlen(chab));
for (i = 0; i < 2000; i++) {
if (bufron[i] != chac[i]) {
printf("Failed at %d\n", i);
return (1);
}
}
}
I'm working on a simple program that uses OpenSSL to do basic RSA encryption and decryption. It is working fine for small messages (<16 bytes), but fails for anything over that. I understand that a limitation of public key cryptography is that you cannot encrypt anything longer than the key size. In my case, I'm using a 1024bit key, so I should have 128bytes to work with (maybe slightly less due to padding), correct? If so, that's not what I'm experiencing.
Here's the output from my program with 15 bytes:
Generating RSA keypair...done.
Message to encrypt: 0123456789ABCDE
16 bytes encrypted
Decrypted message: 0123456789ABCDE
And with 16 bytes:
Generating RSA keypair...done.
Message to encrypt: 0123456789ABCDEF
16 bytes encrypted
140153837057696:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:467:
Decrypted message: (null)
It seems that no matter what, only a total of 16 bytes are encrypted.
My encryption function (updated with fix):
unsigned char* rsa_seal(EVP_PKEY *pub_key, unsigned char *msg, size_t **enc_msg_len, unsigned char **sym_key, int *sym_key_len, unsigned char **iv) {
size_t msg_len = strlen((char*)msg);
unsigned char *encrypt = malloc(EVP_PKEY_size(pub_key));
EVP_CIPHER_CTX *ctx = malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
*sym_key = malloc(EVP_PKEY_size(pub_key));
*iv = malloc(EVP_MAX_IV_LENGTH);
**enc_msg_len = 0;
if(!EVP_SealInit(ctx, EVP_aes_128_cbc(), sym_key, sym_key_len, *iv, &pub_key, 1)) {
ERR_print_errors_fp(stderr);
encrypt = NULL;
goto return_free;
}
if(!EVP_SealUpdate(ctx, encrypt, (int*)*enc_msg_len, msg, (int)msg_len)) {
ERR_print_errors_fp(stderr);
encrypt = NULL;
goto return_free;
}
if(!EVP_SealFinal(ctx, encrypt, (int*)*enc_msg_len)) {
ERR_print_errors_fp(stderr);
encrypt = NULL;
goto return_free;
}
return_free:
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
ctx = NULL;
return encrypt;
}
The corresponding decryption function (updated with fix):
char* rsa_open(EVP_PKEY *pri_key, unsigned char *enc_msg, size_t *enc_msg_len, unsigned char *sym_key, int sym_key_len, unsigned char *iv) {
size_t dec_len = 0;
unsigned char *decrypt = malloc((*enc_msg_len) + EVP_MAX_IV_LENGTH);
if(decrypt == NULL) return NULL;
EVP_CIPHER_CTX *ctx = malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
if(!EVP_OpenInit(ctx, EVP_aes_128_cbc(), sym_key, sym_key_len, iv, pri_key)) {
ERR_print_errors_fp(stderr);
decrypt = NULL;
goto return_free;
}
if(!EVP_OpenUpdate(ctx, decrypt, (int*)&dec_len, enc_msg, (int)*enc_msg_len)) {
ERR_print_errors_fp(stderr);
decrypt = NULL;
goto return_free;
}
if(!EVP_OpenFinal(ctx, decrypt, (int*)&dec_len)) {
ERR_print_errors_fp(stderr);
decrypt = NULL;
goto return_free;
}
decrypt[dec_len] = '\0';
return_free:
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
ctx = NULL;
return (char*)decrypt;
}
The key generation function:
int rsa_init(EVP_PKEY **rsa_keypair) {
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if(!EVP_PKEY_keygen_init(ctx)) {
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KEY_LENGTH)) {
ERR_print_errors_fp(stderr);
return -1;
}
if(!EVP_PKEY_keygen(ctx, rsa_keypair)) {
ERR_print_errors_fp(stderr);
return -1;
}
EVP_PKEY_CTX_free(ctx);
return 0;
}
And finally, my main:
int main() {
EVP_PKEY *rsa_keypair = NULL; // RSA keypair
char msg[BUFFER]; // Message to encrypt
unsigned char *encrypt = NULL; // Encrypted message
char *decrypt = NULL; // Decrypted message
// Generate key pair
printf("Generating RSA keypair...");
if(rsa_init(&rsa_keypair) == -1) {
fprintf(stderr, "\nError generating RSA keypair.\n");
exit(1);
}
printf("done.\n");
// Get the message to encrypt
printf("Message to encrypt: ");
fgets(msg, BUFFER-1, stdin);
msg[strlen(msg)-1] = '\0';
// Load error strings in anticipation of error
ERR_load_crypto_strings();
// Encrypt the message
size_t *encrypt_len = malloc(sizeof(size_t));
unsigned char *sym_key = NULL;
unsigned char *iv = NULL;
int sym_key_len;
encrypt = rsa_seal(rsa_keypair, (unsigned char*)msg, &encrypt_len, &sym_key, &sym_key_len, &iv);
printf("%d bytes encrypted\n", (int)*encrypt_len);
// Decrypt it
decrypt = rsa_open(rsa_keypair, (unsigned char*)encrypt, (size_t*)encrypt_len, sym_key, sym_key_len, iv);
printf("Decrypted message: %s\n", decrypt);
free(encrypt);
free(decrypt);
free(encrypt_len);
free(sym_key);
free(iv);
EVP_PKEY_free(rsa_keypair);
return 0;
}
Any help is greatly appreciated! Thank you.
EDIT: As pointed out by math below, it seems that the answer to my mistake was hiding in the OpenSSL here: https://www.openssl.org/docs/crypto/EVP_EncryptInit.html#
This is because you do not properly handle out and outl parameters in EVP_SealUpdate(), EVP_SealFinal(), EVP_OpenUpdate() and EVP_OpenFinal().
Each EVP_XxxxUpdate() and EVP_XxxxFinal() call will contribute to the output buffer. So, you are required to keep track of the seal/open process by summing each outl returned and providing the expected buffer each time (start of buffer + already handled bytes).
unsigned char* rsa_seal(...)
{
...
**enc_msg_len = 0;
EVP_SealUpdate(ctx, encrypt + **enc_msg_len, &outl, msg, (int)msg_len);
**enc_msg_len += outl;
EVP_SealFinal(ctx, encrypt + **enc_msg_len, &outl);
**enc_msg_len += outl;
...
}
char* rsa_open(...)
{
...
dec_len = 0;
EVP_OpenUpdate(ctx, decrypt + dec_len, &outl, enc_msg, (int)*enc_msg_len);
dec_len += outl;
EVP_OpenFinal(ctx, decrypt + dec_len, &outl);
dec_len += outl;
...
}
The program was working with 15-bytes buffer because in that case, the EVP_XxxxUpdate() call is returning 0 in outl (not enough data to seal/open a block), hiding the problem in your code logic.
Note: The data is not directly encrypted using the RSA key but using a generated symetric key (AES-128 in your case). This is why the block size is 16 bytes.