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: 请叫我dafeigekey: 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,其下载地址为:
3、 如果对加密后的数据进行转码处理时选择了用Base64加密,需要导入个jar包(如果使用Eclipse开发,需要下载并导入jar包,如果是 Android Studio开发,则添加依赖就行):
javabase64-1.3.1.jar,其下载地址为:
加解密的代码里面需要用到几个类(接口),我已尽可能做了详细的注释,如果想进一步了解其内容,可以看已下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大飞哥3cpassword: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),其下载地址为:https://pan.baidu.com/s/1gfxhtpp 或者 http://blog.csdn.net/dafeige8/article/details/76019911 如果想了解“ 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的字符串");
3、为什么没有192位密钥的加解密:
所以JDK_1.8(改名后也叫JDK8)之前的版本需要导入第三方的Base64包。
有了最128的和256的,还要192有何用?终于搞定,大家可以copy代码在自己的程序中试试,如果有什么疑问,欢迎留言!如果感觉还不错,请点个赞吧!