AES difference between iPhone (Objective-c) and Java - java

I have been tearing my hair out all day trying to solve this...
I have an objective-c client running on the iPhone, connecting to a Java server. The iPhone is encrypting data using AES but I cannot decrypt it on the server. I am using a known passphrase and message (single string) and am generating the byte array on the iPhone, generating a comparison byte array on the Java server using the same key and message but the byte arrays are completely different (and hence can't be decoded on the Java side).
The client is using the CommonCrypto library with the following settings...
Data is an NSData holding the word "message" using dataUsingEncoding:NSASCIIStringEncoding
Key is an NSData holding the phrase "1234567891123456" again using the encoding as above.
Algorithm is kCCAlgorithmAES128
Options is kCCOptionsPKCS7Padding (which I believe equates to ECB on the server?!)
The server is using the following code...
byte[] key = "1234567891123456".getBytes();
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal("message".getBytes());
BUT the data in encryptedData does not match that which is being generated in the objective-c code, the byte arrays are completely different.
Can anyone see anything obvious I am doing wrong? I think the settings are all the same... :(
UPDATE - As requested....
Ok so here goes....
iPhone client is encrypting the following string "message"
It uses the key "1234567891123456"
It uses an initialisation vector of "1010101010101010"
It is using AES128, with CBC mode (as far as I can tell) and options of kCCOptionsPKCS7Padding.
The result of the encryption (with base64 encoding) is UHIYllDFAXl81ZM7OZPAuA==
The server is encrypting the same string, with the same key and initialisation vector.
It is using the following Cipher.getInstance("AES/CBC/PKCS5Padding");
The result of the encryption (with base64 encoding) is ALBnFIHysLbvAxjvtNo9vQ==
Thanks.
UPDATE 2 - As requested...
Here is the iPhone code....
NSData *toencrypt = [#"message" dataUsingEncoding:NSASCIIStringEncoding];
NSData *pass = [#"1234567891123456" dataUsingEncoding:NSASCIIStringEncoding];
NSData *iv = [#"1010101010101010" dataUsingEncoding:NSASCIIStringEncoding];
CCCryptorStatus status = kCCSuccess;
NSData *encrypted = [toencrypt dataEncryptedUsingAlgorithm:kCCAlgorithmAES128 key:pass initializationVector:iv options:kCCOptionPKCS7Padding error:&status];
NSString *text = [NSString base64StringFromData:encrypted length:[encrypted length]];
The NSData category for encrypting comes from here...
http://github.com/AlanQuatermain/aqtoolkit/tree/master/CommonCrypto/
Incidentally, I have checked the byte arrays that are in toencrypt, pass and iv and they match those that are on the server.

This is not my area but it looks like on the client you have PKCS7 but on the server you have PKCS5.

I just ran across the exact same problem. I am using CommonCrypto on the iOS client, using settings:
NSData * encrypted = [data dataEncryptedUsingAlgorithm:kCCAlgorithmAES128 key:pass initializationVector:iv options:kCCOptionPKCS7Padding error:&status];
The server uses Cipher.getInstance("AES/CBC/PKCS5Padding"); with the same key and initialization vector as the client.
After banging my head against the wall for the last few hours, I finally followed Jason's advice and checked the dataEncryptedUsingAlgorithm routine and printed out keyData right after FixKeyLengths. It turns out my 128bit key was extended to 192bit with 0s added to the end. After fixing this, everything works properly. :)
Update: My answer was posted almost 2 years ago, and this issue seems to be fixed in the latest NSData+CommonCrypto code. Specifically, this was the part that caused the issue:
static void FixKeyLengths( CCAlgorithm algorithm, NSMutableData * keyData, NSMutableData * ivData )
{
NSUInteger keyLength = [keyData length];
switch ( algorithm )
{
case kCCAlgorithmAES128:
{
if ( keyLength <= 16 )
{
[keyData setLength: 16];
}
else if ( keyLength <= 24 )
{
[keyData setLength: 24];
}
else
{
[keyData setLength: 32];
}
break;
}
The first check keyLength <= 16 wasn't there before.
If you are still experiencing problems now, it's probably something else.

What mode is the iPhone using with AES? You don't list anything, so perhaps that means it's using no chaining (ECB).
However, on the Java side, you are using CBC, but not specifying an initialization vector. That is definitely wrong. If you really are using CBC, you have to have the IV that was used during encryption. The IV is not secret; it can be sent along with the ciphertext.
If you are really using ECB, there is no IV, but your Java specifies the wrong mode.

Based on your samples, the server is doing it right, and the client is not.
Looking at the data, I would guess that the key is wrong. Please show us the iPhone code, especially the code to go from "1234567891123456" to your key.

I recently ran across this in another project. The problem was that the key was one byte too long to fit into the char buffer inside of the dataEncryptedUsingAlgorithm method.
The problem was that the getBytes method on the NSString was soft-failing. It would copy most of the string into the buffer, but then since the key was one byte too long, it would "mark" the operation as failed by setting the first char to NUL (char 0).
Step into that method in Xcode and see what your key char[16] buffer looks like. It may have this same problem, and have the contents { 0, '2', '3', '4', ... }.

Related

Trying to use PyCryptodome AES ECB decryption with offsets

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

AES decrypt in python when key and IV is Java array of bytes

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'

Why can I still decrypt parts of my data after flipping a bit in the ciphertext?

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.

ColdFusion or Java equivalent to PHP encryption/decryption

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.

Encrypt in java, decrypt in node.js

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.

Categories

Resources