PHP AES/CBC/128/PKCS7Padding 实现

AES-128-CBC

在 PHP 中实现 AES 加密, 常见的方法包括使用 mcrypt 或 OpenSSL

使用 mcrypt 扩展

适用于较老的 PHP 版本 (如 PHP 5.x 和部分 7.x)。mcrypt 扩展从 PHP 7.2 开始被废弃, 且需要额外安装。

安装 mcrypt 扩展

CentOS 7 / PHP 7.2

yum install php72-mcrypt

Debian / Ubuntu / PHP 7.0

sudo apt-get install php7-mcrypt

AES-128-CBC 加密类

// AES-128-CBC 加密类
class AES {
    private static $key = 'YourKeyxxxxxxxxx'; // 密钥
    private static $iv = 'YourIvxxxxxxxxxx';  // 初始化向量

    // 加密方法
    public static function encrypt($data) {
        $encrypted = mcrypt_encrypt(
            MCRYPT_RIJNDAEL_128,        // 使用 AES-128
            self::$key,                 // 密钥
            self::pkcs7_pad($data),     // 填充数据
            MCRYPT_MODE_CBC,            // CBC 加密模式
            self::$iv                   // IV 初始化向量
        );

        return base64_encode($encrypted);
    }

    // 解密方法
    public static function decrypt($data) {
        $decrypted = mcrypt_decrypt(
            MCRYPT_RIJNDAEL_128,        // 使用 AES-128
            self::$key,                 // 密钥
            base64_decode($data),       // 解码后的数据
            MCRYPT_MODE_CBC,            // CBC 解密模式
            self::$iv                   // IV 初始化向量
        );

        return self::pkcs7_unpad($decrypted); // 去除填充
    }

    // PKCS7 填充方法
    private static function pkcs7_pad($text) {
        $blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad); // 填充至整块
    }

    // 去除 PKCS7 填充
    private static function pkcs7_unpad($text) {
        $len = strlen($text);
        $pad = ord($text[$len - 1]); // 获取填充字节值
        return substr($text, 0, $len - $pad); // 去除填充
    }
}

使用实例

$data = "test";

// 加密
$encrypted = AES::encrypt($data);
echo $encrypted . "\n";

// $privateKey = "7854156156611111";
// $iv = "0000000000000000";

// 解密
$encryptedData = "L7AswKt5/t1gND4ct22Odw==";
$decrypted = AES::decrypt($encryptedData);
echo $decrypted . "\n";

使用 OpenSSL 实现 AES 加密

对于现代 PHP 版本, mcrypt 已经被废弃, 建议使用 openssl_encryptopenssl_decrypt。以下是实现 AES 加密和解密的代码, 采用 PKCS7 填充

class AES {
    /**
     * @param string $string 需要加密的字符串
     * @param string $key 密钥
     * @return string
     */
    public static function encrypt($string, $key) {
        // 使用 SHA1PRNG 算法生成加密用的 16 字节密钥 (Java 兼容)
        $key = substr(openssl_digest(openssl_digest($key, 'sha1', true), 'sha1', true), 0, 16);
        
        // 生成随机的 16 字节 IV
        $iv = openssl_random_pseudo_bytes(16);
        
        // 使用 AES-128-CBC 加密
        $data = openssl_encrypt($string, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
        
        // 将 IV 和加密数据合并, 并转为 HEX 格式字符串
        $data = strtoupper(bin2hex($iv . $data));
        return $data;
    }

    /**
     * @param string $string 需要解密的字符串
     * @param string $key 密钥
     * @return string
     */
    public static function decrypt($string, $key) {
        // 使用 SHA1PRNG 算法生成解密用的 16 字节密钥 (Java 兼容)
        $key = substr(openssl_digest(openssl_digest($key, 'sha1', true), 'sha1', true), 0, 16);
        
        // 将 HEX 格式字符串转换为二进制数据
        $data = hex2bin($string);
        
        // 从数据中提取 IV (前 16 字节) 和密文
        $iv = substr($data, 0, 16);
        $cipherText = substr($data, 16);
        
        // 使用 AES-128-CBC 解密
        $decrypted = openssl_decrypt($cipherText, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
        return $decrypted;
    }
}

// 测试
$encrypt = AES::encrypt('test', 'blog.acesheep.com');
$decrypt = AES::decrypt($encrypt, 'blog.acesheep.com');
echo "加密后: " . $encrypt . "\n";
echo "解密: " . $decrypt . "\n";

初始化向量 (IV)

  1. CBC 模式要求一个初始化向量 (IV), 长度必须与块大小一致 (AES 的块大小为 16 字节)
  2. 加密时, 使用 openssl_random_pseudo_bytes(16) 生成随机 IV
  3. IV 必须与密文一起存储, 因为解密时需要它

原文

AES/CBC/128/PKCS5Padding加密解密算法 (iOS、Android、JavaScript、PHP)
PHP AES Encryption/Decryption Class with PKCS5 padding
PHP AES加解密(AES-128-ECB|sha1|bin2hex)
md5 Hash Generator
在线AES加密解密、AES在线加密解密、AES encryption and decryption

最后更新于 2019-07-10
使用 Hugo 构建
主题 StackJimmy 设计