patternjavaMinor
Java conversion of base 16 to base 64
Viewed 0 times
conversionjavabase
Problem
I wrote a solution in Java to solve the problem here. I use
This seems quite inefficient to me, and I would have no idea how to do this if I couldn't use
```
import java.math.BigInteger;
public class Base64{
//according to RFC 4648 and RFC 2045
public final static char[] base64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
public final static char base64pad = '='; //unused
public static void main(String[] args){
String test = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
System.out.println( base16to64( test ) );
}
/**Return the base 64 value of an input base 16 string.
*/
public static String base16to64(String hex){
BigInteger value = base16to10(hex); //base 10 value
// --- convert base 10 to 64 ---
int digits = 1; // # digits in base 64 we'll need
while( value.compareTo(new BigInteger("64").pow(digits)) != -1 ){ //while the decimal value >= 64^i
digits++;
}
String ret = "";
for(int i = digits - 1; i >= 0; i--){ //start at most significant digit
//this digit must be an A (represents 0)
if( value.subtract(new BigInteger("64").pow(i)).compareTo(BigInteger.ZERO) == -1){
ret += "A";
}
else{
for(int d = 63; d >= 1; d--){
//digit found: [ value - (64^i)*d >= 0 ] with d the greatest possible
if( value.subtract(new BigInteger("64").pow(i).multiply(
new BigInteger(Integer.toString(d)))).compareTo(BigInteger.ZERO) != -1){
value = value.subtract(new BigInteger("64").pow(i).multiply( new BigInteger(Integer.
BigInteger to convert first from base 16 to base 10, then base 10 to base 64. This seems quite inefficient to me, and I would have no idea how to do this if I couldn't use
BigInteger. I'm sure there's a nice and clever way to do this using a lot more bit manipulation.```
import java.math.BigInteger;
public class Base64{
//according to RFC 4648 and RFC 2045
public final static char[] base64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
public final static char base64pad = '='; //unused
public static void main(String[] args){
String test = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
System.out.println( base16to64( test ) );
}
/**Return the base 64 value of an input base 16 string.
*/
public static String base16to64(String hex){
BigInteger value = base16to10(hex); //base 10 value
// --- convert base 10 to 64 ---
int digits = 1; // # digits in base 64 we'll need
while( value.compareTo(new BigInteger("64").pow(digits)) != -1 ){ //while the decimal value >= 64^i
digits++;
}
String ret = "";
for(int i = digits - 1; i >= 0; i--){ //start at most significant digit
//this digit must be an A (represents 0)
if( value.subtract(new BigInteger("64").pow(i)).compareTo(BigInteger.ZERO) == -1){
ret += "A";
}
else{
for(int d = 63; d >= 1; d--){
//digit found: [ value - (64^i)*d >= 0 ] with d the greatest possible
if( value.subtract(new BigInteger("64").pow(i).multiply(
new BigInteger(Integer.toString(d)))).compareTo(BigInteger.ZERO) != -1){
value = value.subtract(new BigInteger("64").pow(i).multiply( new BigInteger(Integer.
Solution
You are working too hard. Java has a Base64 class that does most of the work for you, all you need to do is combine it and the constructor for BigInteger, and you are done.
For the linked example input:
49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d
We get the correct output:
SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t
If you wanted to implement the base 64 conversion yourself, note that you are still doing too much work. Your
One function completely cut out.
However, if you don't want to use
So take the last
But noticing the "Cryptopals Rule":
Always operate on raw bytes, never on encoded strings. Only use hex and base64 for pretty-printing.
You should probably parse the hex string into a
public static String base16to64(String hex){
return Base64.getEncoder().encodeToString(new BigInteger(hex, 16).toByteArray());
}For the linked example input:
49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d
We get the correct output:
SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t
If you wanted to implement the base 64 conversion yourself, note that you are still doing too much work. Your
base16to10 function could be replaced withBigInteger value = new BigInteger(hex, 16);One function completely cut out.
However, if you don't want to use
BigInteger at all (this is generally a bad idea, but for programming practice, you might want to), notice that log(64)/log(16) == 1.5. Thus, for every 1.5 hex digits, we can obtain 1 base 64 digits.So take the last
2 hex digits of the hex string: 6d. In binary, this is 01101101. We only need to consider 101101, which is 45. So we take the 45th element of the base 64 alphabet: t. That's the last digit of the base 64 string. Now just shift the hex string over by 6 bits, and repeat (note, shifting by 6 bits won't be clean, so it's probably easier to instead process 3 hex digits to 2 base 64 digits, and take off the last two elements of the hex string).But noticing the "Cryptopals Rule":
Always operate on raw bytes, never on encoded strings. Only use hex and base64 for pretty-printing.
You should probably parse the hex string into a
byte[] or ArrayList before working on it. Honestly, I think this is way easier to do using BigInteger.Code Snippets
public static String base16to64(String hex){
return Base64.getEncoder().encodeToString(new BigInteger(hex, 16).toByteArray());
}BigInteger value = new BigInteger(hex, 16);Context
StackExchange Code Review Q#111630, answer score: 5
Revisions (0)
No revisions yet.