C - Encrypt and decrypt a string with AES - javascript

I'm trying to understand how to use this c library (tiny-AES-c). As a web developer, I'm looking to get an equivalent C code for this JS fiddle.
The JS code is straightforward:
// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123');
console.log("Encrypted: " + ciphertext.toString());
// Decrypt
var bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123');
var plaintext = bytes.toString(CryptoJS.enc.Utf8);
console.log("Decrypted: " + plaintext);
Given a message to encrypt and a secret, the code generates the encrypted data and transform the results to a string.
My C code:
int main()
{
uint8_t in[] = "my message";
uint8_t key[] = "secret key 123";
struct AES_ctx ctx;
AES_init_ctx(&ctx, key);
printf("ORIG: %s",(char*) in);
// Encrypt
AES_ECB_encrypt(&ctx, in);
printf("\nENC: %s",(char*) in);
// Decrypt
AES_ECB_decrypt(&ctx, in);
printf("\nDEC: %s",(char*) in);
return 0;
}
The output:
ORIG: my message
ENC: ̤�+��5<n]EYK�ظ/����
DEC: my message%
I understand that I shouldn't try to print the result as a string, but couldn't figure out how to get similar (to the JS) results, using the tiny-AES-c API, plus when I tried using longer messages I got strange results, leading me to think I'm using this library the wrong way.
Q: What would be the C code equivalent to the above JS?

I am the original author of the AES library you reference.
When using ECB and CBC modes of operation, you need to make sure your key, iv and the input/output blocks are all 16 bytes long. You also need to decide on which padding scheme you want to use.
You can use CTR-mode to avoid padding and stop worrying about block-sizes. This generally makes the AES algorithm much easier to use as there are fewer edge-cases to handle.
BTW this is also stated in the project README:
No padding is provided so for CBC and ECB all buffers should be mutiples of 16 bytes. For padding PKCS7 is recommendable.
ECB mode is considered unsafe for most uses and is not implemented in streaming mode. If you need this mode, call the function for every block of 16 bytes you need encrypted. See wikipedia's article on ECB for more details.
EDIT:
If you extend your arrays so that they are 16 bytes long and zero-pad them (or alternatively, declare them static so they will be zero-initialized automatically), I think it should work for you :)

uint8_t in[16] = "my message";
your buffer needs to be a multiple of 16.

As #Morten Jensen suggested, you can use the CTR-mode IE:AES_CTR_xcrypt_buffer
int main()
{
uint8_t key[] = "secret key 123";
uint8_t in[] = "my message";
uint8_t iv[16] = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
printf("Length: %lu",strlen((char*)in));
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CTR_xcrypt_buffer(&ctx, in, strlen((char*)in));
printf("\nENC: %s",(char*) in); // don't use this string as an input
AES_init_ctx_iv(&ctx, key, iv);
AES_CTR_xcrypt_buffer(&ctx, in, strlen((char*)in));
printf("\nDEC: %s",(char*) in);
return 0;
}
Remember, printing the encrypted data is wrong, you should go over the output and convert it to base64 if you want to match your JS example.

Related

Implement C# encryption in CryptoJS

