Hash与对称加密算法

4,545 阅读15分钟

前言

以下是iOS签名原理系列的文章链接

  1. 密码学之RSA加密算法
  2. Hash与对称加密算法
  3. 理解iOS签名原理

本篇是这个系列的第2篇,下面进入正题

1. Hash(散列函数)

1.1 什么是Hash?

Hash,一般翻译做散列、哈希,是把任意长度的输入通过Hash算法变成固定长度的输出,该输出就是Hash值(哈希值,或散列值)。这种转换是一种压缩映射,也就是,哈希值的空间通常远小于输入的空间,不同的输入可能会Hash成相同的输出。简单的说Hash就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数

Hash算法也被称为散列算法、哈希算法,虽然被称为算法,但实际上它更像是一种思想。Hash算法没有一个固定的公式,只要符合Hash思想的算法都可以被称为是Hash算法。

1.2 哈希碰撞

由于Hash是无限集的数据向有限集的单方向映射,所以难免会出现:对不同的数据可能得到相同的Hash值,这种现象称为 哈希碰撞,或称 散列碰撞。如下图所示:

黑客攻击的一种方法,就是设法制造【哈希碰撞】,从而破解用户的密码,窃取用户的信息。

X个二进制位的Hash值,产生碰撞的可能性是 2X 分之一。如

16个二进制位的Hash值,产生碰撞的可能性是 65536 分之一;
32个二进制位的Hash值,产生碰撞的可能性是 4,294,967,296 分之一。

由此可见,有效地防止哈希碰撞的方法,就是 扩大Hash值的取值空间。当然,更长的哈希值意味着更大的存储空间、更多的计算,这将影响性能和成本。实际应用中,开发者必须做出抉择,在安全与成本之间找到平衡。

1.3 Hash函数的性质

所有Hash函数都有一个基本特性:如果两个哈希值是不相同的(根据同一函数),那么这两个哈希值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果。反之则不然,即 如果两个哈希值相同,两个输入值很可能是相同的,但不绝对肯定二者一定相等(可能出现哈希碰撞)

Hash函数还有以下一些特点:

  • 算法是公开的,且很难找到逆向规律,即没法进行逆运算。
  • 如果输入数据相同,那么经过同一Hash函数输出值必然相同。

Hash函数能使对一个数据序列的访问过程更加迅速有效,通过Hash函数,数据元素将被更快地定位。

1.4 常见Hash的算法及应用

说到Hash,就不得不提到一些著名的Hash算法。在众多Hash算法中,MD5、SHA1、SHA256可以说是应用最广泛的了,而它们都是以MD4为基础设计的。下面简单介绍一下这几个Hash算法:

  • MD4

MD4是MIT的 Ronald L. Rivest 在1990年设计的,MD是Message Digest(消息摘要)的缩写。它是基于32位操作数的位操作来实现的。

  • MD5

MD5是 Rivest 于1991年对MD4的改进版本。MD5比MD4复杂,速度要慢一点,但更安全,在抗分析和抗差分方面表现更好。

  • SHA1

SHA1是由 NIST NSA 设计为同DSA一起使用的,它对长度小于2^64的输入,产生长度为160bit的Hash值,因此抗穷举性更好。但在相同的硬件上,SHA1较MD5会稍慢一些。

  • SHA256

SHA256是SHA1的后继者,对于任意长度的消息,SHA256都会产生一个256位长的Hash值。与SHA1相比,SHA256的抗穷举性更好,但速度则慢一些。

Hash算法广泛应用于 错误校正语音识别信息安全 中,其在信息安全的应用主要体现在以下几个方面:

  • 文件校验

即校验文件是否被篡改。

  • 数字签名

由于非对称算法的运算速度较慢,所以在数字签名协议中,先对文件进行运算得到其Hash值(其Hash值又被称为【数字摘要】),然后对数字摘要进行数字签名,在统计上认为与对文件本身进行数字签名是等效的。

  • 鉴权协议

如下的鉴权协议又被称作挑战--认证模式:在传输信道是可被侦听,但不可被篡改的情况下,这是一种简单而安全的方法。

注意:

Hash算法加密的对象是 文件的二进制内容,只要文件的二进制内容没发生改变,其Hash值不变。

如,假设有一张文件名为【001.png】的图片,改名为【002.txt】,比较其改名前后md5值是否发生改变;将其压缩后,再试试看

2. Hash示例:密码加密

在实际运用中,服务器是不会保存用户密码的,从安全角度考虑,App不会有【找回密码】功能,有的只是【重置密码】。那么,用户注册时,客户端发给服务器的密码是怎样的呢?

接下来,我们以MD5加密算法为例,探讨一下对密码进行怎样的处理才能使其足够安全。

  1. 对密码进行一次 MD5算法加密(假设密码是【123456】)

