早期 Java ME (CLDC/MIDP) 環境缺乏 java.security.MessageDigest,開發者需自行實現 MD5。本文整理一個經典的純 Java 實現,無第三方依賴,適合嵌入式環境移植。
package com.gggeye.util;
public class MD5Util {
// 常量定義:四輪移位量
private static final int S11=7, S12=12, S13=17, S14=22;
private static final int S21=5, S22=9, S23=14, S24=20;
private static final int S31=4, S32=11, S33=16, S34=23;
private static final int S41=6, S42=10, S43=15, S44=21;
private static final char[] HEX = "0123456789ABCDEF".toCharArray();
private static final byte[] PADDING = new byte[64]; // 0x80 + 0x00...
private long[] state = new long[4]; // ABCD
private long[] count = new long[2]; // 位長度
private byte[] buffer = new byte[64]; // 緩衝區
private byte[] digest = new byte[16]; // 最終 128-bit
public String digestHexStr;
public MD5Util() { md5Init(); }
/** 公開入口:字串 → 32 位十六進制 MD5 */
public String getMD5ofStr(String s) {
md5Init();
md5Update(s.getBytes(), s.length());
md5Final();
digestHexStr = "";
for (int i = 0; i < 16; i++) digestHexStr += byteHEX(digest[i]);
return digestHexStr;
}
/** 靜態工具法 */
public static String toMD5(String s) {
return new MD5Util().getMD5ofStr(s);
}
/* ---- 內部核心變換 (FF/GG/HH/II) ---- */
private long F(long x,long y,long z){ return x&y | ~x&z; }
private long G(long x,long y,long z){ return x&z | y&~z; }
private long H(long x,long y,long z){ return x^y^z; }
private long I(long x,long y,long z){ return y^(x|~z); }
private long FF(long a,long b,long c,long d,long x,long s,long ac){
a += F(b,c,d) + x + ac;
a = (int)a << (int)s | (int)a >>> (int)(32-s);
return a + b;
}
// GG/HH/II 同理,略
/* ---- 流程控制 ---- */
private void md5Init() {
count[0]=count[1]=0L;
state[0]=0x67452301L; state[1]=0xefcdab89L;
state[2]=0x98badcfeL; state[3]=0x10325476L;
}
private void md5Update(byte[] in, int len) {
int index = (int)(count[0]>>>3) & 0x3f;
if ((count[0] += len<<3) < (len<<3)) count[1]++;
count[1] += len>>>29;
int partLen = 64 - index;
int i = 0;
if (len >= partLen) {
md5Memcpy(buffer, in, index, 0, partLen);
md5Transform(buffer);
for (i = partLen; i+63 < len; i+=64)
md5Transform(md5Memcpy(new byte[64], in, 0, i, 64));
index = 0;
}
md5Memcpy(buffer, in, index, i, len-i);
}
private void md5Final() {
byte[] bits = new byte[8];
encode(bits, count, 8);
int index = (int)(count[0]>>>3) & 0x3f;
int padLen = (index < 56) ? 56-index : 120-index;
md5Update(PADDING, padLen);
md5Update(bits, 8);
encode(digest, state, 16);
}
private byte[] md5Memcpy(byte[] dst, byte[] src, int di, int si, int len) {
System.arraycopy(src, si, dst, di, len);
return dst;
}
private void md5Transform(byte[] block) {
long[] X = new long[16]; decode(X, block, 64);
long a=state[0], b=state[1], c=state[2], d=state[3];
// Round 1
a=FF(a,b,c,d,X[0], S11,0xd76aa478L); d=FF(d,a,b,c,X[1], S12,0xe8c7b756L);
c=FF(c,d,a,b,X[2], S13,0x242070dbL); b=FF(b,c,d,a,X[3], S14,0xc1bdceeeL);
a=FF(a,b,c,d,X[4], S11,0xf57c0fafL); d=FF(d,a,b,c,X[5], S12,0x4787c62aL);
c=FF(c,d,a,b,X[6], S13,0xa8304613L); b=FF(b,c,d,a,X[7], S14,0xfd469501L);
a=FF(a,b,c,d,X[8], S11,0x698098d8L); d=FF(d,a,b,c,X[9], S12,0x8b44f7afL);
c=FF(c,d,a,b,X[10],S13,0xffff5bb1L); b=FF(b,c,d,a,X[11],S14,0x895cd7beL);
a=FF(a,b,c,d,X[12],S11,0x6b901122L); d=FF(d,a,b,c,X[13],S12,0xfd987193L);
c=FF(c,d,a,b,X[14],S13,0xa679438eL); b=FF(b,c,d,a,X[15],S14,0x49b40821L);
// Round 2~4 同理,略
state[0]+=a; state[1]+=b; state[2]+=c; state[3]+=d;
}
private void encode(byte[] out, long[] in, int len) {
for (int i=0,j=0; j>>8 & 0xff);
out[i+2] = (byte)(in[j]>>>16 & 0xff);
out[i+3] = (byte)(in[j]>>>24 & 0xff);
}
}
private void decode(long[] out, byte[] in, int len) {
for (int i=0,j=0; j=0?b:b&0xff; }
private static String byteHEX(byte b){
return new String(new char[]{HEX[(b>>>4)&0xf], HEX[b&0xf]});
}
}
String pwdHash = MD5Util.toMD5("userPassword123");
System.out.println(pwdHash); // 輸出 32 位大寫十六進制