Decrypt string via OpenSSL - c

My goal is to decrypt a base64 encoded string.
Running the command fully works:
openssl enc -base64 -d -aes-128-cbc -iv '{iv}' -K {hex_key} <<< {hex_enc_password}
I'd like to do the exact same via the C api. I know there're many similar questions on stackoverflow, but nothing seems to work for me, so I kindly ask you not to flag this as duplicate.
Info:
hex_enc_password is base64 encoded
hex_key is a string as byte-string (via hexlify()) (example: 0x1a,0xc4 -> "1ac4")
My current Code:
char* key = "lbzA54tAAg3E/jPxiJc34e==";
unsigned char *hex_key = hexlify(key);
char encrypted[] = {0x3d,0xd2,0x02,0xc3,0xae,0x1e,0xf5,0x7e,0x33,0xc3,0x1d,0x7b,0x1e,0x48,0x73,0x84};
size_t output_length;
unsigned char *hex_enc_password = base64_encode(encrypted, sizeof(encrypted), &output_length);
decrypt(hex_enc_password, strlen(hex_enc_password),hex_key, iv);
char* decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ) {
int outLen1 = 0; int outLen2 = 0;
unsigned char *outdata = malloc(ciphertext_len+1);
bzero(outdata, ciphertext_len+1);
//setup decryption
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit(ctx,EVP_aes_128_cbc(),key,iv); //returns 1
EVP_DecryptUpdate(ctx,outdata,&outLen1,ciphertext,ciphertext_len); //returns 1
EVP_DecryptFinal(ctx,outdata + outLen1,&outLen2); //returns 0
return outdata;
}
Any ideas what could be wrong?

I finally got it to work.
As described in the comments above, nothing should be encoded in base64. Calling the command line binary with the -base64 simply makes openssl decode it before continuing. Same goes for hexlifying, just skip that.
Everything besides that should be the same as above.

Related

OpenSSL EVP AES Encryption and Base64 Encoding producing unusable results

