I'm encountering some weird behavior when I use Java's MessageDigest
to compute the SHA-256 hash of a BigInteger
. It appears that sometimes the hash value has 256 bits, but sometimes it has only 255 bits. This is the code that I'm using to test BigInteger hashing:
@Test
public void testSHA256LengthConsistent() {
MessageDigest sha256 = null;
try {
sha256 = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
Assert.fail("NoSuchAlgorithmException. Can't construct the MessageDigest.");
}
BigInteger[] tests = {new BigInteger("15902493"), new BigInteger("5189087324092341824"), new BigInteger("7153293421609183203421127438153268")};
for(BigInteger testNum : tests) {
byte[] hash = sha256.digest(testNum.toByteArray());
Assert.assertEquals(32, hash.length); //256 bits is 32 bytes
BigInteger hashedInt = new BigInteger(1, hash);
Assert.assertEquals(256, hashedInt.bitLength());
}
}
(Yes, I'm using JUnit 4). This test fails on the third test number, where the second assert fails with "Expected 256 but was 255."
Is there something wrong with the way I'm converting BigIntegers to and from byte arrays? All of the examples I can find for Java's MessageDigest use it to hash Strings, not BigIntegers, so I don't know if there's a "standard" way to use BigIntegers with MessageDigest. Alternatively, is this a bug or edge case in the way Java does SHA-256, and there's something with 7153293421609183203421127438153268 (a number I generated randomly) that results in an off-by-one error in the hash?
By the way, I've already tried converting the hash to a negative BigInteger (using new BigInteger(-1, hash)
) to see if it was a problem with the sign bit, but I get the exact same result.
Leading zeros are ignored
byte[] bytes = {0x0, 0x1};
System.out.println(new BigInteger(1, bytes).bitLength());
prints
1
not 16 as you appear to expect.
I should have read the Javadoc because it states in BigInteger.bitLength()
number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit.