• 数据加密和BCrypt哈希算法应用 | StartDT Tech Lab 15


    写在前面

    这是奇点云全新技术专栏「StartDT Tech Lab」的第15期。

    在这里,我们聚焦数据技术,分享方法论与实战。一线的项目经历,丰富的实践经验,真实的总结体会…滑到文末,可以看到我们的往期内容。

    本篇由奇点云后端架构师「心诚」带来:

    作者:心诚

    阅读时间:约11分钟

    1. 加密与数据保护

    大数据的时代,在日常生活和工作中,我们常常面临着主动或被动地(被)收集个人信息,包括一些敏感信息,如账号密码、身份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算法的工作过程:

    1. 首先对任意大小输入信息(转化成二进制后)进行信息填充(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)。

    2. 接下来进行初始化MD缓冲区(Initialize MD Buffer)使用保留的4个32bits向量(Word A~D,低字节在前)如下图:

    3. 开始循环计算(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

    4. 在第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),最后的字符串就是密码的密文了。

    2. 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

    3. 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");else System.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算法,其安全性得到了广泛的认可,大家不妨尝试在项目或产品中使用/升级。

  • 相关阅读:
    Go中的错误处理
    【优化算法】基于matlab反向策略的麻雀搜索算法【含Matlab源码 1918期】
    【hcie-cloud】【2】华为云Stack解决方案介绍、缩略语整理 【下】
    设计模式 - 单例模式理解及相关问题解决方法
    MySQL-锁分类-2
    Vue原理篇——响应式实现(双向数据绑定)
    MySQL并发事务会引起的问题
    如何恢复xp笔记本盘符找不到的数据
    局域网和广域网的区别
    【Qt】之【项目】整理可参考学习的git项目链接(持续更新)
  • 原文地址:https://blog.csdn.net/StartDT/article/details/126744264