Java安全——訊息摘要

尊淵發表於2016-09-02

標籤(空格分隔): Java 安全


[toc]


概念

訊息摘要(Message Digest)又稱為數字摘要(Digital Digest)。它是一個唯一對應一個訊息或文字的固定長度的值,它由一個單向Hash加密函式對訊息進行作用而產生。如果訊息在途中改變了,則接收者通過對收到訊息的新產生的摘要與原摘要比較,就可知道訊息是否被改變了。因此訊息摘要保證了訊息的完整性。 訊息摘要採用單向Hash 函式將需加密的明文”摘要”成一串128bit的密文,這一串密文亦稱為數字指紋(Finger Print),它有固定的長度,且不同的明文摘要成密文,其結果總是不同的,而同樣的明文其摘要必定一致。這樣這串摘要便可成為驗證明文是否是”真身”的”指紋”了。 <<百度百科>>

訊息摘要其實是我們日常開發中經常遇到的,比如MD5演算法就是一種摘要。它是Java安全提供者體系中最簡單的標準引擎。本文不會討論演算法本身,只關注Java語言體系中的實現和使用方法。具體演算法原理,後續撰文學習並補充。

使用

訊息摘要通過MessageDigest類實現。訊息摘要通過getInstance()方法獲取演算法例項。通過update()方法為訊息摘要增加內容位元組。根據digest()方法利用累計的內容位元組計算最後的摘要。

下面的test列出了一些細節:

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import org.apache.commons.codec.binary.Hex;

public class MessageDigestTest {

    private static String TEST_DATA = "i am a test";

    public static void main(String[] args)
                    throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA");
        print(md);
        md = MessageDigest.getInstance("MD5");
        print(md);
        md = MessageDigest.getInstance("MD2");
        print(md);
        md = MessageDigest.getInstance("SHA-256");
        print(md);
        md = MessageDigest.getInstance("SHA-384");
        print(md);
        md = MessageDigest.getInstance("SHA-224");
        print(md);
        md = MessageDigest.getInstance("SHA-512");
        print(md);
    }

    private static void print(MessageDigest md) throws UnsupportedEncodingException {
        System.out.println("Algorithm:	" + md.getAlgorithm());
        System.out.println("	Provider:	" + md.getProvider());
        long start = System.nanoTime();
        md.update(TEST_DATA.getBytes("UTF-8"));
        byte[] digest = md.digest();
        long time = System.nanoTime() - start;
        System.out.println("	Time cost:	" + time + "ns");
        System.out.println("	Byte length:	" + digest.length);
        String digestBase64Str = Base64.getEncoder().encodeToString(digest);
        System.out.println("	Base64:	" + digestBase64Str + "	len:	" + digestBase64Str.length());
        String digestHexStr = Hex.encodeHexString(digest);
        System.out.println("	Hex:	" + digestHexStr + "	len:	" + digestHexStr.length());
    }
}

輸出結果如下:

Algorithm:    SHA
    Provider:    SUN version 1.8
    Time cost:    432582ns
    Byte length:    20
    Base64:    Bj5BsD+J+PLpYZYVDR+14WwSKlg=    len:    28
    Hex:    063e41b03f89f8f2e96196150d1fb5e16c122a58    len:    40
Algorithm:    MD5
    Provider:    SUN version 1.8
    Time cost:    46030ns
    Byte length:    16
    Base64:    dCVplUXLIKBlw/aWvoOCyA==    len:    24
    Hex:    7425699545cb20a065c3f696be8382c8    len:    32
Algorithm:    MD2
    Provider:    SUN version 1.8
    Time cost:    80611ns
    Byte length:    16
    Base64:    v4z32/PgSXZCrWvnOcRiaQ==    len:    24
    Hex:    bf8cf7dbf3e0497642ad6be739c46269    len:    32
