为何SHA-256消息摘要在Java与j2objc环境下结果不一致?
问题分析与解决
这不是你的操作失误,而是j2objc的jre_emul库中MessageDigest的实现和原生Java SE在digest(byte[] buf, int offset, int len)方法的行为上存在差异导致的。
先拆解原生Java代码的执行逻辑
你的代码在原生Java 8中的行为完全符合规范:
- 创建SHA-256的
MessageDigest实例,更新64个空字节; - 调用
md.digest():计算64个空字节的SHA-256哈希,返回结果存入tmp,同时重置MessageDigest实例的状态; - 调用
md.digest(tmp, 0, tmp.length):此时md已经被重置,没有任何待计算的输入数据,因此会计算空输入的SHA-256哈希,并将结果写入tmp数组中; - 最终输出的是空输入SHA-256的Base64编码,也就是你看到的
47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=,这个值是标准的空输入SHA-256哈希结果。
j2objc输出不同的原因
j2objc的jre_emul库对MessageDigest.digest(byte[] buf, int offset, int len)的实现没有严格遵循Java规范:
- 它可能错误地将传入的
tmp数组当作输入数据而非输出缓冲区,也就是调用这个方法时,实际计算的是tmp数组(即64个空字节的SHA-256结果)的哈希值,而非空输入的哈希; - 也有可能是
md.digest()方法没有正确重置MessageDigest的状态,导致后续调用digest(buf, offset, len)时,继续基于之前的64个空字节状态计算,再叠加tmp的数据,最终得到不同的结果。
兼容Java和j2objc的修正方案
避免使用行为不一致的digest(byte[] buf, int offset, int len)方法,改用更明确、兼容的写法来实现你的需求:
import java.security.DigestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class TestScrypt { public static void main(String[] args) throws NoSuchAlgorithmException, DigestException { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(new byte[64]); byte[] tmp = md.digest(); // 改用明确的重置+计算+复制逻辑,替代digest(buf, offset, len) md.reset(); byte[] emptyHash = md.digest(); System.arraycopy(emptyHash, 0, tmp, 0, emptyHash.length); System.out.println("Result:" + Base64.getEncoder().encodeToString(tmp)); } }
这个写法在原生Java和j2objc编译后都会输出相同的结果,因为它完全规避了有行为差异的方法,用更直观的步骤实现了覆盖tmp数组的逻辑。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

