package com.stone.security;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
/**
* AES 算法 对称加密,密码学中的高级加密标准 2005年成为有效标准
* @author stone
* @date 2014-03-10 06:49:19
*/
public class AES {
static Cipher cipher;
static final String KEY_ALGORITHM = "AES";
static final String CIPHER_ALGORITHM_ECB = "AES/ECB/PKCS5Padding";
/*
*
*/
static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
/*
* AES/CBC/NoPadding 要求
* 密钥必须是16字节长度的;Initialization vector (IV) 必须是16字节
* 待加密内容的字节长度必须是16的倍数,如果不是16的倍数,就会出如下异常:
* javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
*
* 由于固定了位数,所以对于被加密数据有中文的, 加、解密不完整
*
* 可 以看到,在原始数据长度为16的整数n倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,
* 其它情况下加密数据长 度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],
* 除了NoPadding填充之外的任何方 式,加密数据长度都等于16*(n+1).
*/
static final String CIPHER_ALGORITHM_CBC_NoPadding = "AES/CBC/NoPadding";
static SecretKey secretKey;
public static void main(String[] args) throws Exception {
method1("a*jal)k32J8czx囙国为国宽");
method2("a*jal)k32J8czx囙国为国宽");
method3("a*jal)k32J8czx囙国为国宽");
method4("123456781234囙为国宽");// length = 16
method4("12345678abcdefgh");// length = 16
}
/**
* 使用AES 算法 加密,默认模式 AES/ECB
*/
static void method1(String str) throws Exception {
cipher = Cipher.getInstance(KEY_ALGORITHM);
//KeyGenerator 生成aes算法密钥
secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);//使用加密模式初始化 密钥
byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
System.out.println("method1-加密:" + Arrays.toString(encrypt));
cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
byte[] decrypt = cipher.doFinal(encrypt);
System.out.println("method1-解密后:" + new String(decrypt));
}
/**
* 使用AES 算法 加密,默认模式 AES/ECB/PKCS5Padding
*/
static void method2(String str) throws Exception {
cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
//KeyGenerator 生成aes算法密钥
secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);//使用加密模式初始化 密钥
byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
System.out.println("method2-加密:" + Arrays.toString(encrypt));
cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
byte[] decrypt = cipher.doFinal(encrypt);
System.out.println("method2-解密后:" + new String(decrypt));
}
static byte[] getIV() {
String iv = "1234567812345678"; //IV length: must be 16 bytes long
return iv.getBytes();
}
/**
* 使用AES 算法 加密,默认模式 AES/CBC/PKCS5Padding
*/
static void method3(String str) throws Exception {
cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
//KeyGenerator 生成aes算法密钥
secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用加密模式初始化 密钥
byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
System.out.println("method3-加密:" + Arrays.toString(encrypt));
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用解密模式初始化 密钥
byte[] decrypt = cipher.doFinal(encrypt);
System.out.println("method3-解密后:" + new String(decrypt));
}
/**
* 使用AES 算法 加密,默认模式 AES/CBC/NoPadding 参见上面对于这种mode的数据限制
*/
static void method4(String str) throws Exception {
cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC_NoPadding);
//KeyGenerator 生成aes算法密钥
secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用加密模式初始化 密钥
byte[] encrypt = cipher.doFinal(str.getBytes(), 0, str.getBytes().length); //按单部分操作加密或解密数据,或者结束一个多部分操作。
System.out.println("method4-加密:" + Arrays.toString(encrypt));
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用解密模式初始化 密钥
byte[] decrypt = cipher.doFinal(encrypt);
System.out.println("method4-解密后:" + new String(decrypt));
}
}
——————————————updated on 2017/08/29———————————————————
**使用32字节(256位)密钥 **
前文,method1-4,打印出来的 密钥长度都是16字节长度;都是通过KeyGenerator自动生成的。
jdk中 AES中的Cipher,对应的密钥长度为128位的,按8bits一个字节算,整好就是16个字节。
如果要指定为32字节长度(256位)密钥,如下实现:
/**
* 指定密钥长度为32位字节。 默认只能16位,是因为美国的出口政策限制。
* @param key_32
* @param str
* @throws Exception
*/
static void method5(String key_32, String str) throws Exception {
System.out.println();
cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
secretKey = new SecretKeySpec(key_32.getBytes("utf-8"), "AES");
System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypt = cipher.doFinal(str.getBytes(), 0, str.getBytes().length);
System.out.println("method5-加密:" + Arrays.toString(encrypt));
cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
byte[] decrypt = cipher.doFinal(encrypt);
System.out.println("method5-解密后:" + new String(decrypt, "utf-8"));
}
调用代码:
String key_32 = "0123456789abcdef0123456789ABCDEF";
method5(key_32, "123456781234囙为国宽");
如上,通过SecretkeySpec,可以指定32位字节密钥。
但是,运行后,发现报错了:"java.security.InvalidKeyException: Illegal key size or default parameters…"
网络搜索后发现如下解决方法:http://blog.csdn.net/l1028386804/article/details/42706039
因我的环境是jdk8,在oracle搜索到jce-jdk8的下载页面:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
最后,大功告成:
密钥的长度为:32
method5-加密:[-9, -118, -4, 2, 76, 48, 60, 4, -100, 19, 24, -14, 93, -20, -19, 77, -79, 105, -78, 53, 17, 29, -65, 36, 104, -60, 83, 31, -76, -26, 54, -120]
method5-解密后:123456781234囙为国宽
如果像安卓app,要在客户端实现32位字节密钥加、解密,估计还是不行的;只能考虑自己实现一套AES的算法了,同时服务端也使用相应算法
使用"AES/CBC/NoPadding"引发的问题
不考虑上面的256位密钥问题,在默认情况下,这种padding,要求 密钥、IV长度都是16字节,内容字节长度必须是16的倍数,如果不是16的倍数,则引发异常。
如果一定要使用这种padding,而无法保证内容为16字节的倍数,可以考虑自实现一套规则,补全内容长度为16*n
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://stone.blog.csdn.net/article/details/20960029
内容来源于网络,如有侵权,请联系作者删除!