php使用aes加密

2021-09-11
/**
 * @description: aes各模式加解密
 */
class AesAlgorithm
{
    /**
     * @description: 秘钥长度
     * @var int
     */
    protected $keyLength;

    /**
     * @description: 加密算法
     * @var string
     */
    protected $algotithm;

    /**
     * @description: 加密位数-加密秘钥长度映射
     * @var array
     */
    protected $keyLengths = [
        128 => 16,
        192 => 24,
        256 => 32,
    ];

    /**
     * @description: 加密模式对应iv长度
     * @var array
     */
    protected $ivLength = [
        'cbc' => 16,
        'ecb' => 0,
        'cfb' => 16,
        'ofb' => 16,
        'ccm' => 12,
        'gcm' => 12,
        'ocb' => 12,
        'ctr' => 16,
        'xts' => 16,
    ];

    /**
     * @description: 验证算法
     * @param {*}
     * @return {*}
     */
    public function verifyAlgotithm()
    {
        list($aes, $length, $mode) = explode('-', $this->algotithm, 3);
        $this->keyLength = $this->keyLengths[$length] ?? false;
        if (!$this->keyLength) {
            throw new \Exception('AES加密长度错误');
        }
        if (!method_exists($this, $mode)) {
            throw new \Exception('AES加密模式错误');
        }
    }
    
    /**
     * @description: ecb模式,简单,有利于并行计算,误差不会被传送;
     *                          但不能隐藏明文的模式,可能对明文进行主动攻击
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充 0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function ecb(string $data, int $digits, string $key, int $options = 0, bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-ecb';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options));
        }
        echo base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options)) . PHP_EOL;
        return openssl_encrypt($data, $this->algotithm, $this->_key($key), $options);
    }

    /**
     * @description: cbc模式,不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
     *                          但不利于并行计算,误差会传递,需要初始化向量IV
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充  0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param string $iv 初始化向量
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function cbc(string $data, int $digits, string $key, int $options = 0, string $iv = '', bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-cbc';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
        }
        return base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
    }
    
    /**
     * @description: cfb模式,隐藏了明文模式,分组密码转化为流模式,可以及时加密传送小于分组的数据;
     *                          但是不利于并行计算;误差传送:一个明文单元损坏影响多个单元;唯一的IV;
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充  0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param string $iv 初始化向量
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function cfb(string $data, int $digits, string $key, int $options = 0, string $iv = '', bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-cfb';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
        }
        return base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
    }
    
    /**
     * @description: ofb模式,隐藏了明文模式;分组密码转化为流模式;可以及时加密传送小于分组的数据;
     *                           但不利于并行计算;对明文的主动攻击是可能的;误差传送:一个明文单元损坏影响多个单元;
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充  0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param string $iv 初始化向量
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function ofb(string $data, int $digits, string $key, int $options = 0, string $iv = '', bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-ofb';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
        }
        return base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
    }

    /**
     * @description: ctr模式,Counter
     *                       计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。
     *                       这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充  0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param string $iv 初始化向量,ecb模式不需要,其他模式需要,且长度不一
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function ctr(string $data, int $digits, string $key, int $options = 0, string $iv = '', bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-ctr';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
        }
        return base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options, $this->_iv($iv)));
    }

    /**
     * @description: ccm模式,在无线通信系统中较为常用的一种加密算法,因其效率和安全性都具有较高的标准,被广泛的应用于802.XX和BLE的协议中;
     *                        包括CCM算法和AES算法两部分
     *                        解密时需要加密时引用传递的tag
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充  0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param string $iv 初始化向量
     * @param string $tag 使用AEAD密码模式(GCM 或 CCM)时传引用的验证标签。
     * @param string $aad 附加的验证数据(GCM 或 CCM)模式
     * @param int $tag_length 验证tag的长度。GCM模式时,它的范围是4到16,解密时不需要(GCM 或 CCM)模式
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function ccm(string $data, int $digits, string $key, int $options = 0, string $iv = '', string $tag = NULL, string $aad = '', int $tag_length = 16, bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-ccm';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options, $this->_iv($iv), $tag, $aad));
        }
        return base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options, $this->_iv($iv), $tag, $aad, $tag_length));
    }

    /**
     * @description: gcm模式,对称加密采用Counter模式,并带有GMAC消息认证码;一种非常快速但可以说是复杂的CTR模式和GHASH的组合
     * 解密时需要加密时引用传递的tag
     * @param string $data 需要加解密的数据
     * @param int $digits 加密位数
     * @param string $key 加密key,注意key长度
     * @param int $options 加密填充  0(pkcs7填充返回base64编码内容)其他方式填充返回空字符串;OPENSSL_RAW_DATA (pkcs7填充返回二进制数据)
     * @param string $iv 初始化向量
     * @param string $tag 使用AEAD密码模式(GCM 或 CCM)时传引用的验证标签。
     * @param string $aad 附加的验证数据(GCM 或 CCM)模式
     * @param int $tag_length 验证tag的长度。GCM模式时,它的范围是4到16,解密时不需要(GCM 或 CCM)模式
     * @param bool $isDe 解密/加密
     * @return {*}
     */
    public function gcm(string $data, int $digits, string $key, int $options = 0, string $iv = '', string $tag = NULL, string $aad = '', int $tag_length = 16, bool $isDe = false):string
    {
        $this->algotithm = 'aes-' . (string)$digits . '-gcm';
        $this->verifyAlgotithm();
        if ($isDe) {
            return trim(openssl_decrypt(base64_decode($data), $this->algotithm, $this->_key($key), $options, $this->_iv($iv), $tag, $aad));
        }
        return base64_encode(openssl_encrypt($data, $this->algotithm, $this->_key($key), $options, $this->_iv($iv), $tag, $aad, $tag_length));
    }

    /**
     * @description: ocb模式,它允许一次通过加密和身份验证。 但是在美国有专利;php加密后无法解密
     * @return {*}
     */
    private function _ocb()
    {

    }

    /**
     * @description: xts模式,适用于网络存储的加密模式,无法加解密,192位没有此模式
     * @return {*}
     */
    private function _xts()
    {

    }

    /**
     * @description: key处理,补全/截断指定长度
     * @param string $key
     * @return {*}
     */
    private function _key($key)
    {
        return substr(str_pad($key, $this->keyLength, 0), 0, $this->keyLength);
    }

    /**
     * @description: iv处理,补全/截断指定长度
     * @param string $iv
     * @return {*}
     */
    private function _iv($iv)
    {
        $ivLength = openssl_cipher_iv_length($this->algotithm);
        return substr(str_pad($iv, $ivLength, 0), 0, $ivLength);
    }

    /**
     * @description: 获取openSSL库的错误,错误消息已被队列化,可以查询到多条错误信息,最后一个是最近的一个错误。
     * @param 
     * @return array
     */
    public function errors()
    {
        $result = [];
        while ($data = openssl_error_string() !== false) {
            $result[] = $data;
        }
        return $result;
    }
}

 

{/if}