当需要加密的填充? [英] When is padding required for encryption?

查看:140
本文介绍了当需要加密的填充?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我问一个问题在这里<一个href=\"http://stackoverflow.com/questions/35215385/why-aes-java-decryption-return-extra-characters\">why AES解密java的回报多余的字符?有关获取额外的字符,当我解密加密的数据。由于用户评论Ebbe M.佩德森我现在明白了,这个问题是不是使用了PHP和Android的Java code都是相同的填充机制。因此,我改变了Java code到

的Java code

 公共类加密{    私人字符串IV =fedcba9876543210; //虚拟IV(改变它!)
    私人IvParameterSpec ivspec;
    私人SecretKeySpec keyspec;
    私人密码的密码;    私人字符串SecretKey的=0123456789ABCDEF; //虚拟SecretKey的(改变它!)    公共加密()
    {
        ivspec =新IvParameterSpec(iv.getBytes());        keyspec =新SecretKeySpec(SecretKey.getBytes(),AES);        尝试
        {
            密码= Cipher.getInstance(AES / CBC / PKCS5PADDING); //AES / CBC / NoPadding
        }
        赶上(抛出:NoSuchAlgorithmException E)
        {
            // TODO自动生成catch块
            e.printStackTrace();
        }
        赶上(NoSuchPaddingException E){
            // TODO自动生成catch块
            e.printStackTrace();
        }
    }    公众的byte []加密(字符串文本)抛出异常
    {
        如果(文字== NULL || text.length()== 0)
            抛出新的异常(空字符串);        字节]加密= NULL;        尝试{
            cipher.init(Cipher.ENCRYPT_MODE,keyspec,ivspec);            加密= cipher.doFinal(padString(文本).getBytes());
        }赶上(例外五)
        {
            抛出新的异常([加密]+ e.getMessage());
        }        返回加密;
    }    公共字节[]解密(字符串code)抛出异常
    {
        如果(code == NULL || code.length()== 0)
            抛出新的异常(空字符串);        字节[]解密= NULL;        尝试{
            cipher.init(Cipher.DECRYPT_MODE,keyspec,ivspec);            解密= cipher.doFinal(hexToBytes(code));
        }赶上(例外五)
        {
            抛出新的异常([解密]+ e.getMessage());
        }
        返回解密;
    }    公共静态字符串bytesToHex(字节[]数据)
    {
        如果(数据== NULL)
        {
            返回null;
        }        INT LEN = data.length;
        字符串str =;
        的for(int i = 0; I&LT; LEN,我++){
            如果((数据[1] - 安培;为0xFF)LT; 16)
                海峡= STR +0+ java.lang.Integer.toHexString(数据[1] - 安培; 0xFF的);
            其他
                海峡= STR + java.lang.Integer.toHexString(数据[1] - 安培; 0xFF的);
        }
        返回海峡;
    }
    公共静态的byte [] hexToBytes(字符串str){
        如果(STR == NULL){
            返回null;
        }否则如果(str.length()2){
            返回null;
        }其他{
            INT LEN = str.length()/ 2;
            字节[]缓冲区=新的字节[LEN]
            的for(int i = 0; I&LT; LEN,我++){
                缓冲液[I] =(字节)的Integer.parseInt(str.substring(ⅰ* 2,I * 2 + 2),16);
            }
            返回缓冲区;
        }
    }    私有静态字符串padString(字符串源)
    {
        焦炭paddingChar ='';
        INT大小= 16;
        INT X = source.length()%的大小;
        INT padLength =大小 - X;        的for(int i = 0; I&LT; padLength;我++)
        {
            源+ = paddingChar;
        }        返回源;
    }
}

然后我说同样的 PKCS5padding 功能,我的PHP 的mcrypt 类:

