How to Add/Remove Pkcs7 Padding from an Aes Encrypted String

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



Leave a reply



Submit