数据加密和 BCrypt 哈希算法应用 | StartDT Tech Lab 15
这是奇点云全新技术专栏「StartDT Tech Lab」的第 15 期。在这里,我们聚焦数据技术,分享方法论与实战。一线的项目经历,丰富的实践经验,真实的总结体会…滑到文末,可以看到我们的往期内容。本篇由奇点云后端架构师「心诚」带来:
作者:心诚阅读时间:约 11 分钟
索 引
1 加密与数据保护
2 BCrypt 的安全性
3 BCrypt 的实现和应用场景
4 总结
加密与数据保护
大数据的时代,在日常生活和工作中,我们常常面临着主动或被动地(被)收集个人信息,包括一些敏感信息,如账号密码、身份 ID、银行卡号、行为习惯等。这些信息又主动或被动地运用在统计、行为分析、授权审核等方面。虽然可以带来无限便利,但无意间发生的泄露等事故会损害个人/公司信息安全,同时带来重大经济损失。
因此,数据安全的重要性是毋庸置疑的。而数据加密是保障数据安全的重要技术之一。
目前常见的加密算法有:· 对称加密算法
· 非对称加密算法
· Hash 算法
在介绍加密算法前,我们先来了解下加密密钥(Encryption Keys):加密密钥,即加密算法生成的特定字符密钥,用于加密和解密一段信息。这些密钥的使用方式的不同可以进一步说明对称和非对称加密之间的区别。
· 对称加密算法如下图所示,对称加密最大的特点就是加解密过程使用的密钥是同一个(密钥 A),它的安全性很大程度上依赖于密钥的保密程度,一旦密钥泄露,攻击者可轻松访问加密过的信息。
虽然有泄露密钥的风险,但和非对称加密算法相比,它是相同密钥长度下运算速度较快、计算资源较少的一种加密方式。目前主流的对称加密算法有 DES、AES 等。
· 非对称加密算法相对于对称加密,非对称加密的特点是需要两个密钥,如下图所示,一个公开密钥(Public Key), 一个私有密钥(Private Key)。公钥和私钥是一对的,如果数据使用公钥进行加密,只有对应的私钥才能解密,反之亦然。
由于有公私钥之分,非对称加密可以把公钥公开,私钥自己保存,安全性因此有了很大的提升。相应也有缺点:加密和解密耗时相对长、速度慢,只适合对少量数据进行加密。常用的非对称加密算法有 RSA、DSA、DH 等。
· Hash 算法 Hash 算法又称为摘要算法、散列算法,可以将任意长度的二进制明文串映射为较短的(通常是相同长度的)二进制串(Hash 值),并且不同的明文很难映射为相同的 Hash 值。如下图所示(二进制解码后字符):
Hash 算法的特别之处在于它是一种单向算法,即不可逆。
因为此特点,Hash 算法通常用在保存不可还原的密码存储,信息/文件完整校验等地方。常用的 Hash 算法有 MD5、SHA、HMAC、HMAC-MD5 等。
1.1
使用哈希(Hash)进行加密
严格意义上 Hash 并不是一种加密方式,如上所述,只是一种将目标文本转换成具有相同长度的、不可逆的消息摘要,加密(Encryption)却是将目标文本转换成具有不同长度的、可逆的密文。
我们以 MD5(Message-Digest Algorithm V5 版本)为例,来描述 Hash 算法的工作过程:
首先对任意大小输入信息(转化成二进制后)进行信息填充(Append Padding Bits),使其位长对 512 求余的结果等于 448,最终的信息位长为:n*512+448,n 为一个非负整数,n 可以是零。
规则为:a) 在信息的后面填充一个 1 和无数个 0,直到满足上面的条件时才停止用 0 对信息的填充。b) 在这个结果后面附加一个以 64 位二进制表示的填充前信息长度(单位为 Bit),如果二进制表示的填充前信息长度超过 64 位,则取低 64 位。最后的信息长度为 n*512+448+64=(n+1)*512(单位 bit)。
接下来进行初始化 MD 缓冲区(Initialize MD Buffer)使用保留的 4 个 32bits 向量(Word A~D,低字节在前)如下图:
开始循环计算(Process Message in 16-Word Blocks),每 512bit 作为一组,第一组输入为 a=A, b=B, c=C, d=D,对每组数据进行 16*4 的向量运算。具体规则参考:https://www.freesoft.org/CIE/RFC/1321/7.htm
在第 3 步处理完所有的 512bit 的分组后,得到一组新的 A, B, C, D 的值,将这些值按 ABCD 的顺序级联,输出值即为 MD5 加密后的 Hash 值(低字节在前)。
1.2
哈希(Hash)的安全性
尽管 Hash 算法是单向的,不可逆的,不同明文得到的 Hash 值是不同的,但在一定条件下不同的原始数据,可能会得到相同的 MD5 值,这就是 Hash 的碰撞现象。
以 MD5 为例,它之所以应用这么广泛,就是因为它的可靠性——很难有两个不同的输入得到相同的 MD5。不过虽然概率低,但确实还是有:2004 年山东大学的王晓云就破解了 MD5,找到了 Hash 碰撞。(参见https://eprint.iacr.org/2004/199)
另外一种破解 MD5 加密的方式是使用彩虹表(预先计算的散列链),一般用于恢复/破解固定长度的纯文本密码。
这是以空间换时间的典型实践。相较于每次尝试都计算 Hash 的暴力破解处理方式,它所花费的时间更少,所需储存空间更多。使用加盐值(salt)的方式可以使这种攻击难以实现。根据情况可使用固定或随机盐值来增强密文的破解难度。
1.3
加密算法 BCrypt 是什么?
BCrypt 是一种跨平台的文件加密工具,使用的是布鲁斯·施内尔在 1993 年发布的 Blowfish 加密算法。它是一种可生成随机盐值的单向 Hash 加密算法,Hash 值中包含了上一步生成的盐值(22 个字符)的不可逆加密算法。同一种明文,每次被加密后的密文都不一样,并且不可反向破解生成明文,破解难度非常大。
BCrypt 加密后的密文结构如下图所示:
其中密文结构为:$是分割符, 2y 是 BCrypt 加密版本号,10 是 cost 的值,紧随其后的前 22 位是盐值(salt),最后的字符串就是密码的密文了。
BCrypt 的安全性
2.1
随机密文
BCrypt 也是一种单向 Hash 加密算法,因此它不可被反向破解生成明文。由于计算中使用了随机盐值,并且在密文中包含了 salt 值,默认情况下每次生成的密文都是不同的。随机密文带来的好处是:避免了如果两个人或多个人的密码相同,密码加密后保存到数据库会得到相同的结果,以防破解一个就可以知道其他人的密码。
例如:如果名为 A 的用户可以查看数据库,那么他可以观察到自己的密码和别人的密码加密后的结果都是一样,那么,别人用的和自己就是同一个密码,这样就可以利用别人的身份登录了。
在使用 MD5 加密的时候,也可以通过加 salt 值来间接生成不同密文(不是随机密文),前提是使用固定 salt 值,比如用户名+网站域名。
2.2
时间成本
时间成本对于防止密文被破解来说有非常重要意义,如果破解一个密文的时间成本足够高到一定程度,当采用暴力搜索攻击花费的时间超过以年来计算的时候,可以认为此加密方式是安全的。
对于 BCrypt 来说,产生上文所描述的密文结构主要经历两个步骤:首先,基于期望的 cost、salt 值和明文(密码)使用一个称为 EksBlowfishSetup 的方法初始化 Blowfish 状态(最耗时的步骤),通过 cost 不同值来大幅提升计算次数;然后,基于 24 位初始向量“OrpheanBeholderScryDoubt”使用 Blowfish 算法的 ECB 模式进行加密。工作流程如下图所示:
因此真正意义上 BCrypt 是基于 Blowfish 密码设计的一种时间成本差异较大(根据 cost 值设定)的 Hash 函数。具体关于 Blowfish 算法移步:https://zh.wikipedia.org/wiki/Blowfish
BCrypt 的实现和应用场景
即使使用了 BCrypt 加密算法,也并不能保证密码不会被泄露,上文已经提到过 BCrypt 进行加密的时间成本,为了有个整体概念,根据 ResearchGate 的报告:
可以看到原始密码的长度(bcrypt 行)直接影响了解密的时间,因此为了解决这个长度导致的时间差异问题,可以使用 MD5/HMAC 来解决。具体实践方式如下:bcrypt(HMAC(password, key), 随机 salt 值) = 密文 Hash 值基于此场景,保证了无论用户密码如何复杂,BCrypt 加密后对应的时间成本是固定的。
3.1
BCrypt 算法实现(JAVA)
BCrypt 有很多程序语言提供了基础的实现,以 JAVA 为例,jBCrypt 是一个开源的 Blowfish 算法实现,使用它为密码加密非常简单:// Hash a password for the first timeString hashed = BCrypt.hashpw(password, BCrypt.gensalt());// gensalt's log_rounds parameter determines the complexity// the work factor is 2**log_rounds, and the default is 10String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));// Check that an unencrypted password matches one that has// previously been hashedif (BCrypt.checkpw(candidate, hashed))System.out.println("It matches");elseSystem.out.println("It does not match");←左滑阅读
3.2
BCrypt 应用(Spring Security)
Spring Security 作为企业级应用的主流安全框架,在 5.0.0 以后移除了对 MD5 加密的支持(弃用),推荐使用 BCrypt 算法支持类:BCryptPasswordEncoder,他实现了 PasswordEncoder 接口的 encode 和 matches 方法,来进行密码加密和匹配。主要代码参考:
Encode 实现方法:public String encode(CharSequence rawPassword) {if (rawPassword == null) {throw new IllegalArgumentException("rawPassword cannot be null");} else {String salt;if (this.random != null) {salt = BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);} else {salt = BCrypt.gensalt(this.version.getVersion(), this.strength);}return BCrypt.hashpw(rawPassword.toString(), salt);}}←左滑阅读 Matches 实现方法:public boolean matches(CharSequence rawPassword, String encodedPassword) {if (rawPassword == null) {throw new IllegalArgumentException("rawPassword cannot be null");} else if (encodedPassword != null && encodedPassword.length() != 0) {if (!this.BCrypt_PATTERN.matcher(encodedPassword).matches()) {this.logger.warn("Encoded password does not look like BCrypt");return false;} else {return BCrypt.checkpw(rawPassword.toString(), encodedPassword);}} else {this.logger.warn("Empty encoded password");return false;}}←左滑阅读
4 总结
DT 时代,数据保护应成为企业最重要的一个技术环节。以数据智能服务商为例,无论是客户项目或是公司产品,都应使用足够安全的加密方式来保护核心信息资产。
BCrypt 加密方式作为主流的单向不可逆 Hash 算法,其安全性得到了广泛的认可,大家不妨尝试在项目或产品中使用/升级。
注:本文插图源自网络。
版权声明: 本文为 InfoQ 作者【奇点云】的原创文章。
原文链接:【http://xie.infoq.cn/article/bb92ba02fc2192702c1e0012f】。文章转载请联系作者。
评论