PHP的mcrypt类

 类的mcrypt
{
    私人$ IV ='fedcba9876543210'; #Same在JAVA
    私人$键='0123456789ABCDEF'; #Same在JAVA
函数的mcrypt()
{
}功能加密($ STR){    // $键= $这个 - &GT; HEX2BIN($键);
    $ IV = $这个 - &GT; 4;    $ TD = mcrypt_module_open('Rijndael算法-128','','CBC',$ IV);    mcrypt_generic_init($ TD,$这个 - &GT;的关键,$ IV);
    $加密= mcrypt_generic($ TD,$海峡);    mcrypt_generic_deinit($ TD);
    mcrypt_module_close($ TD);    返回BIN2HEX($加密);
}解密功能($code){
    // $键= $这个 - &GT; HEX2BIN($键);
    $code = $这个 - &GT; HEX2BIN($code);
    $ IV = $这个 - &GT; 4;    $ TD = mcrypt_module_open('Rijndael算法-128','','CBC',$ IV);    mcrypt_generic_init($ TD,$这个 - &GT;的关键,$ IV);
    $解密= mdecrypt_generic($ TD,$code);    mcrypt_generic_deinit($ TD);
    mcrypt_module_close($ TD);    返回utf8_en code(修剪($解密));
}保护功能HEX2BIN($ hexdata){
    $ bindata ='';    为($ I = 0; $ I&LT;的strlen($ hexdata); $ I + = 2){
        。$ bindata = CHR(hexdec(SUBSTR($ hexdata,$ 1,2)));
    }    返回$ bindata;
}功能pkcs5_pad($文本,$块大小)
{
    $垫= $块大小 - (的strlen($文本)%$块大小);
    返回$文本。 str_repeat(CHR($垫),$垫);
}功能pkcs5_unpad($文本)
{
    $垫= ORD($ {文字的strlen($文本)-1});
    如果($垫及GT;的strlen($文本))返回false;
    如果(strspn($文字,CHR($垫)的strlen($文本) - $垫)= $垫!)返回false;
    返回SUBSTR($文字,0,-1 * $垫);
}}

立即目前的问题是发送/接收 UTF-8 字符,而不是如何去code / EN code UTF-8 字符。
当我送包含例如超过3个或少于3个字符的阿拉伯语/波斯语也就是说它没有返回值。
例如:
如果我把单词خوب(这恰好有3个字符)我得到خوب,这是正确的;但
如果我送مچکرم(其中有5个字符)我什么也没有。

我发现的问题是,我没有用我的PHP code解密数据后去填充功能,所以我固定的:

PHP code

 &LT; PHP
$数据=的file_get_contents('PHP://输入');
$ BLOCK_SIZE = mcrypt_get_block_size(Rijndael的-128,CBC);
require_onceencryption.php
$ etool =新的mcrypt();
$数据= $ etool-&GT;解密($的数据);
$数据= $ etool-&GT; pkcs5_unpad($数据); //&LT; ------使用unpad功能
$数据= json_de code($的数据,真实);$数据= $数据[请求];
$ etool =新的mcrypt();
$数据= $ etool-&GT; pkcs5_pad($数据,$ BLOCK_SIZE);
$数据= $ etool-&GT;加密($的数据);
$阵列=阵列('数据'=&GT; $的数据);
回声json_en code($数组);

这是Java code得到它

 的JSONObject J =新的JSONObject(sb.toString()); //某人是字符串生成器
结果= j.get(数据)的toString()。
结果=新的String(etool.decrypt(结果),UTF-8);
结果=新的String(result.getBytes(ISO-8859-1));
Log.d(成功遥远的,结果);

现在的问题是相反的!我可以得到含有超过或少于3波斯/阿拉伯字符的话,但含有不言的究竟的3个字符。

我想我应该检查并不需要去填充?但如何如果是这样?


解决方案

 函数pkcs5_pad($文本,$块大小)
{
    $垫= $块大小 - (的strlen($文本)%$块大小);
    返回$文本。 str_repeat(CHR($垫),$垫);
}功能pkcs5_unpad($文本)
{
    $垫= ORD($ {文字的strlen($文本)-1});
    如果($垫及GT;的strlen($文本))返回false;
    如果(strspn($文字,CHR($垫)的strlen($文本) - $垫)= $垫!)返回false;
    返回SUBSTR($文字,0,-1 * $垫);
}

这code是使用PHP的字符串函数,它默认的原始二进制操作,而忽略编码。