Algorithm:    SHA-256
    Provider:    SUN version 1.8
    Time cost:    205859ns
    Byte length:    32
    Base64:    lRctjSY3GOo6erswsyheP21CNtAAGxnsUHVF17u7RYg=    len:    44
    Hex:    95172d8d263718ea3a7abb30b3285e3f6d4236d0001b19ec507545d7bbbb4588    len:    64
Algorithm:    SHA-384
    Provider:    SUN version 1.8
    Time cost:    258382ns
    Byte length:    48
    Base64:    /B642f1Y+PPKgeIJHFpbEDLJwX2nqWaMaC8nVQuboRWN5MZeKpGaLiySWUEr4xB9    len:    64
    Hex:    fc1eb8d9fd58f8f3ca81e2091c5a5b1032c9c17da7a9668c682f27550b9ba1158de4c65e2a919a2e2c9259412be3107d    len:    96
Algorithm:    SHA-224
    Provider:    SUN version 1.8
    Time cost:    76539ns
    Byte length:    28
    Base64:    6m2pv1G0lnWlKO4ahn7iJAQF8XMo5cHR3LBO3w==    len:    40
    Hex:    ea6da9bf51b49675a528ee1a867ee2240405f17328e5c1d1dcb04edf    len:    56
Algorithm:    SHA-512
    Provider:    SUN version 1.8
    Time cost:    143649ns
    Byte length:    64
    Base64:    Vv6LL9QqfeCq1ClbN8JdMTcU1PzUbsRQKgmc7vVDbcHTB6i/SryXIjWcRcrfYa6n2QxstmSwUY9AME3pTmykNw==    len:    88
    Hex:    56fe8b2fd42a7de0aad4295b37c25d313714d4fcd46ec4502a099ceef5436dc1d307a8bf4abc9722359c45cadf61aea7d90c6cb664b0518f40304de94e6ca437    len:    128

訊息摘要實現是通過Java自己Sun的實現來做的。我們常見的是MD5和SHA演算法。

安全訊息摘要

考慮到訊息摘要的不安全性——拿到原文並知道演算法,就可以得到正確的訊息摘要。人們又加入了金鑰元素,得到了安全訊息摘要——MAC(Message Authentication Code)。MAC的特點是僅通過原文是無法計算出安全訊息摘要的。還需要一個雙方都知道的訊息金鑰。如果有人修改了訊息本體,又修改了訊息摘要,但是因為沒有使用金鑰,那麼將被發現破壞了資料。計算MAC的方法有很多,但是核心Java API沒有相關實現。Java的實現主要是通過JCE提供者來實現的。Mac類對應訊息摘要的MessageDigest類。計算訊息摘要時需要一個金鑰。金鑰的管理是另一個話題了。這裡主要講MAC的演算法,JCE實現主要基於HmacSHA1和HmacMD5。具體provider提供了哪些MAC相關的演算法,它們比較如何,見下面的程式碼:

package com.taobao.cd.security;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

import org.apache.commons.codec.binary.Hex;

public class MacTest {

    public static String TEST_DATA = "I am test";

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        // TODO Auto-generated method stub
        Mac mac = Mac.getInstance("HmacSHA1");
        print(mac);
        mac = Mac.getInstance("HmacMD5");
        print(mac);
        mac = Mac.getInstance("SslMacMD5");
        print(mac);
        mac = Mac.getInstance("HmacSHA384");
        print(mac);
        mac = Mac.getInstance("HmacSHA256");
        print(mac);
        mac = Mac.getInstance("HmacSHA224");
        print(mac);
        mac = Mac.getInstance("SslMacSHA1");
        print(mac);
        mac = Mac.getInstance("HmacSHA512");
        print(mac);
    }

    private static void print(Mac mac) throws NoSuchAlgorithmException, InvalidKeyException {
        System.out.println("Algorithm:	" + mac.getAlgorithm());
        System.out.println("	Provider:	" + mac.getProvider());
        KeyGenerator kg = KeyGenerator.getInstance("DES");
        SecretKey key = kg.generateKey();
        long start = System.nanoTime();
        mac.init(key);
        mac.update(TEST_DATA.getBytes());
        byte[] digest = mac.doFinal();
        long time = System.nanoTime() - start;
        System.out.println("	Time cost:	" + time + "ns");
        System.out.println("	Byte length:	" + digest.length);
        String digestBase64Str = Base64.getEncoder().encodeToString(digest);
        System.out.println("	Base64:	" + digestBase64Str + "	len:	" + digestBase64Str.length());
        String digestHexStr = Hex.encodeHexString(digest);
        System.out.println("	Hex:	" + digestHexStr + "	len:	" + digestHexStr.length());
    }
}