So I'm trying to reproduce an encryption and encoding operation in C, that I've managed to make work in C#, JScript, Python and Java. Now, it's mostly just for obfuscating data - not actual encryption - so it's basically for aesthetic purposes only.
First thing's first, the data string that's being encrypted looks like this:
"[3671,3401,736,1081,0,32558], [3692,3401,748,1105,0,32558], [3704,3401,774,1162,0,32558], [3722,3401,774,1162,0,32558], [3733,3401,769,1172,0,32558]"
Biggest first issue for C is that this can vary in length. Each [x,y,z,a,b,c] represents some data point, and the actual string that will be encrypted can have anywhere from one data point, to 100. So I'm sure my memory management might be broken somewhere as well. Second issue is, I don't seem to be getting the correct expected result after encoding. After encrypting, the byte result of the C cipher is the same as the python cipher. But when I encode to base64 in C, it does not get the expected result at all.
#include <X11/Xlib.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <errno.h>
#include <linux/input.h>
#include <fcntl.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/params.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
void PBKDF2_HMAC_SHA_1(const char* pass, int passlen, const unsigned char* salt, int saltlen, int32_t iterations, uint32_t outputBytes, char* hexResult, uint8_t* binResult)
{
unsigned int i;
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iterations, EVP_sha1(), outputBytes, digest);
for (i = 0; i < sizeof(digest); i++)
{
sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
binResult[i] = digest[i];
}
}
int main(void){
char intext[] = "[3671,3401,736,1081,0,32558], [3692,3401,748,1105,0,32558], [3704,3401,774,1162,0,32558], [3722,3401,774,1162,0,32558], [3733,3401,769,1172,0,32558]";
int outlen, final_length;
EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();
size_t i;
char sid[] = "u9SXNMeTkvyBr3n81SJ7Lj216w04gJ99";
char pk[] = "jeIHjod1cZeM1U04cy8z7488AeY1Sl25";
uint32_t outputBytes = 48;
uint32_t iterations = 128;
unsigned char byteresult[2*outputBytes+1];
char hexresult[2*outputBytes+1];
memset(byteresult,0,sizeof(byteresult));
uint8_t binResult[outputBytes+1];
memset(binResult,0,sizeof(binResult));
char *finResult = NULL;
char key[65];
memset(key,0,sizeof(key));
char * keystart = hexresult +32;
char iv[33];
memset(iv,0,sizeof(iv));
PBKDF2_HMAC_SHA_1(sid,strlen(sid),pk,strlen(pk),iterations,outputBytes,hexresult,binResult);
memcpy(key, keystart,64);
memcpy(iv, hexresult,32);
EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), NULL,(unsigned char *)key, (unsigned char *)iv, 1);
unsigned char *outbuf;
int outbuflen = sizeof(intext) + EVP_MAX_BLOCK_LENGTH - (sizeof(intext) % 16);
outbuf = (unsigned char *)malloc(outbuflen);
EVP_CipherUpdate(ctx, outbuf, &outbuflen,(unsigned char *)intext, strlen(intext));
EVP_CipherFinal_ex(ctx, outbuf + outbuflen, &final_length);
outlen += final_length;
EVP_CIPHER_CTX_free(ctx);
char bytesout[strlen(outbuf) + outbuflen];
int buflen = 0;
for (i=0;i< outbuflen + final_length;i++)
{
buflen += 1;
sprintf(bytesout + (i * 2),"%02x", outbuf[i]);
}
printf("bytesout: %s\n", bytesout);
char outtext[sizeof(bytesout)];
memset(outtext,0, sizeof(outtext));
int outtext_len = sizeof(outtext);
EVP_ENCODE_CTX *ectx = EVP_ENCODE_CTX_new();
EVP_EncodeInit(ectx);
EVP_EncodeBlock(outtext, bytesout, sizeof(bytesout));
EVP_EncodeFinal(ectx, (unsigned char*)outtext, &outtext_len);
EVP_ENCODE_CTX_free(ectx);
printf("b64Encoded String %s \n", outtext);}
Makefile:
gcc simplecipher.c -o simplecipher -lX11 -lncurses -lssl -lcrypto
Result:
bytesout: eafafcde5c00eb6e649d61a09f9b52d13dd8c783d73afcbc03dfb5cea0cd3ab627528ec1b2997105871d570c0b972349943800aacd063093d97f7f39554775aa4256bd26599dde66bb76b925d9f021f6b657d1a91eb08e1900b6ad91f7f65b97e1a7e17b8d959a65d6893af458e26761536b3ffdf470f89f1aac24ca02782fb8a691c25b368549387890dc73143bb213e0ce616264e5b30add3b480c24f5edc6
b64Encoded String ZWFmYWZjZGU1YzAwZWI2ZTY0OWQ2MWEwOWY5YjUyZDEzZGQ4Yzc4M2Q3M2FmY2JjMDNkZmI1Y2VhMGNkM2FiNjI3NTI4ZWMxYjI5OTcxMDU4NzFkNTcwYzBiOTcyMzQ5OTQzODAwYWFjZDA2MzA5M2Q5N2Y3ZjM5NTU0Nzc1YWE0MjU2YmQyNjU5OWRkZTY2YmI3NmI=
When I do a similar script in python:
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
from Cryptodome.Protocol.KDF import PBKDF2
from Crypto.Util.Padding import pad
import binascii
symmetric_key = "u9SXNMeTkvyBr3n81SJ7Lj216w04gJ99"
salt = "jeIHjod1cZeM1U04cy8z7488AeY1Sl25"
pbbytes = PBKDF2(symmetric_key.encode("utf-8"), salt.encode("utf-8"), 48, 128)
iv = pbbytes[0:16]
key = pbbytes[16:48]
half_iv=iv[0:8]
half_key=key[0:16]
cipher = AES.new(key, AES.MODE_CBC, iv)
cipher = AES.new(binascii.hexlify(bytes(half_key)), AES.MODE_CBC, binascii.hexlify(bytes(half_iv)))
print("test encoding:")
intext = b"[3671,3401,736,1081,0,32558], [3692,3401,748,1105,0,32558], [3704,3401,774,1162,0,32558], [3722,3401,774,1162,0,32558], [3733,3401,769,1172,0,32558]"
print("intext pre padding: ", intext)
paddedtext = pad(intext,16)
print("intext post padding: ", paddedtext)
en_bytes = cipher.encrypt(paddedtext)
print("encrypted bytes: ", binascii.hexlify(bytearray(en_bytes)))
en_data = base64.b64encode(en_bytes)
en_bytes_string = ''.join(map(chr, en_bytes))
print("encoded bytes: ", en_data)
Result:
encrypted bytes: b'eafafcde5c00eb6e649d61a09f9b52d13dd8c783d73afcbc03dfb5cea0cd3ab627528ec1b2997105871d570c0b972349943800aacd063093d97f7f39554775aa4256bd26599dde66bb76b925d9f021f6b657d1a91eb08e1900b6ad91f7f65b97e1a7e17b8d959a65d6893af458e26761536b3ffdf470f89f1aac24ca02782fb8a691c25b368549387890dc73143bb213e0ce616264e5b30add3b480c24f5edc6'
encoded bytes: b'6vr83lwA625knWGgn5tS0T3Yx4PXOvy8A9+1zqDNOrYnUo7BsplxBYcdVwwLlyNJlDgAqs0GMJPZf385VUd1qkJWvSZZnd5mu3a5JdnwIfa2V9GpHrCOGQC2rZH39luX4afhe42VmmXWiTr0WOJnYVNrP/30cPifGqwkygJ4L7imkcJbNoVJOHiQ3HMUO7IT4M5hYmTlswrdO0gMJPXtxg=='
So as you can see, the encoded portion comes out completely differently in the C application. In Jscript, C#, and Java it comes out exactly as in the python script. The encrypted portion, however, is the same between the two. Just encoding seems to break it. Now this could be 100% because I've absolutely butchered something when passing the bytes/char arrays around. I just can't seem to find out where in the chain I've broken down here. Any suggestions?
The C code base64s the wrong buffer. namely bytesout, which is already an ASCII text:
for (i=0;i< outbuflen + final_length;i++)
{
buflen += 1;
sprintf(bytesout + (i * 2),"%02x", outbuf[i]);
}
You need to encode outbuf instead.
PS: the code cries for a serious cleanup.
Alright,
Just wanted to say thanks to everyone who commented, and answered but I did figure it out this morning, basically using
EVP_EncodeBlock(outtext, outbuf, buflen);
Is what solved it. Before I'd pass in either sizeof(outtext) or sizeof(outbuf) and that would only encode what looked like a part of the first data point (likely up to the first ',' or something). But this fixes it. I can now encrypt a string of datapoints regardless of their starting size, and decrypt it in python. I had buflen in there just to debug the amount of bytes that were being written to the bytesout char array, but it seemed to do the trick.
Cheers, everyone!
I was trying to do the same thing, and just finished doing so. I believe your question is misleading. You are not actually encoding a digest in base64. Rather, you are encoding the hexadecimal representation of a digest in base64 (as user58697 already stated in his own response). Also, as specified in Ian Abbott's comment, you're using EVP_ENCODE_CTX wrong.
I believe most people would actually want to encode the digest itself in base64. If you're trying to implement stuff like xmlenc (and I assume most specifications that use these base64 encoded digests), it can be done in the following fashion, using libcrypto~3.0:
void base64_digest(const char* input, int input_length)
{
// Generating a digest
EVP_MD_CTX* context = EVP_MD_CTX_new();
const EVP_MD* md = EVP_sha512();
unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len;
EVP_DigestInit_ex2(context, md, NULL);
EVP_DigestUpdate(context, input, input_length);
EVP_DigestFinal_ex(context, md_value, &md_len);
// Encoding digest to base64
char output[EVP_MAX_MD_SIZE]; // not sure this is the best size for this buffer,
// but it's not gonna need more than EVP_MAX_MD_SIZE
EVP_EncodeBlock((unsigned char*)output, md_value, md_len);
// cleanup
EVP_MD_CTX_free(context);
printf("Base64-encoded digest: %s\n", output);
}
Incidentally, the result will be much shorter (with padding, 88 characters is the expected length, while I believe you'll get 172 characters by encoding the hex digest instead).
You also don't need to use EVP_ENCODE_CTX, EVP_EncodeInit nor EVP_EncodeFinal, as EVP_EncodeBlock doesn't need any of these.
For C++ developers, I also have an implementation at https://github.com/crails-framework/libcrails-encrypt (check out the MessageDigest class).

Openssl in Windows Kernel Mode

Is there a way I can use the OpenSSL library in windows kernel mode? I want to make a windows filter driver that intercepts the read and write operations and encrypts and decrypts the buffers. I already made a driver that can replace the buffers with some arbitrary content but now I need to encrypt the original content. I tried to include the OpenSSL dll-s in the project and i can compile and install the driver but when I try to start it I get this error
System error 2 has occurred.
The system cannot find the file specified.
This is the code that does the encryption. I know its not safe to use a static key but its just for testing.
void encrypt_aes_ctr(unsigned char* key, unsigned char* iv, unsigned char* data, unsigned char* out_data,int in_len,int*out_len)
{
int len;
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();;
EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv);
EVP_EncryptUpdate(ctx, out_data, out_len, data, in_len);
EVP_EncryptFinal_ex(ctx, out_data + *out_len, &len);
*out_len += len;
}
And this is the call I make in the SwapPreWriteBuffers function
unsigned char key[32] = { 1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8 };
unsigned char iv[16] = { 1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4 };
int len;
encrypt_aes_ctr(key, iv, origBuf, newBuf, writeLen, &len);
You should use CNG API which is Microsoft Standard API For Crypto.
for example for Encrypt
Here is Code Example that use BCrypt in Kernel (Random)

