别再说自己是单身狗了,你这个年纪,狗都死了。
前言
最近试了一下微信支付,其中涉及到了一些加密与解密的算法。由于我没有研究过加密与解密这方面的内容,在完全不懂的情况下走了不少弯路。所以我就记录一下学习的过程,同时也希望这篇文章能帮助到一些同学,让大家能对加密与解密稍微了解一点,不至于上来就抓瞎。
说起这个加密与解密,大家可能听过很多各种加密算法,什么MD5,什么对称加密,什么非对称加密,还有什么公钥私钥,签名,证书什么的。当然,这些都只是听过名字而已。
且听我用浅薄的见识,大言不惭的讲一讲其中部分加密算法如何使用,我这人学习有个毛病就是学习某块知识的时候我得了解它怎么用,用在什么地方,不然光看理论就觉得知识飘在空中,脚步踏不到实地上,跟没学会似的。
相关代码放在这里:https://gitee.com/siumu/blog_code.git
哈希算法(Hash)又称摘要算法(Digest)
首先我们来聊一聊哈希算法。我们刚开始接触加密的时候可能就是MD5加密了。最常见的就是给用户的密码用密文存储,而MD5就是一种Hash算法。除了MD5之外,还有很多哈希算法,比如SHA-1,SHA-256,SHA-512等等。这些算法用起来比较简单,只需要用工具类直接调用并生成一个字符串就可以了。
哈希算法还有个目的就是为了验证原始数据是否被篡改。那什么时候会用到哈希算法呢?举个例子:我们要下载一些软件的时候,如果你下载的不对,它就会绑定很多垃圾软件,污染你的电脑。这就是原始数据被篡改了,你下载的不是官方最原始的软件数据。如下图所示:
这是下载Tomcat的地方,每一个文件后边都跟了这个文件用哈希算法生成的摘要。大家把文件下载下来,用同样的算法生成一次摘要,如果跟官方给出的摘要相同,则说明没有被篡改过,如果不相同,那就说明你下载的这个文件被篡改过。
大家也可以注意一下,说真的,如果不是了解了一点哈希算法,我还真不知道下载软件的后面跟的这个sha512是用来干嘛的。
有关更多哈希算法的东西大家可以看看廖雪峰的网站,讲的非常好。
对称加密解密
对称加密就是密钥加密。使用相同的密钥,对原始数据进行加密和解密。这个密钥只有你知我知,我用密钥加密,你用密钥解密。只要这个密钥不泄露,别人即使得到密文也无法解密。
对称加密一般就是AES、DES等。但是最经常用的还是AES。
我们直接来看案例。微信小程序官方使用的加密解密算法。这是我当时做微信小程序获取小程序用户敏感信息的时候遇到的。如果大家做过小程序,那肯定会遇到过。
我之前没有看过加密解密相关的内容,有些名词只是听过并没有去学习过,所以第一次看到这个的时候,我都蒙了,完全不知道怎么解密。也不知道这些密钥,初始向量都代表啥意思。
那么我们就以这个微信官方的例子并结合Java代码来慢慢的解释
首先我们看到AES,DES就知道这是个对称加密算法。在Java代码里,这些算法都被封装成了一个个的工具类,我们可以直接使用。我用的是Hutool这个工具包,简直不要太好用,各种各样的工具都有。
但是这个AES-128-CBC,数据采用PKCS#7填充这是什么意思呢?
其实,这个128表示的就是密钥的长度,一般都是128位。当然也有192位和256位。我们可以看一下Java代码里怎么使用这个密钥。
我们可以看到,构建AES对象的时候有两个构造方法,第二个有参构造里的那个key,毫无疑问就是密钥。我们知道对称加密是要有密钥的,怎么会有无参构造呢?其实无参构造内部使用了随机的密钥。
那么接下来我们自定义一个密钥来试试。
我自定义一个密钥是xiumu
,我们发现它加密数据就会报错,说我们的密钥非法,只有5个字节。那我们换成一个128位的密钥。一个字节是8位,128位就是16个字节。理论上就可以了。
这样呢我们就加密成功了。
CBC,这又是什么意思呢?这是算法的加密工作模式,除了CBC,还有ECB、PCBC等等。不同的工作模式,算法也就不同。
CBC工作模式需要一个初始向量iv。AES并不是把明文数据一下子加密成密文的,它是先把明文分块,比如每128位分成一块,每块单独加密。而CBC模式怎么加密呢?它是每块明文与前一块的密文异或操作之后再加密。但是第一块明文跟谁异或呢?就是这个初始向量iv,同样这个iv也需要128位。
数据采用PKCS#7填充?这个AES算法需要设置填充模式,上文我们知道,它把明文分块处理,比如它每128位是一块,分到最后就剩下几位了,不够128位怎么办?这就需要填充了,给明文填充到128位。填充模式有PKCS#7、PKCS#5等等。填充模式的作用是一样的,只不过是填充模式不同,填充的字符不同。
如此一来,我们已经完全知道这个AES-128-CBC,数据采用PKCS#7填充应该如何操作了。
小Tips:Base64_Decode
一般这种对称加密都需要结合Base64编码来使用,Base64只是一种编码方式,并不算什么加密。我们可以看看先看看对称加密最后加密出来的字节数组内容是什么:
大家看到这些是不是有点熟悉,是不是联想到了ASCII码?它有可打印字符和不可打印字符。而Base64编码就是把密文的内容全部编码成可打印的内容。据说是因为有些设备对这些不可打印字符有不同的处理,可能会出错。但是转化成可打印字符的之后,不管经过多少设备,内容都不会发生改变。如图所示
使用AES进行加密解密
代码就直接贴在这里好了
public class Security {
// 密钥
public static String KEY = "zVvzigsRbE4IxICj";
// 初始向量iv
public static String IV = "aai5AXLpLxehRPgm";
/**
* 加密数据
* @param data 原始数据
* @return
*/
public static String encrypt(String data) {
// AES算法,设置加密工作模式,填充模式,密钥
AES aes = new AES(Mode.CBC,Padding.PKCS5Padding,KEY.getBytes(StandardCharsets.UTF_8));
// 设置初始向量
aes.setIv(IV.getBytes(StandardCharsets.UTF_8));
// 加密数据
byte[] encryptData = aes.encrypt(data);
// 返回Base64编码之后的密文
return Base64Encoder.encode(encryptData);
}
/**
* 对密文进行解密
* @param encryptData 密文
* @return
*/
public static String decrypt(String encryptData) {
// AES算法,设置加密工作模式,填充模式,密钥
AES aes = new AES(Mode.CBC,Padding.PKCS5Padding,KEY.getBytes(StandardCharsets.UTF_8));
// 设置初始向量
aes.setIv(IV.getBytes(StandardCharsets.UTF_8));
// 解密
byte[] decryptData = aes.decrypt(Base64Decoder.decode(encryptData));
// 返回解密之后的数据
return new String(decryptData);
}
// 测试AES加密解密
public static void main(String[] args) {
// 原始数据
String data = "人生除了生死,其余都是擦伤";
// 加密
String encryptData = encrypt(data);
System.out.println(encryptData);
// 解密
String decryptData = decrypt(encryptData);
System.out.println(decryptData);
}
}