ci里怎么使用3des加密?

黒之染
  • 3.1k

原来是使用下面这个类做的3des加密,但是升级PHP7.1后,mcrypt整个移除了,现在用的是ci框架,它应该是支持3des加密的,但是加密出来的字符串跟以前的不一样,请问ci里应该怎么加密?或者PHP7.1怎么做到3des加密?因为现在需要对接一个外部系统,他们要求用3des加密...

<?php
class Des3 {

public static function encrypt($str, $key) {
    $td = self::gettd($key);
    $ret = strtoupper(bin2hex(mcrypt_generic($td, self::pkcs5_pad($str, 8))));
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    return $ret;
}

public static function decrypt($str, $key) {
    $td = self::gettd($key);
    $ret = self::pkcs5_unpad(mdecrypt_generic($td, self::hex2bin(strtolower($str))));
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    return $ret;
}

public static function hex2bin($hexData) {
    $binData = "";
    for($i = 0; $i  < strlen ( $hexData ); $i += 2) {
        $binData .= chr ( hexdec ( substr ( $hexData, $i, 2 ) ) );
    }
    return $binData;
}

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

private static 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);
}

private static function getiv() {
    return pack('H16', '0102030405060708');
}

private static function gettd($key) {
    $iv = self::getiv();
    $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
    @mcrypt_generic_init($td, $key, $iv);
    return $td;
}
}

现在使用1楼码友的推荐,用了 openssl_encrypt ,但是这个不知道怎样才能设置成是 3des 加密,我是这样的:

$sign = openssl_encrypt (
        'abc9966',
        'des-ede3-cbc' ,//感觉是这个函数没用对,但该用哪个呢?
        'ABCLYHG22r5juqqlFRVfYdse',
        0 ,//OPENSSL_RAW_DATA OPENSSL_ZERO_PADDING
        '' //也有可能是 iv 没设置,该怎样设置呢?
        );
        

加密出来的串也不对


ci我是这样加密的,当然,加密出来的串也是不对的:

$sign2 = $this->encryption->encrypt(
        'abc9966',
        array(
            'driver' => 'openssl',
            'cipher' => 'tripledes',
            'mode' => 'cbc',
            'hmac'=>false,
            'raw_data'=>true,
            'key' => 'ABCLYHG22r5juqqlFRVfYdse',
        )
    );
    

请问ci里应该怎么加密?或者PHP7.1怎么做到3des加密?


clipboard.png

ci里, tripledes 支持的密钥长度是 56112168,但是我的密钥是24,这个有问题吗?但是接口那边给的密钥就是这个长度,PHP用旧的那个类加密出来他们是可以解密的,ci自带类就不行...


找到一篇文章:http://blog.csdn.net/aloneswo...,里面提到“3des-ecb加密方式;24位密钥,不足24位的右补0x00;”。这与ci的说法不一致,而且ci里提到的3des并不支持ecb模式


最终能加密了!!!感谢一楼码友的提醒!!!
加密方法我写在此问题的答案中,为了感谢1楼,所以没有采纳我自己的答案

回复
阅读 5.3k
4 个回答
✓ 已被采纳

以前用这个库加密,现在用ci的话也可以用这个库加密,将它放到ci项目的libraries目录下(一般为application\libraries),命名为Des3.php

然后在控制器中载入这个类,$this->load->library('Des3')Des3首字母大小写无所谓,但是对应文件和类名要一致且除了首字母之外的大写会被强制转换成小写,所以不建议在中间使用大写字母);

载入之后,在控制器中,使用$this->des3->encrypt()就可以使用Des3类里面的encrypt方法了,其他类似。

Update

鉴于7.1废弃了mcrypt库,建议使用openssl_encryptopenssl_decrypt进行加密解密。

参见:
https://stackoverflow.com/que...
http://php.net/manual/en/func...

终于找到答案了。先贴最终代码,用法跟Des3类一样:

class TripleDES{
    public static function encrypt($str,$key){
        $str = self::pkcs5_pad($str, 8);
        if (strlen($str) % 8) {
            $str = str_pad($str,
                strlen($str) + 8 - strlen($str) % 8, "\0");
        }
        $sign = openssl_encrypt (
            $str,
            'DES-EDE3' ,
            $key,
            OPENSSL_RAW_DATA | OPENSSL_NO_PADDING ,
            ''
        );

        return strtoupper(bin2hex($sign));
    }

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

说说找到这个方法的过程吧...实在是找到了很多互相矛盾的资料,
比如以下

