Different AES implementations don't agree - javascript

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.

Related

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.

C - Encrypt and decrypt a string with AES

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.

Encrypt AES-GCM in JavaScript, decrypt in Java

We are using the SJCL (Stanford Javascript Crypto Library) to encrypt in JavaScript and we are supposed to implement decryption in Java.
The encryption code looks like this:
<script src='https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.7/sjcl.js'></script>
<script>
var keyString = '2d73c1dd2f6a3c981afc7c0d49d7b58f';
var key = sjcl.codec.base64.toBits(keyString);
var cipher = new sjcl.cipher.aes(key)
var data = sjcl.codec.utf8String.toBits('Hello Crypto!');
var salt = sjcl.codec.base64url.toBits('kLME6vN-WdU_W9XVN9a1Z3E_p8HQ5C7X1La-3ZjEml1ytVRMfvtEXzeapbce2LjFI1dHEGtWv9bZ_U6K2CG1-K4lQPunFXWxXmsTQIXlGfwmpveg2AFeLFiqGmALnfbP');
var encrypted = sjcl.encrypt(keyString,'Hello Crypto!',{mode:'gcm',salt:salt});
console.log(encrypted)
</script>
The resulting encryption looks like the following:
{
"iv":"+xmg3yZF/LSWNFpXf03wUw==",
"v":1,
"iter":10000,
"ks":128,
"ts":64,
"mode":"gcm",
"adata":"",
"cipher":"aes",
"salt":"kLME6vN+WdU/W9XVN9a1Z3E/p8HQ5C7X1La+3ZjEml1ytVRMfvtEXzeapbce2LjFI1dHEGtWv9bZ/U6K2CG1+K4lQPunFXWxXmsTQIXlGfwmpveg2AFeLFiqGmALnfbP",
"ct":"Nq+9tXfc0zs0/m3OfDp0MmTXc9qD"
}
We were supplied with sample Java code to decrypt the code but this code assumes that IV and salt is the same thing so it won't work with the JSON that we are getting from the JS lib (having IV and salt as two separate values):
final byte[] symKeyData = DatatypeConverter.parseHexBinary(keyHex);
final byte[] ivData = ivSalt.getBytes(Charset.forName("UTF-8"));
final byte[] encryptedMessage = DatatypeConverter.parseBase64Binary(encryptedMessageString);
final Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "BC");
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
final IvParameterSpec iv = new IvParameterSpec(ivData);
cipher.init(Cipher.DECRYPT_MODE, symKey, iv);
final byte[] encodedMessage = cipher.doFinal(encryptedMessage);
final String message = new String(encodedMessage, Charset.forName("UTF-8"));
Can anybody explain how the resulting JSON from the JavaScript library can be decrypted correctly in Java?
SJCL convenience libs use PBKDF2 with 1000 iterations to derive the key. The documentation and code can be found here.
However, if this is an assignment:
the IV is the wrong size, it should be 12 bytes, not 16;
the salt size is way too large;
the cipher variable in the JavaScript is entirely ignored;
the iteration count is left at the default, which is normally considered too low.
So I don't know who wrote it, but they should not be releasing such code.
The salt is used for PBKDF2, the IV is for the symmetric cipher, so they are not the same.

Javascript to PHP - RSA encrypt with modulus and exponent