思考:直接将【e10adc3949ba59abbe56e057f20f883e】作为【密码】发给服务器可以吗?

由于Hash的特点:对相同数据的Hash运算,得到的结果是一样的。因此,只要有足够多的这种明文密文对应关系的数据,利用穷举字符组合的方式,是可以反向查询到原始数据的。实际上,提供这种服务的网站有很多。这里向大家介绍一个作者自己常用的:MD5在线加密解密。接下来反向查询一下:

显然,【对密码进行一次MD5算法加密】在安全上仍然是不足的。

这里可能会有读者有疑问,只要用户将密码设置得足够复杂(多种类型字符混排之类),应该就不会这么简单被反向查询到吧。的确如此,但是密码的安全不应该寄希望于用户的【自主行为】。

  1. 给密码加盐
    给密码加盐的目的是让密码复杂一些。具体做法是,写个全局的静态常量salt,将其与密码拼接起来,使密码看上去复杂一些,然后再进行MD5加密。如salt="sdfSD_FHja%(s"。(此时密码为【sdfSD_FHja%(s123456】)

同样,对其反向查询一下

看来,加盐的做法使密码相对安全了。

思考,【盐】泄露了怎么办?

  1. HMAC加密方案
    此方案利用Hash算法,以一个消息M和一个密钥K作为输入,生成其Hash值。

密钥K是随机选取的(每个用户的密钥K都是随机值),可以采用一种强伪随机发生器,并且密钥需要周期性更新。

当用户注册时,将原始密码进行上述【1】或【2】处理后,发给服务器,服务器为该用户生成一个密钥K,将其发给客户端的同时,对用户密码和密钥K再进行MD5运算。客户端会将密钥K保存到本地,以后每次登录都对密码经过HMAC加密方案处理后,再将结果发给服务器校验。代码如下:

static NSString *salt = @"sdfSD_FHja%(";

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSString *pwd = @"123456";
    pwd = [salt stringByAppendingString:pwd].md5String;
    NSLog(@"加盐后的密码是:%@", pwd);
    
    // 假设本地保存的密钥K为"Um7f1ltAibSF!$w@8"
    NSString *key = @"Um7f1ltAibSF!$w@8";
    pwd = [pwd hmacMD5StringWithKey:key];
    NSLog(@"现在的密码是:%@", pwd);
}

其结果为

反查询一下

从上面的流程不难看出,HMAC加密方案 使密码与设备(用户注册时的设备)进行了绑定(其它设备无密钥K)。其它设备想登录此账号时,向服务器请求获取密钥K,服务器会检测用户是否开启了设备锁,如果开启了,就通知授权设备请求授权,授权通过后才会将密钥K发给其它设备。显然,HMAC加密方案相对【1】、【2】的处理,更加安全了。

注意:

思考:上述对密码的三种处理,均将最终密码发送给服务器,这样做有没有什么隐患?

  1. 对密码进行有效时间处理

即在请求登录之前,可以向服务器请求最新时间(如201910230836,精确到分),再进行类似这样的处理:(HMAC哈希值+"201910230836").md5,将其发送给服务器,服务器再进行校验,如果密码有效期小于2分钟,则先校验当前时间下的值(假设过了1分钟):
(HMAC哈希值+"201910230837").md5
如果不匹配,再校验前1分钟的值:
(HMAC哈希值+"201910230836").md5

毫无疑问,在这种机制下,客户端登录的密码具有时效性,其有效时间越短安全性越强。

3. 数字签名

接下来谈一谈Hash在信息安全上的应用。设想一个这样的场景:

假设你要买一件10块钱的商品,你在客户端将订单信息发送给服务端,请求支付;
黑客窃取了你的订单信息,并将金额改成20元,然后发送给服务端;
服务端扣了你20元,同时生成消费信息,并发送给你;
黑客窃取了消费信息,又将金额改成10元,继续发送给你;
你收到了这10元的消费信息。

在这个过程中,黑客神不知鬼不觉地篡改了你提交的信息,造成了你的损失。那么该如何保护你的信息的完整性呢?这就要用到数字签名技术了。

3.1 什么是数字签名

数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是 非对称密钥加密技术数字摘要技术 的应用。

  1. 关于非对称加密算法的介绍,不熟悉的同学请移步 密码学之RSA加密算法
  2. 数字摘要是将任意长度的消息变成固定长度的短消息。是不是很熟悉?其实就是Hash。数字摘要就是采用Hash算法将【明文】输出为一串固定长度(如128位)的密文,这一串密文又称为数字指纹。

3.2 数字签名流程

一般来说,非对称加密是用来处理短消息的,而相对于较长的消息则显得有些吃力。当然,可以将长的消息分成若干小段,然后再分别签名。不过,这样做非常麻烦,而且会带来数据完整性的问题。比较合理的做法是在 数字签名前对消息先进行数字摘要

