最安全的加密算法Bcrypt防止数据泄露详解

目录
  • 引言
  • 哈希(Hash)与加密(Encrypt)
  • 不可逆的MD5为什么是不安全的?
  • 如何防御彩虹表的破解?
  • 一个更难破解的加密算法Bcrypt
  • PasswordEncoder 接口

引言

这是《Spring Security 进阶》专栏的第三篇文章,给大家介绍一下Spring Security 中内置的加密算法BCrypt,号称最安全的加密算法,究竟有着什么魔力能让黑客闻风丧胆

哈希(Hash)与加密(Encrypt)

哈希(Hash)是将目标文本转换成具有相同长度的、不可逆的杂凑字符串(或叫做消息摘要),而加密(Encrypt)是将目标文本转换成具有不同长度的、可逆的密文。

  • 哈希算法往往被设计成生成具有相同长度的文本,而加密算法生成的文本长度与明文本身的长度有关。
  • 哈希算法是不可逆的,而加密算法是可逆的。

HASH 算法是一种消息摘要算法,不是一种加密算法,但由于其单向运算,具有一定的不可逆性,成为加密算法中的一个构成部分。

JDK的String的Hash算法。代码如下:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

从JDK的API可以看出,它的算法等式就是

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

其中s[i]就是索引为i的字符,n为字符串的长度。

HashMap的hash计算时先计算hashCode(),然后进行二次hash。代码如下:

// 计算二次Hash
int hash = hash(key.hashCode());
static int hash(int h) {
	h ^= (h >>> 20) ^ (h >>> 12);
	return h ^ (h >>> 7) ^ (h >>> 4);
}

可以发现,虽然算法不同,但经过这些移位操作后,对于同一个值使用同一个算法,计算出来的hash值一定是相同的。

那么,hash为什么是不可逆的呢?

假如有两个密码3和4,我的加密算法很简单就是3+4,结果是7,但是通过7我不可能确定那两个密码是3和4,有很多种组合,这就是最简单的不可逆,所以只能通过暴力破解一个一个的试。

在计算过程中原文的部分信息是丢失了。一个MD5理论上是可以对应多个原文的,因为MD5是有限多个而原文是无限多个的。

不可逆的MD5为什么是不安全的?

因为hash算法是固定的,所以同一个字符串计算出来的hash串是固定的,所以,可以采用如下的方式进行破解。

  • 暴力枚举法:简单粗暴地枚举出所有原文,并计算出它们的哈希值,看看哪个哈希值和给定的信息摘要一致。
  • 字典法:黑客利用一个巨大的字典,存储尽可能多的原文和对应的哈希值。每次用给定的信息摘要查找字典,即可快速找到碰撞的结果。
  • 彩虹表(rainbow)法:在字典法的基础上改进,以时间换空间。是现在破解哈希常用的办法。

对于单机来说,暴力枚举法的时间成本很高(以14位字母和数字的组合密码为例,共有1.24×10^25种可能,即使电脑每秒钟能进行10亿次运算,也需要4亿年才能破解),字典法的空间成本很高(仍以14位字母和数字的组合密码为例,生成的密码32位哈希串的对照表将占用5.7×10^14 TB的存储空间)。但是利用分布式计算和分布式存储,仍然可以有效破解MD5算法。因此这两种方法同样被黑客们广泛使用。

如何防御彩虹表的破解?

虽然彩虹表有着如此惊人的破解效率,但网站的安全人员仍然有办法防御彩虹表。最有效的方法就是“加盐”,即在密码的特定位置插入特定的字符串,这个特定字符串就是“盐(Salt)”,加盐后的密码经过哈希加密得到的哈希串与加盐前的哈希串完全不同,黑客用彩虹表得到的密码根本就不是真正的密码。即使黑客知道了“盐”的内容、加盐的位置,还需要对H函数和R函数进行修改,彩虹表也需要重新生成,因此加盐能大大增加利用彩虹表攻击的难度。

一个网站,如果加密算法和盐都泄露了,那针对性攻击依然是非常不安全的。因为同一个加密算法同一个盐加密后的字符串仍然还是一毛一样滴!

一个更难破解的加密算法Bcrypt

