0

I'm having a trouble while I tried to encode a String in Java.

I have the follwing code in C#, and the string Bpz2Gjg01d7VfGfD8ZP1UA==, when I execute C# code I'm getting:

QnB6MkdqZzAxZDdWZkdmRDhaUDFVQT090

public static void Main(string[] args)
{
    string strWord = "Bpz2Gjg01d7VfGfD8ZP1UA==";
    byte[] encbuff = Encoding.UTF8.GetBytes(strWord);
    string strWordEncoded = HttpServerUtility.UrlTokenEncode(encbuff);
    Console.WriteLine(strWordEncoded);
}

I'm trying to replicate the previous code in Java, in the first attempt I used the javax.xml.bind.DatatypeConverter Class:

public static void main(String[] args) {
    String strWord = "Bpz2Gjg01d7VfGfD8ZP1UA==";
    byte[] encbuff = strWord.getBytes(StandardCharsets.UTF_8);
    String strWordEncoded = DatatypeConverter.printBase64Binary(encbuff);
    System.out.println(strWordEncoded);
}

But I'm getting the following String ( missing the last zero compared to C# string):

QnB6MkdqZzAxZDdWZkdmRDhaUDFVQT09

In my second attempt I used the BouncyCastle Base64 encoder:

public static void main(String[] args) {
   String strWord = "Bpz2Gjg01d7VfGfD8ZP1UA==";
   byte[] encbuff = strWord.getBytes(StandardCharsets.UTF_8);
   String strWordEncoded = new String(Base64.encode(encbuff));
   System.out.println(strWordEncoded);
}

But I'm getting the exact same previous String( still missing the last zero):

QnB6MkdqZzAxZDdWZkdmRDhaUDFVQT09

Does anyone know what may be happening?

TimeToCode
  • 901
  • 2
  • 16
  • 34
  • c# method that you are using might be using base64-url encoding. There are various of base64 encoding types and base64-url encoding is different than default base64 encoding. If you want to to use base64 encoding try Base64 form java.util in Java8. – Michał Krzywański Mar 29 '19 at 17:57
  • Also, the `UrlTokenEncode` method does some mapping of symbol chars from regular base64 encoding, so they can safely be used in urls, namely it replaces `+` with `-` and `/` with `_`. This mapping would also be necessary, if you need the exact functionality in Java. However, this does not apply in your case. – Christoph Herold Mar 29 '19 at 18:05
  • Also, if you use the standard .NET `Convert.ToBase64String(encbuff)` method, it will NOT include the extra 0 at the end, thus being the same as the Java equivalents. It looks like the `UrlTokenEncode` method is adding it for some reason. – Christoph Herold Mar 29 '19 at 18:08

2 Answers2

1

I've had a look at the .NET framework code. UrlTokenEncode actually removes any extra = padding symbols from the end of the base64 string and replaces them with the number of padding symbols, so either 0, 1, or 2. This is what's causing the extra 0 at the end of your string. So be aware: the HttpServerUtility.UrlTokenEncode method is NOT a plain Base64 encoder. It actually uses Convert.ToBase64String internally for the regular encoding and adds some more on top (see my comments on the question). If you need to create this exact string, you will need to implement the same changes in Java on top of the regular base64 encoding.

Christoph Herold
  • 1,799
  • 13
  • 18
1

I found a solution based on the comments made to me, basically I look at the source code of the method in the Reference Source of Microsoft.

Then I translated the C# code to Java code, and it looks like this:

public static String UrlTokenEncode(byte[] input) {
     try {
         if (input == null) {
         return null;
         }

         if (input.length < 1) {
             return null;
         }

         String base64Str = null;
         int endPos = 0;
         char[] base64Chars = null;

         base64Str = Base64.toBase64String(input);
         if (base64Str == null) {
             return null;
         }

         for (endPos = base64Str.length(); endPos > 0; endPos--) {
             if (base64Str.charAt(endPos - 1) != '=') {
                 break;
             }
         }

         base64Chars = new char[endPos + 1];
         base64Chars[endPos] = (char) ((int) '0' + base64Str.length() - endPos);
         for (int iter = 0; iter < endPos; iter++) {
             char c = base64Str.charAt(iter);
              switch (c) {
                 case '+':
                     base64Chars[iter] = '-';
                     break;
                 case '/':
                     base64Chars[iter] = '_';
                     break;
                 case '=':
                     base64Chars[iter] = c;
                 break;
                 default:
                     base64Chars[iter] = c;
                 break;
             }
          }
         return new String(base64Chars);
     } catch (Exception e) {
         return null;
     }
}

Finally I tested the method and I got the desire output:

public static void main(String[] args) {
   String strWord = "Bpz2Gjg01d7VfGfD8ZP1UA==";
   byte[] encbuff = strWord.getBytes(StandardCharsets.UTF_8);
   String strWordEncoded = UrlTokenEncode(encbuff);
}

M2NIclh4eEwxRGp2MEsyeFc0SHVDZz090

TimeToCode
  • 901
  • 2
  • 16
  • 34