Java实现AES的128、256位密钥加解密算法 并 解决Illegal key size or default parameters问题

Android开发中为了代码在传输过程中的安全,会对代码进行一些加密,Android中常用的加密方式,细数有一下几种:

1、DSA加密

2、RSA加密

3、DES加密
4、AES加密
5、MD5算法
6、Base64加密算法
7、异或加密算法

AES、DES为对称加密,RSA、DSA为非对称加密。今天只详细讲解AES,其他的后期再补充。今天主要是实现AES的128、256位密钥加解密 并 解决Illegal key size or default parameters问题。在AES之前,DES加密比较流行,其密钥较短、加密处理简单,加密速度快,但后来有人通过网络联合运算的方式短时间之内破解了DES的加密数据,使之成为了一种不安全的加密方式,新版本的DES的加密效率有太低;相对来说AES使用的密钥更长(分128、192、256位),内部有更简洁精确的数学算法。


一、128位密钥的加解密算法

因cipher.doFinal(byteContent)加密后的数据是个byte[]类型的数组,而有些时候我们需要的是个String类型的字符创,所以对于加密后的结果进行处理时,用了两种方式做了处理:一种是将加密结果转化为十六进制的数据再做处理;一种是用Base64做完转换功能再做处理。那么有人会问了,为什么不直接将byte[]直接转换为String呢,请接着往下看,代码注释中和最后总结中都有答案。

在这儿,先做一下前期准备:

1、如果想了解所谓的:“加密服务提供者”——即:Provider,“所请求算法的名称”——即:algorithm,“转换名称”——即:transformation(如:AES/ECB/PKCS7Padding)等这样的字眼字眼的详细内容,那就跟我来

2、如果对加密后的数据进行转码处理时选择了用Base64加密(这个下文会说的更详细一些),需要导入个jar包(如果使用Eclipse开发,需要下载并导入jar包,如果是Android Studio开发,则添加依赖就行):

其下载地址为:


(方法一:将加密结果转化为十六进制的数据再做处理)

加解密的代码里面需要用到几个类(接口),我已尽可能做了详细的注释,如果想进一步了解其内容,可以看已下API:

用到的重要API地址:

KeyGenerator ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/javax/crypto/KeyGenerator.html

SecureRandom ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/java/security/SecureRandom.html

MessageDigest ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/security/MessageDigest.html

SecretKey ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/javax/crypto/SecretKey.html

Cipher ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/javax/crypto/Cipher.html

String ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/lang/String.html

Integer ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/lang/Integer.html


1、加密:

/**
 * 加密
 * 
 * @param content	要加密的内容
 * @param key		用来生成128位密钥的密码
 * @return
 */