輸出如下:

Algorithm:    HmacSHA1
    Provider:    SunJCE version 1.8
    Time cost:    259396ns
    Byte length:    20
    Base64:    SVaesT3JpL9Emz5O40aZhpUrX5s=    len:    28
    Hex:    49569eb13dc9a4bf449b3e4ee3469986952b5f9b    len:    40
Algorithm:    HmacMD5
    Provider:    SunJCE version 1.8
    Time cost:    136070ns
    Byte length:    16
    Base64:    hBDkDPJ1CpyYqvFN48chqQ==    len:    24
    Hex:    8410e40cf2750a9c98aaf14de3c721a9    len:    32
Algorithm:    SslMacMD5
    Provider:    SunJCE version 1.8
    Time cost:    174089ns
    Byte length:    16
    Base64:    YiEubyE7y+M4JesHQOAV7A==    len:    24
    Hex:    62212e6f213bcbe33825eb0740e015ec    len:    32
Algorithm:    HmacSHA384
    Provider:    SunJCE version 1.8
    Time cost:    648249ns
    Byte length:    48
    Base64:    GLic/lwKBA1DdQVbNF5y7L/H8o+0gcO1n02JcdUJMnE9MRwyfHWamEsAJL3ycfFy    len:    64
    Hex:    18b89cfe5c0a040d4375055b345e72ecbfc7f28fb481c3b59f4d8971d50932713d311c327c759a984b0024bdf271f172    len:    96
Algorithm:    HmacSHA256
    Provider:    SunJCE version 1.8
    Time cost:    26007ns
    Byte length:    32
    Base64:    7LGdb6W8WPYfOXx0WQQ8f35zSGCxs/6op6eOqStuGxA=    len:    44
    Hex:    ecb19d6fa5bc58f61f397c7459043c7f7e734860b1b3fea8a7a78ea92b6e1b10    len:    64
Algorithm:    HmacSHA224
    Provider:    SunJCE version 1.8
    Time cost:    125344ns
    Byte length:    28
    Base64:    adoBxnZuCle/ip4FyfCgiQUCmlaN1+RDch9Q9g==    len:    40
    Hex:    69da01c6766e0a57bf8a9e05c9f0a08905029a568dd7e443721f50f6    len:    56
Algorithm:    SslMacSHA1
    Provider:    SunJCE version 1.8
    Time cost:    50672ns
    Byte length:    20
    Base64:    RxCsQguxNXgtqcobgqWdvJeYTKo=    len:    28
    Hex:    4710ac420bb135782da9ca1b82a59dbc97984caa    len:    40
Algorithm:    HmacSHA512
    Provider:    SunJCE version 1.8
    Time cost:    365908ns
    Byte length:    64
    Base64:    /Vf5JsSle43/t4aUMkDfdDeUFg3CA0OQAt5izo0bgoUmaVrVOv3tjTLwmrrL/3kACVn38aIDSAIlz8725gl6lA==    len:    88
    Hex:    fd57f926c4a57b8dffb786943240df743794160dc203439002de62ce8d1b828526695ad53afded8d32f09abacbff79000959f7f1a203480225cfcef6e6097a94    len:    128

這裡使用的key是通過KeyGenerator生成的。在實際應用中,應該有一方來生成並匯出到可管理的KeyStore,使用方載入Keystore再進行摘要生成和校驗。


相關文章