Java: convert byte[] to base36 String
Asked Answered
A

2

8

I'm a bit lost. For a project, I need to convert the output of a hash-function (SHA256) - which is a byte array - to a String using base 36.

So In the end, I want to convert the (Hex-String representation of the) Hash, which is

43A718774C572BD8A25ADBEB1BFCD5C0256AE11CECF9F9C3F925D0E52BEAF89

to base36, so the example String from above would be:

3SKVHQTXPXTEINB0AT1P0G45M4KI8U0HR8PGB96DVXSTDJKI1

For the actual conversion to base36, I found some piece of code here on StackOverflow:

public static String toBase36(byte[] bytes) {
    //can provide a (byte[], offset, length) method too
    StringBuffer sb = new StringBuffer();
    int bitsUsed = 0; //will point how many bits from the int are to be encoded
    int temp = 0;
    int tempBits = 0;
    long swap;
    int position = 0;

    while((position < bytes.length) || (bitsUsed != 0)) {
        swap = 0;
        if(tempBits > 0) {
            //there are bits left over from previous iteration
            swap = temp;
            bitsUsed = tempBits;
            tempBits = 0;
        }
        //fill some bytes
        while((position < bytes.length) && (bitsUsed < 36)) {
            swap <<= 8;
            swap |= bytes[position++];
            bitsUsed += 8;
        }
        if(bitsUsed > 36) {
            tempBits = bitsUsed - 36; //this is always 4
            temp = (int)(swap & ((1 << tempBits) - 1)); //get low bits
            swap >>= tempBits; //remove low bits
            bitsUsed = 36;
        }
        sb.append(Long.toString(swap, 36));
        bitsUsed = 0;
    }
    return sb.toString();
}

Now I'm doing this:

// this creates my hash, being a 256-bit byte array
byte[] hash = PBKDF2.deriveKey(key.getBytes(), salt.getBytes(), 2, 256);

System.out.println(hash.length); // outputs "256"
System.out.println(toBase36(hash)); // outputs total crap

the "total crap" is something like

-7-14-8-1q-5se81u0e-3-2v-24obre-73664-7-5-5cor1o9s-6h-4k6hr-5-4-rt2z0-30-8-2u-8-onz-4a2j-6-8-18-8trzza3-3-2x-6-4153to-4e3l01me-6-azz-2-k-4ckq-nav-gu-irqpxx-el-1j-6-rmf8hs-1bb5ax-3z25u-2-2r-t5-22-6-6w1v-1p

so it's not even close to what I want. I tried to find a solution now, but it seems I'm a bit lost here. How do I get the base36-encoded String representation of the Hash that I need?

Arbitrary answered 18/6, 2015 at 12:16 Comment(0)
P
9

Try using BigInteger:

String hash = "43A718774C572BD8A25ADBEB1BFCD5C0256AE11CECF9F9C3F925D0E52BEAF89";
//use a radix of 16, default would be 10 
String base36 = new BigInteger( hash, 16 ).toString( 36 ).toUpperCase();
Progestational answered 18/6, 2015 at 12:35 Comment(6)
@NateS please don't make edits that actually change an answer. If there are questions I encourage you to ask them via comments - in the case of your edit the constructor BigInteger(String, int) is visible. If that's not the case in your code please make sure you're using the right types (note that there's a BigInteger class provided by IBM which might lack that constructor).Progestational
This does not really convert the hash to a Base36 string. It only returns the string representation of the number in base 36. You need to further convert that big number to an actual Base36 string by doing long division with divisor = 36 and dividend = that numberHaloid
Check this to verify: ideone.com/wzD0gyHaloid
@RushilPaul you should read the JavaDoc carefully. On BigInteger.toString(radix) it states: "If the radix is outside the range from Character.MIN_RADIX (which is 2) to Character.MAX_RADIX (which is 36) inclusive,it will default to 10 (as is the case for Integer.toString)." - So your "proof" using toString(64) is actually converted to toString(10) and hence the output will be different.Progestational
Yes you're right. The first time I tried it with my custom division code, I was getting a different answer than what toString(36) was giving me, but that was because my baseCharset had letters first and digits later. The Java API places the digits first and letters later. So then I tried comparing the hash with the Base64 Java library and didn't notice this detail. Thanks for pointing it out.Haloid
For verification: ideone.com/X6OVRCHaloid
L
3

This might work:

BigInteger big = new BigInteger(your_byte_array_to_hex_string, 16);

big.toString(36);

Lacylad answered 18/6, 2015 at 12:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.