I have code in javascript https://notepad.pw/codeherejs
var c = 'd3bcef1f00424f3261c89323fa8cdfa12bbac400d9fe8bb627e8d27a44bd5d59dce559135d678a8143beb5b8d7056c4e1f89c4e1f152470625b7b41944a97f02da6f605a49a93ec6eb9cbaf2e7ac2b26a354ce69eb265953d2c29e395d6d8c1cdb688978551aa0f7521f290035fad381178da0bea8f9e6adce39020f513133fb';
var f = '10001';
var g = new RSAKey;
g.setPublic(c, f);
var result = g.encrypt(password)
It working fine. And give me result isresult = 1e2738d762382f82f8412b87e9dd9a18aaa52ea28f6b204bb0f1e0f710a973aaa417e533d39127fd1d8959c553ae53dd83738e4eb6544cb77fd08438afd33594c42ff7e5186bd23908b642188b82b7552140af82f7bd5d768770cd9305573640739be4a70bbc2bd190c5a192685ab88c5a612680005eff2f37944c8e24803ea2
I conver the code to PHP, i use phpseclib
$fm_modulus = 'd3bcef1f00424f3261c89323fa8cdfa12bbac400d9fe8bb627e8d27a44bd5d59dce559135d678a8143beb5b8d7056c4e1f89c4e1f152470625b7b41944a97f02da6f605a49a93ec6eb9cbaf2e7ac2b26a354ce69eb265953d2c29e395d6d8c1cdb688978551aa0f7521f290035fad381178da0bea8f9e6adce39020f513133fb';
$fm_exponent = '10001';
$rsa = new Crypt_RSA();
$modulus = new Math_BigInteger(base64_decode($fm_modulus), 256);
$exponent = new Math_BigInteger(base64_decode($fm_exponent), 256);
$rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
$pass_ok = $rsa->encrypt($pass);
PHP return for me the symbol, i don't know how to convert it to plain text (same result in javascript). I try base64, hash sha256 sha512. All false, please help me
$fm_modulus and $fm_exponent aren't base64-encoded nor are they in base-256. They're hex encoded and that's it. So try this:
$rsa = new Crypt_RSA();
$modulus = new Math_BigInteger($fm_modulus, 16);
$exponent = new Math_BigInteger($fm_exponent, 16);
I was able to recognize this by the fact that $fm_exponent is 10001. 10001 is the hex encoding of 65537, which is a pretty common RSA exponent.
I also suspect the padding mode phpseclib is using may not be correct. You might need to do $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_NONE); or $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);.
It actually is somewhat hard to know for sure since you didn't post the private key so the result can't be decrypted. You also didn't post the plaintext of what you're trying to encrypt. As Maarten Bodewes said, with good padding modes, the ciphertext will be different every time you try to encrypt the same plaintext but it's possible your JS library isn't using a good padding mode. phpseclib, by default, uses OAEP, which does have randomized output, but "textbook RSA" doesn't use randomized padding (or any padding, really).

Porting AES decryption from CryptoJS to PyCrypto

Here is a JavaScript part which decodes a string with AES encryption
var p = 'some large string'
var s = 'Q05WTmhPSjlXM1BmeFd0UEtiOGg='
var y = CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Base64.parse(p)
}, CryptoJS.enc.Base64.parse(s), {
iv CryptoJS.enc.Hex.parse("random")
});
var v = y.toString(CryptoJS.enc.Utf8)
I am trying to code a similar decoding function in python with importing AES.
Could anyone help me with this one. I can't figure out all equivalent code for js to python.
I looked up this page
Python AES Decryption Routine (Code Help)
and
AES - Encryption with Crypto (node-js) / decryption with Pycrypto (python)
Not sure if they have the code similar to the js I have here
"y.toString(CryptoJS.enc.Utf8)"
This in python what it means
I have tried something like this from another source
from base64 import b64decode
from Crypto.Cipher import AES
iv = 'random'
key = 'Q05WTmhPSjlXM1BmeFd0UEtiOGg='
encoded = b64decode('some large string')
dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
value = dec.decrypt(encoded)
There are multiple problems with your CryptoJS code and Python code.
Wrong key size
Your key s contains only 20 bytes (160 bit) which doesn't constitute any of the valid key sizes for AES which are 128 (10), 192 (12) and 256 bit (14 rounds). CryptoJS will silently run the key schedule for a 160 bit key with 11 rounds which PyCrypto doesn't support (see AES.c).
You can reduce the key to 128 bit like this in CryptoJS:
var key = CryptoJS.enc.Base64.parse('Q05WTmhPSjlXM1BmeFd0UEtiOGg=');
key.sigBytes = 16;
key.clamp();
or in Python:
key = b64decode('Q05WTmhPSjlXM1BmeFd0UEtiOGg=')[:16]
Wrong character encoding
You forgot to decode the key from a Base64 string in Python and you forgot to decode the IV from hex. The character '0' and the byte 0x00 are entirely different. There's an easier way to define an all zero IV:
iv = "\0"*16
No unpadding
CryptoJS uses PKCS#7 padding by default, but PyCrypto doesn't implement any padding and only handles data as a multiple of the block size. After you decrypt something, you need to remove the padding yourself in Python:
value = value[:value[-1]]
(the last byte determines how many bytes are padding bytes). More on that here.
Other considerations:
You really shouldn't be setting the IV to a static value. The IV should be randomly generated for every encryption using the same key. Otherwise, you will lose semantic security. Since the IV doesn't have to be secret, you can put it in front of the ciphertext and slice it off before decryption.

Categories

Resources