java AES 加密,报javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decryp

java 使用AES解密报这个异常,字面理解很容易,就是解密的字符串的数组必须是16的倍数

javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher

    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:922)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:833)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at com.symmetric.aes.TestAES.testDecrpyt(TestAES.java:200)
    at com.symmetric.aes.TestAES.main(TestAES.java:48)


1.使用的代码:

package com.symmetric.aes;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

public class TestAES2 {

	private static final String enType_AES = "AES";
	private static final String pathStr = "D://aes.key";
	private static final String testStr = "AES tips you shoutld try again";
	
	
	public static void main(String[] args) {
		SecretKey genSecretKey = testGenerateKey();
		String string = genSecretKey.toString();
		
//		byte[] testEncryptBytes = testEncryptBytes(testStr, genSecretKey);
//		String testDecode = testDecrptBytes(testEncryptBytes, genSecretKey);
//		System.out.println(testDecode);  //直接操作数组,加密解密正常
		String testEncrypt = testEncrypt(testStr, genSecretKey);
		String testDecode2 = testDecrpt(testEncrypt, genSecretKey);
		System.out.println(testDecode2);
	}
	
	/**
	 * 生成密钥
	 * 通过传递SecureRandom对象进行初始化
	 * 不指定种子或密钥
	 * @return
	 */
	public static SecretKey testGenerateKey(){
		SecretKey genSecretKey = null;
		try {
			KeyGenerator kGenerator = KeyGenerator.getInstance(enType_AES);
			SecureRandom sRandom = new SecureRandom();
			kGenerator.init(sRandom);//不使用种子,每次生成的都不同
			genSecretKey = kGenerator.generateKey();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return genSecretKey;
	}
	
	
	/**
	 * 加密
	 * @param str
	 * @param genSecretKey
	 * @return
	 */
	public static byte[] testEncryptBytes(String str,SecretKey genSecretKey){
		byte encrypt [] = null;
		try {
			Cipher cipher = Cipher.getInstance(enType_AES);
			cipher.init(Cipher.ENCRYPT_MODE, genSecretKey);//加密模式,密钥
			//cipher.update(str.getBytes());
			encrypt = cipher.doFinal(str.getBytes());
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) {
			e.printStackTrace();
		}
		return encrypt;
	}
	
	public static String testEncrypt(String str,SecretKey genSecretKey){
		byte[] testEncryptBytes = testEncryptBytes(str, genSecretKey);
		System.out.println(Arrays.toString(testEncryptBytes));
		System.out.println("加密后的数组长度"+testEncryptBytes.length);
		return (new String(testEncryptBytes));
	}
	
	
	
	
	/**
	 * 解密
	 * @param str
	 * @param genSecretKey
	 * @return
	 */
	public static String testDecrptBytes(byte[] bytes,SecretKey genSecretKey){
		String decoderStr = null;
		try {
			Cipher cipher = Cipher.getInstance(enType_AES);
			cipher.init(Cipher.DECRYPT_MODE, genSecretKey);//解密模式
			//cipher.update(str.getBytes());
			byte[] doFinal = cipher.doFinal(bytes);
			decoderStr = new String(doFinal);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
			e.printStackTrace();
		}
		return decoderStr;
	}
	
	public static String testDecrpt(String str,SecretKey genSecretKey){
		System.out.println(Arrays.toString(str.getBytes()));
		System.out.println("数组的大小"+str.getBytes().length);
		return testDecrptBytes(str.getBytes(), genSecretKey);
	}
}

2.分析出现此异常的情况:

如果不把加密后的数组拼接为字符串,直接返回,然后使用这个加密后的数组进行解密没有任何错误

但是把加密后的数组拼接为字符串,然后解密时在把此字符串转为数组,就会出现此异常


3.具体分析:

发现当把字节数组转为字符串后,在把字符串.getBytes()获得数组,发现两个字节数组前后不一样了,,这是报错的位置所在

声明:new String(byte[]),和"str".getBytes(),两个方法使用的编码一样,然后换成其他编码也出现这样情况


3.原因:

3.1. 为什么数组转字符串,字符串然后转数组会出现,前后两个字节数组的值会不同,因为并不是每个字节数和编码集上的字符都有对应关系,如果一个字节数在编码集上没有对应,编码new String(byte[]) 后,往往解出来的会是一些乱码无意义的符号:例如:��,

但是解码的时候,�这个字符也是一个字符在编码表中也有固定的字节数用来表示,所有解码出来的值必定是编码表中对应的值,除非你的字节数组中的字节数正好在编码表中有对应的值,否则编码,解码后的字节数组会不一样

误区: 误以为所有的字节数组都可以new String(),然后在通过String.getBytes()还原

3.2.再说这个异常报:解密的字节数组必须是16的倍数,这得从AES的原理说起,AES是把数据按16字节分组加密的,所有如果数组长度不是16的倍数会报错

AES原理:AES是对数据按128位,也就是16个字节进行分组进行加密的,每次对一组数据加密需要运行多轮。而输入密钥的长度可以为128、192和256位,也就是16个字节、24个字节和32个字节,如果用户输入的密钥长度不是这几种长度,也会补成这几种长度。无论输入密钥是多少字节,加密还是以16字节的数据一组来进行的,密钥长度的不同仅仅影响加密运行的轮数。


4.解决的办法:

4.1 可以用base64对参生的数组进行编码,然后在解码,这样不会像new String(byte[]),getBytes()那样造成数组前后不一致,一开始,我看到大部分人都是用base64,我也只是以为多一层编码看起来安全一些而已,没想到base64对数组的处理是不会造成误差的

4.2 就是直接返回数组,然后再用数组解密咯

你可能感兴趣的:(java,exception,security)