public static String encrypt(String content, String key) {  
        try {    
            /**
             *  KeyGenerator : 是个类,此类提供(对称)密钥生成器的功能。在生成密钥后,可以重复使用同一个 KeyGenerator 对象来生成更多的密钥。
             *  使用其中的getInstance(String algorithm)方法进行构造对象;
             *  algorithm : 所请求密钥算法的标准名称。
             */
            KeyGenerator kgen = KeyGenerator.getInstance("AES");  
            /**
             * Random : 此类的实例用于生成伪随机数流。
             * SecureRandom : 是Random的直接子类。此类提供强加密随机数生成器 (RNG)。还必须生成非确定性输出(而)。
             * SecureRandom(byte[] seed) :  构造一个实现默认随机数算法的安全随机数生成器 (RNG)。使用指定的种子字节设置种子。
             * init(int keysize, SecureRandom random) : 使用用户提供的随机源初始化此密钥生成器,使其具有确定的密钥大小。
             * keysize : 密钥大小。这是特定于算法的一种规格,是以位数为单位指定的。
             * random : 此密钥生成器的随机源
             */
            kgen.init(128, new SecureRandom(key.getBytes())); 
            /**
             * java.security包中有接口 Key,SecretKey是Key的子接口,SecretKeySpec是SecretKey的实现类。
             * Key : Key 是所有密钥的顶层接口。它定义了供所有密钥对象共享的功能。
             * SecretKey : 此接口不包含方法或常量。其唯一目的是分组秘密密钥(并为其提供类型安全)。
             * SecretKeySpec : 可以使用此类来根据一个字节数组构造一个 SecretKey
             * 
             * generateKey() : 生成一个密钥。
             */
            SecretKey secretKey = kgen.generateKey();
            /**
             * getEncoded() : 是Key接口中的方法;返回基本编码格式的密钥,如果此密钥不支持编码,则返回 null。
             */
            byte[] enCodeFormat = secretKey.getEncoded(); 
            /**
             * SecretKeySpec(byte[] key, String algorithm) : SecretKeySpec的构造方法之一,根据给定的字节数组构造一个密钥。
             * key : 密钥的密钥内容。复制该数组的内容来防止后续修改。
             * algorithm : 与给定的密钥内容相关联的密钥算法的名称。
             */
            SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, "AES");  
            /**
             * Cipher : 该类为加密和解密提供加密密码功能。
             * getInstance(String transformation) : 通过指定转换模式的方式获得实例化对象。
             * transformation : 转换的名称,例如:AES 或者  DES/CBC/PKCS5Padding。转换始终包括加密算法的名称(例如,DES),后面可能跟有一个反馈模式和填充方案。
             */
            Cipher cipher = Cipher.getInstance("AES");
            byte[] byteContent = content.getBytes("utf-8");  
            /**
             * init(int opmode, Key key) : 用密钥初始化此 cipher。为以下 4 种操作之一初始化该 cipher:加密、解密、密钥包装或密钥打开,这取决于 opmode 的值。
             * opmode : 此 cipher 的操作模式(其为如下之一:ENCRYPT_MODE、DECRYPT_MODE、WRAP_MODE 或 UNWRAP_MODE)
             * key : 密钥
             */
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);  
            /**
             * doFinal(byte[] input) : 按单部分操作加密或解密数据,或者结束一个多部分操作。数据将被加密或解密(具体取决于此 Cipher 的初始化模式)。
             * input : 输入的数组,即:要加密或解密的内容
             */
            byte[] result = cipher.doFinal(byteContent); 
            /**
             * parseByte2HexStr(String result) : 自定义的一套将二进制数据转换为十六进制的数据的方法; 
             *
             * !注:在这儿,加密后的byte数组是不能强制转换成字符串的(即:new String(result)); 换言之,字符串和byte数组在这种情况下不是互逆的。
             * 处理方式有两种:
             * 		1.将result转化为十六进制的数据再做处理(需要自己写一个转换方法);
             * 		2.将result进行Base64(也可以用 Base64)再次加密在进行强制转换(不需要自己写方法,省事儿)。
             */
            return parseByte2HexStr(result);
//          return new String(Base64.encode(result));
            
        } catch (Exception e) {  
                e.printStackTrace();  
        }
        return null;  
}  

2、解密

其中用到的类和方法与加密的都一样,可以参考加密的注释。


	/**
	 * 解密
	 * @param content	要解密的内容
	 * @param key		用来生成128位密钥的密码
	 * @return
	 */
	public static String decrypt(String content, String key) {  
        try {  
			KeyGenerator kgen = KeyGenerator.getInstance("AES");  
			kgen.init(128, new SecureRandom(key.getBytes()));  
			SecretKey secretKey = kgen.generateKey();  
			byte[] enCodeFormat = secretKey.getEncoded();  
			SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, "AES");              
			Cipher cipher = Cipher.getInstance("AES");// 创建密码器  
            cipher.init(Cipher.DECRYPT_MODE, keySpec);// 初始化  
            byte[] result = cipher.doFinal(parseHexStr2Byte(content));  
//          byte[] result = cipher.doFinal(Base64.decode(content.getBytes()));  
            return new String(result); 
                
        } catch (Exception e) {  
                e.printStackTrace();  
        }
        return null;  
	}  
	


