加解密算法分析
日常开发中,无论你是使用什么语言,都应该遇到过使用加解密的使用场景,比如接口数据需要加密传给前端保证数据传输的安全;HTTPS使用证书的方式首先进行非对称加密,将客户端的私匙传递给服务端,然后双方后面的通信都使用该私匙进行对称加密传输;使用MD5进行文件一致性校验,等等很多的场景都使用到了加解密技术。
很多时候我们对于什么时候要使用什么样的加解密方式是很懵的。因为可用的加解密方案实在是太多,大家对加解密技术的类型可能不是很清楚,今天这篇文章就来梳理一下目前主流的加解密技术,本篇文档只针对算法做科普性说明,不涉及具体算法分析。日常使用的加解密大致可以分为以下四类:
散列函数(也称信息摘要)算法
对称加密算法
非对称加密算法
组合加密技术
1. 散列函数算法
听名字似乎不是一种加密算法,类似于给一个对象计算出hash值。所以这种算法一般用于数据特征提取。常用的散列函数包括:MD5、SHA1、SHA2(包括SHA128、SHA256等)散列函数的应用很广,散列函数有个特点,它是一种单向加密算法,只能加密、无法解密。
1.1 MD5
先来看MD5算法,MD5算法是广为使用的数据特征提取算法,最常见的就是我们在下载一些软件,网站都会提供MD5值给你进行校验,你可以通过MD5值是否一致来检查当前文件是否被别人篡改。MD5算法具有以下特点:
任意长度的数据得到的MD5值长度都是相等的;
对原数据进行任一点修改,得到的MD5值就会有很大的变化;
散列函数的不可逆性,即已知原数据,无法通过特征值反向获取原数据。(需要说明的是2004年的国际密码讨论年会(CRYPTO)尾声,王小云及其研究同事展示了MD5、SHA-0及其他相关杂凑函数的杂凑冲撞。也就是说,她找出了第一个 两个值不同,但 MD5 值相同的碰撞的例子。这个应该不能称之为破解)
1.2 MD5用途:
防篡改。上面说过用于文件完整性校验。
用于不想让别人看到明文的地方。比如用户密码入库,可以将用户密码使用MD5加密存储,下次用户输入密码登录只用将他的输入进行MD5加密与数据库的值判断是否一致即可,这样就有效防止密码泄露的风险。
用于文件秒传。比如百度云的文件秒传功能可以用这种方式来实现。在你点击上传的时候,前端同学会先计算文件的MD5值然后与服务端比对是否存在,如果有就会告诉你文件上传成功,即完成所谓的秒传。
在JDK中提供了MD5的实现:java.security包中有个类MessageDigest,MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。
MessageDigest 对象使用getInstance函数初始化,该对象通过使用 update 方法处理数据。任何时候都可以调用 reset 方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算。
对于给定数量的更新数据,digest 方法只能被调用一次。digest 被调用后,MessageDigest 对象被重新设置成其初始状态。
下面的例子展示了使用JDK自带的MessageDigest类使用MD5算法。同时也展示了如果使用了update方法后没有调用digest方法,则会累计当前所有的update中的值在下一次调用digest方法的时候一并输出:
package other; import java.security.MessageDigest; /** * @author: rickiyang * @date: 2019/9/13 * @description: */ public class MD5Test { static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; public static void main(String[] args) { try { //申明使用MD5算法 MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update("a".getBytes());// System.out.println("md5(a)=" + byte2str(md5.digest())); md5.update("a".getBytes()); md5.update("bc".getBytes()); System.out.println("md5(abc)=" + byte2str(md5.digest())); //你会发现上面的md5值与下面的一样 md5.update("abc".getBytes()); System.out.println("md5(abc)=" + byte2str(md5.digest())); } catch (Exception e) { e.printStackTrace(); } } /** * 将字节数组转换成十六进制字符串 * * @param bytes * @return */ private static String byte2str(byte[] bytes) { int len = bytes.length; StringBuffer result = new StringBuffer(); for (int i = 0; i < len; i++) { byte byte0 = bytes[i]; result.append(hex[byte0 >>> 4 & 0xf]); result.append(hex[byte0 & 0xf]); } return result.toString(); } }
输出:
md5(a)=0CC175B9C0F1B6A831C399E269772661
md5(abc)=900150983CD24FB0D6963F7D28E17F72
md5(abc)=900150983CD24FB0D6963F7D28E17F72
1.3 SHA系列算法