Loading

关于 MD5 加密中 ByteToHex 操作使用位运算的原理

先上一段使用 MD5 加密的代码

MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytesArr1 = txt.getBytes(StandardCharsets.UTF_8);
byte[] bytesArr2 = md5.digest(bytesArr1);
StringBuilder encrypt = new StringBuilder("");
for (byte temp : bytesArr2) {
    encrypt.append(Integer.toHexString((temp & 0x000000FF) | 0xFFFFFF00).substring(6));
}
return encrypt.toString();
  1. 这里有这样一段代码: (temp & 0x000000FF) | 0xFFFFFF00, 根据运算符优先级其实它等同于 temp & 0x000000FF | 0xFFFFFF00
  2. 这段代码的意义就是 ByteToHex 的核心, 根据代码上下文可知 temp 是遍历 md5.digest() 返回值的 byte 类型临时变量, 其总长度为 16, 而下面的 for 循环就是将这个长度为 16 的 byte 数组转换为 hex 字符串
  3. 转换的规则为: 如果 byte 的值大于 15 则直接将其 16 进制形式的文本追加到 hex 字符串上, 否则先在 hex 字符串上追加一个 0 后追加到 hex 字符串上(其实等同于 temp/16, temp%16 两位数字, 前提是 temp 必须为正数)
  4. 上述转换规则的原因是: 一个 byte 数据为 8 个二进制位, 而一个 hex 数据(如: f)最大占用 4 个二进制位, 因此将一个 byte 数据对照为一个两位 hex 数据, 不足两位则在前面补 0, 从而使得 MD5 的返回值为 32 位
  5. 代码 Integer.toHexString((temp & 0x000000FF) | 0xFFFFFF00).substring(6) 就是上述内容的具体实现

补充内容:
在以模为 256 的系统中, -13 == +243, 我们称 13243 这两个数在模为 256 的系统中互为补数, 如: 20 - 13 == 20 + 243 - 256 == 7

补数: 补数_百度百科

  1. temp & 0x000000FF 的目的是将 temp 为负数的数据转换为其对应符号为正的补数, 而 temp 为正的 byte 数据不受其影响(此时无论 temp 原本为正数还是负数, 其左侧 24 位都为 0)
  2. 再拿上一步的运算结果与 0xFFFFFF00 进行 或运算 使得其 HexString 的长度一定为 8 位(因为如果左侧为 0 则会被省略, 导致 byte 对应 hex 的长度可能为 2 位(值大于等于 16), 也有可能为 1 位(值小于 16)。而 0xFFFFFF00 保证了左侧 24 位都为 1, 即: HexString 左侧 6 位一定为 FFFFFF, 从而使得其 HexString 的长度一定为 8)
  3. 最终再使用 substring(6) 将左侧 6 位剪去, 这样就保证了在 byte 值小于 16 的数据前面进行补 0 操作。

Integer.toHexString((temp & 0x000000FF) | 0xFFFFFF00).substring(6) 等效于 Integer.toHexString((temp & 0x000000FF) | 0xF00).substring(1)

posted @ 2021-10-07 22:04  xtyuns  阅读(321)  评论(0编辑  收藏  举报