目录

后来我发现,BCryptPasswordEncoder 是这个思路的实现的最优解,如果你看到这篇博客,可以不用看了,直接使用 spring securityBCryptPasswordEncoder


源代码:

package ijava.xin.utils;

import sun.misc.BASE64Encoder;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; /**
* 动态盐的MD5加密
* @author An
*/
@SuppressWarnings("unused")
public class Md5Util { @SuppressWarnings("unused")
/**
* 普通MD5,只是实现下,不推荐使用,是不可逆的,但是聪明的人想到了查表,导致普通MD5的安全壁垒 GG 了;
* @author AN
* @time 2018年7月26日10:55:01
* @return 加密的字符
*/
public static String MD5(String input) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return "JDK不支持该算法,检查下JDK";
} catch (Exception e) {
e.printStackTrace();
return "";
} byte[] byteArray = input.getBytes(); byte[] md5Bytes = md5.digest(byteArray); System.out.println( new BASE64Encoder().encode(md5Bytes));
StringBuffer hexValue = new StringBuffer();
// 将加密完的字符串,全部转成0-9、a-f的字符串
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
// 如果小于16,也就是16进制只有1位的情况下。前面补0
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString(); } /**
* 加盐MD5
*
* @param password 原始密码
* @return 加盐的MD5字符串
* @author An
* @time 2018年7月26日10:55:55
*/
public static String generate(String password) {
// 生成随机盐,长度12位
byte[] bytes = new byte[12];
SecureRandom random = new SecureRandom();
random.nextBytes(bytes); StringBuilder builder = new StringBuilder();
// 将字节数组变为字符串
for (int i = 0; i < bytes.length; i++) {
// 将生成的值,全部映射到0-255 之间
int val = ((int) bytes[i]) & 0xff;
if (val < 16) {
// 为了控制盐的长度,这里小于16 的值,我们将它补为 大于16的值;
// 这样,生的盐的长度是固定的:bytes * 2 ;
builder.append(Integer.toHexString(val + 16));
} else {
builder.append(Integer.toHexString(val));
}
} // 最终的盐,长度是 12*2 = 24 ;
String salt = builder.toString(); // 先加盐Md5一把,再将 MD5 转换成 24位的 base64 位编码
password = md5Hex(password + salt); char[] cs = new char[salt.length() + password.length()]; for (int i = 0; i < cs.length; i += 4) { // 密码编码
cs[i] = password.charAt(i / 2);
cs[i + 2] = password.charAt(i / 2 + 1);
// 盐编码
cs[i + 1] = salt.charAt(i / 2);
cs[i + 3] = salt.charAt(i / 2 + 1); }
return new String(cs);
} /**
* 校验加盐后是否和原文一致
*
* @param password
* @param md5
* @return true 代表密码验证通过
* @author AN
* @time 2018年7月26日10:56:24
*/
public static boolean verify(String password, String md5) {
// 解码密码
char[] cs1 = new char[24];
// 解码盐
char[] cs2 = new char[24];
// 从MD5 中取出盐
for (int i = 0; i < md5.length(); i += 4) {
// 取出盐
cs2[i / 2] = md5.charAt(i + 1);
cs2[i / 2 + 1] = md5.charAt(i + 3);
// 取出密码的MD5值(经过Base64转换后的MD5)
cs1[i / 2] = md5.charAt(i + 0);
cs1[i / 2 + 1] = md5.charAt(i + 2);
} String salt = new String(cs2); return md5Hex(password + salt).equals(new String(cs1));
} /**
* 获取十六进制字符串形式的MD5摘要
*/
private static String md5Hex(String src) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bs = md5.digest(src.getBytes());
return new String(new BASE64Encoder().encode(bs));
} catch (Exception e) {
return null;
}
} }

函数用法讲解:

  • 获取动态加盐的MD5串

    使用 public static String generate(String password) 方法;将原始密码传进去,返回加密过后的密码;

  • 检验密码是否正确

    使用 public static boolean verify(String password, String md5)参数一密码参数二加密过后的密码,方法内部对参数一的密码进行加密,与参数二进行比对,一致就代表密码正确;


用法代码实例:

	    // 原文
String plaintext = "621959";
// plaintext = "123456";
System.out.println("原始:" + plaintext);
System.out.println("普通MD5后:" + Md5Util.MD5(plaintext)); // 获取加盐后的MD5值
String ciphertext = Md5Util.generate(plaintext);
System.out.println("加盐MD5字符串的长度"+ciphertext.length());
System.out.println("加盐后MD5:" + ciphertext);
System.out.println("是否是同一字符串:" + Md5Util.verify(plaintext, ciphertext));

