php 加密算法

401 阅读1分钟

使用 openssl 来加密

<?php

/**
 * php加密类
 * User: zhangcf
 * Date: 2022/03/14
 * Time: 下午17:22
 **/

namespace addons\tsm\library;

use mysql_xdevapi\Exception;

class ThreeDesUtil
{
    private static $Algorithm = 'DES-EDE3-CBC';//定义加密算法

    /**
     * 创建密钥,长度为24字节
     * @param $key
     * @return bool/string
     */
    static function createKeyBytes($key){
        $dest='';
        $temp=self::hexStringToBytes($key);
        try{
            if(strlen($temp)<24){
                $dest=substr($temp,0,strlen($temp));
                $dest.=substr($temp,0,24-strlen($temp));
            }
        }catch (Exception $exception){
            $exception->getTrace();
        }
        return $dest;
    }

    /**
     * 加密,ecb模式
     * @param $key  密钥
     * @param $src  被加密的数据缓冲区(明文)  十六进制
     * @return null|string  加密后的密文  十六进制
     */
    public static function encryptMode($key, $src, $iv){
        try{
            if(!is_null($src)&&!is_null($key)){
                $result = openssl_encrypt(
                    self::hexStringToBytes($src),
                    self::$Algorithm,
                    self::createKeyBytes($key),
                    OPENSSL_NO_PADDING,
                    self::hexStringToBytes($iv)
                );
                return self::convertByteToHexString($result);
            }else{
                return 'KEY/内容为空';
            }
        }catch (Exception $exception){
            $exception->getTrace();
        }
        return null;
    }

    /** 解密,ecb模式
     * @param $key 加密使用的密钥
     * @param $src 加密后的密文  十六进制
     * @return string 解密后的明文  十六进制
     */
    public static function decryptMode($key,$src){
        try{
            if(!is_null($src)&&!is_null($key)){
                $result=openssl_decrypt(self::hexStringToBytes($src),
                    self::$Algorithm,
                    self::createKeyBytes($key),
                    OPENSSL_NO_PADDING,
                    '');
                return self::convertByteToHexString($result);
            }else{
                return 'KEY/内容为空';
            }
        }catch (Exception $exception){
            $exception->getTrace();
        }
    }
    private static function hexStringToBytes($hexString){
        $len=strlen($hexString);
        $buf=[];
        $i=0;
        $j=0;
        if(($len%2)==1){
            $buf[$j++]=chr(self::fromDigit($hexString[$i++]));
        }
        while ($i<$len){
            $buf[$j++]=chr((self::fromDigit($hexString[$i++])<<4)|self::fromDigit($hexString[$i++]));
        }
        return implode($buf);
    }
    private static function convertByteToHexString($bytes){
        $result='';
        for ($i=0;$i<strlen($bytes);$i++){
            $temp=ord($bytes[$i])&0xff;
            $tempHex=dechex($temp);
            if(strlen($tempHex)<2){
                $result.='0'.$tempHex;
            }else{
                $result.=$tempHex;
            }
        }
        return $result;
    }
    private static function fromDigit($ch){
        if (ord($ch) >= ord('0') && ord($ch)<= ord('9')) {
            return ord($ch) - ord('0');
        }
        if (ord($ch) >= ord('A') && ord($ch) <= ord('F')) {
            return ord($ch) - ord('A') + 10;
        }
        if (ord($ch) >= ord('a') && ord($ch) <= ord('f')) {
            return ord($ch) - ord('a') + 10;
        }
        throw new InvalidArgumentException('invalid hex digit:'.$ch);
    }



