两台电脑之间通过网络通信会有潜在的安全问题:

  • 消息被窃听,传输的明文数据被截获,需要对消息进行加密
  • 消息被篡改,数据在传输过程中被恶意修改,需要接收者判断是否被修改
  • 伪造发送消息,需要接收者判断是否是伪造的消息

计算机密码学技术就是针对以上问题提出的应对方案。要编写安全的程序,我们要注意:

  • 对于数据加密,应使用目前还没有被快速破解的加密算法
  • 对于数据校验,应使用专门的消息认证码算法

数据加密算法

java.security 包中提供了一些摘要算法,其他的算法(如 SM4 等)通常需要引入第三方包:

  1. <dependency>
  2. <groupId>org.bouncycastle</groupId>
  3. <artifactId>bcprov-jdk15to18</artifactId>
  4. </dependency>

Java 标准库允许添加外部算法,引入算法包后可以注册到标准库中:Security.addProvider(new BouncyCastleProvider());
通过静态方法 MessageDigest.getInstance() 获取算法。hutool 对此封装了更多的静态方法方便我们使用。

摘要加密

:::success 哈希算法也称摘要算法,特点是输入任意一组数据,根据规则计算出来的值是固定长度的。
理论上哈希算法是不可逆的,所以通常用来做数据完整性验证。 ::: :::success 哈希碰撞:
由于哈希算法是将无限的输入集合映射到有限的输出集合,那么必定会产生哈希碰撞。而输出的长度越长,哈希碰撞的概率就越低。
彩虹表攻击:
从哈希值反推明文只能通过暴力穷举法。但是黑客会先计算好常用明文和其哈希值的对照表,直接去表中查找,这就是彩虹表攻击。所以对于使用哈希值存储密码口令等不要使用常见的简单口令。
避免彩虹表攻击的方法就是在加密时对明文添加额外的随机数,又称作加盐(salt),这样通过彩虹表也无法直接反推出明文。 ::: 常见的摘要加密算法:

Java hashCode 方法

Java Object 类包含了 hashCode 方法,该方法返回数据的哈希结果。其中:

  • 每个对象调用 hashCode 方法返回其对应的内存地址
  • String 类重写了该方法,放回

对称加密


非对称加密


异或加密

异或(xor)是一个逻辑运算符,在 Java 等程序语言中用 ^ 表示,规则为两个二进制数值相同为 0,不同为 1,异或运算也叫半加运算,其运算法则相当于不带进位的二进制加法。
异或的一个重要性质:将一个数进行两次相同的异或会还原,即 a xor b xor b = a。如果 a xor b = c,那么已知 abc 中的任意两个,都可以通过异或运算获得另一个。
异或的用途:

  • 将二进制指定的位翻转
  • 不使用额外空间交换两个变量的值

通过异或运算,在原文、密钥、密文中,只要已知其中两个,便可获得另一个,因此可以进行简单的加密:

  • message xor key = cipherText
  • cipherText xor key = message :::success 数学家香农证明了满足下面两个条件,异或加密是无法破解的:

  • key 的长度大于等于 message

  • key 是一次性的,且每次都要随机产生 ::: 示例: 微信 dat 文件异或解密dat 文件是数据流形式的数据文件。微信将图片等聊天文件就缓存为了 dat 文件。由于微信聊天图片保存需要进行手动操作,而且不能大批量的保存,但是图片文件其实已经下载到本地,只是格式为 dat 文件,因此直接对 dat 文件进行批量解密为图片,更方便和高效。
    微信的 dat 文件其实就是对源文件进行了异或加密,我们可以下载一个源文件和它对应的 dat 文件,用十六进制编辑器打开后按位进行异或,获取加密密钥之后进行解密,程序示例(JDK 8):

    1. private void decode() throws IOException {
    2. // 源文件夹
    3. String fileDir = "C:\\Users\\Java\\dat";
    4. // 目标文件名
    5. String fileExtension = ".jpg";
    6. // 计算出的解码密钥
    7. int decodingKey = 0x80;
    8. File dir = new File(fileDir);
    9. File[] files = dir.listFiles((file, s) -> s.endsWith(".dat"));
    10. if (null == files) {
    11. return;
    12. }
    13. for (int i = 0; i < files.length; i++) {
    14. byte[] allBytes = Files.readAllBytes(files[i].toPath());
    15. byte[] newBytes = new byte[allBytes.length];
    16. for (int j = 0; j < allBytes.length; j++) {
    17. newBytes[j] = (byte) (allBytes[j] ^ decodingKey);
    18. }
    19. Files.write(Paths.get(fileDir + File.separator + i + fileExtension), newBytes);
    20. }
    21. }

数据校验算法

HMAC算法


数列摘要算法

校验码一般用于校验错误,没有纠错的能力。且只能校验简单的错误,比如单个数字错误、相邻顺序错乱等,并不能检测出故意伪造修改等错误。

奇偶校验

对于二进制数据:

  • 奇校验:所有传送的数位(含字符的各数位和校验位)中,1 的个数为奇数
  • 偶校验:所有传送的数位(含字符的各数位和校验位)中,1 的个数为偶数

注意:

  • 奇校验产生不了全 0 的代码
  • 只能检测出奇数个错误,偶数个错误检测不出来

    海明校验

CRC循环冗余校验

模10算法

又叫做 Luhn 算法:

  • 从数列最后一位数字开始,奇数位乘以 1,偶数位乘以 2,如果乘以 2 的结果是两位数,就将个位数和十位数相加返回
  • 把所有数字相加得到总和
  • 整除 10 余数为校验码

用于信用卡号、银行卡号校验。

模11算法

规则:

  • 从数列最后一位数字开始,每一位乘以不同的系数 n,根据系数规则的不同存在不同的模 11 算法
  • 把所有数字相加得到总和
  • 整除 11 余数为校验码,余数为 10 时根据算法不同可设定为字母或其他数字

用于身份证号校验。


参考文献:

  • 廖雪峰的官方网站
  • 阮一峰的网络日志