同样是上述的场景,结合数字签名技术之后,其流程如下:

  1. 当你发起10元的支付订单信息时,客户端用Hash算法(如MD5、SHA1等)将此订单信息加密生成【数字摘要】,同时用服务端的 公钥 对数字摘要进行加密,这个加密后的摘要也就是【数字签名】;
  2. 客户端将【订单信息】和【数字签名】一起发给服务端;
  3. 服务端收到后,用 私钥 解析数字签名,得到【数字摘要1】,同时,再对【订单信息】用同样的Hash算法(约定好的)加密生成【数字摘要2】,进而匹配数字摘要1和数字摘要2:如果一致,说明订单信息是完整真实的,可以进行扣款;否则说明订单信息被篡改,订单无效。

数字签名是个加密的过程,数字签名验证是个解密的过程。

PS. 我们使用的支付宝,其支付过程就使用了数字签名技术进行安全验证。

总结:数字签名有两种功效

  1. 能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。
  2. 能确定消息的完整性。因为数字签名的特点是它代表了文件的特征,文件如果发生改变,数字摘要的值也将发生变化。

4. 对称加密

4.1 什么是对称加密

使用相同的密钥对信息进行加密和解密,这种加密方式叫做 对称加密。也就是说,就是明文通过密钥加密得到密文,密文通过密钥解密得到明文。

让我们简单比较一下 非对称加密Hash对称加密

  • 非对称加密用于加密少量数据,加密效率低,但是非常安全。
  • Hash用于信息识别,验证信息的真伪和完整性
  • 对称加密用于加密大量数据,加密速度快、效率高,但是一旦密钥泄露,密文将毫无安全性可言。

4.2 常用的对称加密算法

常用的对称加密算法有

  • DES:是数据加密标准,比较典型,但是现在用得少。

特点:强度小、速度较快,

  • 3DES:是基于DES,对一块数据用三个不同的密钥进行三次加密。

特点:强度较DES高一些,速度也快。但是三个密钥不便于管理,因此用得也少。

  • AES:高级加密标准,被普通使用。

特点:速度快,安全级别高;
PS. 苹果的钥匙串访问、美国国家安全局等都是用AES。如果你想玩对称加密,也建议用AES。

4.3 对称加密算法的应用模式

主要有两种应用模式,即ECB和CBC。

  • ECB:电子密码本模式。每一块数据,独立加密。

最基本的加密模式,也就是通常理解的加密,相同的明文将永远加密成相同的密文,无初始向量,容易受到密码本重放攻击,一般情况下很少用。

  • CBC:密码分组链接模式。使用一个密钥和一个**初始化向量[IV]**对数据执行加密。
  1. 明文被加密前要与前面的密文进行异或运算后再加密,因此只要选择不同的初始向量,相同的密文加密后会形成不同的密文,这是目前应用最广泛的模式。
  2. CBC可以有效的保证密文的完整性,如果一个数据块在传递是丢失或改变,后面的数据将无法正常解密。
  3. 它的主要缺点在于加密过程是串行的,无法被并行化,而且消息必须被填充到块大小的整数倍。
  4. 常应用于防窃听技术中。

4.4 对称加密算法示例:

接下来,我们用终端来玩一玩对称加密算法。操作流程如下:

  1. 准备明文文件(message.txt)

  1. 对明文进行 AES(ECB)加密
  • 生成密文文件(msg1.bin)
openssl enc -aes-128-ecb -K 616263 -nosalt -in message.txt -out msg1.bin

参数说明:

  1. enc:表示是对称加密
  2. -K:密钥K值为616263(实际上是abc,即0x610x620x63)
  3. -nosalt:不加盐(openssl会随机加盐,此处不需要)
  4. -out:加密后的密文输出到msg1.bin文件中
  • 修改明文内容(如将第二行的第二个数字1改成a),再进行 AES(ECB)加密,生成密文文件(msg2.bin)

  • 对比一下 msg1.bin 和 msg2.bin

显然后面的数据块并不受到前面数据块内容改变的影响,充分说明ECB模式是每一块数据进行独立加密。

  1. 还原明文文件,对明文进行 AES(CBC)加密
  • 生成密文文件(msg3.bin)
openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out msg3.bin

参数说明:

  1. -iv:初始化向量值为0102030405060708(随机取的)
  • 修改明文内容,再进行 AES(CBC)加密,生成密文文件(msg4.bin)

  • 对比一下 msg3.bin 和 msg4.bin

显然后面的数据块受到了前面数据块内容改变的影响,充分说明了CBC可以有效的保证密文的完整性,即如果一个数据块在传递是丢失或改变,后面的数据将无法正常解密。

参考资料

文末

本文github