输出:

原始:621959
普通MD5后:be28f86b08d8a8fd19fbc02fcb114aec
加盐MD5字符串的长度48
加盐后MD5:F27812O70foeqcD6x1h0GcQ3U2c5e9ce99x3u1hfubw9=2=9
是否是同一字符串:true

对比普通 MD5 的优点

  • 普通MD5

MD5 是一种 不可逆 的加密算法,也就是说,你无法从加密过后的字符串中,逆向推导出原始字符串;

既然无法逆向推导出,那么为什么普通的 MD5 ,又是那么的脆弱不堪呢?

因为 普通MD5 的加密,是不变的,即同一个密码的加密,永远是相同的;

后来不知道从哪天起,坏人之间流行起了 “彩虹表”,这个神兵利器正是利用它的不变性, 彩虹表 中记录着一个密码链,它将一个加密的密码的各种可能的原始密码,记录在表中;

比如 13daddsd ,是 “123” 的 MD5 加密,是 “145” 的 MD2 的加密,是 “121” 的 SHA 的加密字符串;

这样当坏人们,获取到 13daddsd 这样的一条加密字符串以后,它们就去查彩虹表,得到原始密码可能是:"123"、“121”、“145” ;很快就能破译出原始密码!

  • 固定盐MD5

加盐的MD5,由于坏人们,不知道加的盐是什么,盐是怎么加进去,他要想再生成彩虹表,基本是不可行的,因为,需要为一个网站的一种盐、一种加盐方式生成一个彩虹表,并且这张彩虹表仅仅对这个网站有效,对其他站点,是无效的;若想攻破多个站点,需要多张彩虹表,,这样的工程是吓人的!并且他们也很难知道,我们的盐是什么,怎么加的;

既然加盐已经很安全了,为什么还需要动态的盐呢;因为,我们还需要防止数据库管理员,防止内鬼拖库

对于固定的盐,相同的密码生成的字符串,也是相同的;黑客由于不知道盐是什么,不想费劲的去做;而数据库管理员,管理数据库,加密的密码,就摆在眼前,他只要自己生成一些密码,就可以知道哪些用户的密码是什么了。

比如,他对“2312”进行加密,得到了“dwe121d12”,然后检索数据库,只要是加密的密码字符串是 dwe121d12 ,那么原始密码都是“2312”;

  • 动态盐MD5

因此,我们需要动态盐;动态盐,因为盐是动态的,每次都不同,然后和密码混在一起,加密;即使是相同的密码,每次生成的加密字符串也不一样;


