校验和
为检测数据错误而计算的值。用于数据传输和文件存储时的完整性验证。
校验和是为验证数据完整性而计算出来的固定长度的值。通过在发送端与接收端比较校验和,可以确认数据在传输过程中是否损坏。原理很简单:把整个数据用特定的算法处理一遍,生成一个短小的、类似"指纹"的值。
代表性的方式有 CRC (循环冗余校验)、Adler-32、MD5、SHA-256 等。CRC-32 生成 32 位的校验值,用于以太网帧的校验序列和 ZIP 归档。Adler-32 是 zlib 的规范 (RFC 1950) 中定义的方式,把逐字节累加得到的两个和分别对 65521 (小于 65536 的最大素数) 取余来计算。该 RFC 表示,这种方式比 CRC-32 快得多,而漏检错误的概率极低。MD5 生成 128 位 (32 个十六进制字符) 的摘要,但 IETF 的 RFC 6151 (2011 年) 认为,在像电子签名那样需要抗碰撞性的用途中,MD5 已不再可以接受,并列举了在当时 2.6 GHz 的台式机 CPU 上约 10 秒、在 1.6 GHz 的笔记本电脑上也能在 1 分钟以内找到碰撞的报告。如果目的是检测篡改,就选 SHA-256。
实务中最贴近日常的用法是验证分发的文件。在命令行中,可以用 sha256sum (Linux)、shasum -a 256 (macOS)、certutil -hashfile 文件名 SHA256 或 Get-FileHash (Windows) 来计算。这里成为陷阱的是校验和的获取途径。即使去核对与分发文件并列摆在同一页面上的值,只要那个页面被整体替换掉,两边就会彼此一致,篡改也就无法检测出来。使用带电子签名的校验和文件,或者至少让那个值通过另一条途径来确认,这是验证成立的前提条件。核对时不要用肉眼去看开头几个字符,而要像 sha256sum -c 文件名.sha256 这样机械地逐一比对。
校验和与哈希值容易被混为一谈,但严格来说两者目的不同。校验和主要以检测偶发的数据损坏为目的,哈希值则以检测有意的篡改和唯一标识数据为目的。这一差别直接关系到方式的选定。CRC-32 的计算很简单,因此容易在保持校验值为指定值的同时改写数据,无法用于检测篡改。想找出的是传输途中的比特错乱,还是第三方的改写,据此在 CRC 系与密码学哈希函数之间区别使用。实务中把 SHA-256 称作校验和的场合也很多,就叫法而言两者的界限是模糊的。
计算文本数据的校验和时,需要注意即使看上去一样,字节序列不同值就会不同。同一个字符串在 UTF-8 与 Shift_JIS 下字节序列不同,换行符是 LF 还是 CRLF、行首有没有 BOM、末尾有没有一个换行,都会得到不同的值。如果 Git 被设置为在检出时转换换行符,那么即使是从同一次提交取出的文件,跨环境的校验和也不会一致。若想比较的是作为字符串的同一性,就先把编码方式、换行符、BOM 统一成同一形态做规范化,然后再计算。
从字符计数的角度看,校验和的字符串长度按算法固定。用十六进制表示时,CRC-32 是 8 个字符,MD5 是 32 个字符,SHA-256 是 64 个字符。输入无论是 1 个字节还是 10 GB,输出长度都不变,因此数据库的列可以按 CHAR(64) 这样的定长来设计。不过,把 CRC-32 作为数值输出的工具有时会省掉开头的零,所以在做字符串比较之前需要把位数补齐。十六进制的大写与小写指的是同一个值,因此比较时先统一到其中一种再逐一比对。