MD5算法的原理可简要的叙述为:MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

       总体流程如下图所示,每次的运算都由前一轮的128位结果值和当前的512bit值进行运算

                                                              

digestutils md5带盐解密 md5加盐加密原理_ci

       如果直接通过MD5进行hash散列加密,那么该密码很容易遭到攻击导致密码泄露。加salt可以在一定程度上解决该问题,salt盐其本质上是用户在注册首次填写密码时,由系统根据算法自动在密码加一些“调料”,再进行散列。当用户进行登录时,系统为用户提供的密码加上相同的“调料”,然后再进行散列,最终去比较散列值以此来确定密码的正确性。

       

package XXX;

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


public class MD5Utils {

    private static final String HEX_NUMS_STR = "0123456789ABCDEF";
    private static final Integer SALT_LENGTH = 12;

    /**
     * 将16进制字符串转换成数组
     *
     * @return byte[]
     * @author jacob
     */
    public static byte[] hexStringToByte(String hex) {
        /* len为什么是hex.length() / 2 ?
         * 首先,hex是一个字符串,里面的内容是像16进制那样的char数组
         * 用2个16进制数字可以表示1个byte,所以要求得这些char[]可以转化成什么样的byte[],首先可以确定的就是长度为这个char[]的一半
         */
        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;
    }

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

    /**
     * 验证
     *
     * @param cipher   用户输入
     * @param dbCipher 数据库保存的
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static boolean validCipher(String cipher, String dbCipher)
            throws NoSuchAlgorithmException, UnsupportedEncodingException {
        byte[] cipherIndb = hexStringToByte(dbCipher);
        byte[] cipherBytes = cipher.getBytes("UTF-8");
        //定义salt  
        byte[] salt = new byte[SALT_LENGTH];
        System.arraycopy(cipherIndb, 0, salt, 0, SALT_LENGTH);
        //创建消息摘要对象  
        MessageDigest md = MessageDigest.getInstance("MD5");
        //将盐数据传入消息摘要对象  
        md.update(salt);
        //md.update(cipherBytes);
        byte[] digest = md.digest();
        //声明一个对象接收数据库中的口令消息摘要  
        byte[] digestIndb = new byte[cipherIndb.length - SALT_LENGTH];
        //获得数据库中口令的摘要  
        System.arraycopy(cipherIndb, SALT_LENGTH, digestIndb, 0, digestIndb.length);
        //比较根据输入口令生成的消息摘要和数据库中的口令摘要是否相同  
        if (Arrays.equals(digest, digestIndb)) {
            //口令匹配相同  
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获得md5之后的16进制字符
     *
     * @param cipher 用户输入字符
     * @return String md5加密后字符
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public static String getEncryptedCipher(String cipher)
            throws NoSuchAlgorithmException, UnsupportedEncodingException {
        //拿到一个随机数组,作为盐  
        byte[] cryptotext = null;
        byte[] cipherBytes = cipher.getBytes("UTF-8");
        SecureRandom sc = new SecureRandom();                                       
        byte[] salt = new byte[SALT_LENGTH];
        sc.nextBytes(salt);
        //声明摘要对象,并生成  
        MessageDigest md = MessageDigest.getInstance("MD5");
        //计算MD5函数
        md.update(salt);
        //md.update(cipherBytes);
        //计算后获得字节数组,这就是那128位了即16个元素
        byte[] digest = md.digest();
        cryptotext = new byte[salt.length + digest.length];
        System.arraycopy(salt, 0, cryptotext, 0, SALT_LENGTH);
        System.arraycopy(digest, 0, cryptotext, SALT_LENGTH, digest.length);
        return byteToHexString(cryptotext);
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    try {
            //设置得到加盐后的密文
            String encryptedCryptotext = MD5Util.getEncryptedCipher("123456");
            System.out.println(encryptedCryptotext);
            //登录时校验加密
            boolean validCipher = MD5Util.validCipher(encryptedCryptotext, "F95E23DF4D40FB8070E1580FAE0203F4B4793C39730A9A82D1A294A2");
            if (validCipher) {
                System.out.println("欢迎登录");
            } else {
                System.out.println("登录失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}