JAVA密码加密算法.RSA算法(非对称加密算法)和密码加盐MD5

版权声明:cxyo.cn https://blog.csdn.net/weixin_44075537/article/details/84995451

密码加盐MD5

Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。
是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值.
MD5算法具有以下特点:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。
2、容易计算:从原数据计算出MD5值很容易。
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
MD5应用
一致性验证
典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改
安全访问认证
MD5还广泛用于操作系统的登陆认证上,如Unix、各类BSD系统登录密码、数字签名等诸多方面。如在Unix系统中用户的密码是以MD5(或其它类似的算法)经Hash运算后存储在文件系统中。当用户登录的时候,系统把用户输入的密码进行MD5 Hash运算,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确

盐(Salt)
在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。
以上这句话是维基百科上对于 Salt 的定义,但是仅凭这句话还是很难理解什么叫 Salt,以及它究竟起到什么作用。
第一代密码
早期的软件系统或者互联网应用,数据库中设计用户表的时候,大致是这样的结构:
1
2
3
4
5
6
7 mysql> desc User;
±---------±-------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±---------±-------------±-----±----±--------±------+
| UserName | varchar(50) | NO | | | |
| PassWord | varchar(150) | NO | | | |
±---------±-------------±-----±----±--------±------+
数据存储形式如下:
1
2
3
4
5
6
7 mysql> select * from User;
±---------±---------+
| UserName | PassWord |
±---------±---------+
| lichao | 123 |
| akasuna | 456 |
±---------±---------+
主要的关键字段就是这么两个,一个是登陆时的用户名,对应的一个密码,而且那个时候的用户名是明文存储的,如果你登陆时用户名是 123,那么数据库里存的就是 123。这种设计思路非常简单,但是缺陷也非常明显,数据库一旦泄露,那么所有用户名和密码都会泄露,后果非常严重。
第二代密码
为了规避第一代密码设计的缺陷,聪明的人在数据库中不在存储明文密码,转而存储加密后的密码,典型的加密算法是 MD5 和 SHA1,其数据表大致是这样设计的:
1
2
3
4
5
6
7 mysql> desc User;
±---------±-------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±---------±-------------±-----±----±--------±------+
| UserName | varchar(50) | NO | | | |
| PwdHash | char(32) | NO | | | |
±---------±-------------±-----±----±--------±------+
数据存储形式如下:
1
2
3
4
5
6
7 mysql> select * from User;
±---------±---------------------------------+
| UserName | PwdHash |
±---------±---------------------------------+
| lichao | 202cb962ac59075b964b07152d234b70 |
| akasuna | 250cf8b51c773f3f8dc8b4be867a9a02 |
±---------±---------------------------------+
假如你设置的密码是 123,那么数据库中存储的就是 202cb962ac59075b964b07152d234b70 或 40bd001563085fc35165329ea1ff5c5ecbdbbeef。当用户登陆的时候,会把用户输入的密码执行 MD5(或者 SHA1)后再和数据库就行对比,判断用户身份是否合法,这种加密算法称为散列。
严格地说,这种算法不能算是加密,因为理论上来说,它不能被解密。所以即使数据库丢失了,但是由于数据库里的密码都是密文,根本无法判断用户的原始密码,所以后果也不算太严重。
第三代密码
本来第二代密码设计方法已经很不错了,只要你密码设置得稍微复杂一点,就几乎没有被破解的可能性。但是如果你的密码设置得不够复杂,被破解出来的可能性还是比较大的。
好事者收集常用的密码,然后对他们执行 MD5 或者 SHA1,然后做成一个数据量非常庞大的数据字典,然后对泄露的数据库中的密码就行对比,如果你的原始密码很不幸的被包含在这个数据字典中,那么花不了多长时间就能把你的原始密码匹配出来。这个数据字典很容易收集,CSDN 泄露的那 600w 个密码,就是很好的原始素材。
于是,第三代密码设计方法诞生,用户表中多了一个字段:
1
2
3
4
5
6
7
8 mysql> desc User;
±---------±------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±---------±------------±-----±----±--------±------+
| UserName | varchar(50) | NO | | | |
| Salt | char(50) | NO | | | |
| PwdHash | char(32) | NO | | | |
±---------±------------±-----±----±--------±------+
数据存储形式如下:
1
2
3
4
5
6
7 mysql> select * from User;
±---------±---------------------------±---------------------------------+
| UserName | Salt |PwdHash |
±---------±---------------------------±---------------------------------+
| lichao | 1ck12b13k1jmjxrg1h0129h2lj | 6c22ef52be70e11b6f3bcf0f672c96ce |
| akasuna | 1h029kh2lj11jmjxrg13k1c12b | 7128f587d88d6686974d6ef57c193628 |
±---------±---------------------------±---------------------------------+
Salt 可以是任意字母、数字、或是字母或数字的组合,但必须是随机产生的,每个用户的 Salt 都不一样,用户注册的时候,数据库中存入的不是明文密码,也不是简单的对明文密码进行散列,而是 MD5( 明文密码 + Salt),也就是说:
MD5(‘123’ + ‘1ck12b13k1jmjxrg1h0129h2lj’) = ‘6c22ef52be70e11b6f3bcf0f672c96ce’
MD5(‘456’ + ‘1h029kh2lj11jmjxrg13k1c12b’) = ‘7128f587d88d6686974d6ef57c193628’
由于加了 Salt,即便数据库泄露了,但是由于密码都是加了 Salt 之后的散列,坏人们的数据字典已经无法直接匹配,明文密码被破解出来的概率也大大降低。
是不是加了 Salt 之后就绝对安全了呢?淡然没有!坏人们还是可以他们数据字典中的密码,加上我们泄露数据库中的 Salt,然后散列,然后再匹配。但是由于我们的 Salt 是随机产生的,假如我们的用户数据表中有 30w 条数据,数据字典中有 600w 条数据,坏人们如果想要完全覆盖的坏,他们加上 Salt 后再散列的数据字典数据量就应该是 300000* 6000000 = 1800000000000,一万八千亿啊,干坏事的成本太高了吧。但是如果只是想破解某个用户的密码的话,只需为这 600w 条数据加上 Salt,然后散列匹配。可见 Salt 虽然大大提高了安全系数,但也并非绝对安全。
实际项目中,Salt 不一定要加在最前面或最后面,也可以插在中间嘛,也可以分开插入,也可以倒序,程序设计时可以灵活调整,都可以使破解的难度指数级增长。