实现思路:

  • 简单MD5

    底层加密算法来自 MessageDigest 类,先将原始密码转换成**字节数组,然后进行加密计算,得到一个加密过后的字节数组;对加密过后的数组进行处理,防止传输的过程中丢失信息,应为加密后的字符串,在传输的过程中,可能会出现UNICODE编码识别的字符;因此,进行处理一下,可以选择直接使用Base64编码,将它们全部转成码值在0 - 63**之间的字符;

    这里我没有用 Base64 编码,自己写了一个算法,将加密的字符串转成**0-9、a-f**的字符串;思想都一样;

  • 动态盐MD5

  1. 动态盐的生成

    使用随机数生成,使用 SecureRandom 类,生成随机数;生成的随机数填充在字节数组中,然后将字节数组转成字符串,转之前,同样进行一次转换,将它们全部转成**0-9、a-f**的字符串;

    转到时候,需要注意下,为了控制盐的最终长度,对小于16的数值,进行了加16,为了得到2位数的16进制,得到动态盐;

  2. 将盐加在密码屁股后面,进行一个MD5加密,得到加密的字符串

  3. 动态盐 嵌套到返回的加密字符串中,为了下次验证试验;

    嵌套的时候,注意奇数位是密码,偶数位是盐,由于之前控制了盐的长度是**24**,MD5经过 Base64 编码的长度也是 24 ;(*其中 Base64 编码的长度:设字符串长度为n ,长度为 ⌈n/3⌉4 ⌈⌉ 代表上取整

    由于控制了长度是固定的48位,因此,嵌套的时候,很容易嵌套;

  4. 验证

    从给定的加密的字符串中获取当时的 动态盐、加盐的 MD5 字符串,根据当初嵌套的规则取;

    然后将传入的密码,用加密的盐混合一下,再MD5,进行比对,即可得知密码是否一致;

(二十八)动态盐的MD5加密算法(java实现)的更多相关文章

  1. Web 前端开发人员和设计师必读文章推荐【系列二十八】

    <Web 前端开发精华文章推荐>2014年第7期(总第28期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  2. VMware vSphere 服务器虚拟化之二十八 桌面虚拟化之安装View传输服务器

    VMware vSphere 服务器虚拟化之二十八 桌面虚拟化之安装View传输服务器 View 传输服务器用于管理和简化数据中心与在最终用户本地系统上检出使用的 View 桌面之间的数据传输.必须安 ...

  3. Saiku使用iframe嵌入页面访问地址配置化(二十八)--DWR的基本使用

    Saiku使用iframe嵌入页面使用时ip与端口配置化(二十八)--DWR的基本使用 DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开 ...

  4. Bootstrap <基础二十八>列表组

    列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...

  5. Citrix 服务器虚拟化之二十八 XenApp6.5发布文档内容

    Citrix 服务器虚拟化之二十八  XenApp 6.5发布文档内容 XenApp可发布以下类型的资源向用户提供信息访问,这些资源可在服务器或桌面上虚拟化: 1)  服务器桌面:发布场中服务器的整个 ...

  6. WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]

    原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过ServiceMetadataBehavior实现了基于WS-ME ...

  7. Bootstrap入门(二十八)JS插件5:工具提醒

    Bootstrap入门(二十八)JS插件5:工具提醒 工具提示在使用过程中比较常见,但是实现起来有些麻烦,而bootstrap则很好地解决了这个问题. 我们来写一个简单的实例 先引入CSS文件和JS文 ...

  8. mysql进阶(二十八)MySQL GRANT REVOKE用法

    mysql进阶(二十八)MySQL GRANT REVOKE用法   MySQL的权限系统围绕着两个概念: 认证->确定用户是否允许连接数据库服务器: 授权->确定用户是否拥有足够的权限执 ...

  9. JAVA之旅(二十八)——File概述,创建,删除,判断文件存在,创建文件夹,判断是否为文件/文件夹,获取信息,文件列表,文件过滤

    JAVA之旅(二十八)--File概述,创建,删除,判断文件存在,创建文件夹,判断是否为文件/文件夹,获取信息,文件列表,文件过滤 我们可以继续了,今天说下File 一.File概述 文件的操作是非常 ...

随机推荐

  1. Ubuntu 14.04 tar 打包系统安装到新机器

    制作Ubuntu14.04 的u启动盘,重启电脑进入要克隆的系统,打包整个根目录 su cd / tar -cvpzf /media/cdrom/backup.tar.gz / --exclude=/ ...

  2. CF1200A

    CF1200A 解法: 给出长度为n的字符串,字符串由'L'.'R'以及数字0~9组成.旅馆有10间房子,L代表客人从左边入住,R代表客人从右边入住,数字则表示第i间房子客人退房了.问经过这n次操作后 ...

  3. win10下linux子系统的文件夹的布局

    我这里的目录为:C:\Users\com\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\Loca ...

  4. python数据分析与应用

    python数据分析与应用笔记 使用sklearn构建模型 1.使用sklearn转换器处理数据 import numpy as np from sklearn.datasets import loa ...

  5. 第11组 Alpha冲刺(6/6)

    第11组 Alpha冲刺(6/6)   队名 不知道叫什么团队 组长博客 https://www.cnblogs.com/xxylac/p/11913626.html 作业博客 https://edu ...

  6. vagrant系列三:vagrant搭建的php7环境

    原文:https://blog.csdn.net/hel12he/article/details/51107236 前面已经把vagrant的基础知识已经基本过了一遍 了,相信只要按着教程来,你已经搭 ...

  7. the requested PHP extension dom is missing from your system

    composer  出错 the requested PHP extension dom is missing from your system 解决办法    yum install  php70w ...

  8. 转载:OutOfMemoryError系列(2): GC overhead limit exceeded

    这是本系列的第二篇文章, 相关文章列表: OutOfMemoryError系列(1): Java heap space OutOfMemoryError系列(2): GC overhead limit ...

  9. Qt编写安防视频监控系统5-视频回放

    一.前言 一般视频回放都会采用GB28181国标来处理,这样可以保证兼容国内各大厂家的NVR,毕竟在同一的国家标准下,大家都会统一支持国标的,就不需要根据各个厂家的SDK来做兼容处理,烦得很,厂家越来 ...

  10. JVM-类加载子系统

    类的生命周期 1.加载 将.class二进制字节流流从磁盘读到内存中(通过文件的全限定名读取) 2.连接 2.1验证:验证字节码文件的正确性 2.2准备:给类的静态变量分配内存,并赋予默认值(比如 i ...