BCrypt是由Niels Provos和David Mazières设计的密码哈希函数,他是基于Blowfish密码而来的,并于1999年在USENIX上提出。

除了加盐来抵御rainbow table 攻击之外,bcrypt的一个非常重要的特征就是自适应性,可以保证加密的速度在一个特定的范围内,即使计算机的运算能力非常高,可以通过增加迭代次数的方式,使得加密速度变慢,从而可以抵御暴力搜索攻击。

Bcrypt可以简单理解为它内部自己实现了随机加盐处理。使用Bcrypt,每次加密后的密文是不一样的。

对一个密码,Bcrypt每次生成的hash都不一样,那么它是如何进行校验的?

  • 虽然对同一个密码,每次生成的hash不一样,但是hash中包含了salt(hash产生过程:先随机生成salt,salt跟password进行hash);
  • 在下次校验时,从hash中取出salt,salt跟password进行hash;得到的结果跟保存在DB中的hash进行比对。

在Spring Security 中 内置了Bcrypt加密算法,构建也很简单,代码如下:

@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

生成的加密字符串格式如下:

$2b$[cost]$[22 character salt][31 character hash]

比如:

$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
\__/\/ \____________________/\_____________________________/
 Alg Cost      Salt                        Hash

上面例子中,$2a$ 表示的hash算法的唯一标志。这里表示的是Bcrypt算法。

10 表示的是代价因子,这里是2的10次方,也就是1024轮。

N9qo8uLOickgx2ZMRZoMye 是16个字节(128bits)的salt经过base64编码得到的22长度的字符。

最后的IjZAgcfl7p92ldGxad68LJZdL17lhWy是24个字节(192bits)的hash,经过bash64的编码得到的31长度的字符。

PasswordEncoder 接口

这个接口是Spring Security 内置的,如下:

public interface PasswordEncoder {
   String encode(CharSequence rawPassword);
   boolean matches(CharSequence rawPassword, String encodedPassword);
   default boolean upgradeEncoding(String encodedPassword) {
      return false;
   }
}

这个接口有三个方法:

  • encode方法接受的参数是原始密码字符串,返回值是经过加密之后的hash值,hash值是不能被逆向解密的。这个方法通常在为系统添加用户,或者用户注册的时候使用。
  • matches方法是用来校验用户输入密码rawPassword,和加密后的hash值encodedPassword是否匹配。如果能够匹配返回true,表示用户输入的密码rawPassword是正确的,反之返回fasle。也就是说虽然这个hash值不能被逆向解密,但是可以判断是否和原始密码匹配。这个方法通常在用户登录的时候进行用户输入密码的正确性校验。
  • upgradeEncoding设计的用意是,判断当前的密码是否需要升级。也就是是否需要重新加密?需要的话返回true,不需要的话返回fasle。默认实现是返回false。

例如,我们可以通过如下示例代码在进行用户注册的时候加密存储用户密码

//将User保存到数据库表,该表包含password列
user.setPassword(passwordEncoder.encode(user.getPassword()));

BCryptPasswordEncoder 是Spring Security推荐使用的PasswordEncoder接口实现类

public class PasswordEncoderTest {
  @Test
  void bCryptPasswordTest(){
    PasswordEncoder passwordEncoder =  new BCryptPasswordEncoder();
    String rawPassword = "123456";  //原始密码
    String encodedPassword = passwordEncoder.encode(rawPassword); //加密后的密码
    System.out.println("原始密码" + rawPassword);
    System.out.println("加密之后的hash密码:" + encodedPassword);
    System.out.println(rawPassword + "是否匹配" + encodedPassword + ":"   //密码校验:true
            + passwordEncoder.matches(rawPassword, encodedPassword));
    System.out.println("654321是否匹配" + encodedPassword + ":"   //定义一个错误的密码进行校验:false
            + passwordEncoder.matches("654321", encodedPassword));
  }
}

上面的测试用例执行的结果是下面这样的。(注意:对于同一个原始密码,每次加密之后的hash密码都是不一样的,这正是BCryptPasswordEncoder的强大之处,它不仅不能被破解,想通过常用密码对照表进行大海捞针你都无从下手),输出如下:

