加解密算法分析

日常开发中 , 无论你是使用什么语言 , 都应该遇到过使用加解密的使用场景 , 比如接口数据需要加密传给前端保证数据传输的安全;HTTPS使用证书的方式首先进行非对称加密 , 将客户端的私匙传递给服务端 , 然后双方后面的通信都使用该私匙进行对称加密传输;使用MD5进行文件一致性校验 , 等等很多的场景都使用到了加解密技术 。
很多时候我们对于什么时候要使用什么样的加解密方式是很懵的 。因为可用的加解密方案实在是太多 , 大家对加解密技术的类型可能不是很清楚 , 今天这篇文章就来梳理一下目前主流的加解密技术 , 本篇文档只针对算法做科普性说明 , 不涉及具体算法分析 。日常使用的加解密大致可以分为以下四类:

  1. 散列函数(也称信息摘要)算法
  2. 对称加密算法
  3. 非对称加密算法
  4. 组合加密技术
1. 散列函数算法#听名字似乎不是一种加密算法 , 类似于给一个对象计算出hash值 。所以这种算法一般用于数据特征提取 。常用的散列函数包括:MD5、SHA1、SHA2(包括SHA128、SHA256等)散列函数的应用很广 , 散列函数有个特点 , 它是一种单向加密算法 , 只能加密、无法解密 。
1.1 MD5先来看MD5算法 , MD5算法是广为使用的数据特征提取算法 , 最常见的就是我们在下载一些软件 , 网站都会提供MD5值给你进行校验 , 你可以通过MD5值是否一致来检查当前文件是否被别人篡改 。MD5算法具有以下特点:
  1. 任意长度的数据得到的MD5值长度都是相等的;
  2. 对原数据进行任一点修改 , 得到的MD5值就会有很大的变化;
  3. 散列函数的不可逆性 , 即已知原数据 , 无法通过特征值反向获取原数据 。(需要说明的是2004年的国际密码讨论年会(CRYPTO)尾声 , 王小云及其研究同事展示了MD5、SHA-0及其他相关杂凑函数的杂凑冲撞 。也就是说 , 她找出了第一个 两个值不同 , 但 MD5 值相同的碰撞的例子 。这个应该不能称之为破解)
1.2 MD5用途:
  1. 防篡改 。上面说过用于文件完整性校验 。
  2. 用于不想让别人看到明文的地方 。比如用户密码入库 , 可以将用户密码使用MD5加密存储 , 下次用户输入密码登录只用将他的输入进行MD5加密与数据库的值判断是否一致即可 , 这样就有效防止密码泄露的风险 。
  3. 用于文件秒传 。比如百度云的文件秒传功能可以用这种方式来实现 。在你点击上传的时候 , 前端同学会先计算文件的MD5值然后与服务端比对是否存在 , 如果有就会告诉你文件上传成功 , 即完成所谓的秒传 。
在JDK中提供了MD5的实现:JAVA.security包中有个类MessageDigest , MessageDigest 类为应用程序提供信息摘要算法的功能 , 如 MD5 或 SHA 算法 。信息摘要是安全的单向哈希函数 , 它接收任意大小的数据 , 输出固定长度的哈希值 。
MessageDigest 对象使用getInstance函数初始化 , 该对象通过使用 update 方法处理数据 。任何时候都可以调用 reset 方法重置摘要 。一旦所有需要更新的数据都已经被更新了 , 应该调用 digest 方法之一完成哈希计算 。
对于给定数量的更新数据 , digest 方法只能被调用一次 。digest 被调用后 , MessageDigest 对象被重新设置成其初始状态 。
下面的例子展示了使用JDK自带的MessageDigest类使用MD5算法 。同时也展示了如果使用了update方法后没有调用digest方法 , 则会累计当前所有的update中的值在下一次调用digest方法的时候一并输出:
Copypackage 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();}}


推荐阅读