新平台开发的系统和老系统混合使用,遇到个烦人问题。老系统对字符串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)
这种写法会导致两端计算结果不一样。修正如下:
javascript
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('');
}