I'm encrypting the string "aaaaaaaaaaaaaaaa" (a 16 byte UTF8 string) using AES 128 CBC with a blank iv and key (16 0's) and getting different results
in PHP:
echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,pack("H*", "00000000000000000000000000000000"),"aaaaaaaaaaaaaaaa",MCRYPT_MODE_CBC,pack("H*", "00000000000000000000000000000000")))
returns "kmwP6gWv1l9ZMdKanGs/nA=="
in Node.js:
let cipher = require('crypto').createCipheriv('aes-128-cbc',Buffer.alloc(16),Buffer.alloc(16))
console.log(cipher.update('aaaaaaaaaaaaaaaa','utf8','base64') + cipher.final('base64'))
returns "kmwP6gWv1l9ZMdKanGs/nHeUidae8Z4dK0HU7p2z+1c="
the first bit (bolded) is identical to PHP, but the PHP value has an extra 'A=' and then Node value has a whole extra 'HeUidae8Z4dK0HU7p2z+1c'
I'll admit that I'm pretty shaky on what's going on here - what am I missing here?
edit ... but not so shaky that I don't understand that what I'm doing here isn't particularly secure. Don't worry about whether this is the 'right' way to do encryption - please focus on the fact that the results ought to line up.
edit2 - I can, however, get close using hex rather than base64 -
PHP: 926c0fea05afd65f5931d29a9c6b3f9c
Node: 926c0fea05afd65f5931d29a9c6b3f9c779489d69ef19e1d2b41d4ee9db3fb57
the second chunk of Node hex is returned by the .final method, and I don't understand what it's for.
AES works on a block size of 16 bytes. You are encrypting the string "hello world", which is 11 bytes long in UTF8. Thus, padding is used to increase the length of your string to 16 bytes.
Node, and as it should, uses PKCS5 Padding to pad your plain-text to 16 bytes and then encrypts it.
mcrypt, as it shouldn't, uses zero bytes to pad your plain-text. mcrypt is deprecated and has been abandoned for a decade.
Because your two padding schemes are different, the plain-text is actually different before we even get to the point where we apply AES.
My advice: use the openssl_* functions in PHP instead. And don't use a static IV. Using a static IV makes your program vulnerable to some of the same vulnerabilities as ECB mode, which is not good!
Related
Ok so here's the sample code from the page http://phpseclib.sourceforge.net/crypt/examples.html
<?php
include('Crypt/AES.php');
include('Crypt/Random.php');
$cipher = new Crypt_AES(); // could use CRYPT_AES_MODE_CBC
// keys are null-padded to the closest valid size
// longer than the longest key and it's truncated
//$cipher->setKeyLength(128);
$cipher->setKey('abcdefghijklmnop');
// the IV defaults to all-NULLs if not explicitly defined
$cipher->setIV(crypt_random_string($cipher->getBlockLength() >> 3));
$size = 10 * 1024;
$plaintext = str_repeat('a', $size);
echo $cipher->decrypt($cipher->encrypt($plaintext));
?>
Before anything, why is the line //$cipher->setKeyLength(128); is being commented out?
And if I want to set my key to '1234568790', is there anything I should do? Because it's much shorter than they length of the key in the example above (abcdefghijklmnop).
And finally if the plaintext that I want to encrypt is short, something like "my name is james bond", is there anything extra that I should do? Because from the code above, it seems the plaintext's length should be 10 x 1024. (Why is that?)
Before anything, why is the line //$cipher->setKeyLength(128); is being commented out?
Say you pass a 17 byte key to phpseclib via setKey. What do you suppose ought to happen? In the 1.0 and 2.0 versions if setKeyLength isn't called then the key is null-padded to 24 bytes (eg. 192-bits). But if setKeyLength is called it'll be truncated to 16 bytes. In the master branch (which, as I understand it, will eventually become the 3.0 branch), an exception is thrown if the key length isn't valid. In that version it may still be desirable to call setKeyLength if, for example, if you want to set the key length as being 128-bits in one part of the code and then actually set the key in another part of the code. ie. you could do the length checking or phpseclib could do the length checking.
And if I want to set my key to '1234568790', is there anything I should do? Because it's much shorter than they length of the key in the example above (abcdefghijklmnop).
1234568790 isn't technically long enough to be a valid key. It's 80 bytes long. The smallest key AES supports is 128-bits. Maybe you should consider doing something like setPassword('1234568790') instead of setKey('1234568790'). setPassword will use a key-derivation function to generate a key from the password.
In phpseclib 1.0 and 2.0 setKey will, none-the-less, accept that as a key. It'll just null pad the key. The master branch otoh will throw an exception since the key isn't actually long enough.
And finally if the plaintext that I want to encrypt is short, something like "my name is james bond", is there anything extra that I should do? Because from the code above, it seems the plaintext's length should be 10 x 1024. (Why is that?)
10 * 1024 is just an example. phpseclib, by default, will use PKCS7 padding to make strings long enough. eg. your string is 21 bytes long (if I counted correctly). So if you call $cipher->encrypt('my name is james bond') then chr(11) will be appending to the plaintext 11 times, by phpseclib and then the string will be encrypted. This is because block algorithms need to have the the plaintext be a multiple of the block length. So the ciphertext will be 32 bytes long. When you then call $cipher->decrypt('...') you'll get the orig 21-byte string back. I could elaborate further on PKCS7 padding, but I think I've answered your immediate question.
According to the codeigniter documentation, the encryption key for an AES-128 MUST be set to 128bits/16bytes (16 characters) random string. I tested my output using var_dump function
(assuming I have set a 16 character key to the config file and already loaded the library)
My code:
$plain_text = 'Hello World';
$encrypted = $this->encryption->encrypt($plain_text);
var_dump($encrypted);
var_dump($this->encryption->decrypt($encrypted));
die();
The output
string(176) (A 176 character encrypted data appears)
string(11) Hello World
Sorry I'm not able to put the exact encrypted data but I believe is doesn't make sense to show it.
I would like to know if the encryption output is perfectly normal output considering its size(176 bytes)
AES is a block cipher so padding is to be expected.
A block cipher (as opposed to a stream cipher) can only operate on a pre-determined size and must pad a smaller plaintext in order reach that size before it can operate on it.
With that said, 176 bytes (11 x 16-byte blocks) is a lot bigger than 11 bytes and my guess is that part of the overhead may be because codeigniter encodes its own information (i.e. structured data such as type of variable, length of string, etc.) before encrypting it.
Since you are able to decrypt the ciphertext using the same key, it's probably not a bug in the implementation and is working as intended, though I can't speak for whether it was implemented properly and securely.
One of my app needs to download a database with the content encrypted in AES 256. So I've used on server side phpAES to encode the strings in AES CBC with an IV.
On the iOS side I'm using FBEncryptor to decrypt the string.
This is the code on the server side:
$aes = new AES($key, "CBC", $IV);
$crypt = $aes->encrypt($string);
$b64_crypt = base64_encode($crypt);
On the iOS side I'm doing this:
NSString* decrypt = [FBEncryptorAES decryptBase64String:b64_crypt keyString:key iv:iv];
Actually everythings works fine on iOS 8. The problem is on iOS 7 where the decoded string is truncated at random length.
Thoughts?
Don't use phpAES. You're shooting yourself in the foot with an enormous cannon.
From their page:
The free version only supports ECB mode, and is useful for encrypting/decrypting credit card numbers.
This is incredibly wrong and misleading. ECB mode is not suitable for any purpose except as a building block for other modes of operation. You want an AEAD mode; or, failing that, CBC or CTR with HMAC-SHA2 and a CSPRNG-derived IV/nonce. Using unauthenticated encryption is a very bad idea.
For interoperability with iOS, you should use libsodium.
Objective-C: SodiumObjc or NAChloride
PHP: libsodium-php (also available in PECL)
If you cannot use libsodium, your best bet is OpenSSL and explicitly not mcrypt, and a compatible interface on the iOS side.
All currently supported versions (5.4+) of PHP expose openssl_encrypt() and openssl_decrypt() which allow fast and secure AES-CBC and AES-CTR encryption. However, you should consider using a library that implements these functions for you instead of writing them yourself.
The truncation could be the result of incompatible padding.
phpAES uses non-standard null padding similar to mcrypt, this is unfortunate since the standard for padding is PKCS#7. It is unfortunate that one has to read the code to find that out. It is important to supply a 256-bit (32-byte) key since that sets the key size for the algorithm.
FBEncryptor only supports PKCS#7 padding.
So, these two methods are incompatible.
One solution is to add PKCS#7 padding to the string in php prior to calling phpAES which will not then add the null padding. Then FBEncryptor will be compatible with the encrypted data.
PKCS#7 padding always adds padding. The padding is a series by bytes with the value of the number of padding bytes added. The length of the padding is the block_size - (length(data) % block_size.
For AES where the block is is 16-bytes (and hoping the php is valid, it had been a while):
$pad_count = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($pad_count), $pad_count);
Please add to the question working example keys, iv clear data and encrypted data as hex dumps.
I have to encrypt and decrypt data in AES CTS mode (ciphertext stealing, sometimes referred as AES-XTS) in PHP to interoperate with a remote system written in .NET platform. In .NET 4, this mode is supported natively.
For PHP, I cannot find a solution, based on the manual, mcrypt does not seem to have support for this mode.
Could anyone please explain the difference between plain CBC and CBC-CTS? Is it possible to make the latter work in PHP with using existing modules/libraries?
This is the steps from the wikipedia article interjected with my comments explaining the
LDn = Decrypt (K, Cn−1). Decrypt the second to last ciphertext block(the second to last 128bit/16 byte chunk chunk), using zeros as IV.
You can do this with the standard PHP mcrypt function, just pass
$second_to_last_cipher=array_slice($your_cipher_text_byte_array,count($your_cipher_text_byte_array)-32,16)
to mcrypt_decrypt with a null Iv
$second_to_last_clear = mcrypt_decrypt"MCRYPT_RIJNDAEL_128",$key,$second_to_last_ciphe)
Cn = Cn || Tail (Dn, B−M). Pad the ciphertext to the nearest multiple of the block size using the last B−M bits of block cipher decryption of the second-to-last ciphertext block.
Copy the last n bytes of the value you just decrypted into the last block of ciphertext.
$n = 16 - ($second_to_last_clear % 16)
Then use array copy to copy the data
Swap the last two ciphertext blocks.
Simply switch the swap the contents of the last and second two last cells in your ciphertext array
Decrypt the ciphertext using the standard CBC mode up to the last block.
make the standard decryption call.
Exclusive-OR the last ciphertext (was already decrypted in step 1) with the second last ciphertext.
Self explanatory.
Truncate the plaintext to the length of the original ciphertext.
I have found an implementation of AES algorithm in C, you can find it with source code here.
The author's older implementation was used in Android. So I think the implementation would be very promising.
Finally, after you have downloaded the source code, then check the file aesxam.c, there is a very good example of CTS with CBC for file encryption.
All credits go to Brian Gladman.
Brian Gladman now maintains his own github repo for AES.
I'm running Windows Server 2k8 (maybe that's half the problem?) Anyway, I'm getting different values out of different Blowfish modules in various languages. Is there one which can be relied upon as the standard?
For the following examples, assume the key is password and the plaintext 12345678.
a. The Online Encrypt Tool with Algorithm set to Blowfish mode ECB and Base64 Encode the output checked gives 2mADZkZR0VM=. I've been using this a my reference point, wise or otherwise.
b. The following Perl code uses Crypt::ECB and MIME::Base64
use MIME::Base64;
use Crypt::ECB;
$crypt = Crypt::ECB->new;
$crypt->padding(PADDING_NONE);
$crypt->cipher('Blowfish') || die $crypt->errstring;
$crypt->key('password');
$enc = $crypt->encrypt("12345678");
print encode_base64($enc);
This outputs 2mADZkZR0VM= with PADDING_NONE (which compares well with 'a.' above). However, when padding is set to PADDING_AUTO it outputs 2mADZkZR0VOZ5o+S6D3OZw== which is, in my mind at least, a bug as the plaintext is 8 characters long and in no need of padding.
c. If I use Crypt::Blowfish as below
#! c:\perl\bin
use Crypt::Blowfish;
use MIME::Base64;
my $key;
my $plaintext;
$key = "password";
$plaintext = "12345678";
my $cipher = new Crypt::Blowfish $key;
my $ciphertext = $cipher->encrypt($plaintext);
my $encoded = encode_base64( $ciphertext );
print $encoded;
then I get 2mADZkZR0VM= which matches 'a.' above. Trouble with this module though is that one has to segment things into 8 byte chunks for encoding; it has no chunker of its own.
d. If I use the source at http://linux.die.net/man/3/bf_ecb_encrypt (which I did for a recent PHP ext project) then I get the same answer as 'a.'. I'm inclined to trust this code the most as it's in use in SSLeay and OpenSSL.
e. The BlowfishEx.EXE in DI Management's Blowfish: a Visual Basic version with PKCS#5 padding gives 2mADZkZR0VOZ5o+S6D3OZw== which is the same as the Crypt::ECB results with PADDING_AUTO. With Padding set to None I get 2mADZkZR0VM= which matches 'a.'
I've partly answered my own question writing this: looks like I just have to modify DI Management's code for the VB6 project. And maybe suggest the same to the writer of Crypt::ECB.
But the question remains: is there a trustworthy blowfish reference platform?
Your problem doesn't seem to be with whether they implement the Blowfish algorithm correctly. In the "no padding" variant, they all seem to produce identical results.
That leaves a question of whether an 8-byte input should be padded at all. The answer to this is pretty simple: PKCS #7 requires that there is always at least one byte of padding added to the input. The reason for this is simple: on the receiving end, there needs to be an unambiguous way to remove the padding. In the case of PKCS#7, the value of the padding bytes is always the number of bytes of padding that needs to be stripped off by the recipient.
So, when you receive material that's been padded with PKCS7, you decrypt the block, then look at the last byte in the decrypted output, and remove that many bytes from the block. Without a block of padding on the end, the receiver would normally look at the last byte of actual data, and whatever value that byte happened to contain, strip off that many bytes (though it should probably tell you there's a problem if the number is larger than 8). In any case, to assure correct operation, there must always be at least one byte of padding. In your case, that means the input gets expanded out to 16 bytes instead of 8.