javax.crypto.BadPaddingException

2020-01-09  本文已影响0人  流沙飞雪

在做AES加解密的时候遇到这个问题,发现win现每次加密结果都一样,但是linux下每次都是随机的,后来在网上找到原因。

原因:SecureRandom 实现完全随操作系统本身的內部状态,除非调用方在调用 getInstance 方法,然后调用 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。关于SecureRandom类的详细介绍,参考网上文章

解决办法:调用getInstance方法

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG") ;
secureRandom.setSeed(encodeRules.getBytes());

最终代码如下:

@Slf4j
public class AESUtil {
    /**
     * 注意key和加密用到的字符串是不一样的 加密还要指定填充的加密模式和填充模式 AES密钥可以是128或者256,加密模式包括ECB, CBC等 * ECB模式是分组的模式,CBC是分块加密后,每块与前一块的加密结果异或后再加密 第一块加密的明文是与IV变量进行异或
     */
    public static final String KEY_ALGORITHM = "AES";
    public static final String ECB_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    public static final String RANDOM_ALGORITHM = "SHA1PRNG";
    public static final String ENCODING = "UTF-8";

    /*
     * 加密
     * 1.构造密钥生成器
     * 2.根据ecnodeRules规则初始化密钥生成器
     * 3.产生密钥
     * 4.创建和初始化密码器
     * 5.内容加密
     * 6.返回字符串
     */
    public static SecretKeySpec genKey(String myKey) {
        //MessageDigest sha = null;
        try {
            //1.构造密钥生成器,指定为AES算法,不区分大小写
            KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
            //2.根据myKey规则初始化密钥生成器
            //生成一个128位的随机源,根据传入的字节数组
            SecureRandom secureRandom = SecureRandom.getInstance(RANDOM_ALGORITHM);
            secureRandom.setSeed(myKey.getBytes());
            kgen.init(128, new SecureRandom(myKey.getBytes()));
            //3.产生原始对称密钥
            SecretKey secretKey = kgen.generateKey();
            //4.获得原始对称密钥的字节数组
            byte[] encodeFormat = secretKey.getEncoded();
            //5.根据字节数组生成AES密钥
            return new SecretKeySpec(encodeFormat, KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            log.error("初始化密钥错误:", e);
        }
        return null;
    }

    /**
     * 使用ECB模式进行加密。 加密过程三步走: 1. 传入算法,实例化一个加解密器 2. 传入加密模式和密钥,初始化一个加密器 3. * 调用doFinal方法加密
     *
     * @param strToEncrypt
     * @param secret
     * @return
     */
    public static String encrypt(String strToEncrypt, String secret) {
        try {
            //6.根据指定算法AES自成密码器
            Cipher cipher = Cipher.getInstance(ECB_CIPHER_ALGORITHM);
            //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
            cipher.init(Cipher.ENCRYPT_MODE, genKey(secret));
            //8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            //9.根据密码器的初始化方式--加密:将数据加密 cipher.doFinal
            //10.将加密后的数据转换为字符串
            //这里用Base64Encoder中会找不到包
            //解决办法:
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(ENCODING)));
        } catch (Exception e) {
            log.error("AES加密错误: ", e);
        }
        return null;
    }

    /**
     * 解密
     *
     * @param strToDecrypt
     * @param secret
     * @return
     */
    public static String decrypt(String strToDecrypt, String secret) {
        try {
            Cipher cipher = Cipher.getInstance(ECB_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, genKey(secret));
            return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt.getBytes(ENCODING))));
        } catch (Exception e) {
            log.error("AES解密错误: ", e);
        }
        return null;
    }

    public static void main(String[] args) {
        final String secretKey = "cZmmOQM#j2X32OaR";

        String originalString = "44,2020-01-09";
        String encryptedString = AESUtil.encrypt(originalString, secretKey);
        String decryptedString = AESUtil.decrypt(encryptedString, secretKey);

        System.out.println(originalString);
        System.out.println(encryptedString);
        System.out.println(decryptedString);
    }
}

本文参考 https://blog.csdn.net/seapeak007/article/details/79747309

上一篇下一篇

猜你喜欢

热点阅读