As a bit of context to this I am converting a java file to python and am on the last operation. I'm at about 200 LOC so it makes it that much more edge of the seat...
Anyways, in java the operation is:
Cipher cipher = Cipher.getInstance("AES/ecb/nopadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keys[i], "AES"));
//doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)
cipher.doFinal(save, saveOffset, 16, save, saveOffset);
In python I have this:
from Crypto.Cipher import AES
cipher = AES.new(bytes(keys[i]), AES.MODE_ECB)
cipher.decrypt(?????)
This is taken from the package under decrypt():
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
The length must be multiple of the cipher block length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
As you can see .decrypt() doesn't really have an input for offsets, but I was wondering if there was some way around this?
This is why I decided to post on SO, would I be able to send:
temp_bytes = save[offset]
temp_decrypt = cipher.decrypt(temp_bytes)
save[offset] = temp_decrypt
Or when decrypting does it use the whole file as context and i will get the wrong output? I would love to just do it and test it but the output is just gibberish that i will have to write another program to parse (another java to python project).
What ended up working was:
save[saveOffset:saveOffset+16] = cipher.decrypt(save[saveOffset:saveOffset+16])
Did not expect it to work so easily
I have the following 2 values :
AES key it is a Java array of bytes
64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73
IV also a Java array of bytes
50,52,3,36,-90,81,109,-56,24,122,-3,27,-96,56,118,-60
The encryption mode is : AES/CBC/PKCS5PADDING
Now I need to use these 2 values to decrypt my cipher text using python. I am trying to use pycrypto but am open on exploring other options as well.
Now because Java's array of bytes is signed and (it looks like) python (2.7) does not recognize signed array of bytes, I am having trouble doing this decryption.
I did find a bunch of SO discussions around similar topics, but most of them were generating the IV and the key, and not really using an already existing one, for eg.
Encrypt & Decrypt using PyCrypto AES 256
Using a byte array as key for AES algorithm in Python (looked promising but did not help much)
Python PyCrypto encrypt/decrypt text files with AES
So the above solutions have not worked for me, unless I did something fundamentally wrong while tweaking them for my need.
Here's one of my attempts (bits and pieces taken from the SO discussions mentioned above)
from Crypto import Random
from Crypto.Cipher import AES
import base64
key =
[64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73]
iv = [50,52,3,36,-90,81,109,-56,24,122,-3,27,-96,56,118,-60]
aes_key = ''.join(map(lambda x: chr(x % 256), key))
iv_data = ''.join(map(lambda x: chr(x % 256), iv))
def pad(s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def decrypt(ciphertext, key):
ciphertext = base64.b64decode(ciphertext)
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
return plaintext.rstrip(b"\0")
And then calling decrypt() as :
decrypt('HaCiIld192v2QE3/zcuAxhpr39HRXvsHQYSJE/VT9FuWWK7envVDRaaDR6gpmh0zmw09/440O7UAtVb5qBLddZH37TASWUMXAN0Idy7B0VtpgPgMHOLGJG6axWCHgieaTleFDaRKP+z7WN+4fj5RGw==', aes_key)
gives the output as :
Out[9]:
'\xdd\x99\xab\x8bI\xcb\xf0?\xcd\x1c\x1eb\x109\xe8c\xc0\x105\xa6\xdb\xe4\xdb\x08\xc7\xdbHT\x08.\x10\x9e\xe1g\xfe\xd1\xc2\x92\xdbH\x8e\x9bV\xae^\r:\x98%\x8d\x06\xdf\xaf\t\xb5m\xfb\xf3Q+s"CK\xf4\xc2\xe7\xd2\xfd\xfdq\xcc)\xcf\x99&\xffj\xc4\xc1\x1a$ML\xe9\xe7(\xef\x1c\x81vx\xf56'
I tried :
a = b'\xdd\x99\xab\x8bI\xcb\xf0?\xcd`\x1c\x1eb\x109\xe8c\xc0\x105\xa6\xdb\xe4\xdb\x08\xc7\xdbHT\x08.\x10\x9e\xe1g\xfe\xd1\xc2\x92\xdbH\x8e\x9bV\xae^\r:\x98%\x8d\x06\xdf\xaf\t\xb5m\xfb\xf3Q+`s"CK\xf4\xc2\xe7\xd2\xfd\xfdq\xcc)\xcf\x99&\xffj\xc4\xc1\x1a$ML\xe9\xe7(\xef\x1c\x81vx\xf56'
a.encode("hex")
and output is :
'dd99ab8b49cbf03fcd601c1e621039e863c01035a6dbe4db08c7db4854082e109ee167fed1c292db488e9b56ae5e0d3a98258d06dfaf09b56dfbf3512b607322434bf4c2e7d2fdfd71cc29cf9926ff6ac4c11a244d4ce9e728ef1c817678f536'
which is definitely not the plain text I was expecting.
So I need help figuring this out, as to how can I decrypt (using Python 2.7) an AES encrypted cipher text given the key and IV as Java array of bytes?
As per suggestions in the comments below (#MaartenBodewes thanks) I tried this :
So, I checked the base64 of the bytearray, key, in java as :
byte arr[] = new byte[] {64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73};
String base64 = new String(Base64.encodeBase64(arr));
and then to achieve the same in python did some changes to python code above to do the same as : (I realized that java treats a byte array as an array of signed 2s complement data, from an excellent discussion at Java byte array contains negative numbers, and so had to accommodate for that in python)
key = [64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73]
b64encode(bytearray([(lambda x:int((bin(x & 0xff)),2))(x) for x in key]))
and both these resulted in the same output as :
QEO/WO2K8MuvniytUqZ8cIgqXENoFywIAqCW5DljTEk=
And I thought that not that I am sure that both these keys (Java and python) are the same, the decryption should work. And hence I tried once again:
decrypt('HaCiIld192v2QE3/zcuAxhpr39HRXvsHQYSJE/VT9FuWWK7envVDRaaDR6gpmh0zmw09/440O7UAtVb5qBLddZH37TASWUMXAN0Idy7B0VtpgPgMHOLGJG6axWCHgieaTleFDaRKP+z7WN+4fj5RGw==', str(aes_key))
And I got the exact same output as earlier:
'\xdd\x99\xab\x8bI\xcb\xf0?\xcd\x1c\x1eb\x109\xe8c\xc0\x105\xa6\xdb\xe4\xdb\x08\xc7\xdbHT\x08.\x10\x9e\xe1g\xfe\xd1\xc2\x92\xdbH\x8e\x9bV\xae^\r:\x98%\x8d\x06\xdf\xaf\t\xb5m\xfb\xf3Q+s"CK\xf4\xc2\xe7\xd2\xfd\xfdq\xcc)\xcf\x99&\xffj\xc4\xc1\x1a$ML\xe9\xe7(\xef\x1c\x81vx\xf56'
I am using in my application AES algorithm to encrypt my data. My key is of 256 bit. The encrypted token formed is of this sort:
pRplOI4vTs41FICGeQ5mlWUoq5F3bcviHcTZ2hN
Now if I change one bit of the token alphabet from upper case to lower case say some thing like this:
prplOI4vTs41FICGeQ5mlWUoq5F3bcviHcTZ2hN
Some part of the token is getting decrypted along with junk value. My concern is why even some part of the data is getting visible when as such one bit is changed.My code for encryption is as follows:
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Key secretKeySpecification = secretKeyData.getKey();
cipher.init(
Cipher.ENCRYPT_MODE,
secretKeySpecification,
new IvParameterSpec(secretKeyData.getIV().getBytes("UTF-8")));
byte[] bytesdata = cipher.doFinal(data.getBytes());
String encodedData = new BASE64Encoder().encode(bytesdata)
My code for decryption is:
Key secretKeySpecification = decryptionKeyDetails.getKey();
cipher.init(Cipher.DECRYPT_MODE, secretKeySpecification,
new IvParameterSpec(decryptionKeyDetails.getIV()
.getBytes("UTF-8")));
byte[] bytesdata;
byte[] tempStr = new BASE64Decoder()
.decodeBuffer(splitedData[0]);
bytesdata = cipher.doFinal(tempStr);
return new String(bytesdata);
Ciphertext modes of operation have specific forms of error propagation. There is such as thing as Bi-IGE (Bi-directional Infinite Garble Extension, that does change the whole plaintext if any error is introduced. However, it requires more than one pass, and it still won't protect you from getting random data if a bit was changed.
In the end, listen to Oleg and Codes (and Wikipedia and even me) and add an authentication tag to your ciphertext. Validate the authentication tag (e.g. HMAC) before decryption. Don't forget to include other data in your protocol such as the IV, or you may have a plaintext for which the first block has been changed.
I have an API spec that reads:
Encryption Algorithm
The API utilizes the AES-128 (also known as Rijndael-128) algorithm with a 192-bit key in
CBC mode for encryption and decryption of sensitive pieces of information – the password parameter in the user/signin and user/signup methods, the authentication token, etc. The steps of the algorithm are listed below:
Encryption
Pad the input data so that its size is a multiple of the encryption algorithm block size – 16 bytes. In case the length of input data is a multiple of 16, a block of additional 16 bytes needs to be appended. The value of each pad byte is the number of pad bytes as an 'unsigned char'. That is, the last byte of the padded data should always be between 0x01 and 0x10.
Generate a 16-byte long initialization vector (IV) for the encryption algorithm.
Encrypt the padded data using AES-128 with the EK and the generated IV.
Combine the IV with the encrypted data.
Encode the result with urlsafe Base64. The urlsafe Base46 alphabet uses '–' instead of '+' and '_' instead of '/'.
Decryption
Base64-decode the input data.
Extract the first 16 bytes – these are the IV for the AES algorithm.
Decrypt the data using AES-128 with the EK and IV.
Read the value of the last byte of the decrypted data and remove that many bytes off its tail.
The only example provided by the supplier of this API is in PHP, using mcrypt. I know absolutely nothing about PHP, and am not an encryption expert. I need to be able to represent the above algorithm using ColdFusion 10.
I started by trying to take the example PHP files and find equivalents in either the ColdFusion tag or function library, and then by looking for a Java library with the same interface. I just don't know enough to make this happen.
Is there someone here that can point me in the right direction, or work with me offline to assist?
EDIT:
Here's the example given, for the basic task of doing a "check" on the keys (partner key and encryption key) provided to me for use with the API.
Object Client.php, has this constructor:
public function __construct($hostApiUrl, $partnerKey, $encryptionKey, $token = null)
{
$this->_pk = $partnerKey;
$this->_ek = $encryptionKey;
$this->_crypt = new Crypt($encryptionKey);
$this->_url = rtrim($hostApiUrl, '/') . self::BASE_URL;
if ($token) {
$this->setUserSession($token);
}
}
and this is the function I'm attempting to use:
public function checkKeys()
{
$secret = $this->_encodeParam($this->_ek);
$result = $this->call('partner/checkkeys', array(
'secret' => $secret
));
if (!$result || !$this->_isCodeOk($result->code)) {
return false;
}
return true;
}
So the client object already has the partner key and encryption key when this method is called, obviously.
so the "secret" is created by "encoding" the encryption key provided, using _encodeParam() method. that looks like this:
protected function _encodeParam($secret)
{
$secret = "{$secret}:{$this->_pk}";
return $this->_crypt->encrypt($secret);
}
so the secret is appended with the partner key. and then encrypted using this method in the crypt object (AES_BLOCK_SIZE is set as 16):
public function encrypt($data)
{
$pad = self::AES_BLOCK_SIZE - strlen($data) % self::AES_BLOCK_SIZE;
$data .= str_repeat(chr($pad), $pad);
if (stristr(PHP_OS, 'win') !== false) {
$random_source = MCRYPT_RAND;
} else {
$random_source = MCRYPT_DEV_URANDOM;
}
$iv = mcrypt_create_iv(self::AES_BLOCK_SIZE, $random_source);
mcrypt_generic_init($this->_td, $this->_key, $iv);
$data = $iv . mcrypt_generic($this->_td, $data);
mcrypt_generic_deinit($this->_td);
return self::urlsafe_b64encode($data);
}
this is returned back to the above checkKeys() function which sends the request to the API, which then returns a response. That actual API call is a POST which is easy enough to generate of course, but all those encryption hoops, including the MCRYPT library calls, are where I get stuck trying to determine the equivalent in CF10 or Java.
If I were to get an example thus far, I think I'd stand a chance of replicating the other functions in the crypt object (the ones that are even necessary, which may not be, since some may be built right into the CF encrypt() and decrypt() functions). This seems like a reasonable starting point, however.
I need to encrypt in java and decrypt with node.js. The decryption result is corrupted.
Here is the java code:
public String encrypt(SecretKey key, String message){
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] stringBytes = message.getBytes("UTF8");
byte[] raw = cipher.doFinal(stringBytes);
// converts to base64 for easier display.
BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(raw);
return base64;
}
Here is the node.js code:
AEse3SCrypt.decrypt = function(cryptkey, encryptdata) {
encryptdata = new Buffer(encryptdata, 'base64').toString('binary');
var decipher = crypto.createDecipher('aes-128-cbc', cryptkey);
decipher.setAutoPadding(false);
var decoded = decipher.update(encryptdata);
decoded += decipher.final();
return decoded;
}
As a key I use: "[B#4ec6948c"
The jave encrypted result is: "dfGiiHZi8wYBnDetNhneBw=="<br>
The node.js result is garbich....
In java I use "PKCS5Padding". What should be done in node.js regarding padding? I made setAutoPadding(false). If I don't do it I get error decipher fail. (only from node.js version 0.8).
I tried to remove utf8 encoding from the java in order to be complementary with the node.js but it didn't work.
Any idea what is wrong?
As a key I use: "[B#4ec6948c"
That sounds very much like you're just calling toString() on a byte array. That's not giving you the data within the byte array - it's just the default implementation of Object.toString(), called on a byte array.
Try using Arrays.toString(key) to print out the key.
If you were using that broken key value in the node.js code, it's no wonder it's giving you garbage back.
I tried to remove utf8 encoding from the java in order to be complementary with the node.js
That's absolutely the wrong approach. Instead, you should work out how to make the node.js code interpret the plaintext data as UTF-8 encoded text. Fundamentally, strings are character data and encryption acts on binary data - you need a way to bridge the gap, and UTF-8 is an entirely reasonable way of doing so. The initial result of the decryption in node.js should be binary data, which you then "decode" to text via UTF-8.
I'm afraid I don't know enough about the padding side to comment on that.