What default parameters uses OpenSSL -pbkdf2?

I encrypted a file with this command:
openssl enc -aes-192-cbc -e -pbkdf2 -in <infile> -out <outfile> -pass pass: <password>
Now I'm trying to do decrypt it in c and to take advantage of pbkdf2 I'm using the function:
int PKCS5_PBKDF2_HMAC (const char * pass, int passlen,
                        const unsigned char * salt, int saltlen, int iter,
                        const EVP_MD * digest,
                        int keylen, unsigned char * out);
But the problem is: I know the parameters pass, passlen, keyless and *out...
How do I know what are the parameters for the salt, iter and digest that correspond to the command written above?
The openssl enc command is not a straight encryption of the input file. It adds a "magic" value on the front along with the salt. The magic value is the string "Salted__" (note the double underscore) followed by 8 bytes which is a randomly generated salt. Alternatively you can specify your own salt on the command line with the "-S" option (specified in hex). You can specify the digest to use with the "-md" argument. The default is sha256. You can specify the number of iterations with the "-iter" argument. The default is 10000.

aes-gcm using libgcrypt api in C

I'm playing with libgcrypt (v1.6.1 on Gentoo x64) and i've already implemented (and tested thorugh the AEs test vectors) aes256-cbc and aes256-ctr. Now i am looking at aes256-gcm but i have some doubt about the workflow. Below there is a skeleton of a simple encryption program:
int main(void){
unsigned char TEST_KEY[] = {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4};
unsigned char TEST_IV[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
unsigned char TEST_PLAINTEXT_1[] = {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a};
unsigned char cipher[16] = {0};
int algo = -1, i;
const char *name = "aes256";
algo = gcry_cipher_map_name(name);
gcry_cipher_hd_t hd;
gcry_cipher_open(&hd, algo, GCRY_CIPHER_MODE_GCM, 0);
gcry_cipher_setkey(hd, TEST_KEY, 32);
gcry_cipher_setiv(hd, TEST_IV, 16);
gcry_cipher_encrypt(hd, cipher, 16, TEST_PLAINTEXT_1, 16);
char out[33];
for(i=0;i<16;i++){
sprintf(out+(i*2), "%02x", cipher[i]);
}
out[32] = '\0';
printf("%s\n", out);
gcry_cipher_close(hd);
return 0;
}
In GCM mode there want also these instruction:
gcry_cipher_authenticate (gcry cipher hd t h , const void * abuf , size t abuflen )
gcry_error_t gcry_cipher_gettag (gcry cipher hd t h , void * tag , size t taglen )
So the correct workflow of the encryption program is:
gcry_cipher_authenticate
gcry_cipher_encrypt
gcry_cipher_gettag
But what i haven't undestood is:
abuf is like a salt? (so have i to generate it using gcry_create_nonce or similar?)
If i want to encrypt a file, void *tag is what i have to write to the outfile?
1) gcry_cipher_authenticate is for supporting authenticated encryption with associated data. abuf is data that you need to authenticate but do not need to encrypt. For example, if you are sending a packet, you might want to encrypt the body, but you must send the header unencrypted for the packet to be delivered. The tag generated by the cipher will provide integrity for both the encrypted data and the data sent in plain.
2) The tag is used after decryption to make sure that the data has not been tampered with. You append the tag to the encrypted text. Note, that it is computed on encrypted data and associated (unencrypted) data, so you will need both when decrypting.
You can check these documents for more information on GCM:
http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
Also, you can probably get faster answers to cryptography questions like this on http://crypto.stackexchange.com.