3、字符串/字节数组 ——> 十六进制数字

    /**
     * 将二进制数组 ——> 十六进制数字
     * @param buf	要转换的数组
     * @return
     */
    private static String parseByte2HexStr(byte buf[]) {  
        StringBuffer sb = new StringBuffer();  
        for (int i = 0; i < buf.length; i++) {
            /**
             * Integer : 该类在对象中包装了一个基本类型 int 的值(即:Integer是int的包装类)。该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。
             * toHexString(int i) : 以十六进制的无符号整数形式返回一个整数参数的字符串表示形式。
             * i : 要转换成字符串的整数。
             * buf[i] & 0xFF : 将字节数组中每个字节拆解成2位16进制整数(原因是:每个字节(即:byte)占8位(即:bit),16进制的基数是由4位组成)
             */
            String hex = Integer.toHexString(buf[i] & 0xFF);  
            if (hex.length() == 1) {  
            	/**
            	 * 因为toHexString(int i)将需要转换的i值转换为十六进制(基数 16)的无前导 0 的 ASCII 数字字符串,所以要重新加上
            	 */
                hex = '0' + hex;  
            }  
            /**
             * toUpperCase() : 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
             */
            sb.append(hex.toUpperCase());  
        }  
        return sb.toString();  
    }  

4、十六进制数字 ——> 字符串/字节数组


	/**
	 * 将十六进制数字 ——> 二进制数组
	 * @param hexStr	要转换的数组
	 * @return
	 */
	public static byte[] parseHexStr2Byte(String hexStr) {  
        if (hexStr.length() < 1)  
                return null;  
        byte[] result = new byte[hexStr.length()/2]; 
        /**
         * hexStr.length()/2 : 将每2位16进制整数组装成一个字节(原因是:每个字节(即:byte)占8位(即:bit),16进制的基数是由4位组成)
         */
        for (int i = 0;i< hexStr.length()/2; i++) {  
        		/**
        		 * parseInt(String s, int radix) : 使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
        		 * s : 包含要分析的整数表示形式的 String。
        		 * radix : 分析 s 时使用的基数。
        		 * 如果发生以下任意一种情况,则抛出一个 NumberFormatException 类型的异常:
        		 * 		1、第一个参数为 null 或一个长度为零的字符串。
        		 * 		2、基数小于 Character.MIN_RADIX 或者大于 Character.MAX_RADIX。
        		 * 		3、假如字符串的长度超过 1,那么除了第一个字符可以是减号 '-' ('u002D’) 外,字符串中的任何字符都不是指定基数的数字(即:第二个参数radix)。
        		 * 		4、字符串表示的值不是 int 类型的值。
        		 */
                int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); 
                /**
                 * String : 该类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例来实现。
                 * substring(int beginIndex, int endIndex) : 返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,一直到索引 endIndex - 1 处的字符。
                 * beginIndex : 开始处的索引(包括)。
		 * endIndex : 结束处的索引(不包括)。
                 */
                int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);  
                result[i] = (byte) (high * 16 + low);  
        }  
        return result;  
	} 



5、测试结果:

public static void main(String[] args) {
	// 需要加密的内容
	String content = "请叫我dafeige";
	// 生成密钥需要的密码值
	String key = "dafeige";
	System.out.println("content: " + content + "\nkey: " + key);
	// 内容加密后的值
	String encode = encrypt(content, key);
	// 被加密的内容解密后的值
	String decode = decrypt(encode, key);
	System.out.println("encode: " + encode + "\ndecode: " + decode);
}

输出的结果:

content: 请叫我dafeige
key: dafeige
encode: B49019AC97E24981989A03CFC4B3DF4B2B16B197404F3EA7FC92286E69D4CAE2
decode: 请叫我dafeige

测试完美成功!


