ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MD5加盐加密工具类

2020-03-15 15:02:07  阅读:254  来源: 互联网

标签:加密 数据库 value 密码 加盐 validPassword 长度 MD5


所谓加盐就是系统随机生成一个字符串,

这个字符串和用户的密码混合到一起进行MD5加密。并在数据库保存这个盐(随机字符串)

 

当验密的时候,取出盐,再用待验证的密码走一遍加密的过程(这个过程唯一不同的是,盐不是随机的,是取出

之前保存的)。

因为盐都是一样的,如果密码是正确的,那么加密后的密码和数据库存必然是一样的。

这个加盐的过程保证了,注册的时候,因为盐是随机的,所以同样的一个密码,比如111111,每次加密后

的密文是不一样的。(因为混入了盐)

我参考了别人的代码,把盐放入了最终的密码前N位,而不是单独存一个字段。这个不影响整体的算法。

代码:

package com.example.gate.util;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;


/**
 * MD5加盐加密算法
 * 4位盐,32位密码共36位长度
 * 
 * @author lsy
 *
 */
public class Md5SaltUtil {

	public static void main(String[] args) {

		//测试加密和验密
		for(int i=0;i<3;i++) {
			System.out.println("\r\n");
			//加密过程---------------
			String password = "111111";
			String dbpass = Md5SaltUtil.getEncryptedPwd(password);//加密后存放在数据库
			System.out.println("#######################");
			//验密过程---------------跟数据库密码比对
			String passwordValid="111111";//待验证的密码
			boolean falg= Md5SaltUtil.validPassword(passwordValid, dbpass);//用待验证的密码 和数据库的密码进行验密
			if(falg) {
				System.out.println(passwordValid+"验密通过.");
			}else {
				System.out.println(passwordValid+"验密失败........................");
			}
			
			//验密过程---------------跟数据库密码比对
			String passwordValidError="111111xxxx";//待验证的密码
			boolean falg2= Md5SaltUtil.validPassword(passwordValidError, dbpass);//用待验证的密码 和数据库的密码进行验密
			if(falg2) {
				System.out.println(passwordValidError+"验密通过.");
			}else {
				System.out.println(passwordValidError+"验密失败........................");
			}
		}
	}

	private static final String HEX_NUMS_STR = "0123456789ABCDEF";
	private static final Integer SALT_LENGTH = 2;
	private static final boolean isDebug = true;//是否打印debug日志

	/**
	 * 将16进制字符串转换成字节数组
	 * 
	 * @param hex
	 * @return
	 */
	public static byte[] hexStringToByte(String hex) {
		int len = (hex.length() / 2);
		byte[] result = new byte[len];
		char[] hexChars = hex.toCharArray();
		for (int i = 0; i < len; i++) {
			int pos = i * 2;
			result[i] = (byte) (HEX_NUMS_STR.indexOf(hexChars[pos]) << 4 | HEX_NUMS_STR.indexOf(hexChars[pos + 1]));
		}
		return result;
	}
	
//	private static byte[] fromHex(String hex)
//    {
//        byte[] binary = new byte[hex.length() / 2];
//        for(int i = 0; i < binary.length; i++)
//        {
//            binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);
//        }
//        return binary;
//    }