  1. 3des 到底支不支持 ECB 模式,这个导致一楼的码友都疑惑,为什么我用 openssl 时,不选DES-ECB模式?

  2. Des3类里有用到iv,为什么我不用?

  3. ci的加密类里列出的表中,3des密钥并不支持24位,为什么给接口我的人给的密钥是24位数的?

其实以上的矛盾,我现在还没有答案,毕竟不是专门学密码学的人,我也不知道该不该深入。

最终的解决方案来自于这个问题的答案,这个答案中有人贴出了这代码

$message = "Lorem ipsum";
$key = "123456789012345678901234";
$iv = "12345678";

$message_padded = $message;
if (strlen($message_padded) % 8) {
    $message_padded = str_pad($message_padded,
        strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");
}
$encrypted_mcrypt = mcrypt_encrypt(MCRYPT_3DES, $key,
    $message, MCRYPT_MODE_CBC, $iv);
$encrypted_openssl = openssl_encrypt($message_padded, "DES-EDE3-CBC", 
    $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);

printf("%s => %s\n", bin2hex($message), bin2hex($encrypted_mcrypt));
printf("%s => %s\n", bin2hex($message_padded), bin2hex($encrypted_openssl));

例子列了 mcryptopenssl3des加密区别,我也注意到它的密钥,是24位。然而我直接把明文密钥iv换成我的,发现这例子输出的两个密文是一样的,但是它们跟Des3类的却是不一样。

思考了一下,如果我一步一步把例子的mcrypt版改成跟Des3类的一致,把相同的修改应用到例子的openssl版,到最终是不是可以让这3个加密方法改成一致的?

就按照这个思路走了!于是第一个问题出现了,例子的mcrypt版是用 MCRYPT_MODE_CBCDes3类是用 MCRYPT_MODE_ECB ,这个倒是好改,但是 openssl3des并不支持 ECB啊!改了例子的mcrypt版,那例子的openssl版呢?这个实在没想到方法,一个个试吧,试到例子的mcrypt版例子的openssl版的密文一致为止。先试opensslDES-EDE3,结果一试就对了:

mcrypt_encrypt(MCRYPT_3DES, $key,$message, MCRYPT_MODE_ECB, $iv);
openssl_encrypt($message_padded, "DES-EDE3", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);

以上两个输出的密文是一致的!

好了,本以为例子的mcrypt版Des3类的差别仅仅是加密模式,然而我改成MCRYPT_MODE_ECB后,例子的mcrypt版密文跟Des3类的还是不一样,密文前半段是一样的,到后面就不一样了。类似这样的感觉:

f156grd235645s6fdf123g5r6
f156grd235645s6fdfef135gr

思考了一下,看了一下Des3类的实现,发现Des3类mcrypt_generic($td, self::pkcs5_pad($str, 8))Des3类还给明文先打包了一下,行,我也加上打包函数

$message = "Lorem ipsum";
$key = "123456789012345678901234";
$iv = '';

$message_padded = $message;
$message_padded = self::pkcs5_pad($message_padded, 8);
if (strlen($message_padded) % 8) {
    $message_padded = str_pad($message_padded,
        strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");
}
//$message_padded = self::pkcs5_pad($message_padded, 8);//原来是在这打包的,运行了一下发现密文不一样,就改到上面了
$encrypted_mcrypt = mcrypt_encrypt(MCRYPT_3DES, $key,
    self::pkcs5_pad($message, 8), MCRYPT_MODE_ECB, $iv);
$encrypted_openssl = openssl_encrypt($message_padded, "DES-EDE3",
    $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);

print_r([
    strtoupper(bin2hex($encrypted_mcrypt)),
    strtoupper(bin2hex($encrypted_openssl)),
]);

最终,例子被我改成了上面这样。至此输出的两个密文,都跟Des3类的一致了,别提有多高兴了~~于是封装了起来

openssl的key也不一定都得24位,只要使用你的pkcs5_pad方法进行补0即可,需要是8的倍数

基于楼主的总结 自己尝试的写出来使用 open_ssl 的解密方法,希望能帮助一下有需要的人们吧

public function decrypt($message) {
        $encrypted_openssl = pack("H*", $message);
        $message_padded = openssl_decrypt($encrypted_openssl, "DES-EDE3-CBC",
            $this->_key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $this->_iv);
        $message_padded = rtrim(rtrim($message_padded, chr(0)), chr(7));
        return $message_padded;
    }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