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

@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

  1. > 打开`ViewController.m`文件,写入以下代码:
  2. >

import “ViewController.h”

import “NSString+Hash.h”

@implementation ViewController

  • (void)viewDidLoad { [super viewDidLoad]; }

-(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event { NSString * pwd = @”123456”; NSLog(@”密码:%@”,pwd.md5String); }

@end

  1. > 运行项目,点击屏幕,输出以下内容:
  2. >

密码:e10adc3949ba59abbe56e057f20f883e

  1. >
  2. ---
  3. > `MD5`生成的密文无法还原成原文,但它依然可以被破解,而它的破解指的是碰撞。如果计算出来的`MD5`值和已知的`MD5`值一样,即找到了它的原文。已知的`MD5`值越多,破解的成功率越高
  4. > 对于专业网站来说,`MD5`值的反向查询成功率还是很高的<br />
  5. ![](https://upload-images.jianshu.io/upload_images/9297953-4a283bb6edd59099.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  6. > 案例2
  7. > 对用户密码加盐
  8. > 打开`ViewController.m`文件,写入以下代码:
  9. >

static NSString * salt = @”LKSJDFLKJ&^&@@”;

-(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event { NSString * pwd = @”123456”; pwd = [pwd stringByAppendingString:salt].md5String; NSLog(@”密码:%@”,pwd.md5String); }

  1. > 运行项目,点击屏幕,输出以下内容:
  2. >

密码:2359298f49af5695a4b846e95bdea467

  1. >
  2. ---
  3. > `盐``密钥`很相似,如果`盐`被泄露,也会造成很大的安全隐患<br />
  4. ![](https://upload-images.jianshu.io/upload_images/9297953-dafd5b9a2aa3855e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  5. > 盐的更换同样困难,需要平台所有用户更换密码。而且加盐的方式对于开发者依赖太高,如果开发者带盐跑路,对公司来说也是一件痛苦的事情
  6. > 案例3
  7. > 使用`HMAC`加密方式
  8. > 打开`ViewController.m`文件,写入以下代码:
  9. >

-(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event { NSString * pwd = @”123456”; pwd = [pwd hmacMD5StringWithKey:@”Zang”]; NSLog(@”密码:%@”,pwd); }

  1. > 运行项目,点击屏幕,输出以下内容:
  2. >

密码:f0538e52ec3f5b0a27660cd321b1cb97

  1. >
  2. ---
  3. > `HMAC`加密的特点:
  4. > - 使用一个`密钥`加密数据做两次散列
  5. > - `密钥`由服务端提供,服务端为每个账号生成一个`密钥`,再传递给客户端
  6. > - 单一`密钥`的泄漏,只影响一个用户
  7. > - 单一`密钥`的更换,只需要对应用户重新输入密码即可
  8. > 使用`HMAC`加密的流程
  9. > 注册:
  10. > - 将账号传递给服务端
  11. > - 服务端针对账号随机生成一个`密钥`,返回给客户端
  12. > - 客户端将`密钥`存储在本地,例如:钥匙串
  13. > - 注册时,明文密码使用`HMAC`加密,将密文传递服务端
  14. > 登录:
  15. > - 如果本地没有`密钥`,先请求服务端获取`密钥`
  16. > - 登录时,明文密码使用`HMAC`加密,将密文传递服务端
  17. > 当设备登录时没有`密钥`,可视为未授权设备,可增加新设备授权流程
  18. > 例如:服务端向老设备发送请求,是否同意新设备登录,用户点击同意,服务端再给新设备发送`密钥`
  19. > 使用`HMAC`加密,密码相对安全,但无法防止重放攻击。攻击者利用网络监听或者其他方式盗取密文密码,之后再把它重新发给认证服务器,可以轻松的欺骗系统进行身份认证
  20. > 案例4
  21. > 对请求增加时效性
  22. > 防止重放攻击最有效的办法就是对密文增加时效性,在`HMAC`加密方式不变的情况下,增加时间戳
  23. > 例如:`(密码.HMAC + 时间戳).md5`
  24. > 打开`ViewController.m`文件,写入以下代码:
  25. >

-(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event { NSString * pwd = @”123456”; pwd = [pwd hmacMD5StringWithKey:@”Zang”]; pwd = [pwd stringByAppendingString:@”202104151608”].md5String; NSLog(@”密码:%@”,pwd); }

  1. > 运行项目,点击屏幕,输出以下内容:
  2. >

密码:d3a8cc619a27e21077ebc7baa03f5e8f

  1. >
  2. ---
  3. > 上述案例中,客户端使用的时间戳,必须是服务器时间。服务端进行验证时,使用相同方式加密,然后和客户端传递的密文对比一致性
  4. > 如果客户端网络延迟,导致请求时间较长,服务端时间变成了下一分钟,验证将无法通过。故此对于时效性的验证,还应增加容错处理
  5. > 服务端验证当前时间戳,如果失败,还应该使用前一分钟的时间戳再次验证
  6. > 对于`HASH + 时间戳`的登录方式,一次请求的有效时间,最长为`1分59秒`,这样可以有效避免重放攻击
  7. #####数字签名
  8. 为什么用签名这个词?因为老外喜欢用支票,支票上面的签名能够证明这玩意是你的。那么数字签名顾名思义,就是用于鉴别数字信息的方法。
  9. > 数字签名是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明
  10. > 数字签名的目的:
  11. > - 防伪造:私有密钥只有签名者自己知道,所以其他人不可能构造出正确的
  12. > - 鉴别身份:由于传统的手工签名一般是双方直接见面的,身份自可一清二楚。在网络环境中,接收方必须能够鉴别发送方所宣称的身份
  13. > - 防篡改:对于数字签名,签名与原有文件已经形成了一个混合的整体数据,不可能被篡改,从而保证了数据的完整性
  14. > - 防重放:对于数字签名,如果采用了对签名报文添加流水号、时间戳等技术,可以防止重放攻击
  15. > - 防抵赖:数字签名可以鉴别身份,不可能冒充伪造,那么,只要保存好签名的报文,也就是保留了证据,签名者就无法抵赖
  16. > - 保密性:有了保密性,截收攻击也就失效了。数字签名可以加密要签名的消息,当然,如果签名的报名不要求机密性,也可以不用加密
  17. > 一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与`HASH`技术的应用
  18. > 服务端下发数据:
  19. > - 对将要传递的数据,生成`HASH`
  20. > - `HASH`值使用`RSA`加密,生成签名
  21. > - 传递数据时,同时传递签名
  22. > 客户端验证数据:
  23. > - 将密文`RSA`解密,获取到服务端下发的`HASH`
  24. > - 使用相同的算法,对数据生成本地`HASH`
  25. > - 将本地和服务端下发的`HASH`值进行对比
  26. > - `RSA`解密成功,`HASH`值对比一致,才能表示本次请求是合法的
  27. > 客户端向服务端传递数据,逻辑一致
  28. #####其他用途
  29. > #####搜索引擎
  30. > 在搜索引擎中分词搜索时,几个关键字无论顺序如何,得到的搜索结果都是一致的
  31. > 例如:`iOS & Swift`
  32. > 无论`iOS``Swift`的顺序如何,只要`iOS``Swift``HASH`值求和,结果是一样的,则视为相同的搜索词,搜索结果就是一致的
  33. > 即:`iOS.HASH + Swift.HASH ≡ SUM ≡ Swift.HASH + iOS.HASH`
  34. > #####版权
  35. > 由于数字文件的便捷性,拷贝传播十分方便,但是由于一些涉及利益的传播性质,往往会侵犯数字文件版权的归属问题
  36. > 文件的`HASH`值,和文件名、后缀名无关,只取决于文件的二进制数据。正版与盗版的区别也在于它们的`HASH`值不同
  37. > #####数据识别
  38. > 网盘的数据识别,例如秒传功能,如果上传文件的`HASH`值在服务器上存在,无需重复上传
  39. > 还有对于一些违规的文件,修改文件名、后缀名也会被识别出来,这种情况只能修改文件的二进制数据
  40. > 对于文件计算`HASH`值,是不是也耗费服务器资源呢?
  41. > 其实,`HASH`去重也是分粒度的,有文件去重,块去重,字节去重,粒度越细的准确率越高,相应的耗费服务器资源肯定也要多
  42. #####终端命令
  43. > #####散列函数
  44. > 计算`MD5`散列结果
  45. >

md5 -s “123456

MD5 (“123456”) = e10adc3949ba59abbe56e057f20f883e

  1. > - `32`个字符的`MD5`散列字符串
  2. >
  3. ---
  4. > 计算`SHA1`散列结果
  5. >

echo -n “123456” | openssl sha1

(stdin)= 7c4a8d09ca3762af61e59520943dc26494f8941b

  1. > - `40`个字符的`SHA1`散列字符串
  2. >
  3. ---
  4. > 计算`SHA256`散列结果
  5. >

echo -n “123456” | openssl sha256

(stdin)= 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92

  1. > - `64`个字符的`SHA256`散列字符串
  2. >
  3. ---
  4. > 计算`SHA512`散列结果
  5. >

echo -n “123456” | openssl sha512

(stdin)= ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413

  1. > - `128`个字符的`SHA512`散列字符串
  2. > #####HMAC散列函数
  3. > 计算`HMAC MD5`散列结果
  4. >

echo -n “123456” | openssl dgst -md5 -hmac “key”

(stdin)= 0abf6bacd23c55fa6ab14eb44a7f5720

  1. > - `32`个字符的`HMAC MD5`散列字符串
  2. >
  3. ---
  4. > 计算`HMAC SHA1`散列结果
  5. >

echo -n “123456” | openssl sha1 -hmac “key”

(stdin)= 4fc32f51f214211618a9598893823519b829ee74

  1. > - `40`个字符的`HMAC SHA1`散列字符串
  2. >
  3. ---
  4. > 计算`HMAC SHA256`散列结果
  5. >

echo -n “123456” | openssl sha256 -hmac “key”

(stdin)= 4df81f55d708ae1720d5f65ef42f3475dc168fa23fde424ac5944f87c309b05f

  1. > - `64`个字符的`HMAC SHA256`散列字符串
  2. >
  3. ---
  4. > 计算`HMAC SHA512`散列结果
  5. >

echo -n “123456” | openssl sha512 -hmac “key”

(stdin)= cc2f51259b61903f6b50ea2cc3653340f1e8c0ae780c927bc1c7f09dcd1d606f7cb3347117f75fa19d9f760f4d538709a969c11036d194b972bf3232f34f30a8

  1. > - `128`个字符的`HMAC SHA512`散列字符串
  2. > #####文件散列函数
  3. > 创建`file.txt`文件,写入以下内容:
  4. >

123456

  1. > 计算文件的`MD5`散列结果
  2. >

md5 file.txt

MD5 (file.txt) = e10adc3949ba59abbe56e057f20f883e

  1. > - `32`个字符的`MD5`散列字符串
  2. >
  3. ---
  4. > 计算文件的`SHA1`散列结果
  5. >

openssl sha1 file.txt

SHA1(file.txt)= 7c4a8d09ca3762af61e59520943dc26494f8941b

  1. > - `40`个字符的`SHA1`散列字符串
  2. >
  3. ---
  4. > 计算文件的`SHA256`散列结果
  5. >

openssl sha256 file.txt

SHA256(file.txt)= 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92

  1. > - `64`个字符的`SHA256`散列字符串
  2. >
  3. ---
  4. > 计算文件的`SHA512`散列结果
  5. >

openssl sha512 file.txt

SHA512(file.txt)= ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413 ```

  • 128个字符的SHA512散列字符串
总结

Hash的特点:

  • 算法是公开的
  • 对相同数据运算,得到的结果相同
  • 对不同数据运算,得到的长度相同
  • 可重复
  • 不可逆
  • 信息摘要,信息“指纹”,是用来做数据识别的

Hash的用途:

  • 密码加密
  • 搜索引擎
  • 版权
  • 数据识别
  • 数字签名

密码加密方式:

  • 使用MD5
  • MD5加盐
  • HMAC加密(比较好的方案)
  • HASH + 时间戳(配合HMAC加密,可防重放攻击)

数字签名的算法

  • HASH + RSA

数字签名的目的:

  • 防伪造
  • 鉴别身份
  • 防篡改
  • 防重放
  • 防抵赖
  • 保密性

数字签名的逻辑

  • 生成原始数据的HASH
  • HASH值进行RSA加密