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

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

问题描述

我在这里提出了一个问题为什么AES java解密返回额外的字符? a>关于在我解密加密数据时获得额外的字符。感谢用户Ebbe M. Pedersen的评论,我现在明白,问题是在PHP和Android Java代码中都没有使用相同的填充机制。所以我将Java代码更改为

Java代码



  public class加密{

private String iv =fedcba9876543210; // Dummy iv(CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
私密密码;

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自动生成的catch块
e.printStackTrace();
}
catch(NoSuchPaddingException e){
// TODO自动生成的catch块
e.printStackTrace();
}
}

public byte [] encrypt(String text)throws异常
{
if(text == null || text.length )== 0)
抛出新异常(空字符串);

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());
}

返回加密;
}

public byte [] decrypt(String code)throws异常
{
if(code == null || code.length()== 0)
抛出新的异常(空字符串);

byte [] decryptpted = null;

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

decryptpted = cipher.doFinal(hexToBytes(code));
} catch(Exception e)
{
throw new Exception([decrypt]+ e.getMessage());
}
返回解密;
}



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

int len = data.length;
String str =; (int i = 0; i if((data [i]& 0xFF))16
str = str +0+ java .lang.Integer.toHexString(数据[1] - 安培;为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]; (int i = 0; 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; (int i = 0; i< padLength; i ++)


{
source + = paddingChar;
}

返回源;
}
}

然后我添加了相同的 PKCS5padding 函数到我的PHP mcrypt class:



PHP mcrypt类



  class MCrypt 
{
private $ iv ='fedcba9876543210'; #如JAVA
private $ key ='0123456789abcdef'; #如JAVA


函数MCrypt()
{
}

函数加密($ 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);

返回bin2hex($ encrypted);
}

函数解密($ 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);
$ decryptpted = mdecrypt_generic($ td,$ code);

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

return utf8_encode(trim($ decryptpted));
}

保护函数hex2bin($ hexdata){
$ bindata =''; ($ i = 0; $ i< strlen($ hexdata); $ i + = 2){
$ bindata。= chr(hexdec(substr($ hexdata,$ i,2)));
}

return $ bindata;
}

函数pkcs5_pad($ text,$ blocksize)
{
$ pad = $ blocksize - (strlen($ text)%$ blocksize);
返回$文本。 str_repeat(chr($ pad),$ pad);
}

函数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);
}}

现在当前的问题是发送/接收 UTF-8 字符,而不是如何解码/编码 UTF-8 字符。
当我发送包含超过3个或少于3个字符的阿拉伯语/波斯语时,它不会返回任何内容。
例如:
如果我发送单词خوب(正好3个字符),我得到خوب这是正确的;但是
如果我发送مچکرم(有5个字符)我什么也没有。



我发现问题是我解密后没有使用unpadding函数我的php代码中的数据,所以我修正了:



PHP代码



 <?php 
$ data = file_get_contents('php:// input');
$ block_size = mcrypt_get_block_size(rijndael-128,cbc);
require_onceencryption.php;
$ etool = new MCrypt();
$ data = $ etool-> decrypt($ data);
$ data = $ etool-> pkcs5_unpad($ data); //< ------使用unpad函数
$ 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);



这里是获取它的Java代码



JSON $ j = new JSONObject(sb.toString()); // sb是字符串构建器
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);

现在的问题是相反的!我可以得到包含超过或少于3个波斯文/阿拉伯字符的单词,但不能包含 3个字符的单词



我想我应该检查是否需要填充?但是如果是这样的话呢?

解决方案

 函数pkcs5_pad($ text,$ blocksize)
{
$ pad = $ blocksize - (strlen($ text)%$ blocksize);
返回$文本。 str_repeat(chr($ pad),$ pad);
}

函数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);
}

此代码使用PHP的字符串函数,默认情况下对原始二进制进行操作忽略编码。



通过 utf8_encode()混合解密的邮件实际上没有意义。该功能将ISO-8559-1代码点(即 0x00 通过 0xFF )重新映射到UTF-8代码点(即 0x00 通过 0x7f ,然后 0xc280 通过 0xc2bf 0xc380 通过 0xc3bf )。



由于PHP默认使用原始二进制文件,所以您根本不需要应用此转换。



注意:我默认说。 PHP中有一个非常愚蠢的功能,称为功能重载,由 mbstring.func_overload PHP.ini指令。如果您使用此功能,则需要重写每个需要测量和/或切片字符串的加密代码使用 strlen() substr()等。



Defuse Security发布并维护一个安全认证加密PHP库,其中包含这些功能的替换,一个href =https://github.com/defuse/php-encryption/blob/399fc4c4798b301681888c2608b0ddd060d477da/src/Crypto.php#L622-L690 =nofollow>抗拒功能重载。






安全通知



首先:您的加密代码已损坏。换句话说: NOT SECURE



第二:如果可以帮助,请避免使用mcrypt进行任何操作。



如果您只需要将数据加密电线,只需使用TLS 。但是,如果(例如)你需要在TLS之上进行点对点加密(例如,所以你的服务器永远不会看到数据),不要自己滚动。 选择一个安全的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天全站免登陆