	/**
	 * 将指定byte数组转换成16进制字符串
	 * 
	 * @param b
	 * @return
	 */
	public static String byteToHexString(byte[] b) {
		StringBuffer hexString = new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			String hex = Integer.toHexString(b[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			hexString.append(hex.toUpperCase());
		}
		return hexString.toString();
	}

//	private static String toHex(byte[] array)
//    {
//        BigInteger bi = new BigInteger(1, array);
//        String hex = bi.toString(16);
//        int paddingLength = (array.length * 2) - hex.length();
//        if(paddingLength > 0)
//            return String.format("%0" + paddingLength + "d", 0) + hex;
//        else
//            return hex;
//    }
	
	/**
	 * 验密
	 * 数据库的密文是:“盐+密文”。
	 * 从数据库的密码中取出盐,用这个盐和待验证密码,再走一遍加密的算法得到“盐+待验证密码的密文”,看结果和数据库密码是否相等。
	 * 
	 * @param password 待验证的密码
	 * @param passwordInDb 存储的密码
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws UnsupportedEncodingException
	 */
	public static boolean validPassword(String password, String passwordInDb) {
		try {
			// 将16进制字符串格式口令转换成字节数组
			byte[] pwdInDb = hexStringToByte(passwordInDb);
			// 声明盐变量
			byte[] salt = new byte[SALT_LENGTH];
			// 将盐从数据库中保存的口令字节数组中提取出来
			System.arraycopy(pwdInDb, 0, salt, 0, SALT_LENGTH);
			if (isDebug) {
				//打印 
				String saltStr=byteToHexString(salt);
				System.out.println("【validPassword】  取出数据库密码["+passwordInDb+"]中的盐====saltStr长度==="+saltStr.length()+",value===【"+saltStr+"】");
			}
			// 创建消息摘要对象
			MessageDigest md = MessageDigest.getInstance("MD5");
			// 将盐数据传入消息摘要对象
			md.update(salt);
			// 将口令的数据传给消息摘要对象
			md.update(password.getBytes("UTF-8"));
			// 生成输入口令的消息摘要
			byte[] digest = md.digest();
			
			
			// 因为要在口令的字节数组中存放盐,所以加上盐的字节长度
			byte[] pwdValid = new byte[digest.length + SALT_LENGTH];
			// 将盐的字节拷贝到生成的加密口令字节数组的前SALT_LENGTH个字节,以便在验证口令时取出盐
			System.arraycopy(salt, 0, pwdValid, 0, SALT_LENGTH);
			// 将消息摘要拷贝到加密口令字节数组从第SALT_LENGTH+1个字节开始的字节
			System.arraycopy(digest, 0, pwdValid, SALT_LENGTH, digest.length);
			String passwordValid = byteToHexString(pwdValid);
			if (isDebug) {
				//打印 
				String digestStr=byteToHexString(digest);
				System.out.println("【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度==="+digestStr.length()+",value==="+digestStr);
				System.out.println("【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度==="+passwordValid.length()+",value==="+passwordValid);
				System.out.println("【validPassword】  比较待验证密码加密["+passwordValid+"],数据库密码["+passwordInDb+"]");
			}
			if(passwordValid!=null && passwordValid.equals(passwordInDb)) {
				return true;
			}else {
				return false;
			}
//			// 声明一个保存数据库中口令消息摘要的变量
//			byte[] digestInDb = new byte[pwdInDb.length - SALT_LENGTH];
//			// 取得数据库中口令的消息摘要
//			System.arraycopy(pwdInDb, SALT_LENGTH, digestInDb, 0, digestInDb.length);
//			// 比较根据输入口令生成的消息摘要和数据库中消息摘要是否相同
//			if (Arrays.equals(digest, digestInDb)) {
//				// 口令正确返回口令匹配消息
//				return true;
//			} else {
//				// 口令不正确返回口令不匹配消息
//				return false;
//			}
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 加密。
	 * 
	 * 随机生成一个盐。用盐和密码加密后得到密文。
	 * 最终返回 “盐+密文”。
	 * 
	 * @param password 待加密的密码
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws UnsupportedEncodingException
	 */
	public static String getEncryptedPwd(String password){
		// 将字节数组格式加密后的口令转化为16进制字符串格式的口令
		String res=null;
		try {
			// 声明加密后的口令数组变量
			byte[] pwd = null;
			// 随机数生成器
			SecureRandom random = new SecureRandom();
			// 声明盐数组变量 SALT_LENGTH
			byte[] salt = new byte[SALT_LENGTH];
			// 将随机数放入盐变量中
			random.nextBytes(salt);
			if(isDebug) {
				//打印 
				String saltStr=byteToHexString(salt);
				System.out.println("【getEncryptedPwd】 ["+password+"]生成盐====saltStr长度==="+saltStr.length()+",value===【"+saltStr+"】");
			}
			// 声明消息摘要对象
			MessageDigest md = null;
			// 创建消息摘要
			md = MessageDigest.getInstance("MD5");
			// 将盐数据传入消息摘要对象
			md.update(salt);
			// 将口令的数据传给消息摘要对象
			md.update(password.getBytes("UTF-8"));
			// 获得消息摘要的字节数组
			byte[] digest = md.digest();
			
			if (isDebug) {
				//打印 
				String disgStr=byteToHexString(digest);
				System.out.println("【getEncryptedPwd】加盐后再加密====disgStr长度==="+disgStr.length()+",value==="+disgStr);
			}
			// 因为要在口令的字节数组中存放盐,所以加上盐的字节长度
			pwd = new byte[digest.length + SALT_LENGTH];
			// 将盐的字节拷贝到生成的加密口令字节数组的前SALT_LENGTH个字节,以便在验证口令时取出盐
			System.arraycopy(salt, 0, pwd, 0, SALT_LENGTH);
			// 将消息摘要拷贝到加密口令字节数组从第13个字节开始的字节
			System.arraycopy(digest, 0, pwd, SALT_LENGTH, digest.length);
			res = byteToHexString(pwd);
			if (isDebug) {
				System.out.println("【getEncryptedPwd】"+password+"加密后最终结果(盐加密码)长度:"+res.length()+",密文==="+res);
			}
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			res=null;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			res=null;
		}
		return res;
	}
}

main方法打印:



【getEncryptedPwd】 [111111]生成盐====saltStr长度===4,value===【B448】
【getEncryptedPwd】加盐后再加密====disgStr长度===32,value===2E7EF281967161E9D9EF7E9A1381909C
【getEncryptedPwd】111111加密后最终结果(盐加密码)长度:36,密文===B4482E7EF281967161E9D9EF7E9A1381909C
#######################
【validPassword】  取出数据库密码[B4482E7EF281967161E9D9EF7E9A1381909C]中的盐====saltStr长度===4,value===【B448】
【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度===32,value===2E7EF281967161E9D9EF7E9A1381909C
【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度===36,value===B4482E7EF281967161E9D9EF7E9A1381909C
【validPassword】  比较待验证密码加密[B4482E7EF281967161E9D9EF7E9A1381909C],数据库密码[B4482E7EF281967161E9D9EF7E9A1381909C]
111111验密通过.
【validPassword】  取出数据库密码[B4482E7EF281967161E9D9EF7E9A1381909C]中的盐====saltStr长度===4,value===【B448】
【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度===32,value===9179A26553AB359249BEC9DDC5343CA7
【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度===36,value===B4489179A26553AB359249BEC9DDC5343CA7
【validPassword】  比较待验证密码加密[B4489179A26553AB359249BEC9DDC5343CA7],数据库密码[B4482E7EF281967161E9D9EF7E9A1381909C]
111111xxxx验密失败........................


【getEncryptedPwd】 [111111]生成盐====saltStr长度===4,value===【7C43】
【getEncryptedPwd】加盐后再加密====disgStr长度===32,value===B7928FC6B19238E0EE3A686A4FFCB6A0
【getEncryptedPwd】111111加密后最终结果(盐加密码)长度:36,密文===7C43B7928FC6B19238E0EE3A686A4FFCB6A0
#######################
【validPassword】  取出数据库密码[7C43B7928FC6B19238E0EE3A686A4FFCB6A0]中的盐====saltStr长度===4,value===【7C43】
【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度===32,value===B7928FC6B19238E0EE3A686A4FFCB6A0
【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度===36,value===7C43B7928FC6B19238E0EE3A686A4FFCB6A0
【validPassword】  比较待验证密码加密[7C43B7928FC6B19238E0EE3A686A4FFCB6A0],数据库密码[7C43B7928FC6B19238E0EE3A686A4FFCB6A0]
111111验密通过.
【validPassword】  取出数据库密码[7C43B7928FC6B19238E0EE3A686A4FFCB6A0]中的盐====saltStr长度===4,value===【7C43】
【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度===32,value===3B4761FEAE380D7F27B78356BA5B4AC8
【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度===36,value===7C433B4761FEAE380D7F27B78356BA5B4AC8
【validPassword】  比较待验证密码加密[7C433B4761FEAE380D7F27B78356BA5B4AC8],数据库密码[7C43B7928FC6B19238E0EE3A686A4FFCB6A0]
111111xxxx验密失败........................


【getEncryptedPwd】 [111111]生成盐====saltStr长度===4,value===【A4F7】
【getEncryptedPwd】加盐后再加密====disgStr长度===32,value===58FF5FBE29CFF096A99885051E934615
【getEncryptedPwd】111111加密后最终结果(盐加密码)长度:36,密文===A4F758FF5FBE29CFF096A99885051E934615
#######################
【validPassword】  取出数据库密码[A4F758FF5FBE29CFF096A99885051E934615]中的盐====saltStr长度===4,value===【A4F7】
【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度===32,value===58FF5FBE29CFF096A99885051E934615
【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度===36,value===A4F758FF5FBE29CFF096A99885051E934615
【validPassword】  比较待验证密码加密[A4F758FF5FBE29CFF096A99885051E934615],数据库密码[A4F758FF5FBE29CFF096A99885051E934615]
111111验密通过.
【validPassword】  取出数据库密码[A4F758FF5FBE29CFF096A99885051E934615]中的盐====saltStr长度===4,value===【A4F7】
【validPassword】  用数据库密码中的盐对待验证密码加盐加密====digestStr长度===32,value===05E3831D30CD6C26C4B55E175E18CBE7
【validPassword】  用数据库密码中的盐对待验证密码加盐加密最终结果====passwordValid长度===36,value===A4F705E3831D30CD6C26C4B55E175E18CBE7
【validPassword】  比较待验证密码加密[A4F705E3831D30CD6C26C4B55E175E18CBE7],数据库密码[A4F758FF5FBE29CFF096A99885051E934615]
111111xxxx验密失败........................

 

 

标签:加密,数据库,value,密码,加盐,validPassword,长度,MD5
来源: https://blog.csdn.net/lushuaiyin/article/details/104878842

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有