I have situation where I need to create the same encryption method which is already up and running in C#. The concept behind this is, from where ever this encrypted key is logged, we will use the same C# project to decrypt it.
Below is the logic used in C#:
using var aes = new AesCryptoServiceProvider
{
Key = Encoding.UTF8.GetBytes(key),
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
aes.GenerateIV();
using var encrypter = aes.CreateEncryptor(aes.Key, aes.IV);
using var cipherStream = new MemoryStream();
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
cipherStream.Write(aes.IV);
tBinaryWriter.Write(Encoding.UTF8.GetBytes(encryptMe));
tCryptoStream.FlushFinalBlock();
}
return Convert.ToBase64String(cipherStream.ToArray());
Key is the same key used in both C# and JavaScript. But still I am not able to generate the same encryption value as in C#.
I tried to go through other Stack Overflow posts related to this topic, but unable to figure the missing part in JavaScript. Can any one please help?
The key used in the C# code is UTF-8 encoded, so on the CryptoJS side the key must be parsed into a WordArray using the UTF-8 encoder (CryptoJS only interprets the key material as key if it is passed as a WordArray; if it is passed as string, it is interpreted as password and a key derivation function is applied, which would not be compatible with the C# code).
Also, the C# code concatenates IV and ciphertext, which must also happen in the CryptoJS code. This is necessary because the IV is required for decryption.
Fixed code:
var plaintext = 'The quick brown fox jumps over the lazy dog';
var key = CryptoJS.enc.Utf8.parse('01234567890123456789012345678901'); // Fix 1: parse as WordArray
var iv = CryptoJS.lib.WordArray.random(128 / 8);
var encrypted = CryptoJS.AES.encrypt(plaintext, key, {iv: iv}); // CBC, PKCS#7 padding by default
var ivCiphertext = iv.clone().concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64); // Fix 2: concatenate IV and ciphertext
console.log(ivCiphertext); // e.g. e9iXcQ2sZ6AA2ne1c3490pAPWOrTGf4UttSSX1lOiKUqwP0oWRPFF83VhZQZMMBu9JKNWIfgS+9D5V39bI4rqg==
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
For a test, the ciphertexts cannot simply be compared because, due to the random IV, each encryption produces different ciphertexts.
One option for a test is to temporarily use the same IV in the C# code and in the CryptoJS code for the test (and only for the test, since a static IV is insecure!), which would produce the same ciphertexts that could then be compared.
Another option for a test is to decrypt the ciphertext produced with the CryptoJS code with the C# code for decryption.

Ruby OpenSSL to node.js Crypt conversion

I have been struggling with this for a couple of days and was wondering if anyone would have the experience to know these two encryption libraries well enough to help.
I am currently creating a SSO payload according to instructions given to me by a vendor. The steps to have this created are highlighted as follows:
Create an AES 256 CBC cypher of the payload
i. The key will be a SHA256 digest of the site token.
2. Base64 encode the initialization vector (IV) and encrypted payload from above
3. CGI-escape the output from step 2.
4. Your final payload would look something like ikUbqiutwMhi%2Bjg6WwUHyeZB76g6LdLGcrKrEV4YpvQ%3D%0A.
SHA256 will always generate a 32-byte hash, but it can’t be displayed nicely in Base64. When it’s displayed as Hex, it is 32 pairs of Hex values (a total of 64 characters on the screen) but representing only 32 bytes.
I was able to get it to work on Ruby with Open SSL, the code is:
require 'digest'
require 'openssl'
require "base64"
require 'cgi'
require 'json'
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
cipher.key = Digest::SHA256.digest(siteToken)
iv = cipher.random_iv
data= unencryptedPayload
encrypted = cipher.update(JSON.generate(data)) + cipher.final
encoded = CGI::escape(Base64.encode64(iv + encrypted))
puts encoded
However, I have not yet had luck with Node.js's Crypto library. This is what I have so far:
const crypto = require('crypto');
// Defining algorithm
const algorithm = 'aes-256-cbc';
// Defining key
//'key' variable is defined and equal to siteToken in the OpenSSL version
//const key = siteToken;
// Defining iv
const iv = crypto.randomBytes(16);
// An encrypt function
function encrypt(text) {
// Creating Cipheriv with its parameter
let cipher = crypto.createCipheriv(
'aes-256-cbc', Buffer.from(key), iv);
// Updating text
let encrypted = cipher.update(text);
// Using concatenation
encrypted = Buffer.concat([encrypted, cipher.final()]);
// Returning iv and encrypted data
return { iv: iv.toString('hex'),
encryptedData: encrypted.toString('hex') };
}
// Displays output
var output = encrypt(unencryptedPayload);
I think my code has so far covered almost all of these except for the SHA256 digest of the site token. Does anyone know how I might achieve this in Node.js terms?
Thanks!

AES ECB Mode giving different results when using different languages

