上一篇博客中说到防御彩虹表攻击最常用的方法就是加盐,那么什么是加盐呢?

一、什么是加盐?

1.背景

现在很多公司后台以hash值形式存储用户密码(虽然本文以MD5哈希函数为例,但becrypt函数最常用的),用于哈希函数存在碰撞的特性,当后台数据库被攻击然后获取到用户密码哈希值时,还是能通过一定的方法(比如彩虹表攻击)破解用户密码。

举个例子:http://www.cmd5.com/

能破解。

2.加盐原理简介

简单来说:由原来的H(p)变成了H(p+salt),相当于哈希函数H发生了变化,每次哈希计算使用的salt是随机的

H如果发生了改变,则已有的彩虹表数据就完全无法使用,必须针对特定的H重新生成,这样就提高了破解的难度。

二、如何加盐?

如何加盐,不同的哈希算法不同的公司不尽相同但思路都是差不多的。本文以MD5的一个简单加盐处理为例,讲解加盐的java实现:

1.生成盐

private static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};/***自定义简单生成盐,是一个随机生成的长度为16的字符串,每一个字符是随机的十六进制字符*/public static String salt() {Random random = new Random();StringBuilder sb = new StringBuilder(16);for (int i = 0; i < sb.capacity(); i++) {sb.append(hex[random.nextInt(16)]);}return sb.toString();}

这只是一个生成盐的想法而已,你可以按自己的想法来,只要保证每次执行生成的盐随机即可。

2.输入加盐

String inputWithSalt = inputStr + salt;//加盐,输入加盐

加盐非常简单吧

3.输出带盐

