MD5加密的Java實現
在各類應用系統中,若是須要設置帳戶,那麼就會涉及到存儲用戶帳戶信息的問題,爲了保證所存儲帳戶信息的安全,一般會採用MD5加密的方式來,進行存儲。首先,簡單得介紹一下,什麼是MD5加密。
MD5的全稱是Message-Digest Algorithm 5 (信息-摘要算法),在90年代初,由MIT Laboratory for Computer Scientce 和RSA Data Security Inc 的 Ronald L.Rivest開發出來,經MD二、MD3和MD4發展而來。是讓大容量信息在用數字簽名軟件簽署私人密匙前被"壓縮"成一種保密的格式(就是把一個任意長度的字節串變換成必定長的大整數)。不論是MD二、MD4仍是MD5,它們都須要得到一個隨機長度的信息併產生一個128位的信息摘要。雖然這些算法的結構或多或少有些類似,但MD2的設計與MD4和MD5徹底不一樣,那是由於MD2是爲8位機器作過設計優化的,而MD4和MD5倒是面向32位的電腦。這三個算法的描述和C語言源代碼在Internet RFCs 1321中有詳細的描述,這是一份最權威的文檔,由Ronald L.Rivest在1992年8月向IETF提交。
(一)消息摘要簡介
一個消息摘要就是一個數據塊的數字指紋。即對一個任意長度的一個數據塊進行計算,產生一個惟一指印(對於SHA1是產生一個20字節的二進制數組)。消息摘要是一種與消息認證碼結合使用以確保消息完整性的技術。主要使用單向散列函數算法,可用於檢驗消息的完整性,和經過散列密碼直接以文本形式保存等,目前普遍使用的算法由MD四、MD五、SHA-1.
消息摘要有兩個基本屬性:
1.兩個不一樣的報文難以生成相同的摘要
2.難以對指定的摘要生成一個報文,而能夠由改報文反推算出該指定的摘要
表明:美國國家標準技術研究所的SHA1和麻省理工學院Ronald Rivest提出的MD5
(二)對字符串進行加密java
package test; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import sun.misc.BASE64Encoder; /** * 對字符串進行加密 * @param str 待加密的字符串 * @return 加密後的字符串 * @throws NoSuchAlgorithmException 沒有這種產生消息摘要的算法 * @throws UnsupportedEncodingException */ public class Demo01 { public static String EncoderByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException{ //肯定算法 MessageDigest md5 = MessageDigest.getInstance("MD5"); BASE64Encoder base64en = new BASE64Encoder(); //加密後的字符串 String newstr = base64en.encode(md5.digest(str.getBytes("utf-8"))); return newstr; } public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException { String str = "0123456789"; System.out.println(EncoderByMd5(str)); } }
(三)驗證密碼是否正確linux
package test; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; /** * 判斷用戶密碼是否正確 * @param newpasswd 用戶輸入的密碼 * @param oldpasswd 數據庫中存儲的密碼--用戶密碼的摘要 * @return * @throws NoSuchAlgorithmException * @throws UnsupportedEncodingException * */ public class Demo02 { public static boolean checkpassword(String newpasswd, String oldpasswd) throws NoSuchAlgorithmException, UnsupportedEncodingException{ if (Demo01.EncoderByMd5(newpasswd).equals(oldpasswd)) { return true; } else { return false; } } public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException { System.out.println("old:"+Demo01.EncoderByMd5("123")); System.out.println("new:"+Demo01.EncoderByMd5("123")); System.out.println(checkpassword("123",Demo01.EncoderByMd5("123"))); } }
由於MD5是基於消息摘要原理的,消息摘要的基本特徵就是很難根據摘要推算出消息報文,所以要驗證密碼是否正確,就必須對輸入密碼(消息報文)從新計算其摘要,和數據庫中存儲的摘要進行對比(即數據庫中存儲的其實爲用戶密碼的摘要),若兩個摘要相同,則說明密碼正確,不一樣,則說明密碼錯誤。git
練習:算法
package test; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Java實現MD5加密算法(使用MessageDigest) * MD5加密算法,即"Message-Digest Algorithm 5 (信息-摘要算法)",它由MD二、MD三、 * MD4發展而來的一種單向函數算法(也就是HASH算法),它是國際著名的公鑰加密算法標準RSA的第一設計者 * R.Rivest於上個世紀90年代初開發而來的。MD5的最大做用在於,將不一樣格式的大容量文件信息在用數字簽名 * 軟件來簽署私人祕鑰前"壓縮"成一種保密格式,關鍵之處在於--這種"壓縮"是不可逆的。Java JDK已經自帶 * 了MD5的實現,只要簡單調用下就能夠。 * * @author Administrator * */ public class CreateMD5 { //靜態方法,便於工具類 public static String getMd5(String plainText){ try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(plainText.getBytes()); byte b[] = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } //32位加密 return buf.toString(); // 16位的加密 // return buf.toString().substring(8,24); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } } public static void main(String[] args) { //測試 System.out.println(CreateMD5.getMd5("hello")); } }
package test; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * 使用java獲取md5值的兩種方法 * 1.Message Digest Algorithm MD5 (中文名爲消息摘要算法第五版) * 爲計算機安全領域普遍使用的一種散列函數,是一種比較經常使用的哈希算法。 * 2.導入包:commons-codec * @author Administrator * */ public class md5_test { //MD5的字符串常量 private final static String[] hexDigits = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"}; public static void main(String[] args) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); System.out.println(byteArrayToHexString(messageDigest.digest("baidu.com".getBytes()))); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } private static String byteArrayToHexString(byte[] b) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) { resultSb.append(byteToHexString(b[i])); } return resultSb.toString(); } /** * 將一個字節轉化成十六進制形式的字符串 * * @param b * @return */ private static String byteToHexString(byte b) { int n = b; if (n < 0) n = 256 + n; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1]+hexDigits[d2]; } }
package test; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * MD5加密 * @author Administrator * */ public class MD5 { public static void main(String[] args) { System.out.println(MD5.getMD5("123456")); } /** * 用md5 編碼後的碼值 * * @param sInput * 明碼 * @return md5加密後的密碼 */ private static String getMD5(String sInput) { String algorithm =""; if (sInput == null) { return "null"; } try { algorithm = System.getProperty("MD5.algorithm","MD5"); } catch (SecurityException se) { } MessageDigest md = null; try { md = MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } byte buffer[] = sInput.getBytes(); for (int count = 0; count < sInput.length(); count++) { md.update(buffer,0,count); } byte bDigest[] = md.digest(); BigInteger bi = new BigInteger(bDigest); return (bi.toString(16)); } }
package test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.Security; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.commons.codec.digest.DigestUtils; import sun.applet.Main; import sun.awt.image.BytePackedRaster; /** * java計算過G文件md5 值計算 * * @author Administrator * */ public class Md5CaculateUtil { private Md5CaculateUtil(){ } private static char[] hexChar = { '0','1','2','3','4','5','6','7','8','9', 'a','b','c','d','e','f' }; public static String getHash(String fileName,String hashType) throws IOException, NoSuchAlgorithmException{ File f = new File(fileName); System.out.println("---------------------------------"); System.out.println("|當前文件名稱:"+f.getName()); System.out.println("|當前文件大小:"+f.length()/1024/1024+"MB"); System.out.println("|當前文件路徑[絕對]:"+f.getAbsolutePath()); System.out.println("|當前文件路徑[---]:"+f.getCanonicalPath()); System.out.println("---------------------------------"); InputStream ins = new FileInputStream(f); byte[] buffer = new byte[8192]; MessageDigest md5 = MessageDigest.getInstance(hashType); int len; while ((len = ins.read(buffer)) != -1) { md5.update(buffer, 0, len); } ins.close(); // 也能夠用apache自帶的計算MD5方法 return DigestUtils.md5Hex(md5.digest()); // 本身寫的轉計算MD5方法 // return toHexString(md5.digest()); } public static String getHash2(String fileName){ File f = new File(fileName); return String.valueOf(f.lastModified()); } protected static String toHexString(byte[] b){ StringBuilder sb = new StringBuilder(b.length*2); for (int i = 0; i < b.length; i++) { sb.append(hexChar[(b[i] & 0xf0) >>> 4]); sb.append(hexChar[b[i] & 0x0f]); } return sb.toString(); } /** * 獲取MessageDigest支持幾種加密算法 */ @SuppressWarnings({"rawtypes","unchecked"}) private static String[] getCryptolmpls(String serviceType){ Set result = new HashSet(); // all prividers Provider[] providers = Security.getProviders(); for (int i = 0; i < providers.length; i++) { // get services provided by each provider Set keys = providers[i].keySet(); for (Iterator it = keys.iterator(); it.hasNext();) { String key = it.next().toString(); key = key.split(" ")[0]; if (key.startsWith(serviceType+".")) { result.add(key.substring(serviceType.length()+1)); } else if (key.startsWith("Alg.Alias."+serviceType+".")) { result.add(key.substring(serviceType.length(), 11)); } } } return (String[]) result.toArray(new String[result.size()]); } public static void main(String[] args) throws NoSuchAlgorithmException, IOException { // 調用方法 // String[] names = getCryptolmpls("MessageDigest"); // for(String name : names){ // System.out.println(name); // } long start = System.currentTimeMillis(); System.out.println("開始計算文件MD5值,請稍後..."); String fileName = "E:\\Office_2010_Tookit_2.2.3XiaZaiBa.zip"; // String fileName = "E:\\SoTowerStudio-3.1.0.exe"; String hashType = "MD5"; String hash = getHash(fileName, hashType); System.out.println("MD5"+hash); long end = System.currentTimeMillis(); System.out.println("一共耗時:"+(end-start)+"毫秒"); } }
package test; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import exception.SpeedException; /** * Java三行代碼搞定MD5加密 * * 對字符串md5加密 * * @param str * @return * */ public class MD5Demo { public static String getMD5(String str) throws SpeedException{ try { // 生成一個MD5加密計算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); // 計算md5函數 md.update(str.getBytes()); // digest()最後肯定返回md5 hash值,返回值爲字符串。由於md5 hash值是16位的hex值,實際上就是8位的字符 // BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示,獲得字符串形式的hash值 return new BigInteger(1,md.digest()).toString(16); } catch (Exception e) { throw new SpeedException("MD5加密出現錯誤"); } } public static void main(String[] args) throws SpeedException { System.out.println(getMD5("123"));; } }
package test; import java.security.MessageDigest; /** * 利用Java自帶的MD5加密 * * @author Administrator * */ public class MD5Util { public final static String MD5(String s){ char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; try { byte[] btInput = s.getBytes(); //得到MD5摘要算法的 MessageDigest 對象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); //使用指定的字節更新摘要 mdInst.update(btInput); //得到密文 byte[] md = mdInst.digest(); //把密文轉換成十六進制的字符串形式 int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } public static void main(String[] args) { System.out.println(MD5Util.MD5("20121221")); System.out.println("加密"); } }
package test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigInteger; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; /** * Java讀取文件MD5的兩種方案 * 1.MessageDigest實現 * 2.org.apache.commons.codec.digest實現 * * @author Administrator * */ public class testMD5 { public static String getMd5ByFile(File file) throws FileNotFoundException{ String value = null; FileInputStream in = new FileInputStream(file); try { MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()); MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(byteBuffer); BigInteger bi = new BigInteger(1,md5.digest()); value = bi.toString(16); } catch (Exception e) { e.printStackTrace(); } finally { if (null != in) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } return value; } public static void main(String[] args) throws IOException { String path ="E:\\commons-codec-1.10-bin.zip"; String v = getMd5ByFile(new File(path)); System.out.println("MD5:"+v.toUpperCase()); FileInputStream fis = new FileInputStream(path); String md5 = DigestUtils.md5Hex(IOUtils.toByteArray(fis)); IOUtils.closeQuietly(fis); System.out.println("MD5:"+md5); // System.out.println("MD5"+DigestUtils.md5Hex("WANGQIUYUN")); } }
package test; import org.apache.commons.codec.digest.DigestUtils; public class ToMain { public static void main(String[] args) { System.out.println(DigestUtils.md5Hex("baidu.com")); } }
package exception; public class SpeedException extends Exception { public SpeedException(String msg) { super(msg); } }
基本的java加密算法MD5等等
簡單的java加密算法有:
BASE64 嚴格地說,屬於編碼格式,而非加密算法
MD5 (Message Digest algorithm 5,信息摘要算法)
SHA (Secure Hash Algorithm,安全散列算法)
HMAC (Hash Message Authentication Code, 散列消息鑑別碼)
Java中4大基本加密算法解析
1.BASE64
Base64是網絡上最多見的用於傳輸8Bit字節代碼的編碼方式之一,你們能夠查看RFC2045~RFC2049,上面有MIME的詳細規範。Base64編碼可用於在HTTP環境下傳遞較長的標識信息。例如,在Java Persistence系統Hibernate中,就採用了Base64來將一個較長的惟一標識符(通常爲128-bit的UUID)編碼爲一個字符串,用做HTTP表單和HTTP GET URL中的參數。在其餘應用程序中,也經常須要把二進制數據編碼爲適合放在URL(包括隱藏表單域)中的形式。此時,採用Base64編碼具備不可讀性,即所編碼的數據不會被人用肉眼所直接看到。
代碼實現:數據庫
package com.cn.單向加密; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * BASE64的加密解密是雙向的,能夠求反解。 * BASE64Encoder和BASE64Decoder是非官方JDK實現類。雖然能夠在JDK裏能找到 * 並使用,可是在API裏查不到。 * JRE中sun和com.sun 開頭包的類都是未被文檔化的,他們屬於java,javax類庫的基礎, * 其中的實現大多數與底層平臺有關,通常來講是不推薦使用的。 * BASE64嚴格地說,屬於編碼格式,而非加密算法 * 主要就是BASE64Encoder、BASE64Decoder兩個類,咱們只須要直到使用對應的方法便可。 * 另外,BASE加密後產生的字節位數是8的倍數,若是不夠位數以=符號填充。 * BASE64 * 按照RFC2045的定義,Base64被定義爲:Base64內容傳送編碼被設計用來把任意序列的8位 * 字節描述爲一種不易被人直接識別的形式 * (The Base64 Content-Transfer-Encoding is designed to represent * arbitrary sequences of octets in a form that need not be humanly * readable.) * 常見於郵件、http加密,截取http信息,你就會發現登陸操做的用戶名、密碼字段經過BASE64加密的。 * * @author Administrator * */ public class BASE64 { /** * BASE64解密 * * @param key * @return * @throws Exception */ public static byte[] decryptBASE64(String key) throws Exception{ return (new BASE64Decoder()).decodeBuffer(key); } /** * BASE64加密 * * @param key * @return * @throws Exception */ public static String encryptBASE64(byte[] key)throws Exception{ return (new BASE64Encoder()).encodeBuffer(key); } public static void main(String[] args) { String str = "12345678"; try { String result1 = BASE64.encryptBASE64(str.getBytes()); System.out.println("result1=====加密數據=====>> "+result1); byte result2[] = BASE64.decryptBASE64(result1); String str2 = new String(result2); System.out.println("str2=====解密數據=====>> "+str2); } catch (Exception e) { e.printStackTrace(); } } }
2.MD5
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用於確保信息傳輸完整
一致。是計算機普遍使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程
語言廣泛已有MD5實現。將數據(如漢字)運算爲另外一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD二、MD3和MD4.普遍用於加密和解密技術,經常使用於文件校驗。
校驗?無論文件多大,通過MD5後都能生成惟一的MD5值。比如如今的ISO校驗,都是
MD5校驗。怎麼用?固然是把ISO通過MD5後產生MD5的值。通常下載linux-ISO的朋友
都見過相愛在鏈接叛變放着的MD5的串。就是用來驗證是否一致的。
代碼實現:apache
package com.cn.單向加密; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * MD5(Message Digest algorithm 5,信息摘要算法) * 一般咱們不直接使用上述MD5加密。 * 一般將MD5產生的字節數組交給BASE64再加密一把,獲得相應的字符串 * * Digest:彙編 * */ public class MD5 { public static final String KEY_MD5 = "MD5"; public static String getResult(String inputStr){ System.out.println("=======加密前的數據:"+inputStr); BigInteger bigInteger = null; try { MessageDigest md = MessageDigest.getInstance(KEY_MD5); byte[] inputData = inputStr.getBytes(); md.update(inputData); bigInteger = new BigInteger(md.digest()); } catch (Exception e) { e.printStackTrace(); } System.out.println("MD5加密後:"+bigInteger.toString(16)); return bigInteger.toString(16); } public static void main(String[] args) { try { String inputStr = "簡單加密8888888888888888888"; getResult(inputStr); } catch (Exception e) { e.printStackTrace(); } } }
MD5算法具備如下特色:
1.壓縮性:任意長度的數據,算出的MD5值長度都是固定的。
2.容易計算:從原數據計算出MD5值很容易。
3.抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所獲得的MD5值都有很大區別。
4.弱抗碰撞:已知原數據和其MD5值,想到一個具備相同MD5值的數據(即僞造數據)是很是困難的。
5.強抗碰撞:想找到兩個不一樣的數據,使它們具備相同的MD5值,是很是困難的。
MD5的做用是讓大容量信息在用數字簽名軟件簽署私人祕鑰前被"壓縮"成一種保密格式(就是把任意長度的字節串變換成必定長的十六進制數字串)。除了MD5之外,其中比較有名的還有sha-一、RIPEMD以及Haval等。
3.SHA
安全哈希算法(Secure Hash Algorithm)主要適用於數字簽名標準(Digital Signature Standard DSS)裏面定義的數字簽名算法(Digital Signature Algorithm DSA)。對於長度小於2~64位的消息,SHA1會產生一個160位的消息摘要。該算法通過加密專家多年來的發展和改進已日益完善,並被普遍使用。該算法的思想是接收一段明文,而後以一種不可逆的方式將它轉換成一段(一般更小)密文,也能夠簡單的理解爲取一串輸入碼(稱爲預映射或信息),並把它們轉化爲長度較短、位數固定的輸出序列即散列值(也稱爲信息摘要或信息認證代碼)的過程。散列函數值能夠說是對明文的一種"指紋"或是"摘要"因此對散列值的數字簽名就能夠視爲對此明文的數字簽名。
java實現:編程
package com.cn.單向加密; import java.math.BigInteger; import java.security.MessageDigest; /** * * SHA(Secure Hash Algorithm,安全散列算法),數字簽名等密碼學應用中重要的工具, * 被普遍地應用與電子商務等信息安全領域。雖然,SHA與MD5經過碰撞法都被破解了, * 可是SHA仍然是公認的安全加密算法,較之MD5更爲安全 * * @author Administrator * */ public class SHA { public static final String KEY_SHA = "SHA"; public static String getResult(String inputStr){ BigInteger sha = null; System.out.println("=======加密前的數據:"+inputStr); byte[] inputData = inputStr.getBytes(); try { MessageDigest messageDigest = MessageDigest.getInstance(KEY_SHA); messageDigest.update(inputData); sha = new BigInteger(messageDigest.digest()); System.out.println("SHA加密後:"+sha.toString(32)); } catch (Exception e) { e.printStackTrace(); } return sha.toString(32); } public static void main(String[] args) { try { String inputStr = "簡單加密"; getResult(inputStr); } catch (Exception e) { e.printStackTrace(); } } }
SHA-1與MD5的比較
由於兩者均由MD4導出,SHA-1和MD5彼此很類似。相應的,他們的強度和其餘特性也是類似,但還有如下幾點不一樣:
| 對強行攻擊的安全性:最顯著和最重要的區別是SHA-1摘要比MD5摘要長32位。使用強行技術,產生任何一個報文使其摘要等於給定報摘要的難度對MD5是2^128數量級的操做,而對SHA-1則是2^160數量級的操做。這樣,SHA-1對強行攻擊有更大的強度。
| 對密碼分析的安全性:因爲MD5的設計,易受密碼分析的攻擊,SHA-1顯得不易受這樣的攻擊。
| 速度:在相同的硬件上,SHA-1的運行速度比MD5慢。
4.HMAC(Hash Message Authentication Code),散列消息鑑別碼,基於祕鑰的Hash算法的認證協議。消息鑑別實現鑑別的原理是,用公開函數和祕鑰產生一個固定長度的值做爲認證標識,用這個標識鑑別消息的完整性。使用一個祕鑰生成一個固定大小的小數據塊,即MAC,並將其加入到消息中,而後傳輸。接收方利用與發送方共享的祕鑰進行鑑別認證等。
中文名"散列消息鑑別碼",主要是利用哈希算法,以一個祕鑰和一個消息爲輸入,生成一個消息照耀做爲輸出。通常的,消息鑑別碼用於驗證傳輸於兩個共同想有一個祕鑰的單位之間的消息。HMAC能夠與任何迭代散列函數捆綁使用。MD5和SHA-1就是這種散列函數。HMAC還可使用一個用於計算和確認消息鑑別值的祕鑰。
HMAC,散列消息鑑別碼,是基於祕鑰的Hash算法的認證協議。它的實現原理是,用公開函數和祕鑰產生一個固定長度的值做爲認證標識,用這個標識鑑別消息的完整性。使用一個祕鑰生成一個固定大小的小數據塊,即MAC,並將其加入到消息中,而後傳輸。接收方利用與發送方共享的祕鑰進行鑑別認證等。
這種結構的主要做用是:
不用修改既可使用適合的散列函數,並且散列函數在軟件方面表現的很好,而且源碼是公開的通用的。
能夠保持散列函數原有的性能而不致其退化。
可使得基於合理的關於底層散列函數假設的消息鑑別機制的加密強度分析,便於理解。
當發現或須要運算速度更快或更安全的散列函數時,能夠很容易的實現底層散列函數的替換。
定義HMAC須要一個加密用散列函數(表示H)和一個祕鑰K。咱們假設H是一個將數據塊用一個基本的迭代壓縮函數來加密的散列函數。咱們用B來表示數據塊的字長。(以上提到的散列函數的分割數據塊字長B=64),用L來表示散列函數的輸出數據字長(MD5中L=16,SHA-1中L=20)。鑑別祕鑰的長度能夠是小於等於數據塊字長的任何正整數值。應用程序中使用的祕鑰長度如果比B大,則首先使用散列函數H做用與它,而後用H輸出的L長度字符串做爲在HMAC中實際使用的祕鑰。通常狀況下,推薦的最小祕鑰K長度是L個字長。(與H的輸出數據長度相等)。
咱們將定義兩個固定且不一樣的字符串ipad,opad:('i','o'表示內部與外部)
ipad = the byte 0x36 repeated B times
opad = the byte 0x5C repeated B times
計算'text' 的HMAC;
H(K XOR opad,H(K XOR ipad, text))
計算步驟:
在祕鑰K後面添加0建立一個字長爲B的字符串。(例如,若是K的字長是20字節,則K後會加入44個零字節0x00)
將上一步生成的B字長的字符串與ipad做疑惑運算
將數據流text填充至第二步的結果字符串中
用H做用於第三步生成的數據流
將第一步生成的B字長字符串與opad做異或運算
再將第四步的結果填充進第五步的結果中
用H做用於第六步生成的數據流,輸出最終結果
祕鑰
用於HMAC的祕鑰能夠是任意長度(比B長的祕鑰將首先被H處理)。但當祕鑰長度小於L時,會下降函數的安全強度。長度大於L的祕鑰也是能夠的,但額外的長度並不能顯著的提升函數的安全強度。
祕鑰必須隨機選取(或使用強大的基於隨機種子的僞隨機生成方法),而且要週期性的更新。目前的攻擊沒有指出一個有效的更換祕鑰的頻率,由於那些攻擊實際上並不可行。然而,週期性更新祕鑰時一個對付函數和祕鑰所存在的潛在缺陷的基本安全措施,並能夠下降泄露祕鑰帶來的危害。數組
java實現代碼:安全
package com.cn.單向加密; import java.security.NoSuchAlgorithmException; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import com.google.common.base.Strings; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * HMAC * HMAC(Hash Message Authentication Code),散列消息鑑別碼, * 基於祕鑰的Hash算法的認證協議。 * 消息鑑別碼實現鑑別的原理是,用公開的函數和祕鑰產生一個固定長度的值做爲認證標識, * 用這個標識鑑別消息的完整性。 * 使用一個祕鑰生成一個固定大小的小數據塊, * 即MAC,並將其加入到消息中,而後傳輸。接收方利用與發送方共享的祕鑰進行鑑別認證等 * * */ /** * 定義加密方式 * MAC算法可選如下多種算法 * HmacMD5 * HmacSHA1 * HmacSHA256 * HmacSHA384 * HmacSHA512 */ public class HMAC { private final static String KEY_MAC = "HmacMD5"; /** * 全局數組 */ private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; /** * 構造函數 */ public HMAC() { } /** * BASE64 加密 * @param key 須要加密的字節數組 * @return 字符串 * @throws Exception */ public static String encryptBase64(byte[] key) throws Exception { return (new BASE64Encoder()).encodeBuffer(key); } /** * BASE64 解密 * @param key 須要解密的字符串 * @return 字節數組 * @throws Exception */ public static byte[] decryptBase64(String key) throws Exception { return (new BASE64Decoder()).decodeBuffer(key); } /** * 初始化HMAC密鑰 * @return */ public static String init() { SecretKey key; String str = ""; try { KeyGenerator generator = KeyGenerator.getInstance(KEY_MAC); key = generator.generateKey(); str = encryptBase64(key.getEncoded()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return str; } /** * HMAC加密 * @param data 須要加密的字節數組 * @param key 密鑰 * @return 字節數組 */ public static byte[] encryptHMAC(byte[] data, String key) { SecretKey secretKey; byte[] bytes = null; try { secretKey = new SecretKeySpec(decryptBase64(key), KEY_MAC); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); bytes = mac.doFinal(data); } catch (Exception e) { e.printStackTrace(); } return bytes; } /** * HMAC加密 * @param data 須要加密的字符串 * @param key 密鑰 * @return 字符串 */ public static String encryptHMAC(String data, String key) { if (Strings.isNullOrEmpty(data)) { return null; } byte[] bytes = encryptHMAC(data.getBytes(), key); return byteArrayToHexString(bytes); } /** * 將一個字節轉化成十六進制形式的字符串 * @param b 字節數組 * @return 字符串 */ private static String byteToHexString(byte b) { int ret = b; //System.out.println("ret = " + ret); if (ret < 0) { ret += 256; } int m = ret / 16; int n = ret % 16; return hexDigits[m] + hexDigits[n]; } /** * 轉換字節數組爲十六進制字符串 * @param bytes 字節數組 * @return 十六進制字符串 */ private static String byteArrayToHexString(byte[] bytes) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { sb.append(byteToHexString(bytes[i])); } return sb.toString(); } /** * 測試方法 * @param args */ public static void main(String[] args) throws Exception { String key = HMAC.init(); System.out.println("Mac密鑰:\n" + key); String word = "123"; System.out.println(encryptHMAC(word, key)); } }