两台电脑之间通过网络通信会有潜在的安全问题:
- 消息被窃听,传输的明文数据被截获,需要对消息进行加密
- 消息被篡改,数据在传输过程中被恶意修改,需要接收者判断是否被修改
- 伪造发送消息,需要接收者判断是否是伪造的消息
计算机密码学技术就是针对以上问题提出的应对方案。要编写安全的程序,我们要注意:
- 对于数据加密,应使用目前还没有被快速破解的加密算法
- 对于数据校验,应使用专门的消息认证码算法
数据加密算法
java.security 包中提供了一些摘要算法,其他的算法(如 SM4 等)通常需要引入第三方包:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
</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):private void decode() throws IOException {
// 源文件夹
String fileDir = "C:\\Users\\Java\\dat";
// 目标文件名
String fileExtension = ".jpg";
// 计算出的解码密钥
int decodingKey = 0x80;
File dir = new File(fileDir);
File[] files = dir.listFiles((file, s) -> s.endsWith(".dat"));
if (null == files) {
return;
}
for (int i = 0; i < files.length; i++) {
byte[] allBytes = Files.readAllBytes(files[i].toPath());
byte[] newBytes = new byte[allBytes.length];
for (int j = 0; j < allBytes.length; j++) {
newBytes[j] = (byte) (allBytes[j] ^ decodingKey);
}
Files.write(Paths.get(fileDir + File.separator + i + fileExtension), newBytes);
}
}
数据校验算法
HMAC算法
数列摘要算法
校验码一般用于校验错误,没有纠错的能力。且只能校验简单的错误,比如单个数字错误、相邻顺序错乱等,并不能检测出故意伪造修改等错误。
奇偶校验
对于二进制数据:
- 奇校验:所有传送的数位(含字符的各数位和校验位)中,1 的个数为奇数
- 偶校验:所有传送的数位(含字符的各数位和校验位)中,1 的个数为偶数
注意:
CRC循环冗余校验
模10算法
又叫做 Luhn 算法:
- 从数列最后一位数字开始,奇数位乘以 1,偶数位乘以 2,如果乘以 2 的结果是两位数,就将个位数和十位数相加返回
- 把所有数字相加得到总和
- 整除 10 余数为校验码
模11算法
规则:
- 从数列最后一位数字开始,每一位乘以不同的系数 n,根据系数规则的不同存在不同的模 11 算法
- 把所有数字相加得到总和
- 整除 11 余数为校验码,余数为 10 时根据算法不同可设定为字母或其他数字
用于身份证号校验。
参考文献:
- 廖雪峰的官方网站
- 阮一峰的网络日志