I am trying to pass an AES encrypted string from a python script into a nodejs script, using ECB mode. The code used is:
To start, I use pycryptodome to encrypt a string into AES
from Crypto.Cipher import AES
key = b'ipu9TUv54yv]isFMh5#;t.5w34E2Ry#{'
cipher = AES.new(key, AES.MODE_ECB)
print(cipher.encrypt(b"foobar "))
This gives me the string \xb0\x07\x93\xf3\x02\xd0\x87\xa4\xaek\x1bS\xccg\xa4H.
However, when i try to reverse the effect with Javascript:
var crypto = require('crypto')
let key = Buffer.from('ipu9TUv54yv]isFMh5#;t.5w34E2Ry#{');
let decipher = crypto.createDecipheriv("aes-256-ecb", key, '');
let result = decipher.update(Buffer.from('\xb0\x07\x93\xf3\x02\xd0\x87\xa4\xaek\x1bS\xccg\xa4H'));
console.log(result.toString())
It gives me a completely different result from the original text: �k��gR�O
Is there something that I am missing that is changing the way that it decrypts?
There are two core issues:
On the node side, you're treating the output of Python as if it's a UTF-8 string. Node will treat it as a UTF-8 string, and the resulting bytes that make up the Buffer are going to be wrong. Dump it out, you'll see it's a 25 byte buffer, not what you intended.
Once you fix that, you'll find the second issue. The crypto library expects padding bytes, even if the only block is exactly the block size. To fix this, always add padding to the plaintext.
So, the encrypt changes to this:
from Crypto.Cipher import AES
key = b'ipu9TUv54yv]isFMh5#;t.5w34E2Ry#{'
cipher = AES.new(key, AES.MODE_ECB)
# Don't need to ensure the plain text is exactly block-size anymore
data = b'foobar'
# Pad it, regardless of it's size
length = 16 - (len(data) % 16)
data += bytes([length]) * length
# And encode the encrypted text. Using hex here, it's easy, though
# often base64 is used
print(cipher.encrypt(data).hex())
And decoding in Node:
var crypto = require('crypto')
let key = Buffer.from('ipu9TUv54yv]isFMh5#;t.5w34E2Ry#{');
let decipher = crypto.createDecipheriv("aes-256-ecb", key, '');
// Using the hex encoding, let Buffer decode it
let result = decipher.update(Buffer.from('bf8242c6046ad5cb47e733dca4d487f1', 'hex'));
// Make sure to give decipher a chance to operate on the final block
result += decipher.final();
console.log(result.toString())
This outputs foobar as expected.

Different AES implementations don't agree