(方法二:使用Base64做转码功能处理)

该方法的加解密中会用到的API:

IvParameterSpec ——> http://jszx-jxpt.cuit.edu.cn/JavaAPI/javax/crypto/spec/IvParameterSpec.html

SecretKey ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/javax/crypto/SecretKey.html

Cipher ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/javax/crypto/Cipher.html


1、加密


	/**
	 * 加密
	 * 
	 * @param sSrc		待加密的数据
	 * @param sKey		加密键
	 * @return			返回加密后的数据
	 */
	public static String encrypt(String sSrc, String sKey){
		if (sKey == null) {
			System.out.print("Key为空null");
			return null;
		}
		// 判断Key是否为16位
		if (sKey.getBytes().length != 16) {
			System.out.print("Key长度不是16位");
			return null;
		} 
		/**
         * SecretKeySpec(byte[] key, String algorithm) : SecretKeySpec的构造方法之一,根据给定的字节数组构造一个密钥。
         * key : 密钥的密钥内容。复制该数组的内容来防止后续修改。
         * algorithm : 与给定的密钥内容相关联的密钥算法的名称。
         */
		SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes(), "AES");
		try {
			/**
             * Cipher : 该类为加密和解密提供加密密码功能。
             * getInstance(String transformation) : 通过指定转换模式的方式获得实例化对象。
             * transformation : 转换的名称,例如:AES 或者  DES/CBC/PKCS5Padding。转换始终包括加密算法的名称(例如,DES),后面可能跟有一个反馈模式和填充方案。
             */
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// "算法/模式/补码方式"
			/**
			 * IvParameterSpec : 该类指定初始化向量(IV)。使用IV的实例是反馈模式中的密码,例如CBC模式下的DES和具有OAEP编码操作的RSA密码。
			 * IvParameterSpec(byte[] iv) : 使用iv IV中的字节创建IvParameterSpec对象。
			 * iv : 具有IV的缓冲区。复制缓冲区的内容以防止后续修改。
			 */
			IvParameterSpec iv = new IvParameterSpec("1234567890123456".getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
			/**
             * init(int opmode, Key key, AlgorithmParameterSpec params) : 用密钥和一组算法参数初始化此 cipher。为以下 4 种操作之一初始化该 cipher:加密、解密、密钥包装或密钥打开,这取决于 opmode 的值。
             * opmode : 此 cipher 的操作模式(其为如下之一:ENCRYPT_MODE、DECRYPT_MODE、WRAP_MODE 或 UNWRAP_MODE)
             * key : 密钥
             * params : 算法参数(1、可以没有这个参数,那在这儿就可以用init(int opmode, Key key)这个构造方法;2、可以为SecureRandom的对象,即一个随机数(AES不可采用这种方法);3、可以为向量iv)
             */
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
			/**
             * doFinal(byte[] input) : 按单部分操作加密或解密数据,或者结束一个多部分操作。数据将被加密或解密(具体取决于此 Cipher 的初始化模式)。
             * input : 输入的数组,即:要加密或解密的内容
             */
			byte[] encrypted = cipher.doFinal(sSrc.getBytes());
			// 此处使用Base64做转码功能,同时能起到2次加密的作用。
			return new String(Base64.encode(encrypted));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	

2、解密

其中用到的类和方法与加密的都一样,可以参考加密的注释。

/**
 * 解密
 * 
 * @param sSrc		待解密的数据
 * @param sKey		解密键
 * @return		返回解密后的数据
 */
public static String decrypt(String sSrc, String sKey){
	byte[] content = sSrc.getBytes();
	byte[] password = sKey.getBytes();
	// 判断Key是否正确
	if (content == null) {
		System.out.print("Key为空null");
		return null;
	}
	// 判断Key是否为16位
	if (password.length != 16) {
		System.out.print("Key长度不是16位");
		return null;
	}
	// 先用Base64解密,因为需要解密的数据在加密之后进行了Base64二次加密
	content = Base64.decode(content);
	SecretKeySpec skeySpec = new SecretKeySpec(password, "AES");
	try {
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		IvParameterSpec iv = new IvParameterSpec("1234567890123456".getBytes());
		cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
		byte[] original = cipher.doFinal(content);
		return new String(original);
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return null;
}

3、测试结果:

public static void main(String[] args) {
	// 需要加密的内容
	String content = "请叫我dafeige";
	// 密钥的生成密码(注意:长度必须为16的整数倍)
	String key = "dafeige123456789";
	System.out.println("content: " + content + "\nkey: " + key);
	// 内容加密后的值
	String encode = encrypt(content, key);
	// 被加密的内容解密后的值
	String decode = decrypt(encode, key);
	System.out.println("encode: " + encode + "\ndecode: " + decode);
}

输出结果:

content: 请叫我dafeige
key: dafeige123456789
encode: UJV2QTmIiUuMx+r2TH4G+LqVqtcKXrG/+fphsLagZZ4=
decode: 请叫我dafeige

完美的结果!


总结:

两种方式的比较:第一种方式对于“生成密钥的密码值”的长度没有要求,多长都行,而第二种方法必须要求其“生成密钥的密码值”的长度为16的整数倍;网上有的说要求第二种方法的待加密和待解密的长度必须为16的整数倍,这个是完全没必要的,因为待加密的内容不管多长,不管是不是16的倍数,加密机制会自动给补够离它最近的并且是大于它的那个16的倍数,也就是说我们待加密的内容长度为16的倍数的时候,刚好不用动,如果不是16的倍数,加密机制会帮我们补齐为16的倍数,只要不修改加密后的内容,用同一个密码值就可以解密出原数据;还有就是后者比前者的代码量少,简单。


二、256位密钥的加解密算法

在这儿,先做一下前期准备:

1、如果想了解所谓的:“加密服务提供者”——即:Provider,“所请求算法的名称”——即:algorithm,“转换名称”——即:transformation(如:AES/ECB/PKCS7Padding)等这样的字眼字眼的详细内容,那就跟我来

2、对于Java自身支持PKCS5Padding填充方式,而Bouncycastle支持PKCS7Padding填充方式,再加上加密解密过程中需要一些密码术算法,所以需要导入jar包:

bcprov-jdk15-133.jar,其下载地址为:

http://pan.baidu.com/s/1cvV7Ca

3、 如果对加密后的数据进行转码处理时选择了用Base64加密,需要导入个jar包(如果使用Eclipse开发,需要下载并导入jar包,如果是 Android  Studio开发,则添加依赖就行):
javabase64-1.3.1.jar,其下载地址为:

http://download.csdn.net/detail/jiangsyace/7690869

加解密的代码里面需要用到几个类(接口),我已尽可能做了详细的注释,如果想进一步了解其内容,可以看已下API:

用到的重要API地址:

KeyGenerator ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/javax/crypto/KeyGenerator.html

SecureRandom ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/java/security/SecureRandom.html

MessageDigest ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/security/MessageDigest.html

SecretKey ——>http://www.oschina.net/uploads/doc/javase-6-doc-api-zh_CN/javax/crypto/SecretKey.html

Cipher ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/javax/crypto/Cipher.html

String ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/lang/String.html

Integer ——>http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/lang/Integer.html


1、加密

/**
 * 加密
 * @param content	需要加密的内容
 * @param password	生成密钥使用的密码
 * @return
 */
public static String encrypt(String content, String password) {  
      try {  
        /**
         *  KeyGenerator : 是个类,此类提供(对称)密钥生成器的功能。在生成密钥后,可以重复使用同一个 KeyGenerator 对象来生成更多的密钥。
         *  使用其中的getInstance(String algorithm)方法进行构造对象;
         *  algorithm : 所请求密钥算法的标准名称。
         */
        KeyGenerator kgen = KeyGenerator.getInstance("AES");  
        /**
         * Random : 此类的实例用于生成伪随机数流。
         * SecureRandom : 是Random的直接子类。此类提供强加密随机数生成器 (RNG)。还必须生成非确定性输出(而)。
         * SecureRandom(byte[] seed) :  构造一个实现默认随机数算法的安全随机数生成器 (RNG)。使用指定的种子字节设置种子。
         * seed : 种子。在这儿用的是一个存放哈希结果的数组,详细看下文的tohash256Deal(String datastr)。
         */
        SecureRandom securerandom = new SecureRandom(tohash256Deal(password));  
        /**
         * init(int keysize, SecureRandom random) : 使用用户提供的随机源初始化此密钥生成器,使其具有确定的密钥大小。
         * keysize : 密钥大小。这是特定于算法的一种规格,是以位数为单位指定的。
         * random : 此密钥生成器的随机源。
         */
        kgen.init(256, securerandom);  
        /**
         * java.security包中有接口 Key,SecretKey是Key的子接口,SecretKeySpec是SecretKey的实现类。
         * Key : Key 是所有密钥的顶层接口。它定义了供所有密钥对象共享的功能。
         * SecretKey : 此接口不包含方法或常量。其唯一目的是分组秘密密钥(并为其提供类型安全)。
         * SecretKeySpec : 可以使用此类来根据一个字节数组构造一个 SecretKey
         * 
         * generateKey() : 生成一个密钥。
         */
        SecretKey secretKey = kgen.generateKey();  
        /**
         * getEncoded() : 是Key接口中的方法;返回基本编码格式的密钥,如果此密钥不支持编码,则返回 null。
         */
        byte[] enCodeFormat = secretKey.getEncoded();  
        /**
         * SecretKeySpec(byte[] key, String algorithm) : SecretKeySpec的构造方法之一,根据给定的字节数组构造一个密钥。
         * key : 密钥的密钥内容。复制该数组的内容来防止后续修改。
         * algorithm : 与给定的密钥内容相关联的密钥算法的名称。
         */
        SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");  
        /**
         * Security : 此类集中了所有的安全属性和常见的安全方法。其主要用途之一是管理提供程序。
         * addProvider(Provider provider) : 将提供程序添加到下一个可用位置。
         * provider : 要添加的提供程序。
         */
        Security.addProvider(new BouncyCastleProvider());  
        /**
         * Cipher : 该类为加密和解密提供加密密码功能。
         * getInstance(String transformation, String provider) : 创建一个实现指定转换的 Cipher 对象,该转换由指定的提供程序提供。
         * transformation : 转换的名称,例如 DES/CBC/PKCS5Padding。描述为产生某种输出而在给定的输入上执行的操作(或一组操作)的字符串。转换始终包括加密算法的名称(例如,DES),后面可能跟有一个反馈模式和填充方案。
         * provider : 提供程序的名称
         */
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");  
        /**
         * init(int opmode, Key key) : 用密钥初始化此 cipher。为以下 4 种操作之一初始化该 cipher:加密、解密、密钥包装或密钥打开,这取决于 opmode 的值。
         * opmode : 此 cipher 的操作模式(其为如下之一:ENCRYPT_MODE、DECRYPT_MODE、WRAP_MODE 或 UNWRAP_MODE)
         * key : 密钥
         */
        cipher.init(Cipher.ENCRYPT_MODE, key);  
        byte[] byteContent = content.getBytes("utf-8"); 
        /**
         * doFinal(byte[] input) : 按单部分操作加密或解密数据,或者结束一个多部分操作。数据被加密还是解密取决于此 cipher 的初始化方式。
         * input : 输入缓冲区
         */
        byte[] cryptograph = cipher.doFinal(byteContent);
        /**
         * !注:在这儿,加密后的byte数组是不能强制转换成字符串的(即:new String(result)); 换言之,字符串和byte数组在这种情况下不是互逆的。
         * 处理方式有两种:
         * 	1.将result转化为十六进制的数据再做处理(需要自己写一个转换方法)—— 用法和“128位密钥的加解密算法”的一模一样,可以参考上文;
         * 	2.将result进行Base64(也可以用 BASE64Encode)再次加密在进行强制转换(不需要自己写方法,省事儿)。(主要编解码方式有Base64, HEX, UUE, 7bit等等。此处看服务器需要什么编码方式)
         */
        return new String(Base64.encode(cryptograph));  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  


2、解密

其中用到的类和方法与加密的都一样,可以参考加密的注释。
/**
 * 解密
 * @param cryptograph
 * @param password
 * @return
 */
public static String decrypt(String cryptograph, String password) {  
    try {  
        KeyGenerator kgen = KeyGenerator.getInstance("AES");  
        SecureRandom securerandom = new SecureRandom(tohash256Deal(password));  
        kgen.init(256, securerandom);  
        SecretKey secretKey = kgen.generateKey();  
        byte[] enCodeFormat = secretKey.getEncoded();  
        SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");  
        Security.addProvider(new BouncyCastleProvider());  
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");  
        cipher.init(Cipher.DECRYPT_MODE, key);  
        byte[] content = cipher.doFinal(Base64.decode(cryptograph.getByte()));  
        return new String(content);  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
    return null;  
}  


3、hash算法处理

/**
 * 将指定字符串做hash算法处理
 * @param datastr	需要被处理的字符串
 * @return
 */
private static byte[] tohash256Deal(String datastr) {  
    try {  
        /**
         * MessageDigest : 该类是个抽象类,此类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。
         * getInstance(String algorithm) : 生成实现指定摘要算法的 MessageDigest 对象。
         * algorithm : 所请求算法的名称。
         * 该对象通过使用 update 方法处理数据。任何时候都可以调用 reset 方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算。
         */
        MessageDigest digester=MessageDigest.getInstance("SHA-256");
        /**
         * update(byte[] input) : 使用指定的字节数组更新摘要。
         */
        digester.update(datastr.getBytes()); 
        /**
         * digest() : 通过执行诸如填充之类的最终操作完成哈希计算。调用此方法后摘要被重置。
         */
        byte[] hex=digester.digest(); 
        /**
         * hex : 存放哈希值结果的字节数组。
         */
        return hex;   
    } catch (NoSuchAlgorithmException e) {  
        throw new RuntimeException(e.getMessage());    
    }  
}  


4、测试结果

public static void main(String[] args) {  
    	
    // 待加密的内容
    String content = "0f我7264在6318a这92b9e13儿c65db7cd大飞哥3c";  
    // 密钥的生成密码
    String password = "1^da_fei$g=e.";  
    System.out.println("content:" + content);  
    System.out.println("password:" + password);  
          
    String encodeResult = encrypt(content, password); 
    System.out.println("encryptResult:" + encodeResult);  
          
    String decodeResult = decrypt(encodeResult, password);  
    System.out.println("decodeResult:" + decodeResult);  
} 


输出结果:

content:0f我7264在6318a这92b9e13儿c65db7cd大飞哥3c
password:1^da_fei$g=e.
encryptResult:8HVOPIDA4cvFTSpaLy6xE25abLgqG7Qtyd9LFa4YrC+OEE5lU27JhFoAcUmd6Epld+dyOJYzb4p0mYlQcA7kaA==
decodeResult:0f我7264在6318a这92b9e13儿c65db7cd大飞哥3c

正是我们想要的!

三、“Illegal key size or default parameters”异常的解决

为了避免出现“java.security.InvalidKeyException: Illegal key size or default parameters”异常,我们需要将local_policy.jar 和 US_export_policy.jar两个jar包文件替换掉自己jre文件(即:%JAVE_HOME%\jre\lib\security)中对应的jar包(不同版本的JDK要安装对应的JCE文件,也就是对应的local_policy.jar 和 US_export_policy.jar),其下载地址为:
如果想了解“ Illegal key size or default parameters”异常更详细的出现的原因及解决方案的话,请看 这里 

四、总结

1、为什么会报“java.security.InvalidKeyException: Illegal key size or default parameters”异常:
因为我们在安装JDK的时候,jre中自带的“local_policy.jar ”和“US_export_policy.jar”是支持128位密钥的加密算法,而当我们要使用256位密钥算法的时候,因其不支持,所以需要下载支持更长的密钥算法jar包,oracle官网有不同JDK版本的无限长密钥算法包——“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files X”(“X”代表不同版本的JDK),这儿可以直接给链接,有需要的可以点击下面一句话:

绝对是纯官网的jar包

2、为什么要用Base64做处理:
在这儿,加密后的byte数组是不能强制转换成字符串的(即:new String(result)),否则会报 java.lang.NumberFormatException: For input string: "�"异常,并且有加密后的字符串返回,不过全都是乱码的,对于之后的解密就更不用提了,坑定失败; 换言之,字符串和byte数组在这种情况下不是互逆的,所以可以通过转码进行解决。
处理方式有两种:
1.将result转化为十六进制的数据再做处理(需要自己写一个转换方法);
2.将result进行Base64(也可以用 BASE64Encode)再次加密在进行强制转换(不需要自己写方法,省事儿)。(主要编解码方式有Base64, HEX, UUE, 7bit等等。此处看服务器需要什么编码方式)
注意:在这儿为什么要导入一个第三方的Base64的jar包,因为Base64是Apache公司提供的技术,从JDK_1.8(改名后也叫JDK8)开始,java.util 包中才包含了对 Base64 的处理,其具体操作为:
// 编码
String encodeBase64 = Base64.getEncoder().encodeToString(“需要Encode的字符串”.getBytes());

// 解码
byte[] decodeBase64 = Base64.getDecoder().decode("需要Decode的字符串");

所以JDK_1.8(改名后也叫JDK8)之前的版本需要导入第三方的Base64包。

3、为什么没有192位密钥的加解密:
有了最128的和256的,还要192有何用?


终于搞定,大家可以copy代码在自己的程序中试试,如果有什么疑问,欢迎留言!如果感觉还不错,请点个赞吧!
  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java程序在Linux服务器上运行时,如果使用AES加密并使用256密钥,则可能会出现“java.security.InvalidKeyException: Illegal key size or default parameters”的异常。这是因为默认情况下,Java不允许使用256密钥进行加密。要解决问题,需要执行以下步骤: 1.下载Java Cryptography Extension (JCE) Unlimited Jurisdiction Policy Files。这些文件包含允许使用256密钥的策略文件。 2.解压缩下载的文件,并将其中的两个JAR文件(local_policy.jar和US_export_policy.jar)复制到$JAVA_HOME/jre/lib/security目录中。如果该目录中已经存在这些文件,请备份它们并替换为新文件。 3.重新启动Java应用程序并尝试使用256密钥进行AES加密。 下面是一个示例代码,演示如何使用256密钥进行AES加密: ```java import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class AesEncryptionExample { private static final String ALGORITHM = "AES"; private static final String KEY = "0123456789abcdef0123456789abcdef"; public static String encrypt(String value) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] encryptedValue = cipher.doFinal(value.getBytes()); return Base64.getEncoder().encodeToString(encryptedValue); } public static String decrypt(String value) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] decodedValue = Base64.getDecoder().decode(value); byte[] decryptedValue = cipher.doFinal(decodedValue); return new String(decryptedValue); } public static void main(String[] args) throws Exception { String originalValue = "Hello World!"; String encryptedValue = encrypt(originalValue); String decryptedValue = decrypt(encryptedValue); System.out.println("Original Value: " + originalValue); System.out.println("Encrypted Value: " + encryptedValue); System.out.println("Decrypted Value: " + decryptedValue); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值