新平台开发的系统和老系统混合使用,遇到个烦人问题。老系统对字符串md5和新系统算的不一样。老系统是很近以前开发的,开发商早没影了,只好反编译,得到算法如下:
public static String Md5(String input){ MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes()); BigInteger bi = new BigInteger(digest); return bi.toString(16); }这段代码的缺陷:
new BigInteger(digest)会把 MD5 字节数组转成有符号整数,结果可能带负号- 不会自动补全前导零,导致长度不固定(不是标准 32 位)
新系统是前端做md5发往后端验证,代码如下:
import md5 from 'js-md5';
md5(data)
这种写法会导致两端计算结果不一样。修正如下:
function eHRMd5(str) { // 1. 用js-md5拿到标准字节数组 const bytes = md5.digest(str); // 2. 转Java风格有符号BigInteger const bi = bytesToJavaSignedHex(bytes); // 3. 输出和后端一致的结果 return bi.toString(16); } // 【纯数字实现】无 8n、无 0n、无任何 BigInt 语法! function bytesToJavaSignedHex(bytes) { let hex = ''; const negative = (bytes[0] & 0x80) !== 0; if (negative) { // 负数:补码计算 let temp = []; let carry = 1; for (let i = bytes.length - 1; i >= 0; i--) { let val = 255 - bytes[i] + carry; carry = val > 255 ? 1 : 0; temp[i] = val & 255; } hex = bytesToHex(temp); hex = '-' + hex.replace(/^0+/, ''); // 去前导零 + 负号 } else { // 正数 hex = bytesToHex(bytes).replace(/^0+/, ''); } return hex || '0'; } // 字节数组转十六进制 function bytesToHex(bytes) { return Array.from(bytes, b => (b & 0xff).toString(16).padStart(2, '0') ).join(''); }