I have to encrypt a piece of data using both C#, and Javascript. I'm using Bouncy Castle in C# and Crypto-JS in Javascript.
The problem I am facing is that even though all the parameters are equal (OFB with no padding, and the IV is always 0), I don't get the same output with both libraries. The consequence of that is also that I can't decrypt with one what was encrypted with the other.
Here is my C# code for encrypting:
byte[] iv = new byte[16];
BufferedBlockCipher aes = new BufferedBlockCipher(new OfbBlockCipher(new AesEngine(), 16));
ParametersWithIV ivAndKey = new ParametersWithIV(new KeyParameter(stretchedKey), iv);
aes.Init(true, ivAndKey);
int minSize = aes.GetOutputSize(privateKey.Length);
byte[] outBuf = new byte[minSize];
int length1 = aes.ProcessBytes(privateKey, 0, privateKey.Length, outBuf, 0);
int length2 = aes.DoFinal(outBuf, length1);
byte[] encryptedKey = iv.Concat(outBuf.Take(length1 + length2)).ToArray();
My Javascript code is the following for encrypting (try it on JSFiddle here: http://jsfiddle.net/gCHAG/424/):
var key = Crypto.util.hexToBytes('59b50e345cab8b6d421b161918ea3fbd7e5921eea7d43d1ac54fa92cca452bb5');
var iv = Crypto.util.hexToBytes('00000000000000000000000000000000');
var message = Crypto.util.hexToBytes('3b16601d0a7e283c1f24d30ec214676885096cb0bbf3998012a2be87c5a58d89');
var encrypted = Crypto.AES.encrypt(message, key, { iv: iv, asBytes: true, mode: new Crypto.mode.OFB(Crypto.pad.NoPadding) });
I get the following from the bouncy castle implementation: 578934dbb576dc986a531f09e8d5abd5b01dc1bfd3ededd222ff8aa6e4bfdbf2
And the following from Crypto-JS: 578946591ce2d787cbe41bec77a58dac66e6007fb722b1af847ecc3bf4212cea
Note how the first two bytes are the same, but then everything else is different.
To top it all up, when trying on an online tool, I get a third output (see http://aes.online-domain-tools.com/link/bd243g1VXbD7LUAS/): 57804D64A8...
I went through everything several times, but I don't see why I get different outputs.
CryptoJS seems to use an output of 128 bits per block for the key stream. You specify 16 bits per block for Bouncy. As 8 or 128 bits per block are common for OFB, and since 128 is the recommended output size, I guess you are just confusing bits and bytes in the Bouncy code.
If you specify new OfbBlockCipher(new AesEngine(), 128) you should be OK.

Encrypting in CryptoJS and decrypting in mCrypt not working properly

For the past 2 days I have been struggling with what I first thought would be a breeze to implemenent.
I am in need of a very simple and non-secure way to send a cipher thru AJAX and decrypt it server-side
I am using AES from CryptoJS:
JS encryption
msg = "message";
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt(msg, key, { iv: iv });
var data_base64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
var iv_base64 = encrypted.iv.toString(CryptoJS.enc.Base64);
var key_base64 = encrypted.key.toString(CryptoJS.enc.Base64);
console.log(data_base64,iv_base64,key_base64) //If I use these keys in the PHP decryption it works
return encrypted.toString();
...and PHP decryption using mCrypt
$encrypted = "f82126a59b76d86946a013d9f575d0d4"; //this is what the JS function above returned.
$key = "000102030405060708090a0b0c0d0e0f"; //same key as in JS function
$iv = "101112131415161718191a1b1c1d1e1f"; //same IV as in JS function
$plaintext = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv ), "\t\0 " );
echo "Original string : " . $encrypted . "<br />\n";
echo "Decrypted string : " . $plaintext . "<br />\n";
Since I am using the same IV's and key's to encrypt I would expect that this would work decrypt just fine. However I seem to be missing something since I still see gibberish in the plaintext.
EDIT:
Well it seems that the whole purpose of AES is that the keys and
IV's I must use are output by CryptoJS (see the console.log's in
the JS function). If I use those the function seems to run just fine.
But I don't want that actually since those keys are dynamically
generated thus each time I ran the JS function they change. I just
need a shared private-key between client and server that is used to
encrypt/decrypt and is static. As simple as that.
Your key is 128 bit (the length of $key is 32 hexadecimal characters, which means 16 byte or 128 bit). However, in your mcrypt_decrypt() call you're telling PHP to use MCRYPT_RIJNDAEL_256 with a 256-bit key. Try using MCRYPT_RIJNDAEL_128 instead. Normally ciphers should adapt to the length of the passed key, but it could be that PHP is padding the key with null bytes to use 256-bit encryption.
Secondly, in PHP mcrypt_decrypt is set to use the CBC mode (see MCRYPT_MODE_CBC). You don't specify which mode CryptoJS should use. Luckily for you, according to docs for CryptoJS, the CBC mode is the one used by default; however, since you're writing portable code you should consider making that explicit.
edit
If it tells you that the key is too long, it's because you're not packing them. You're giving PHP an hex-encoded string long 32 bytes (256 bit), which is not your key! To get the binary data you need to do:
$key = pack('H*', "000102030405060708090a0b0c0d0e0f");
$iv = pack('H*', "101112131415161718191a1b1c1d1e1f");
the pack('H*', $str); function converts an hex representation to the binary string.
MCRYPT_RIJNDAEL_256 is not the same as AES. The 256 relates to the block size of the cipher, not the key size.
Quick history lesson - AES was a competition that was eventually won by an algorithm called Rijndael. Rinjdael is a cipher defined for several block sizes (128, 160, 192, 224 and 256 bits). However, for AES only the 128-bit block size was selected.
The block size defines the size of the IV, when you use a mode that needs an IV (like CBC-mode). So for AES, you'll always need a 128-bit IV, regardless of key size. The supported key sizes are 128, 192 or 256 bits.
In PHP, one can use AES by using the cipher MCRYPT_RIJNDAEL_128.

Categories

Resources