AES php java 互转
php
<?php class AesCrypt { //初始化向量 private $iv = '84195bd96a8a2e7f'; //Mcrypt算法 private $cipher = MCRYPT_RIJNDAEL_128; //Mcrypt支持的加密模型 特别适用于对文件进行加密。 相比 ECB, 它的安全性有明显提升。 private $mode = MCRYPT_MODE_CBC; public function encrypt($str, $key = '') { $str = $this->addPKCS7Padding($str); $encrypted = mcrypt_encrypt($this->cipher, $key, $str, $this->mode, $this->iv); return $encrypted; } public function decrypt($code, $key = '') { $decrypted = mcrypt_decrypt($this->cipher, $key, $code, $this->mode, $this->iv); $decrypted = $this->stripPKSC7Padding($decrypted); return $decrypted; } protected function addPKCS7Padding($source) { $block = mcrypt_get_block_size($this->cipher, $this->mode); $pad = $block - (strlen($source) % $block); $char = chr($pad); $source .= str_repeat($char, $pad); return $source; } public function stripPKSC7Padding($source) { $char = substr($source, -1); $num = ord($char); $source = substr($source, 0, -$num); return $source; } }
注意:php 的 mcrypt_簇 在 7.1.0 版本中开始 deprecated,并在 7.2.0 版本中彻底废弃 ,可以增加@来抑制报错
mcrypt always pads data will the null ('\0') character to fill out to n * blocksize
mcrypt_簇 和 openssl_族对应关系
注意 MCRYPT_RIJNDAEL_256
并不是 AES-256
,如果想使用mcrypt_簇
实现 AES-256
,则你应该使用 MCRYPT_RIJNDAEL_128
算法 + 32
位的 key
MCRYPT_RIJNDAEL_128 & MCRYPT_MODE_CBC + 16位Key = openssl_encrypt(AES-128-CBC, 16位Key) = AES-128 MCRYPT_RIJNDAEL_128 & MCRYPT_MODE_CBC + 24位Key = openssl_encrypt(AES-192-CBC, 24位Key) = AES-192 MCRYPT_RIJNDAEL_128 & MCRYPT_MODE_CBC + 32位Key = openssl_encrypt(AES-256-CBC, 32位Key) = AES-256
java
/** * AES加解密 for原创php代码 * 非PHP重构代码不要使用该方法 */ @Slf4j public class OgnvAesCrypt { /** * 使用固定长度密钥 */ private static final int KEY_LENGTH = 16; /** * 算法/模式/填充方式 */ private static final String AES_CIPHER = "AES/CBC/PKCS7Padding"; private static final String AES_ALGORITHM = "AES"; private static final IvParameterSpec IV = new IvParameterSpec("84195bd96a8a2e7f".getBytes()); static { //support PKCS7Padding if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { Security.addProvider(new BouncyCastleProvider()); } } /** * aes解密 * @param data 加密数据 * @param key 解密密钥 * @return null 参数不符合要求或解密失败 */ public static byte[] decrypt(byte[] data, byte[] key) { if (data == null || key == null) { return null; } if (key.length != KEY_LENGTH) { throw new RuntimeException("Invalid AES key length (must be 16 bytes)"); } try { SecretKeySpec secretKey = new SecretKeySpec(key, AES_ALGORITHM); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, AES_ALGORITHM); Cipher cipher = Cipher.getInstance(AES_CIPHER); cipher.init(Cipher.DECRYPT_MODE, seckey, IV); return cipher.doFinal(data); } catch (Exception e) { log.error("OgnvAesCrypt decrypt fail:{}", e.getMessage(), e); } return null; } /** * aes加密 * @param data 需要加密的内容 * @param key 加密密钥 * @return 参数不符合要求或加密失败 */ public static byte[] encrypt(byte[] data, byte[] key) { if (data == null || key == null) { return null; } if (key.length != KEY_LENGTH) { throw new RuntimeException("Invalid AES key length (must be 16 bytes)"); } try { SecretKeySpec secretKey = new SecretKeySpec(key, AES_ALGORITHM); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, AES_ALGORITHM); Cipher cipher = Cipher.getInstance(AES_CIPHER); cipher.init(Cipher.ENCRYPT_MODE, seckey, IV); return cipher.doFinal(data); } catch (Exception e) { log.error("OgnvAesCrypt encrypt fail:{}", e.getMessage(), e); } return null; } }
AES 是 Rijndael 子集
AES has a fixed block size of 128 bits and a key size of 128, 192, or 256 bits,
whereas Rijndael can be specified with block and key sizes in any multiple of 32 bits, with a minimum of 128 bits and a maximum of 256 bits.
BouncyCastle 是一个提供了很多 Java标准库 哈希算法和加密算法的第三方库
PKCS5Padding是不支持AES算法的,当 Cipher.getInstance("AES/CBC/PKCS5Padding") 时实际使用的是PKCS7Padding,可能是历史遗留问题
pkcs5 vs pkcs7
pkcs5是pkcs7的子集算法
pkcs5: blockSize固定为8byte
pkcs7: blockSize固定为 1~255byte
生成指定长度密钥
口令可以为任意长度,但是不同加密算法对密钥长度有严格要求
//口令 String password = "jksong"; //密钥生成器 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //SHA1PRNG随机算法为:原始秘钥经过两次sha1加密 //php对应代码: substr(openssl_digest(openssl_digest($key, 'sha1', true), 'sha1', true), 0, 密钥位数); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(password.getBytes()); //设置密钥的大小和随机源 keyGenerator.init(128, secureRandom); //生成唯一秘钥 SecretKey secretKey = keyGenerator.generateKey(); byte[] encoded = secretKey.getEncoded(); System.out.println(Hex.encodeHexString(encoded));
参考
mcrypt_encrypt 迁移至 openssl_encrypt 的方法
difference-between-pkcs5-padding-and-pkcs7-padding