计算信息的指纹–哈希函数
目录
1 XCodeGhost 风波
2015年9月17日左右,知名程序员唐巧发布微博声称Xcode有可能被第三方代码注入,而在社交平台上引起轩然大波。乌云网后续发布相关的知识库文章。而在此之前,腾讯安全应急响应中心在跟踪某app的bug时发现异常流量,解析后上报了国家互联网应急中心(CNCERT),后者随即在9月14日发布了预警消息。之后也有国外信息安全组织跟进调查。 受影响的应用程序包括微信、网易云音乐、滴滴打车、高德地图、12306、同花顺、中信银行动卡空间、简书等76种。而事情的起因,是有人将被添加了恶意代码的 XCode 放在百度云盘上,供开发者下载,在使用感染后的XCode发布的App都带有后门,会在最终客户端运行时将隐私信息提交给第三方。 这一事件被称为 "XCodeGhost 事件"。
事件的背后折射出的,是计算机网络信息中的安全问题。 互联网中的大部分用户,都缺乏基本的安全意识,其中就包括计算机重度使用者:程序员。 如何确保我们下载使用的软件是没有经过污染的软件呢?首先确保我们从正规的渠道获取软件,其次,我们需要对软件进行校验。比较常用的方法,是使用哈希函数进行校验。 如 Eclipse.org 为我们提供了 SHA-512 校验码
2 什么是哈希函数
哈希函数,也叫单向散列函数 (one-way hash function) ,有一个输入和一个输出,输入称为 消息 ,输出称为 散列值 ,函数根据消息计算出散列值,可以用来检验消息的完整性。它也称作 消息摘要函数 (Message Digest Function) ,或者 杂凑函数 。消息也被称作 原像 ,散列值也被称作 哈希值 或 指纹 。哈希函数具有以下的特性:
- 散列值长度与消息长度无关
- 散列值与消息内容密切相关,即消息不同,散列值也不同
单向性
在给出散列值 H(M) 的情况下,无法计算出消息 M 的值。抗碰撞性
如果有消息 M1,散列值 H(M1), H(M2) ,且 H(M1) = H(M2) ,很难找出消息 M2 令 M2 ≠ M1
3 常见的几种哈希函数
3.1 MD5
MD5 能够产生 128 bit 的散列值(RFC1321)。 MD5 的强抗碰撞性已经于 2005 年被攻破,也就是说,现在已经能够产生具备相同散列值的两条不同的消息,因此它是不安全的。
3.2 SHA
SHA-1 (Secure Hash Algorthm 1) 能够产生 160 bit 的散列值,SHA-1 的强抗碰撞性已于 2005 年被攻破,已经不够安全。
SHA-2 包含 6 个版本:
名称 | 长度 | 备注 |
SHA-224 | 224 | 将 SHA-256 的结果截去 32 bit |
SHA-256 | 256 | |
SHA-512/224 | 224 | 将 SHA-512 的结果截去 228 bit |
SHA-512/256 | 256 | 将 SHA-512 的结果截去 256 bit |
SHA-384 | 384 | 将 SHA-512 的结果截去 128 bit |
SHA-512 | 512 |
可以看出, 6 种 SHA-2 都是由 SHA-256 和 SHA-512 衍生出来的。
SHA-3 是设计手来替换 SHA-1 的。它于 2012 年在众多的候选算法中选择 Keccak 算法来完成 SHA-3. SHA-3 没有长度限制,它可以生成任意长度的散列值。
3.3 RIPEMD
RIPEMD 的强抗碰撞性已于 2004 年被攻破。不过 RIPEMD 还包括 128/160/256/320 等其他版本。比特币中使用的就是 RIPEMD-160
4 常见的作用
消息认证
可以用哈希函数来构建消息认证码,它将共享密钥和消息进行混合计算得来散列值,用于检测通信过程中的错误、篡改以及伪装基于口令的加密
基于口令的加密(Password Based Encryption PBE)的原理是将口令和盐混合之后计算其散列值,然后将这一散列值作为加密的密钥,这样能够防御针对口令的字典攻击数字签名
伪随机数生成器
一次性口令
一次性口令经常用于服务器对客户端的合法性的认证,生成的口令只在网络中传输一次,因此即使窃听者窃取了口令,也无法使用。
5 使用 CryptoPP 的哈希函数计算散列值
CryptoPP提供了一系列哈希函数,包括 SHA-1, SHA-2 (SHA-224, SHA-256, SHA-384, and SHA-512), SHA-3, Tiger, WHIRLPOOL, RIPEMD-128, RIPEMD-256, RIPEMD-160, RIPEMD-320。 其中 MD5 需要在 Weak 命令空间中使用。
5.1 MD5 算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include <md5.h> byte digest[ CryptoPP::Weak::MD5::DIGESTSIZE ]; std::string message = "abcdefghijklmnopqrstuvwxyz"; CryptoPP::Weak::MD5 hash; hash.CalculateDigest( digest, (const byte*)message.c_str(), message.length() ); CryptoPP::HexEncoder encoder; std::string output; encoder.Attach( new CryptoPP::StringSink( output ) ); encoder.Put( digest, sizeof(digest) ); encoder.MessageEnd(); std::cout << output << std::endl; |
5.2 SHA 算法
1 2 3 4 5 6 7 |
SHA hash; byte digest[SHA::DIGESTSIZE]; hash.Update(pbData1, nData1Len); hash.Update(pbData2, nData2Len); hash.Update(pbData3, nData3Len); hash.Final(digest); |
如果使用 Filter ,还可以这么写:
1 2 3 4 5 6 7 |
SHA256 hash; string message = "abcdefghijklmnopqrstuvwxyz"; string digest; StringSource s(message, true, new HashFilter(hash, new HexEncoder(new StringSink(digest)))); cout << digest << endl; |