混合您解密的消息utf8_en code()实际上并没有什么意义。这个函数重新映射ISO-8559-1 codepoints(即 0×00 0xFF的)为UTF-8 codepoints(即 0×00 0x7F的,那么 0xc280 通过 0xc2bf 0xc380 0xc3bf )。

由于PHP运行在默认情况下,原始的二进制,你并不需要在所有应用此转变。

请注意:我说的默认的。有一个在PHP中一个非常愚蠢的功能,称为函数重载这是由控制中的mbstring.func_overload php.ini指令。如果你的使用的这个功能,你需要重写每一个密码code的一块需要测量和/或切片字符串的使用的strlen() SUBSTR()

消除各类安全发布并维护一个安全认证加密PHP库,它包含了这些功能的替代品该<一个href=\"https://github.com/defuse/php-encryption/blob/399fc4c4798b301681888c2608b0ddd060d477da/src/Crypto.php#L622-L690\"相对=nofollow>抵制函数重载。


安全公告

第一:您的密码code为打破。换句话说:不安全

第二:<一href=\"https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-$c$c-you-re-doing-it-wrong\"相对=nofollow>避免使用过了的mcrypt任何东西,如果你能帮助它。

如果你只是需要您的数据通过线路进行加密,只需使用TLS 。想在这里重新发明轮子只会导致灾难。

但是,如果(例如),你需要在TLS之上对等网络加密(例如,让您的服​​务器永远看不到的数据),不滚你自己的。 <一href=\"https://paragonie.com/blog/2015/11/choosing-right-cryptography-library-for-your-php-project-guide\"相对=nofollow>选择一个安全的PHP加密库,而不是。如果你需要一个跨平台工作,的使用libsodium

I asked a question here why AES java decryption return extra characters? about getting extra characters when I decrypt the encrypted data. Thanks to a comment by user "Ebbe M. Pedersen" I now understand that the problem is not using the same padding mechanism in both the PHP and Android Java code. So I changed the Java code to

Java code

 public class encryption {

    private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
    private IvParameterSpec ivspec;
    private SecretKeySpec keyspec;
    private Cipher cipher;

    private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)

    public encryption()
    {
        ivspec = new IvParameterSpec(iv.getBytes());

        keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");

        try
        {
            cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");//"AES/CBC/NoPadding"
        }
        catch (NoSuchAlgorithmException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public byte[] encrypt(String text) throws Exception
    {
        if(text == null || text.length() == 0)
            throw new Exception("Empty string");

        byte[] encrypted = null;

        try {
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

            encrypted = cipher.doFinal(padString(text).getBytes());
        } catch (Exception e)
        {
            throw new Exception("[encrypt] " + e.getMessage());
        }

        return encrypted;
    }

    public byte[] decrypt(String code) throws Exception
    {
        if(code == null || code.length() == 0)
            throw new Exception("Empty string");

        byte[] decrypted = null;

        try {
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            decrypted = cipher.doFinal(hexToBytes(code));
        } catch (Exception e)
        {
            throw new Exception("[decrypt] " + e.getMessage());
        }
        return decrypted;
    }



    public static String bytesToHex(byte[] data)
    {
        if (data==null)
        {
            return null;
        }

        int len = data.length;
        String str = "";
        for (int i=0; i<len; i++) {
            if ((data[i]&0xFF)<16)
                str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
            else
                str = str + java.lang.Integer.toHexString(data[i]&0xFF);
        }
        return str;
    }


    public static byte[] hexToBytes(String str) {
        if (str==null) {
            return null;
        } else if (str.length() < 2) {
            return null;
        } else {
            int len = str.length() / 2;
            byte[] buffer = new byte[len];
            for (int i=0; i<len; i++) {
                buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
            }
            return buffer;
        }
    }



    private static String padString(String source)
    {
        char paddingChar = ' ';
        int size = 16;
        int x = source.length() % size;
        int padLength = size - x;

        for (int i = 0; i < padLength; i++)
        {
            source += paddingChar;
        }

        return source;
    }
}

Then I added the same PKCS5padding functions to my PHP mcrypt class:

PHP mcrypt class

class MCrypt
{
    private $iv = 'fedcba9876543210'; #Same as in JAVA
    private $key = '0123456789abcdef'; #Same as in JAVA


function MCrypt()
{
}

function encrypt($str) {

    //$key = $this->hex2bin($key);
    $iv = $this->iv;

    $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);

    mcrypt_generic_init($td, $this->key, $iv);
    $encrypted = mcrypt_generic($td, $str);

    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    return bin2hex($encrypted);
}

function decrypt($code) {
    //$key = $this->hex2bin($key);
    $code = $this->hex2bin($code);
    $iv = $this->iv;

    $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);

    mcrypt_generic_init($td, $this->key, $iv);
    $decrypted = mdecrypt_generic($td, $code);

    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    return utf8_encode(trim($decrypted));
}

