加密演算法簡要介紹與JAVA實現
【1】MD5是什麼
MD5即Message-Digest Algorithm 5(資訊-摘要演算法5),用於確保資訊傳輸完整一致。是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法),主流程式語言普遍已有MD5實現。將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,MD5的前身有MD2、MD3和MD4。
該演算法的檔案號為:
RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)
。
MD5的作用是讓大容量資訊在用數字簽名軟體簽署私人金鑰前被”壓縮”成一種保密的格式(就是把一個任意長度的位元組串變換成一定長的十六進位制數字串)。除了MD5以外,其中比較有名的還有sha-1、RIPEMD以及Haval等。
對MD5演算法簡要的敘述可以為:MD5以512位分組來處理輸入的資訊,且每一分組又被劃分為16個32位子分組,經過了一系列的處理後,演算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位雜湊值。
128位雜湊值轉換為16進位制字串為32個字元。即,加密後的結果為32位16進位制的字串。
經典的雜湊(雜湊)演算法還有:MD2、MD4 和 SHA-1(目的是將任意長輸入通過演算法變為固定長輸出,且保證輸入變化一點輸出都不同,且不能反向解密)。
【2】MD5的特點
① 長度固定
不管多長的字串,加密後長度都是一樣長,方便平時資訊的統計和管理。
② 容易計算
從原資料計算出MD5值很容易。
③ 抗修改性
對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
④ 強抗碰撞
已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的(並非不可以)。
【3】MD5加密過程
第一種,加密字串
① 獲取資訊摘要物件:md5
通過資訊摘要單例的建構函式獲取:
MessageDigest md5 = MessageDigest.getInstance("MD5");
② 獲取摘要位元組陣列
兩種方式:
byte[] bytes = str.getBytes();
byte [] digest = md5.digest(bytes);
或
byte[] bytes = str.getBytes();
md5.update(bytes);
byte[] digest = md5.digest();
③ 把摘要陣列中的每一個位元組轉換成16進位制,並拼在一起就得到了MD5值.
第二種,加密檔案
方法傳入的是檔案物件 : file
① 因為是檔案不是方法,所以不是像剛才那樣通過摘要獲取字串。
② 使用到另一個方法即可:就是資訊摘要物件更新:md5.update(byte[] input)方法,用法是通過讀取流,不斷的更新從流中讀到的”資訊陣列”。
③ 然後通過”資訊摘要物件”獲取摘要,不用引數:md5.digest(),此時返回的陣列就已經是包含內容的摘要陣列了。
④ 把摘要陣列中的每一個位元組轉換成16進位制,並拼在一起就得到了MD5值。
同樣來源除了檔案還可以是url,過程同上。
【4】MD5工具類
package com.web.encrypt;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
// 用來將位元組轉換成 16 進製表示的字元
private static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd','e', 'f' };
protected static MessageDigest messagedigest = null;
static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsaex) {
System.err.println(MD5Util.class.getName()
+ "初始化失敗,MessageDigest不支援MD5Util。");
nsaex.printStackTrace();
}
}
// 加密字串
public static String getMD5(String str){
byte[] bs = str.getBytes();
return getMD5FromByte(bs);
}
private static String getMD5FromByte(byte[] source) {
String s = null;
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
md.update(source);
byte tmp[] = messagedigest.digest();
// MD5 的計算結果是一個 128 位的長整數(16個位元組),轉為16進位制則為32個字元
char str[] = new char[16 * 2];
// 每個位元組用 16 進製表示的話,使用兩個字元(一個8位->2個4位->2個16進位制字元),所以表示成 16 進位制需要 32 個字元
int k = 0; // 表示轉換結果中對應的字元位置
for (int i = 0; i < 16; i++) {
// 從第一個位元組開始,對 MD5 的每一個位元組
// 轉換成 16 進位制字元的轉換
byte byte0 = tmp[i]; // 取第 i 個位元組
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
// 取位元組中高 4 位的數字轉換,>>>為邏輯右移,將符號位一起右移
str[k++] = hexDigits[byte0 & 0xf];
// 取位元組中低 4 位的數字轉換
}
s = new String(str);
// 換後的結果轉換為字串
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
// 加密檔案物件
public static String getFileMD5String(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 String getFileMD5String(String filePath) throws FileNotFoundException {
String value = null;
File file=new File(filePath);
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;
}
//引數為url
public static String getFileMD5StringByURL(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
BufferedInputStream fis = null;
fis = new BufferedInputStream(connection.getInputStream());
byte[] buffer = new byte[1024];
int numRead = 0;
while ((numRead = fis.read(buffer)) > 0) {
messagedigest.update(buffer, 0, numRead);
}
fis.close();
return bufferToHex(messagedigest.digest());
}
//引數為位元組陣列
public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}
// 將位元組陣列轉換為16進位制的字串
private static String bufferToHex(byte bytes[]) {
int m = 0;
int n = bytes.length;
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}
private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
char c0 = hexDigits[(bt & 0xf0) >> 4];
// 取位元組中高 4 位的數字轉換, >>> 為邏輯右移,將符號位一起右移
char c1 = hexDigits[bt & 0xf];
// 取位元組中低 4 位的數字轉換
stringbuffer.append(c0);
stringbuffer.append(c1);
}
}