•  

在PHP里实现AES的加密解密功能(包括mcrypt版本和openssl版本)

发表于 2020-08-19,阅读 19

介绍

介绍

福哥需要在php里使用AES加密解密功能,今天整理出来和大家分享一下

早期的PHP实现AES借助的是mcrypt扩展,后来在PHP7之后就换成了openssl扩展来实现了。mcrypt版本代码比较复杂且需要自己实现PKCS7补位的逻辑,而openssl版本则默认使用了PKCS7补位不需要自己来编写代码实现了

通过openssl实现

需要安装php扩展openssl,具体方法就不提供了,php的扩展的安装方式都一样,php7.1以上的版本支持了openssl模块

加密解密对象

加密解密对象,默认 AES-256-CBC 方法

class AES_Encrypt{
    const BLOCK_SIZE = 32;

    private string $method;

    public function __construct(string $method = null){
        if($method == null){
            $method = "AES-256-CBC";
        }
        $this->method = $method;
    }

    public function pkcs7Pad($str){
        $len = mb_strlen($str, '8bit');
        $c = 16 - ($len % 16);
        $str .= str_repeat(chr($c), $c);

        return $str;
    }

    private function pkcs7Encode($text){
        $text_length = strlen($text);

        $amount_to_pad = AES_Encrypt::BLOCK_SIZE - ($text_length % AES_Encrypt::BLOCK_SIZE);
        if ($amount_to_pad == 0) {
            $amount_to_pad = AES_Encrypt::BLOCK_SIZE;
        }

        $pad_chr = chr($amount_to_pad);
        $tmp = "";
        for ($index = 0; $index < $amount_to_pad; $index++) {
            $tmp .= $pad_chr;
        }

        return $text . $tmp;
    }

    private function pkcs7Decode($text){
        $pad = ord(substr($text, -1));
        if ($pad < 1 || $pad > 32) {
            $pad = 0;
        }

        return substr($text, 0, (strlen($text) - $pad));
    }

    public function encrypt(string $data, string $key, string $iv):string {
        $data = $this->pkcs7Encode($data);
        $encrypted = openssl_encrypt($data, $this->method, $key,OPENSSL_ZERO_PADDING, $iv);

        return $encrypted;
    }

    public function decrypt(string $data, string $key, string $iv):string {
        $decrypted = openssl_decrypt($data, $this->method, $key,OPENSSL_ZERO_PADDING, $iv);
        $data = $this->pkcs7Decode($decrypted);

        return $data;
    }
}

options

openssl_encrypt和openssl_decrypt的第三个参数是options,它有着很重要的作用,我们来了解一下

  • 0:默认模式,自动进行 pkcs7 补位,同时自动进行 base64 编码

  • 1:OPENSSL_RAW_DATA,自动进行 pkcs7 补位, 但是不自动进行 base64 编码

  • 2:OPENSSL_ZERO_PADDING,需要自己进行 pkcs7 补位,同时自动进行 base64 编码

通过mcrypt实现

需要安装php扩展mcrypt,具体方法就不提供了,php的扩展的安装方式都一样,php7.1以下的版本支持mcrypt模块

加密解密对象

加密解密对象,默认 AES-128-CBC 方法

class AES_Encrypt{
    const BLOCK_SIZE = 32;

    private $RIJNDAEL;
    private $MODE;

    public function __construct($method = null){
        if($method == null){
            $method = "AES-128-CBC";
        }
        $this->RIJNDAEL = null;
        $this->MODE = null;
        $this->methodArr = explode("-", $method);
        switch($this->methodArr[1]){
            case "128":
                $this->RIJNDAEL = MCRYPT_RIJNDAEL_128;
                break;
            case "192":
                $this->RIJNDAEL = MCRYPT_RIJNDAEL_192;
                break;
            case "256":
                $this->RIJNDAEL = MCRYPT_RIJNDAEL_256;
                break;
        }
        switch($this->methodArr[2]){
            case "CBC":
                $this->MODE = MCRYPT_MODE_CBC;
                break;
            case "CFB":
                $this->MODE = MCRYPT_MODE_CFB;
                break;
            case "ECB":
                $this->MODE = MCRYPT_MODE_ECB;
                break;
            case "NOFB":
                $this->MODE = MCRYPT_MODE_NOFB;
                break;
            case "OFB":
                $this->MODE = MCRYPT_MODE_OFB;
                break;
            case "STREAM":
                $this->MODE = MCRYPT_MODE_STREAM;
                break;
        }
        if($this->RIJNDAEL == null || $this->MODE == null){

            throw new Exception("invalid RIJNDAEL or MODE about '". $method. "'", 2000100);
        }
    }

    public function pkcs7Pad($str){
        $len = mb_strlen($str, '8bit');
        $c = 16 - ($len % 16);
        $str .= str_repeat(chr($c), $c);

        return $str;
    }

    private function pkcs7Encode($text){
        $text_length = strlen($text);

        $amount_to_pad = AES_Encrypt::BLOCK_SIZE - ($text_length % AES_Encrypt::BLOCK_SIZE);
        if ($amount_to_pad == 0) {
            $amount_to_pad = AES_Encrypt::BLOCK_SIZE;
        }

        $pad_chr = chr($amount_to_pad);
        $tmp = "";
        for ($index = 0; $index < $amount_to_pad; $index++) {
            $tmp .= $pad_chr;
        }

        return $text . $tmp;
    }

    private function pkcs7Decode($text){
        $pad = ord(substr($text, -1));
        if ($pad < 1 || $pad > 32) {
            $pad = 0;
        }

        return substr($text, 0, (strlen($text) - $pad));
    }

    public function encrypt($data, $key, $iv){
        $data = $this->pkcs7Encode($data);
        $encrypted = mcrypt_encrypt($this->RIJNDAEL, $key, $data, $this->MODE, $iv);
        $based_encrypted = base64_encode($encrypted);

        return $based_encrypted;
    }

    public function decrypt($data, $key, $iv){
        $data = base64_decode($data);
        $encrypted = mcrypt_decrypt($this->RIJNDAEL, $key, $data, $this->MODE, $iv);
        $encrypted = $this->pkcs7Decode($encrypted);

        return $encrypted;
    }
}

使用

使用AES加密需要原数据、AES私钥、令牌,下面给出一个例子

注意:在openssl版本里的AES-256-CBC方法对应mcrypt版本里的AES-128-CBC,也是微信公众号服务器接口使用的AES算法的方法

示例

准备一个字符串,加密后再解密,最后如果和原字符串相同,证明函数工作正常

$dataOrg = "我是福哥,I like coding!!!";
$AESKey = "rN6LfP9qbILPabc938IixdFds3s5ksIqjcPyYxOPx4v";
$iv = "";

// 初始化
$myAES_Encrypt = new AES_Encrypt();

// 加密字符串
$dataEncrypted = $myAES_Encrypt->encrypt($dataOrg, $AESKey, $iv);

// 解密字符串
$dataDecrypted = $myAES_Encrypt->decrypt($dataEncrypted, $AESKey, $iv);

// 比较
var_dump($dataOrg == $dataDecrypted);


PHP
鬼谷子叔叔
  • 日志:212
  • 回复:13

进入ta的主页