C语言 c++ php mysql nginx linux lnmp lamp lanmp memcache redis 面试 笔记 ppt 设计模式 问题 远程连接

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));

 

 

参考

  PKCS7 / PKCS5 填充算法

  mcrypt_encrypt 迁移至 openssl_encrypt 的方法

  difference-between-pkcs5-padding-and-pkcs7-padding

  aes-rsa-java

  sha1prng

 

posted on 2021-02-28 11:56  思齐_  阅读(782)  评论(0编辑  收藏  举报