输出带盐是我自己取的一个名字而已,这个过程可选不要。实际上是将这次哈希计算过程用到的salt存储到这次hash值中,用于后面进行验证密码时进行hash计算,即注册存储密码时和登陆验证密码时用到的salt要一样,免除了另存hash操作。

    /***@params: [inputStr, type] inputStr是输入的明文;type是处理类型,0表示注册存hash值到库时,1表示登录验证时*@Descrption:  MD5加盐,盐的获取分两种情况;输入明文加盐;输出密文带盐(将salt存储到hash值中)*/public static String MD5WithSalt(String inputStr, int type) {try {MessageDigest md = MessageDigest.getInstance("MD5");//申明使用MD5算法,更改参数为"SHA"就是SHA算法了String salt = null;if (type == 0) {//注册存hash值到库时,new saltsalt = salt();} else if (type == 1) {//登录验证时,使用从库中查找到的hash值提取出的saltString queriedHash=null;//从库中查找到的hash值salt=getSaltFromHash(queriedHash);}String inputWithSalt = inputStr + salt;//加盐,输入加盐String hashResult = byte2HexStr(md.digest(inputWithSalt.getBytes()));//哈希计算,转换输出System.out.println("加盐密文:"+hashResult);/*将salt存储到hash值中,用于登陆验证密码时使用相同的盐*/char[] cs = new char[48];for (int i = 0; i < 48; i += 3) {cs[i] = hashResult.charAt(i / 3 * 2);cs[i + 1] = salt.charAt(i / 3);//输出带盐,存储盐到hash值中;每两个hash字符中间插入一个盐字符cs[i + 2] = hashResult.charAt(i / 3 * 2 + 1);}hashResult = new String(cs);return hashResult;} catch (Exception e) {e.printStackTrace();return e.toString();}}

将salt存到hash值的操作也很简单,假定输出hash值是32字节,我们生成的是16字节的盐,我们可以简单的每两个hash字符中间插入一个盐字符。带盐也很简单吧。

三、后台密码存储和验证过程

这里假定从前端传到后台的密码时明文。

1.注册时存储密码

(1)用户注册时输入的账号、密码p1从前端传到后台;

(2)后台随机生成一个salt;

(3)H(p1+salt)生成哈希值,将此哈希值带盐(存储salt)后的结果hash1存储到数据库中;

2.登录时验证密码

(1)用户登陆时输入的账号、密码p2从前端传到后台;

(2)用登陆账号在数据库中查询账号相同的记录,取其哈希值hash2

(3)从hash2获取salt(保证存储时和验证时salt相同)

(4)H(p2+salt)生成哈希值hash3,判断if(hash2==hash3);若相等登陆成功,否则登陆失败。

四、java实现

哈希函数选择MD5,javaApi中MD5没有加盐的过程,需要我们自己实现加盐。关于Becrypt的加盐更简单,可以看我Becrypt那篇博客的源码。

package EncryptAndDecrypt;import java.security.MessageDigest;
import java.util.Random;/*** 散列加密之32位哈希值的MD5算法,调用JDK里的API*ps:准确来说散列加密不是加密算法,因为它是不可逆的(只能加密,不能解密)*/
public class MyMD5 {private static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};public static void main(String[] args) throws Exception {String input = "123456";System.out.println("MD5加密" + "\n"+ "明文:" + input + "\n"+ "无盐密文:" + MD5WithoutSalt(input));System.out.println("带盐密文:" + MD5WithSalt(input,0));}/***@params: [inputStr] 输入明文*@Descrption: 不加盐MD5*/public static String MD5WithoutSalt(String inputStr) {try {MessageDigest md = MessageDigest.getInstance("MD5");//申明使用MD5算法,更改参数为"SHA"就是SHA算法了return byte2HexStr(md.digest(inputStr.getBytes()));//哈希计算,转换输出} catch (Exception e) {e.printStackTrace();return e.toString();}}/***@params: [inputStr, type] inputStr是输入的明文;type是处理类型,0表示注册存hash值到库时,1表示登录验证时*@Descrption:  MD5加盐,盐的获取分两种情况;输入明文加盐;输出密文带盐(将salt存储到hash值中)*/public static String MD5WithSalt(String inputStr, int type) {try {MessageDigest md = MessageDigest.getInstance("MD5");//申明使用MD5算法,更改参数为"SHA"就是SHA算法了String salt = null;if (type == 0) {//注册存hash值到库时,new saltsalt = salt();} else if (type == 1) {//登录验证时,使用从库中查找到的hash值提取出的saltString queriedHash=null;//从库中查找到的hash值salt=getSaltFromHash(queriedHash);}String inputWithSalt = inputStr + salt;//加盐,输入加盐String hashResult = byte2HexStr(md.digest(inputWithSalt.getBytes()));//哈希计算,转换输出System.out.println("加盐密文:"+hashResult);/*将salt存储到hash值中,用于登陆验证密码时使用相同的盐*/char[] cs = new char[48];for (int i = 0; i < 48; i += 3) {cs[i] = hashResult.charAt(i / 3 * 2);cs[i + 1] = salt.charAt(i / 3);//输出带盐,存储盐到hash值中;每两个hash字符中间插入一个盐字符cs[i + 2] = hashResult.charAt(i / 3 * 2 + 1);}hashResult = new String(cs);return hashResult;} catch (Exception e) {e.printStackTrace();return e.toString();}}/*** @return: salt* @params:* @Descrption: 自定义简单生成盐,是一个随机生成的长度为16的字符串,每一个字符是随机的十六进制字符*/public static String salt() {Random random = new Random();StringBuilder sb = new StringBuilder(16);for (int i = 0; i < sb.capacity(); i++) {sb.append(hex[random.nextInt(16)]);}return sb.toString();}/*** @return: 十六进制字符串* @params: [bytes]* @Descrption: 将字节数组转换成十六进制字符串*/private static String byte2HexStr(byte[] bytes) {/***@Author: DavidHuang*@Time: 19:41 2018/5/10*@return: java.lang.String*@params:  * @param bytes*@Descrption:*/int len = bytes.length;StringBuffer result = new StringBuffer();for (int i = 0; i < len; i++) {byte byte0 = bytes[i];result.append(hex[byte0 >>> 4 & 0xf]);result.append(hex[byte0 & 0xf]);}return result.toString();}/***@return: 提取的salt*@params: [hash] 3i byte带盐的hash值,带盐方法与MD5WithSalt中相同*@Descrption:  从库中查找到的hash值提取出的salt*/public static String getSaltFromHash(String hash){StringBuilder sb=new StringBuilder();char [] h=hash.toCharArray();for(int i=0;i<hash.length();i+=3){sb.append(h[i+1]);}return sb.toString();}}

第一次运行结果:

MD5加密
明文:123456
无盐密文:E10ADC3949BA59ABBE56E057F20F883E
加盐密文:80D05C08F8879B2C84BA0C40143D224F
带盐密文:8D0D8053C0F8F6887791B2BC804B7A0EC4901543DD2B241F

第二运行结果:

MD5加密
明文:123456
无盐密文:E10ADC3949BA59ABBE56E057F20F883E
加盐密文:2CFFE57B054378D926A6FF14A1985F22
带盐密文:21CF7FE957CB0354C37B8DC9256AD6F0F174A719785AF222

可以看到对于相同明文,多次MD5哈希的无盐密文相同,带盐密文和加盐密文不同。由哈希函数的特征很容易明白无盐密文相同。由于每次哈希计算生成的salt是随机的,相当于每次哈希函数不同,所以带盐密文和加盐密文不同。

由此可以看出加盐后安全性更高了吧。

参考:https://blog.csdn.net/dingsai88/article/details/51637977

https://blog.csdn.net/hao_hl1314/article/details/53141005

【密码学】轻松理解“加盐”的原理与java实现相关推荐

  1. 轻松理解之SpringBoot实现原理

    一.什么是SpringBoot? SpringBoot是一个快速开发框架,快速的将一些常用的第三方依赖整合(原理:通过Maven子父工程的方式),简化XML配置,全部采用注解形式,内置Http服务器( ...

  2. Linux下轻松理解防火墙的工作原理及相关设置(三)firewalld服务、包括Direct Rules 和Rich Rules (地址伪装和转发)

    文章目录 firewalld概述 firewall和iptables的不同 firewalld常用命令 firewalld基本管理 1.图形化操作 firewall-config 2.命令化操作 火墙 ...

  3. 红黑二叉树的漫画讲解(轻松理解红黑二叉树原理)

    ------------ 二叉查找树(BST)具备什么特性呢? 1.左子树上所有结点的值均小于或等于它的根结点的值. 2.右子树上所有结点的值均大于或等于它的根结点的值. 3.左.右子树也分别为二叉排 ...

  4. 加解密篇 - 什么是加密加盐 (分析web3j的加盐处理)

    这篇是加解密的最后一篇,来聊聊加密加盐.翻看最近的区块链钱包项目,发现 web3j 的源码中对数据做了加盐处理,正好分析一下它是如何进行加盐处理的. 目录: 什么是加盐 加盐的原理和流程 加盐 dem ...

  5. C++动态内存管理好难怎么办?零基础图文讲解,小白轻松理解原理

    首先我们先了解一下内存: C语言使用malloc/free动态管理内存空间,C++引入了new/delete,new[]/delete[]来动态管理内存. 如果大家在自学C++中遇到困难,想找一个学习 ...

  6. 密码加盐原理及其实现

    目录 1. 背景介绍 2. MD5加密算法 2.1 MD5算法的介绍 2.2 MD5算法的缺点 3. 加盐算法 3.1 什么是加盐算法 3.2 加盐算法的演示 4. 总结 1. 背景介绍 加密密码是现 ...

  7. 【项目】MD5加盐源码理解

    1.shiro中主要的类 简单看一下即可,shiro是一个安全验证框架,相对Spring security使用更为简单.本篇文章使用的的md5加密和加盐是基于shiro框架. 复制代码 主要功能:认证 ...

  8. 数据库密码MD5加密、加盐的理解

    一.MD5加密 1)为什么?如果数据库里用户账号的密码没有加密处理,如果数据库被盗,密码被知道导致用户数据被窃取,这是很危险的.数据库里存放密码的密文会安全一点,密码安全性高的话破解也需要时间. 2) ...

  9. md5 加盐原理和常用的加盐方法

    现在的MD5密码数据库的数据量已经非常庞大了,大部分常用密码都可以通过MD5摘要反向查询到密码明文.为了防止内部人员(能够接触到数据库或者数据库备份文件的人员)和外部入侵者通过MD5反查密码明文,更好 ...

最新文章

  1. PAT(甲级)2019年春季考试 7-3 Telefraud Detection
  2. 用Java实现几种常用排序算法(先实现一个org.rut.util.algorithm.SortUtil)
  3. VUE 使用插件vue-clipboard2复制内容至剪切板(两种使用方法)
  4. 三十二、深入Python中的文件操作
  5. TypeScript 原来可以这么香?!
  6. 应运ajax的几种语言,Ajax指的是什么
  7. minio分布式搭建_分布式存储Minio集群环境搭建
  8. 如何使用Linux重置Windows密码
  9. 【安全】导入本地linux用户到LDAP中
  10. linux swap 分区调控(swap分区 lvm管理)
  11. 如何从零开发一个复杂深度学习模型
  12. Linux 用户的 3 个命令行小技巧
  13. 操作系统学习思维导图——2处理器管理
  14. Base64,Base32,Base16进制的区别:
  15. MVX-Net | 多模型三位像素网络用于3D目标检测
  16. webstorm设置黑色背景
  17. 不可不知道的SEO与SEM术语大全
  18. 孩子该不该学编程?学编程有用吗?
  19. Ubuntu文件管理器的默认设置
  20. PCA降维以及Kmeans聚类实例----python,sklearn,PCA,Kmeans

热门文章

  1. LBS创业者平台掘金:小应用有大机会
  2. spring的15个经典面试题
  3. 谷歌搜索引擎优化初学者指南pdf下载
  4. 100个让ChatGPT更听话的技巧之:提出更具体的问题
  5. 非常经典经典的励志格言
  6. Redis + Java拦截器实现用户匿名和非匿名访问
  7. Python--元类
  8. rabbitmq管理端口开放
  9. Paper templates for Word(Word论文模板)
  10. OJ Problem D: 哪一天,哪一秒?