平常使用MD5加密時,自己封裝處理時,常見有2個問題:
1、轉換爲字符串時,高位的0被捨去;
2、出現負數時會有多個連續的F;
這是因爲MessageDigest返回的結果是無符號數的byte數組,所以一個byte表示2位的十六進制數時,高位可能爲0,而且在JAVA中byte默認是按有符號數的來讀取的,轉換時會出現負數。
public class MD5Utils {
private static final char[] chs =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static String getMD5(String str) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException();
}
byte[] bys = messageDigest.digest(str.getBytes());
StringBuilder sb = new StringBuilder(32);
int index;
for (byte b : bys) {
index = b & 0xff;//將負數byte按無符號數讀取
sb.append(chs[index >> 4]);//16進制數的高位
sb.append(chs[index % 16]);//16進制數的低位
}
return sb.toString();//返回的結果爲32位的16進制數字符串
}
}
其中,因爲MessageDigest返回的結果是用無符號數來表示的,而在JAVA中默認是採用最高位來表示正負的,所以使用了位運算的方式來截取該無符號數:
index = b & 0xff;
等價於:
index = b;
if(index < 0 ){
index += 256;
}
一個byte可以表示2位的16進制數,採用Integer.toHexString(index)轉換時,若小於16會只有1位16進制數。繼續使用位運算+查表法來獲取16進制數:
sb.append(chs[ index >> 4]);
sb.append(chs[ index % 16]);
等價於: if(index < 16){
sb.append('0');
}
sb.append(Integer.toHexString(index));
因爲沒有使用Integer的toHexString(index)和toString(int i, int radix)方法,而是自己重新實現了,所以效率有所提高。