    public static function analogMacBy3Des($key, $data)
    {

        $iv =  hex2bin('0000000000000000');
        $data = hex2bin($data);
        $key = hex2bin($key);
        $leftKey = substr($key, 0, 8);
        $rightKey = substr($key, 8, 8);
        /* 1、CBC-DES加密  left key */
        $result = openssl_encrypt($data, 'DES-CBC', $leftKey, OPENSSL_NO_PADDING, $iv);
        $ecbDecryptData = substr($result, strlen($result)-8, strlen($result));
        var_dump($ecbDecryptData);

        /* 2、 ECB-DES解密  right key */
        $ecbDecryptReturn  = openssl_decrypt($ecbDecryptData,'DES-ECB', $rightKey);
        var_dump($ecbDecryptReturn);
        /* 3、 ECB-DES加密  left key */
        $result = openssl_encrypt($ecbDecryptReturn, 'DES-ECB', $leftKey, OPENSSL_NO_PADDING);
        return self::convertByteToHexString($result);
    }

    /**
     * PBOC-3DES-MAC算法
     *
     * 计算MAC(hex) PBOC_3DES_MAC(符合ISO9797Alg3Mac标准) (16的整数补8000000000000000)
     * 前n-1组使用单长密钥DES 使用密钥是密钥的左8字节) 最后1组使用双长密钥3DES (使用全部16字节密钥)
     *
     * 算法步骤:初始数据为D,初始向量为I,3DES秘钥为K0,秘钥低8字节DES秘钥K1;
     * 1、数据D分组并且填充:将字节数组D进行分组,每组8个字节,分组编号从0开始,分别为D0...Dn;最后一个分组不满8字节的,先填充一个字节80,
     * 后续全部填充00,满8字节的,新增一个8字节分组(80000000 00000000);
     * 2、进行des循环加密:(1)D0和初始向量I进行按位异或得到结果O0;(2)使用秘钥K1,DES加密结果O0得到结果I1,
     * 将I1和D1按位异或得到结果D1;(3)循环第二步骤得到结果Dn; 3、将Dn使用16字节秘钥K0进行3DES加密,得到的结果就是我们要的MAC。
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static function calculatePboc3desMAC($data, $key, $iv)
    {
        $iv =  hex2bin($iv);
        $data = hex2bin($data);
        $key = self::createKeyBytes($key);
        if ($key == null || $data == null)
            throw new RuntimeException("data or key is null.");

        $leftKey = substr($key, 0, 8);;
        // 拆分数据(8字节块/Block)
        $dataLength = strlen($data);
        $blockCount = $dataLength / 8;
        $lastBlockLength = $dataLength % 8;
        $dataBlock = [];
        for ($i = 0; $i < $blockCount; $i++) {
            $copyLength = $i == $blockCount - 1 ? $lastBlockLength : 8;
            // 拷贝数组
            $dataBlock[$i] = substr($data, $i*8, $copyLength);
        }

        if($lastBlockLength == 0) {
            //$dataBlock[$blockCount-1] = hex2bin("8000000000000000");
        }else{
            // 补0+
            $blockCount = $blockCount +1;
            $needLen = 8-$lastBlockLength-2;
            $needHexData = bin2hex($dataBlock[$blockCount-1]).'80'.str_repeat('0', $needLen);
            $dataBlock[$blockCount-1] = hex2bin($needHexData);
        }
        // 1: 初始值 异或运算
        $iv = self::encryptByDesCbc($dataBlock[0], $leftKey, $iv);
        // 2: d1,d2,d2 加密后异或运算
        if($blockCount > 2) {
            for ($i = 1; $i < $blockCount; $i++) {
                $iv = self::encryptByDesCbc($dataBlock[$i], $leftKey, $iv);
            }
        }

        $desXor = self::encryptBy3DesCbc($dataBlock[$blockCount-1], $key, $iv);
        return bin2hex($desXor);
    }

    public static  function xor($binB1, $binB2)
    {
        $len = min(strlen($binB2),strlen($binB1)) ;
        $xor = '';
        for($i = 0; $i< $len; $i++) {
            $xor .= substr($binB1, $i, 1) ^ substr($binB2, $i, 1);

        }
        return $xor;

    }

    //转换为二进制流
    public static function asc2bin($temp)
    {
        $len = strlen($temp);
        $data ='';
        for ($i = 0; $i < $len; $i++) {
            $data .= sprintf('%08b', ord(substr($temp, $i, 1)));
        }
        return $data;
    }


    public static function encryptByDesCbc($data, $key, $iv)
    {
        $result = openssl_encrypt($data, 'des-cbc', $key, OPENSSL_NO_PADDING, $iv);
        return $result;
    }

    public static function decryptByDesCbc($data, $key, $iv)
    {
        $result = openssl_decrypt($data, 'des-cbc', $key, OPENSSL_NO_PADDING, $iv);
        return $result;
    }


    public static function encryptBy3DesCbc($data, $key, $iv)
    {
        // des-ede3-cbc
        $result = openssl_encrypt($data, 'DES-EDE3-CBC', $key, OPENSSL_NO_PADDING, $iv);
        return $result;
    }

    public static function calculate3desMAC($data, $key)
    {


        $iv =  hex2bin('0000000000000000');
        $data = hex2bin($data);
        $key = self::createKeyBytes($key);
        if ($key == null || $data == null) {
            return '';
        }

        $leftKey = substr($key, 0, 8);;
        // 拆分数据(8字节块/Block)
        $dataLength = strlen($data);
        $blockCount = $dataLength / 8 ;
        $dataBlock = [];
        $copyLength = 8;
        for ($i = 0; $i < $blockCount; $i++) {
            // 拷贝数组
            $dataBlock[$i] = substr($data, $i*8, $copyLength);
        }

        $des = self::encryptByDesCbc($dataBlock[0], $leftKey, $iv);
        $des = self::encryptBy3DesCbc($dataBlock[1], $key, $des);
        return bin2hex($des);
    }



    // apud 智能读卡器 和智能卡 的外部认证
    public static function getExternalAuthentication($hexData, $iv = '0000000000000000')
    {
        $VPENC = $VPMAC = $VPDEK = '404142434445464748494A4B4C4D4E4F';
        // 序列计数器: 25-28 4
        $VSeqCounter = substr($hexData, 24, 4);
        // 卡随机数: 29-40 12
        $VCardRandom = substr($hexData, 28, 12);
        // 卡密文:41-56 16
        $VCardCrypt = substr($hexData, 40, 4);

        $VSPENC_DerData = "0182".$VSeqCounter."000000000000000000000000";
        $VSPMAC_DerData = "0101".$VSeqCounter."000000000000000000000000";
        $VSPDEK_DerData = "0181".$VSeqCounter."000000000000000000000000";

        // 过程密钥
        $VSPENC = self::encryptMode($VPENC, $VSPENC_DerData, $iv);
        $VSPMAC = self::encryptMode($VPMAC, $VSPMAC_DerData, $iv);
        $VSPDEK = self::encryptMode($VPDEK, $VSPDEK_DerData, $iv);

        //;---计算主机密文---  [3DES CBC加密]
        // ;2字节计数器+6字节卡随机数+8字节主机随机数,共24字节
        $VDataforHostCrypt = $VSeqCounter.$VCardRandom.'0102030405060708'.'8000000000000000';
        $VHostCrypt = self::encryptMode($VSPENC, $VDataforHostCrypt, $iv);
        // 取最后 16个字符
        $VHostCrypt = substr($VHostCrypt, strlen($VHostCrypt)-16);
        // ;8字节初始值+5字节命令头+8字节MAC
        $VDataforCMAC = '8482'.'00'.'0010'.$VHostCrypt.'800000';
        // ;---计算C-MAC---
        $VCMAC = ThreeDesUtil::calculate3desMAC($VDataforCMAC, $VSPMAC);
        if(!$VCMAC) {
            return '';
        }
        // 8482(LEVEL)0010(VDataIn)
        // CAT(VDataIn, VHostCrypt, VCMAC)
        $VDataIn = $VHostCrypt.$VCMAC;
        $apud = '8482000010'.$VDataIn;
        return strtoupper($apud);

    }
}




aes-128