原始密码123456
加密之后的hash密码:$2a$10$zt6dUMTjNSyzINTGyiAgluna3mPm7qdgl26vj4tFpsFO6WlK5lXNm
123456是否匹配$2a$10$zt6dUMTjNSyzINTGyiAgluna3mPm7qdgl26vj4tFpsFO6WlK5lXNm:true
654321是否匹配$2a$10$zt6dUMTjNSyzINTGyiAgluna3mPm7qdgl26vj4tFpsFO6WlK5lXNm:false

BCrypt 产生随机盐(盐的作用就是每次做出来的菜味道都不一样)。这一点很重要,因为这意味着每次encode将产生不同的结果。

以上就是最安全的加密算法Bcrypt防止数据泄露详解的详细内容,更多关于Bcrypt 加密算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot整合BCrypt实现密码加密

    本文实例为大家分享了SpringBoot整合BCrypt实现密码加密的具体代码,供大家参考,具体内容如下 一. 首先在pom依赖中加入依赖: <!-- security依赖包 (加密) --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> </dependency&

  • Java使用bcrypt实现对密码加密效果详解

    目录 简介 bcrypt加密原理 加密过程 校验过程 bcrypt与md5的区别 示例 1.引入依赖 2.写测试类 3.测试 密文含义 简介 本文用示例介绍使用对密码进行加密的算法:bcrypt. bcrypt是一种自带盐值(自动加盐)的加密方案. bcrypt加密原理 加密过程 先随机生成salt salt跟password进行hash 注意 对于同一个密码,每次生成的hash是不同的 hash中包含了salt 校验过程 从hash中取出salt salt跟password进行hash计算 将

  • 浅析Java 常用的 4 种加密方式(MD5+Base64+SHA+BCrypt)

    一.工具类  md5加密工具类  public class MD5Utils { private static final String hexDigIts[] = {"0","1","2","3","4","5","6","7","8","9","a","b",&qu

  • Java通过BCrypt加密过程详解

    一.概述 在用户模块,对于用户密码的保护,通常都会进行加密.我们通常对密码进行加密,然后存放在数据库中,在用户进行登录的时候,将其输入的密码进行加密然后与数据库中存放的密文进行比较,以验证用户密码是否正确. 目前,MD5和BCrypt比较流行.相对来说,BCrypt比MD5更安全,但加密更慢. 二.使用BCrypt 首先,可以在官网中取得源代码 然后通过Ant进行编译.编译之后得到jbcrypt.jar.也可以不需要进行编译,而直接使用源码中的java文件(本身仅一个文件). 下面是官网的一个D

  • Spring security BCryptPasswordEncoder密码验证原理详解

    一.加密算法和hash算法的区别 加密算法是一种可逆的算法,基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为"密文",但在用相应的密钥进行操作之后就可以得到原来的内容 . 哈希算法是一种不可逆的算法,是把任意长度的输入通过散列算法变换成固定长度的输出,输出就是散列值,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值. 二.源码解析 BCryptPasswordEncoder类实现了PasswordEncoder接口,这个接口中定义

  • 密码哈希函数 Bcrypt的最大密码长度限制详解

    密码哈希函数 Bcrypt的最大密码长度限制 Bcrypt是一个很流行的密码哈希算法,是Niels Provos和DavidMazières基于Blowfish加密算法设计的密码哈希算法,于1999年在USENIX协会上提交.Bcrypt在设计上包含了一个盐Salt来防御彩虹表攻击,还提供了一种自适应功能,可以随着时间的推移,通过增加迭代计数以使其执行更慢,使得即便在增加计算能力的情况下,Bcrypt仍然能保持抵抗暴力攻击. Bcrypt是OpenBSD和SUSE Linux等操作系统默认的密码

  • 最安全的加密算法Bcrypt防止数据泄露详解

    目录 引言 哈希(Hash)与加密(Encrypt) 不可逆的MD5为什么是不安全的? 如何防御彩虹表的破解? 一个更难破解的加密算法Bcrypt PasswordEncoder 接口 引言 这是<Spring Security 进阶>专栏的第三篇文章,给大家介绍一下Spring Security 中内置的加密算法BCrypt,号称最安全的加密算法,究竟有着什么魔力能让黑客闻风丧胆 哈希(Hash)与加密(Encrypt) 哈希(Hash)是将目标文本转换成具有相同长度的.不可逆的杂凑字符串(

  • JAVA加密算法数字签名实现原理详解

    所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行 RSA 算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性).当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证. 在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数(HASH 函数)生成的,对这些 HASH 函数的特殊要求是: 1:接受的输入报文数据没有长度限制: 2:对任何输入报文数据生成固定长度的摘要(数字指纹)输出 3:从报文能方

  • Python实现的rsa加密算法详解

    本文实例讲述了Python实现的rsa加密算法.分享给大家供大家参考,具体如下: 算法过程 1. 随意选择两个大的质数p和q,p不等于q,计算N=pq. 2. 根据欧拉函数,不大于N且与N互质的整数個数為(p-1)(q-1). 3. 选择一个整数e与(p-1)(q-1)互质,并且e小于(p-1)(q-1). 4. 用以下这个公式计算d:d× e ≡ 1 (mod (p-1)(q-1)). 5. 将p和q的记录销毁. (N,e)是公钥,(N,d)是私钥. python代码 # -*- coding

  • Java实现的对称加密算法AES定义与用法详解

    本文实例讲述了Java实现的对称加密算法AES定义与用法.分享给大家供大家参考,具体如下: 一 简介 1.AES是目前使用最多的对称加密算法. 2.AES的优势之一是至今尚未被破解. 3.AES通常用于移动通信系统加密以及基于SSH协议(SSH Client.SecrueCRT)的软件. 二 特点 1.高级的 2.DES替代者 三 AES的参数说明 四 代码实现 package com.imooc.security.aes; import java.security.Key; import ja

  • Django自带的加密算法及加密模块详解

    Django 内置的User类提供了用户密码的存储.验证.修改等功能,可以很方便你的给用户提供密码服务. 默认的Ddjango使用pbkdf2_sha256方式来存储和管理用的密码,当然是可以自定义的. Django 通过PASSWORD_HASHERS 设置选择要使用的算法. 下面有一个列表,列出了Django 支持的哈希算法类.列表的第一个元素 (即settings.PASSWORD_HASHERS[0]) 会用于储存密码, 所有其它元素都是用于验证的哈希值,它们可以用于检查现有的密码.意思

  • c++代码实现tea加密算法的实例详解

    通过c++来实现tea加密算法,最终编译成so文件,以JNI的方式提供给客户端调用,主要需要解决以下三个问题: 实现tea算法,这都有开源的代码可以实现: 解决padding问题: 密钥做一个混淆,防止编译生成的库文件方便的被逆向拿到: 对于tea的加密算法,有成熟的各语言代码可以借鉴,下面是C++的实现: static void tea_encrypt(uint32_t *v, uint32_t *k) { uint32_t v0 = v[0], v1 = v[1], sum = 0, i;

  • 详解Android安全防护之加密算法

    摘要 也许有些开发者或者企业觉得.我们公司的app,数据量这些少,会有那个黑客吃饱了没事做来破解啊.又不是支付宝,或者其他那些用户量很多的应用.如果是这样想的话,那只能说目光短浅了. Android应用常用的加密算法 如果说按加密的内容是否可以还原,可以分为可逆加密和非可逆加密. 非可逆加密:也就是说加密后的数据是不能还原成原来的数据.比如MD5加密 加密一个密码:123456 加密后成: afabsbfbabf437hfbbff73(结果并不一定是这个,只是举例).也就是说加密后的结果afab

  • C BlowFish对称加密算法详解

    1.算法原理 BlowFish算法基于Feistel网络,加密函数迭代执行16轮,分组长度为64位,密钥长度可以从32位到448位.算法由两部分组成,密钥扩展部分和数据加密部分,密钥扩展部分将最长最长为448位的密钥转化成共4168字节长度的子密钥数组,其中,数据加密由一个16轮的Feistel网络完成,每轮由一个密钥相关置换和一个密钥与数据相关的替换组成. (1)子密钥 BlowFish算法子密钥在加密前预计算产生. P数组由18个32位字的子密钥组成.P1,P2''''''.P18 4个8X

随机推荐