在util工具包中创建一个PasswordUtil.java

package cn.xdl.ovls.util;
import java.security.MessageDigest;
import java.util.UUID;
public class PasswordUtil {
	//传一个字符串过来经过md5处理返回一个字符串
	public static String md5(String s) {
	    try {
	    	//MessageDigest是封装md5算法的工具对象还支持SHA算法
	        MessageDigest md = MessageDigest.getInstance("MD5");
	        //通过digest拿到的任意字符串,得到的bates都是等长的
	        byte[] bytes = md.digest(s.getBytes("utf-8"));
	        //这里输出的都是乱码
//	        System.out.println(new String(bytes));
	        //返回的toHex通过下面方法再处理
	        return toHex(bytes);
	    }
	    catch (Exception e) {
	        throw new RuntimeException(e);
	    }
	}
	private static String toHex(byte[] bytes) {
		//把toHex的字符串把二进制转换成十六进制
	    final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
	    StringBuilder ret = new StringBuilder(bytes.length * 2);
	    //循环判断是为了补位操作
	    for (int i=0; i<bytes.length; i++) {
	        ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
	        ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
	    }
	    return ret.toString();
	}
	public static String salt(){
		//使用UUID通用唯一识别码,取第一个-前面的值
		UUID uuid = UUID.randomUUID();
		String[] arr = uuid.toString().split("-");
		return arr[0];
	}
	public static void main(String[] args) {
		System.out.println(md5("123"));
		System.out.println(md5("1234567890"));
		UUID uuid = UUID.randomUUID();
		System.out.println(uuid);
	}
	
}

通过以下代码调用

//TODO 密码加密处理
String salt = PasswordUtil.salt();
//通MD5加密处理
String md5Pwd = PasswordUtil.md5(password+salt);
//设置加密密码
user.setPassword(md5Pwd);
//设置盐值
user.setSalt(salt);

猜你喜欢

转载自blog.csdn.net/weixin_44075537/article/details/84995451