OpenSSL RSA encryption with no padding fails to encrypt correctly

We're trying to perform RSA encryption using the "RSA_public_encrypt()" method (openSSL on Symbian), but we're not quite succeeding.
The encryption itself succeeds, but the encrypted text (which we try to match to a hash) isn't what it should be
(on other platforms, we checked this were we now it is working correctly and we get different values here).
We think this is probably due to the input which isn't provided in the correct format to the "RSA_public_encrypt()" method.
The code:
#define RSA_E 0x10001L
void Authenticator::Test(TDesC8& aSignature, TDesC8& aMessage) {
RSA *rsa;
char *plainkey;
char *cipherkey;
int buffSize;
// create the public key
BIGNUM * bn_mod = BN_new();
BIGNUM * bn_exp = BN_new();
const char* modulus2 = "137...859"; // 309 digits
// Convert to BIGNUM
int len = BN_dec2bn(&bn_mod, modulus2); // with 309 digits -> gives maxSize 128 (OK!)
BN_set_word(bn_exp, RSA_E);
rsa = RSA_new(); // Create a new RSA key
rsa->n = bn_mod; // Assign in the values
rsa->e = bn_exp;
rsa->d = NULL;
rsa->p = NULL;
rsa->q = NULL;
int maxSize = RSA_size(rsa); // Find the length of the cipher text
// maxSize is 128 bytes (1024 bits)
// session key received from server side, what format should this be in ???
plainkey = "105...205"; // 309 digits
cipherkey = (char *)malloc(maxSize);
memset(cipherkey, 0, maxSize);
if (rsa) {
buffSize = RSA_public_encrypt(maxSize, (unsigned char *) plainkey, (unsigned char *) cipherkey, rsa, RSA_NO_PADDING);
unsigned long err = ERR_get_error();
free(cipherkey);
free(plainkey);
}
/* Free */
if (rsa)
RSA_free(rsa);
}
We have a plain key which is 309 characters, but the maxSize is 128 bytes (RSA_NO_PADDING, so input has to be equal to modulus size),
so only the first 128 characters are encrypted (not really sure if this is correct?), which is probably why
our encrypted text isn't what it should be. Or is there something else we don't do correctly ?
What is then the correct way of transforming our plain text so all data of 'plainkey' is encrypted ?
We also tried this with a hexadecimal string '9603cab...' which is 256 characters long, but didn't succeed.
So if correctly converted into bytes (128) could this be used and if yes, how would that conversion work?
Any help is greatly appreciated, thanks!
Take a look at this file, maybe it helps: https://gist.github.com/850935

Resources