HASH概述
Hash
:一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数
常用的
HASH
算法:
MD5
SHA1
SHA256
SHA512
Hash
的特点:
- 算法是公开的
- 对相同数据运算,得到的结果相同
- 对不同数据运算,得到的长度相同(默认
128位
,32
个字符)- 可重复
- 不可逆
- 信息摘要,信息“
指纹
”,是用来做数据识别的
HASH
算法不可逆的原因:
HASH
算法有两个基本特点,可重复和不可逆。HASH
值为32
个字符组成,所表达的数据是有限的,即:16 ^ 32
。但生成HASH
值的原文是无限的,无限的内容生成有限的表现形式,一定会产生多个原文得到相同的HASH
值,这种现象称散列碰撞
。也正是因为如此,从HASH
值反推出原文是不可能的
Hash
的用途:
- 密码加密
- 搜索引擎
- 版权
- 数据识别
- 数字签名
密码加密
通过运用HASH
算法,给用户的密码进行加密。
很多用户多个
App
之间使用相同密码,如果公司App
导致用户密码泄露,可能会造成用户丢失很多数据,公司会背负相应的法律责任
:
所以在网络传输、服务器保存隐私数据,例如:用户的密码,应该传递和保存的是加密后的数据,以免泄露密码加密,应使用不可逆算法。如果密文可被解密,依然存在安全隐患。假设:使用
RSA
加密,密码长度有限,无需担心大数据加密和效率问题,安全性也没问题,但如果私钥泄露,密码还是相当于明文存储。对于密钥的更换也非常困难,无法让平台所有用户全部更换一次密码
密码加密方式:
- 使用
MD5
MD5
加盐HMAC
加密HASH + 时间戳
案例1:
使用
MD5
加密数据使用
NSString+Hash
库,提供以下方法:#import <Foundation/Foundation.h> @interface NSString (Hash) //计算MD5散列结果 - (NSString *)md5String; //计算SHA1散列结果 - (NSString *)sha1String; //计算SHA256散列结果 - (NSString *)sha256String; //计算SHA 512散列结果 - (NSString *)sha512String; //计算HMAC MD5散列结果 - (NSString *)hmacMD5StringWithKey:(NSString *)key; //计算HMAC SHA1散列结果 - (NSString *)hmacSHA1StringWithKey:(NSString *)key; //计算HMAC SHA256散列结果 - (NSString *)hmacSHA256StringWithKey:(NSString *)key; //计算HMAC SHA512散列结果 - (NSString *)hmacSHA512StringWithKey:(NSString *)key; //计算文件的MD5散列结果 - (NSString *)fileMD5Hash; //计算文件的SHA1散列结果 - (NSString *)fileSHA1Hash; //计算文件的SHA256散列结果 - (NSString *)fileSHA256Hash; //计算文件的SHA512散列结果 - (NSString *)fileSHA512Hash; @end
打开
ViewController.m
文件,写入以下代码:#import "ViewController.h" #import "NSString+Hash.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSString * pwd = @"123456"; NSLog(@"密码:%@",pwd.md5String); } @end
运行项目,点击屏幕,输出以下内容:
密码:e10adc3949ba59abbe56e057f20f883e
MD5
生成的密文无法还原成原文,但它依然可以被破解,而它的破解指的是碰撞。如果计算出来的MD5
值和已知的MD5
值一样,即找到了它的原文。已知的MD5
值越多,破解的成功率越高对于专业网站来说,
MD5
值的反向查询成功率还是很高的
案例2:
对用户密码加盐
打开
ViewController.m
文件,写入以下代码:static NSString * salt = @"LKSJDFLKJ&^&@@"; -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSString * pwd = @"123456"; pwd = [pwd stringByAppendingString:salt].md5String; NSLog(@"密码:%@",pwd.md5String); }
运行项目,点击屏幕,输出以下内容:
密码:2359298f49af5695a4b846e95bdea467
盐
和密钥
很相似,如果盐
被泄露,也会造成很大的安全隐患
盐的更换同样困难,需要平台所有用户更换密码。而且加盐的方式对于开发者依赖太高,如果开发者带盐跑路,对公司来说也是一件痛苦的事情
案例3:
使用
HMAC
加密方式打开
ViewController.m
文件,写入以下代码:-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSString * pwd = @"123456"; pwd = [pwd hmacMD5StringWithKey:@"Zang"]; NSLog(@"密码:%@",pwd); }
运行项目,点击屏幕,输出以下内容:
密码:f0538e52ec3f5b0a27660cd321b1cb97
HMAC
加密的特点:
- 使用一个
密钥
加密数据做两次散列密钥
由服务端提供,服务端为每个账号生成一个密钥
,再传递给客户端- 单一
密钥
的泄漏,只影响一个用户- 单一
密钥
的更换,只需要对应用户重新输入密码即可使用
HMAC
加密的流程注册:
- 将账号传递给服务端
- 服务端针对账号随机生成一个
密钥
,返回给客户端- 客户端将
密钥
存储在本地,例如:钥匙串- 注册时,明文密码使用
HMAC
加密,将密文传递服务端登录:
- 如果本地没有
密钥
,先请求服务端获取密钥
- 登录时,明文密码使用
HMAC
加密,将密文传递服务端当设备登录时没有
密钥
,可视为未授权设备,可增加新设备授权流程例如:服务端向老设备发送请求,是否同意新设备登录,用户点击同意,服务端再给新设备发送
密钥
使用
HMAC
加密,密码相对安全,但无法防止重放攻击。攻击者利用网络监听或者其他方式盗取密文密码,之后再把它重新发给认证服务器,可以轻松的欺骗系统进行身份认证
案例4
对请求增加时效性
防止重放攻击最有效的办法就是对密文增加时效性,在
HMAC
加密方式不变的情况下,增加时间戳例如:
(密码.HMAC + 时间戳).md5
打开
ViewController.m
文件,写入以下代码:-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSString * pwd = @"123456"; pwd = [pwd hmacMD5StringWithKey:@"Zang"]; pwd = [pwd stringByAppendingString:@"202104151608"].md5String; NSLog(@"密码:%@",pwd); }
运行项目,点击屏幕,输出以下内容:
密码:d3a8cc619a27e21077ebc7baa03f5e8f
上述案例中,客户端使用的时间戳,必须是服务器时间。服务端进行验证时,使用相同方式加密,然后和客户端传递的密文对比一致性
如果客户端网络延迟,导致请求时间较长,服务端时间变成了下一分钟,验证将无法通过。故此对于时效性的验证,还应增加容错处理
服务端验证当前时间戳,如果失败,还应该使用前一分钟的时间戳再次验证
对于
HASH + 时间戳
的登录方式,一次请求的有效时间,最长为1分59秒
,这样可以有效避免重放攻击
数字签名
为什么用签名这个词?因为老外喜欢用支票,支票上面的签名能够证明这玩意是你的。那么数字签名顾名思义,就是用于鉴别数字信息的方法。
数字签名是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明
数字签名的目的:
- 防伪造:私有密钥只有签名者自己知道,所以其他人不可能构造出正确的
- 鉴别身份:由于传统的手工签名一般是双方直接见面的,身份自可一清二楚。在网络环境中,接收方必须能够鉴别发送方所宣称的身份
- 防篡改:对于数字签名,签名与原有文件已经形成了一个混合的整体数据,不可能被篡改,从而保证了数据的完整性
- 防重放:对于数字签名,如果采用了对签名报文添加流水号、时间戳等技术,可以防止重放攻击
- 防抵赖:数字签名可以鉴别身份,不可能冒充伪造,那么,只要保存好签名的报文,也就是保留了证据,签名者就无法抵赖
- 保密性:有了保密性,截收攻击也就失效了。数字签名可以加密要签名的消息,当然,如果签名的报名不要求机密性,也可以不用加密
一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与
HASH
技术的应用服务端下发数据:
- 对将要传递的数据,生成
HASH
值- 将
HASH
值使用RSA
加密,生成签名- 传递数据时,同时传递签名
客户端验证数据:
- 将密文
RSA
解密,获取到服务端下发的HASH
值- 使用相同的算法,对数据生成本地
HASH
值- 将本地和服务端下发的
HASH
值进行对比RSA
解密成功,HASH
值对比一致,才能表示本次请求是合法的客户端向服务端传递数据,逻辑一致
其他用途
搜索引擎
在搜索引擎中分词搜索时,几个关键字无论顺序如何,得到的搜索结果都是一致的
例如:
iOS & Swift
无论
iOS
和Swift
的顺序如何,只要iOS
和Swift
的HASH
值求和,结果是一样的,则视为相同的搜索词,搜索结果就是一致的即:
iOS.HASH + Swift.HASH ≡ SUM ≡ Swift.HASH + iOS.HASH
版权
由于数字文件的便捷性,拷贝传播十分方便,但是由于一些涉及利益的传播性质,往往会侵犯数字文件版权的归属问题
文件的
HASH
值,和文件名、后缀名无关,只取决于文件的二进制数据。正版与盗版的区别也在于它们的HASH
值不同
数据识别
网盘的数据识别,例如秒传功能,如果上传文件的
HASH
值在服务器上存在,无需重复上传还有对于一些违规的文件,修改文件名、后缀名也会被识别出来,这种情况只能修改文件的二进制数据
对于文件计算
HASH
值,是不是也耗费服务器资源呢?其实,
HASH
去重也是分粒度的,有文件去重,块去重,字节去重,粒度越细的准确率越高,相应的耗费服务器资源肯定也要多
终端命令
散列函数
计算
MD5
散列结果md5 -s "123456 ------------------------- MD5 ("123456") = e10adc3949ba59abbe56e057f20f883e
32
个字符的MD5
散列字符串
计算
SHA1
散列结果echo -n "123456" | openssl sha1 ------------------------- (stdin)= 7c4a8d09ca3762af61e59520943dc26494f8941b
40
个字符的SHA1
散列字符串
计算
SHA256
散列结果echo -n "123456" | openssl sha256 ------------------------- (stdin)= 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
64
个字符的SHA256
散列字符串
计算
SHA512
散列结果echo -n "123456" | openssl sha512 ------------------------- (stdin)= ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413
128
个字符的SHA512
散列字符串
HMAC散列函数
计算
HMAC MD5
散列结果echo -n "123456" | openssl dgst -md5 -hmac "key" ------------------------- (stdin)= 0abf6bacd23c55fa6ab14eb44a7f5720
32
个字符的HMAC MD5
散列字符串
计算
HMAC SHA1
散列结果echo -n "123456" | openssl sha1 -hmac "key" ------------------------- (stdin)= 4fc32f51f214211618a9598893823519b829ee74
40
个字符的HMAC SHA1
散列字符串
计算
HMAC SHA256
散列结果echo -n "123456" | openssl sha256 -hmac "key" ------------------------- (stdin)= 4df81f55d708ae1720d5f65ef42f3475dc168fa23fde424ac5944f87c309b05f
64
个字符的HMAC SHA256
散列字符串
计算
HMAC SHA512
散列结果echo -n "123456" | openssl sha512 -hmac "key" ------------------------- (stdin)= cc2f51259b61903f6b50ea2cc3653340f1e8c0ae780c927bc1c7f09dcd1d606f7cb3347117f75fa19d9f760f4d538709a969c11036d194b972bf3232f34f30a8
128
个字符的HMAC SHA512
散列字符串
文件散列函数
创建
file.txt
文件,写入以下内容:123456
计算文件的
MD5
散列结果md5 file.txt ------------------------- MD5 (file.txt) = e10adc3949ba59abbe56e057f20f883e
32
个字符的MD5
散列字符串
计算文件的
SHA1
散列结果openssl sha1 file.txt ------------------------- SHA1(file.txt)= 7c4a8d09ca3762af61e59520943dc26494f8941b
40
个字符的SHA1
散列字符串
计算文件的
SHA256
散列结果openssl sha256 file.txt ------------------------- SHA256(file.txt)= 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
64
个字符的SHA256
散列字符串
计算文件的
SHA512
散列结果openssl sha512 file.txt ------------------------- SHA512(file.txt)= ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413
128
个字符的SHA512
散列字符串
总结
Hash
的特点:
- 算法是公开的
- 对相同数据运算,得到的结果相同
- 对不同数据运算,得到的长度相同
- 可重复
- 不可逆
- 信息摘要,信息“
指纹
”,是用来做数据识别的
Hash
的用途:
- 密码加密
- 搜索引擎
- 版权
- 数据识别
- 数字签名
密码加密方式:
- 使用
MD5
MD5
加盐HMAC
加密(比较好的方案)HASH + 时间戳
(配合HMAC
加密,可防重放攻击)数字签名的算法
HASH + RSA
数字签名的目的:
- 防伪造
- 鉴别身份
- 防篡改
- 防重放
- 防抵赖
- 保密性
数字签名的逻辑
- 生成原始数据的
HASH
值- 将
HASH
值进行RSA
加密