How to add/remove PKCS7 padding from an AES encrypted string?
Let's see. PKCS #7 is described in RFC 5652 (Cryptographic Message Syntax).
The padding scheme itself is given in section 6.3. Content-encryption Process. It essentially says: append that many bytes as needed to fill the given block size (but at least one), and each of them should have the padding length as value.
Thus, looking at the last decrypted byte we know how many bytes to strip off. (One could also check that they all have the same value.)
I could now give you a pair of PHP functions to do this, but my PHP is a bit rusty. So either do this yourself (then feel free to edit my answer to add it in), or have a look at the user-contributed notes to the mcrypt documentation - quite some of them are about padding and provide an implementation of PKCS #7 padding.
So, let's look on the first note there in detail:
<?php
function encrypt($str, $key)
{
$block = mcrypt_get_block_size('des', 'ecb');
This gets the block size of the used algorithm. In your case, you would use aes
or rijndael_128
instead of des
, I suppose (I didn't test it). (Instead, you could simply take 16
here for AES, instead of invoking the function.)
$pad = $block - (strlen($str) % $block);
This calculates the padding size. strlen($str)
is the length of your data (in bytes), % $block
gives the remainder modulo $block
, i.e. the number of data bytes in the last block. $block - ...
thus gives the number of bytes needed to fill this last block (this is now a number between 1
and $block
, inclusive).
$str .= str_repeat(chr($pad), $pad);
str_repeat
produces a string consisting of a repetition of the same string, here a repetition of the character given by $pad
, $pad
times, i.e. a string of length $pad
, filled with $pad
.$str .= ...
appends this padding string to the original data.
return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
Here is the encryption itself. Use MCRYPT_RIJNDAEL_128
instead of MCRYPT_DES
.
}
Now the other direction:
function decrypt($str, $key)
{
$str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
The decryption. (You would of course change the algorithm, as above). $str is now the decrypted string, including the padding.
$block = mcrypt_get_block_size('des', 'ecb');
This is again the block size. (See above.)
$pad = ord($str[($len = strlen($str)) - 1]);
This looks a bit strange. Better write it in multiple steps:
$len = strlen($str);
$pad = ord($str[$len-1]);
$len
is now the length of the padded string, and $str[$len - 1]
is the last character of this string. ord
converts this to a number. Thus $pad
is the number which we previously used as the fill value for the padding, and this is the padding length.
return substr($str, 0, strlen($str) - $pad);
So now we cut off the last $pad
bytes from the string. (Instead of strlen($str)
we could also write $len
here: substr($str, 0, $len - $pad)
.).
}
?>
Note that instead of using substr($str, $len - $pad)
, one can also write substr($str, -$pad)
, as the substr
function in PHP has a special-handling for negative operands/arguments, to count from the end of the string. (I don't know if this is more or less efficient than getting the length first and and calculating the index manually.)
As said before and noted in the comment by rossum, instead of simply stripping off the padding like done here, you should check that it is correct - i.e. look at substr($str, $len - $pad)
, and check that all its bytes are chr($pad)
. This serves as a slight check against corruption (although this check is more effective if you use a chaining mode instead of ECB, and is not a replacement for a real MAC).
(And still, tell your client they should think about changing to a more secure mode than ECB.)
How to remove PKCS7 padding from an AES encrypted string in android?
The cipher removes the padding automatically. What you see here comes from the conversion of the byte array plainText
to the string. You should only use the first ptLength
bytes instead of the whole array:
new String(plainText, 0, ptLength, "UTF-8")
AES 256 encryption PHP with Padding
AES 256 is fine but what exactly does block size mean?
AES has a fixed block size of 128 bit. A block cipher only works on one block of a specific size. A mode operation extends a block cipher with the ability to work on multiple blocks and a padding enables it to work on plaintexts that are not a multiple of the block size.
AES-128-CBC means AES with key size of 128 bit and the CBC mode of operation. If you want to use AES-256, then you need to tell OpenSSL that: AES-256-CBC. Additionally, you need to use a key that is actually 256 bit long. Your current key is only 128 bit long.
What exactly is PKCS7 padding method and can be implemented with php?
openssl_encrypt()
already does PKCS#7 padding for you and openssl_decrypt()
removes it for you.
What exactly does IV do?
A random IV randomizes the ciphertext which means that encrypting the same plaintext with the same key, but a different IV produces a different ciphertext which is indistinguishable from random noise or other the same encryption with a different IV. Wikipedia has a good description what this actually does.
Keep in mind that an IV must be randomly generated for each iteration. Otherwise, an attacker who observes only the ciphertext may discover that you encrypted the same plaintext multiple times.
Keep in mind that an AES key is supposed to be quite noisy with high entropy. "12345..." looks more like a password. If you want to use passwords, then you need to derive a key from that password. PBKDF2 is a good idea with a random salt and a lot of iterations.
How to make PKCS5 and PKCS7 padding with openssl_public_encrypt
How to make PKCS5 and PKCS7 padding with openssl_public_encrypt?
openssl_public_encrypt
is used with asymmetric encryption (encrypting by public key) and indeed only the listed paddings are available.
PKCS7 padding is used with symmetric encryption (openssl_encrypt).
You can pkcs#7 padding with openssl_encrypt
documentation. Apparently (according to the comments) the pkcs#7 padding is used when no option is specified.
Seems in php you will have to it yourself, see How to add/remove PKCS7 padding from an AES encrypted string?
please note - I am not php developer, so if there's better way, please comment / correct.
Migrate PHP AES encryption from mcrypt to openssl return different encrypted string
Both codes use different AES variants and paddings. The mcrypt code applies AES-128 and Zero padding, the openssl code AES-256 and PKCS7 padding. To make sure that both ciphertexts match, both codes must use the same AES variant and padding.
mcrypt identifies the AES variant from the key size. Since $key
is a 16 bytes key, AES-128 is used. openssl determines the AES variant based on the specification passed in the 2nd parameter. Keys that are too short are padded with 0 values to the required length, keys that are too long are truncated. Here, AES-256-CBC
is specified, i. e. AES-256 is used. The 16 bytes key $key
is therefore padded with 0 values and extended to a size of 32 bytes.
mcrypt implicitly uses Zero padding for encryption, which is not implicitly removed during decryption. PKCS7 padding is not supported. openssl implicitly applies PKCS7 padding for encryption, which is implicitly removed during decryption. Zero padding is not supported. If the openssl code should use Zero padding or the mcyrpt code PKCS7 padding, this must be implemented by yourself.
With regard to the migration from mcrypt to openssl, the openssl code is modified in the following to be functionally identical to the mcrypt code, i.e. AES-128 and Zero padding is used. With regard to the AES variant, only the specification AES-256-CBC
must be changed to AES-128-CBC
. Concerning padding, the default PKCS7 padding must be disabled using OPENSSL_ZERO_PADDING
and Zero padding itself must be implemented (note that the OPENSSL_ZERO_PADDING
flag only disables padding, but does not enable Zero padding; the name is badly chosen):
<?php
$str = "test";
$key = 'o6xSYYAVl2eapPI2';
$iv = 'fedcba9876543210';
function encrypt_openssl($str = NULL, $key, $iv) {
$encrypted = openssl_encrypt(
zeroPad($str, 16), // Zero pad plaintext
'AES-128-CBC', // Choose AES-128
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, // Disable PKCS7 Padding
$iv);
return bin2hex(@$encrypted);
}
function zeroPad($text, $bs) {
$pad = $bs - strlen($text) % $bs;
return ($pad < 16) ? $text . str_repeat("\0", $pad) : $text;
}
echo 'Openssl:'.encrypt_openssl($str,$key,$iv); // Openssl:57c86f3089535b3acfbe65cecbb662b9
For a comparison with your result, note that you have confused the labels of the outputs, i.e. the openssl result is labeled Mcrypt
and vice versa!
A final note: In general, PKCS7 padding is more reliable than Zero padding, as the former contains the information of the padding length. This is not the case with Zero padding, so when removing the padding (i.e. after decryption) it is not possible to distinguish between regular and padding bytes. There are also different Zero padding variants, e.g. one does not pad if the length of the plaintext already corresponds to an integer multiple of the blocksize (this variant uses mcrypt), the other one pads with a complete block in this case.
Related Topics
Why Is the Hash Part of the Url Not Available on the Server Side
Using PHP 5.5'S Password_Hash and Password_Verify Function
"Invalid Parameter Number: Parameter Was Not Defined" Inserting Data
Convert Flat Array to a Delimited String to Be Saved in the Database
Convert Command Line Curl to PHP Curl
Glob() - Sort Array of Files by Last Modified Datetime Stamp
How to Load a Controller from Another Controller in Codeigniter
What Is So Wrong With Extract()
How to Filter a Two Dimensional Array by Value
Set Up the Base Url in Codeigniter
How to Set Order by Params Using Prepared Pdo Statement
Laravel 5.2 Validation Error Not Appearing in Blade
Performance of For VS Foreach in PHP
Detect Language from String in PHP
Where Does PHP'S Error Log Reside in Xampp
PHP: How to Get All Possible Combinations of 1D Array
PHP & MySQL: MySQLi_Num_Rows() Expects Parameter 1 to Be MySQLi_Result, Boolean Given