protected function hex2bin($hexdata) {
    $bindata = '';

    for ($i = 0; $i < strlen($hexdata); $i += 2) {
        $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
    }

    return $bindata;
}

function pkcs5_pad ($text, $blocksize)
{
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

function pkcs5_unpad($text)
{
    $pad = ord($text{strlen($text)-1});
    if ($pad > strlen($text)) return false;
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
    return substr($text, 0, -1 * $pad);
}}

Now the current problem is sending/receiving UTF-8 characters, not how to decode/encode UTF-8 characters. When i send Arabic/Persian words which contain for example more than 3 or less than 3 characters it returns nothing. For example: If I send the word "خوب" (which has exactly 3 characters) I get "خوب" which is correct; but if I send مچکرم (which has 5 characters) I get nothing.

I found that the problem is that I was not using the unpadding function after decrypting the data in my php code, so I fixed that:

PHP code

<?php
$data =file_get_contents('php://input');
$block_size=mcrypt_get_block_size("rijndael-128",'cbc');
require_once "encryption.php";
$etool=new MCrypt();
$data =$etool->decrypt($data);
$data=$etool->pkcs5_unpad($data);//  <------ using unpad function
$data =json_decode($data, true);



$data=$data["request"];
$etool=new MCrypt();
$data=$etool->pkcs5_pad($data,$block_size);
$data=$etool->encrypt($data);
$array=array('data'=>$data);
echo  json_encode($array);

And here is the Java code to get it

JSONObject j=new JSONObject(sb.toString());//sb is string builder
result=j.get("data").toString();
result= new String(etool.decrypt( result ),"UTF-8");
result = new String(result.getBytes("ISO-8859-1"));
Log.d("success remote ",result);

Now the problem is reversed!! I can get words containing more than or less than 3 Persian/Arabic characters, but not words containing exactly 3 characters.

I think I should check "does unpadding required?" but how if so?

解决方案

function pkcs5_pad ($text, $blocksize)
{
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

function pkcs5_unpad($text)
{
    $pad = ord($text{strlen($text)-1});
    if ($pad > strlen($text)) return false;
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
    return substr($text, 0, -1 * $pad);
}

This code is using PHP's string functions, which operate on raw binary by default and ignores encoding.

Blending your decrypted messages through utf8_encode() doesn't actually make sense. That function remaps ISO-8559-1 codepoints (i.e. 0x00 through 0xFF) to UTF-8 codepoints (i.e. 0x00 through 0x7f, then 0xc280 through 0xc2bf and 0xc380 through 0xc3bf).

Since PHP operates on raw binary by default, you don't need to apply this transformation at all.

Note: I said by default. There's a very stupid feature in PHP called function overloading which is controlled by the mbstring.func_overload PHP.ini directive. If you're using this feature, you need to rewrite every piece of cryptography code that needs to measure and/or slice strings to not use strlen(), substr(), etc.

Defuse Security published and maintains a secure authenticated encryption PHP library, which contains replacements for these functions that resist function overloading.


Security Notice

First: Your crypto code is broken. In other words: NOT SECURE.

Second: Avoid ever using mcrypt for anything if you can help it.

If you just need your data to be encrypted over the wire, just use TLS. Trying to reinvent the wheel here will just lead to disaster.

However, if (for example) you need peer-to-peer encryption on top of TLS (e.g. so your server never sees the data), don't roll your own. Pick a secure PHP cryptography library instead. If you need one that works cross-platform, use libsodium.

这篇关于当需要加密的填充?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