美文网首页
深入理解信息摘要加密算法MD5

深入理解信息摘要加密算法MD5

作者: Vechace | 来源:发表于2018-04-19 16:45 被阅读566次

    标签: 算法


    前言:加密算法分对称加密与非对称加密,在常见对称加密算法中,信息摘要算法MD5是被广泛应用的一种,本文深入分析MD5算法的实现及其底层实现

    1.什么是MD5算法?

    MD5信息摘要算法,是将对输入的任意长度的信息进行计算,产生一个128位长度的“指纹”或“信息摘要”。MD5算法适合用在数据签名应用中,在此应用中,一个大的文件必须在类似RSA算法的公用密钥系统中用私人密钥加密前被“压缩”在一种安全模式下。

    2.MD5算法的特性

    * 长度固定:任意长的字符串加密后长度相同,方便平时信息的统计和管理
    * 易计算:字符串和文件加密过程较简单,开发者很容易理解和做出加密工具
    * 细微性:字符串里任意一个字符改变都会引起MD5值改变
    * 不可逆性:提高数据的安全性
    

    3.暴力破解方法:破解概率较低,步骤如下:

    • 第一步:建立一个大型的数据库,将日常的各个语句,通过MD5加密成密文,不断地积累大量的语句,组成一个庞大的数据库
    • 第二步:将截取到的密文,进行数据库匹配,撞库成功即破解

    4.如何实现一个MD5加密算法?

     //一种Java实现,详细介绍见注释
     public static String MD5(String key) {
        //十六进制转换表,0xf
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        try {
            byte[] btInput = key.getBytes();
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获取摘要密文
            byte[] md = mdInst.digest();
    
            //md是字节类型的数组,而MD5是一串十六进制的字符
            // 因此要把密文转换成十六进制的字符串形式,长度为32个字符
            int j = md.length;
            //开辟一个缓冲区,用于存转换结果
            char str[] = new char[j * 2];
            int k = 0;
            //加噪声
            for (int i = 0; i < j; i++) {
            //逐个取摘要密文中的字符
                byte byte0 = md[i];
                //无符号右移4位,与0xf([1111])取与,获得高四位
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                //获取低四位,这也是srt[]数组长度是2*j的原因
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
            } catch (Exception e) {
                logger.error("生成MD5失败", e);
                return null;
            }
        }
    
    

    5.深入MD5底层实现:

    • 信息摘要类的实现:Java对其封装在java.security.MessageDigest包下,部分源码,如下:
    public abstract class MessageDigest extends MessageDigestSpi {
    
        private static final Debug pdebug =
                            Debug.getInstance("provider", "Provider");
        private static final boolean skipDebug =
            Debug.isOn("engine=") && !Debug.isOn("messagedigest");
    
        private String algorithm;
    
        // The state of this digest
        private static final int INITIAL = 0;
        private static final int IN_PROGRESS = 1;
        private int state = INITIAL;
    
        // The provider
        private Provider provider;
        ...
        //主要方法,该类对以下方法作了许多重载,仅列出主要方法名
        //获取单例信息摘要对象
        public static MessageDigest getInstance(String algorithm)
        throws NoSuchAlgorithmException{...}
        
        //更新摘要方法
        public void update(byte input) {...}
        
        //摘要方法
        public byte[] digest() {...}
        
        //更新摘要方法引擎
         protected void engineUpdate(ByteBuffer input) {...}
    }
    
    • 获取单例信息摘要对象的方法,其源码如下:
     public static MessageDigest getInstance(String algorithm, String provider)
            throws NoSuchAlgorithmException, NoSuchProviderException
        {
            if (provider == null || provider.length() == 0)
                throw new IllegalArgumentException("missing provider");
            Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
            if (objs[0] instanceof MessageDigest) {
                MessageDigest md = (MessageDigest)objs[0];
                md.provider = (Provider)objs[1];
                return md;
            } else {
                MessageDigest delegate =
                    new Delegate((MessageDigestSpi)objs[0], algorithm);
                delegate.provider = (Provider)objs[1];
                return delegate;
            }
        }
    

    可以看到,该方法返回一个MessageDigest实例

    • 更新摘要方法,其源码如下:
    /**
         * Updates the digest using the specified array of bytes.
         *
         * @param input the array of bytes.
         */
        public void update(byte[] input) {
            engineUpdate(input, 0, input.length);
            state = IN_PROGRESS;
        }
    

    内部通过调用MessageDigestSpi类的engineUpdate方法实现,其源码如下:

    /**
     * Update the digest using the specified ByteBuffer. The digest is
     * updated using the {@code input.remaining()} bytes starting
     * at {@code input.position()}.
     * Upon return, the buffer's position will be equal to its limit;
    * its limit will not have changed.
    *
    * @param input the ByteBuffer
    * @since 1.5
    */
        protected void engineUpdate(ByteBuffer input) {
            if (input.hasRemaining() == false) {
                return;
            }
            if (input.hasArray()) {
                byte[] b = input.array();
                int ofs = input.arrayOffset();
                int pos = input.position();
                int lim = input.limit();
                engineUpdate(b, ofs + pos, lim - pos);
                input.position(lim);
            } else {
                int len = input.remaining();
                int n = JCAUtil.getTempArraySize(len);
                if ((tempArray == null) || (n > tempArray.length)) {
                    tempArray = new byte[n];
                }
                while (len > 0) {
                    int chunk = Math.min(len, tempArray.length);
                    input.get(tempArray, 0, chunk);
                    engineUpdate(tempArray, 0, chunk);
                    len -= chunk;
                }
            }
        }
    

    内部通过io操作来完成执行操作(这部分较为复杂,待深究)

    • 摘要方法,放回摘要密文,源码如下:
    /**
    * Completes the hash computation by performing final operations
    * such as padding. The digest is reset after this call is made.
    *
    * @return the array of bytes for the resulting hash value.
    */
        public byte[] digest() {
            /* Resetting is the responsibility of implementors. */
            byte[] result = engineDigest();
            state = INITIAL;
            return result;
        }
    

    内部通过调用MessageDigestSpi类的engineDigest方法实现,跟踪engineDigest方法源码,

    protected int engineDigest(byte[] buf, int offset, int len)
        throws DigestException {
    
            byte[] digest = engineDigest();
            if (len < digest.length) throw new DigestException("partial digests not returned");
            if (buf.length - offset < digest.length)
        throw new DigestException("insufficient space in the output "
            + "buffer to store the digest");
            System.arraycopy(digest, 0, buf, offset, digest.length);
            return digest.length;
        }
    

    至此,MD算法主要源码展示完毕。

    6.总结:

    • Java实现MD5算法过程:输入字符串并转换字节数组--->获取java.security.MessageDigest类实例mdInst--->调用MD实例mdInst.update()方法,将字符串数组更新到MD内部进行摘要--->调用MD实例mdInst.digest方法获取摘要字节数组--->将摘要字节数组转换成十六进制字符(32个)--->返回加密字符串结果

    • MessageDigest底层实现:

    • 主要类:MessageDigest类与MessageDigestSpi类

    • 主要方法:

    • getInstance()获取摘要对象实例

    • update()更新摘要字节数组,进行取摘要

    • digest()获取摘要字节数组

    • engineUpdate()执行摘要更新

    • engineDigest()执行摘要操作

    • MD5算法应用场景:实现安全登录、文件加密等,实际使用中,单单使用MD5加密还是不够的,一般会进行加盐值,通过UUID生成系统唯一标识符(其中一种方法),将标识符与待加密字符串进行拼接后,再执行MD5加密,从而进一步提升加密效果。

    希望本文能对你有所帮助,欢迎回来~

    相关文章

      网友评论

          本文标题:深入理解信息摘要加密算法MD5

          本文链接:https://www.haomeiwen.com/subject/zjnwkftx.html