Java and JavaScript truly universal Base64 coding details

  • 2020-05-19 04:47:53
  • OfStack

Java and JavaScript Base64 are encoded

When developing an Java Web application, you may use Java to encode Base64 on the server side and JavaScript to decode it on the client side. This requires the Base64 encoding mechanisms on both sides to remain 1 to 1.

With the Base64 code, you may encounter all kinds of strange situations, even suspect that the code has bug. But that's not the case. Base64 theoretically operates not on strings but on byte arrays. It works by reducing the 255 characters of the ASCII code to 64. In particular, the original 3 bytes were represented by 4 bytes, and the length increased by 1.

1) it is better to encode once and avoid segmental encoding. It is really necessary to encode piecewise. The number of bytes in each segment should be a multiple of 3.

Long byte stream, if you want to read and code at the same time, each segment must be a multiple of 3, otherwise it may be messy in the restore. 1 people like to define arrays with powers of 2, such as byte[1024], because they are not multiples of 3 and may be wrong in the restore. The correct example is:

byte[] bs=new byte[3*100] ....inputStream.read(bs)......encode(bs )....

For strings, 1 should be encoded once in a row to avoid segmentation errors.

Of course, if you segment the code and restore it one at a time, that's fine.

2) make sure the string is restored according to the original encoding.

Because it operates on a byte array, the result is different for GBK-encoded Chinese characters and UTF-8-encoded Chinese characters after Base64 encoding. For example, if the word "we" is GBK, Base64 is ztLDxw==; If the code is UTF-8, it will be 5oiR5Lus when converted to Base64.

That is, "we" == "getBytes" ("GBK") == > Base64

So whatever code is used to convert Java on this side, what code is used to restore JavaScript on this side. To ensure that Java and JavaScript are in common use, we use the code of Unicode (it is inconvenient to convert JavaScript into UTF-8 and GBK, so we use its own Unicode code), as follows:

Server-side:

1) convert getBytes("Unicode") into Unicode byte array.

2) encoded as Base64 string

3) send to the client

Client:

1) Base64 is decoded into byte array

2) press Unicode to restore

The code is as follows (see attachment for related functions) :

Base64. encode (data, "Unicode"); / / java coding

decode64 (data); / / javascript decoding

Attachment 1: Base64 encoder in Java


package websharp.util; 
public class Base64 { 
  private static final byte[] encodingTable = { 
      (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', 
      (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', 
      (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', 
      (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', 
      (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', 
      (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', 
      (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', 
      (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', 
      (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', 
      (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', 
      (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', 
      (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', 
      (byte) '8', (byte) '9', (byte) '+', (byte) '/' 
    }; 
  private static final byte[] decodingTable; 
  static { 
    decodingTable = new byte[128]; 
    for (int i = 0; i < 128; i++) { 
      decodingTable[i] = (byte) -1; 
    } 
    for (int i = 'A'; i <= 'Z'; i++) { 
      decodingTable[i] = (byte) (i - 'A'); 
    } 
    for (int i = 'a'; i <= 'z'; i++) { 
      decodingTable[i] = (byte) (i - 'a' + 26); 
    } 
    for (int i = '0'; i <= '9'; i++) { 
      decodingTable[i] = (byte) (i - '0' + 52); 
    } 
    decodingTable['+'] = 62; 
    decodingTable['/'] = 63; 
  } 
  public static byte[] encode(byte[] data,int offset) { 
    byte[] bytes; 
    int realCount=data.length-offset; 
    int modulus = realCount % 3; 
    if (modulus == 0) { 
      bytes = new byte[(4 * realCount) / 3]; 
    } else { 
      bytes = new byte[4 * ((realCount / 3) + 1)]; 
    } 
    int dataLength = (data.length - modulus); 
    int a1; 
    int a2; 
    int a3; 
    for (int i = offset, j = 0; i < dataLength; i += 3, j += 4) { 
      a1 = data[i] & 0xff; 
      a2 = data[i + 1] & 0xff; 
      a3 = data[i + 2] & 0xff; 
      bytes[j] = encodingTable[(a1 >>> 2) & 0x3f]; 
      bytes[j + 1] = encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]; 
      bytes[j + 2] = encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]; 
      bytes[j + 3] = encodingTable[a3 & 0x3f]; 
    } 
    int b1; 
    int b2; 
    int b3; 
    int d1; 
    int d2; 
    switch (modulus) { 
    case 0: /* nothing left to do */ 
      break; 
    case 1: 
      d1 = data[data.length - 1] & 0xff; 
      b1 = (d1 >>> 2) & 0x3f; 
      b2 = (d1 << 4) & 0x3f; 
      bytes[bytes.length - 4] = encodingTable[b1]; 
      bytes[bytes.length - 3] = encodingTable[b2]; 
      bytes[bytes.length - 2] = (byte) '='; 
      bytes[bytes.length - 1] = (byte) '='; 
      break; 
    case 2: 
      d1 = data[data.length - 2] & 0xff; 
      d2 = data[data.length - 1] & 0xff; 
      b1 = (d1 >>> 2) & 0x3f; 
      b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; 
      b3 = (d2 << 2) & 0x3f; 
      bytes[bytes.length - 4] = encodingTable[b1]; 
      bytes[bytes.length - 3] = encodingTable[b2]; 
      bytes[bytes.length - 2] = encodingTable[b3]; 
      bytes[bytes.length - 1] = (byte) '='; 
      break; 
    } 
    return bytes; 
  } 
  public static byte[] decode(byte[] data) { 
    byte[] bytes; 
    byte b1; 
    byte b2; 
    byte b3; 
    byte b4; 
    data = discardNonBase64Bytes(data); 
    if (data[data.length - 2] == '=') { 
      bytes = new byte[(((data.length / 4) - 1) * 3) + 1]; 
    } else if (data[data.length - 1] == '=') { 
      bytes = new byte[(((data.length / 4) - 1) * 3) + 2]; 
    } else { 
      bytes = new byte[((data.length / 4) * 3)]; 
    } 
    for (int i = 0, j = 0; i < (data.length - 4); i += 4, j += 3) { 
      b1 = decodingTable[data[i]]; 
      b2 = decodingTable[data[i + 1]]; 
      b3 = decodingTable[data[i + 2]]; 
      b4 = decodingTable[data[i + 3]]; 
      bytes[j] = (byte) ((b1 << 2) | (b2 >> 4)); 
      bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2)); 
      bytes[j + 2] = (byte) ((b3 << 6) | b4); 
    } 
    if (data[data.length - 2] == '=') { 
      b1 = decodingTable[data[data.length - 4]]; 
      b2 = decodingTable[data[data.length - 3]]; 
      bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4)); 
    } else if (data[data.length - 1] == '=') { 
      b1 = decodingTable[data[data.length - 4]]; 
      b2 = decodingTable[data[data.length - 3]]; 
      b3 = decodingTable[data[data.length - 2]]; 
      bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4)); 
      bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2)); 
    } else { 
      b1 = decodingTable[data[data.length - 4]]; 
      b2 = decodingTable[data[data.length - 3]]; 
      b3 = decodingTable[data[data.length - 2]]; 
      b4 = decodingTable[data[data.length - 1]]; 
      bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4)); 
      bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2)); 
      bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4); 
    } 
    return bytes; 
  } 
  public static byte[] decode(String data) { 
    byte[] bytes; 
    byte b1; 
    byte b2; 
    byte b3; 
    byte b4; 
    data = discardNonBase64Chars(data); 
    if (data.charAt(data.length() - 2) == '=') { 
      bytes = new byte[(((data.length() / 4) - 1) * 3) + 1]; 
    } else if (data.charAt(data.length() - 1) == '=') { 
      bytes = new byte[(((data.length() / 4) - 1) * 3) + 2]; 
    } else { 
      bytes = new byte[((data.length() / 4) * 3)]; 
    } 
    for (int i = 0, j = 0; i < (data.length() - 4); i += 4, j += 3) { 
      b1 = decodingTable[data.charAt(i)]; 
      b2 = decodingTable[data.charAt(i + 1)]; 
      b3 = decodingTable[data.charAt(i + 2)]; 
      b4 = decodingTable[data.charAt(i + 3)]; 
      bytes[j] = (byte) ((b1 << 2) | (b2 >> 4)); 
      bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2)); 
      bytes[j + 2] = (byte) ((b3 << 6) | b4); 
    } 
    if (data.charAt(data.length() - 2) == '=') { 
      b1 = decodingTable[data.charAt(data.length() - 4)]; 
      b2 = decodingTable[data.charAt(data.length() - 3)]; 
      bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4)); 
    } else if (data.charAt(data.length() - 1) == '=') { 
      b1 = decodingTable[data.charAt(data.length() - 4)]; 
      b2 = decodingTable[data.charAt(data.length() - 3)]; 
      b3 = decodingTable[data.charAt(data.length() - 2)]; 
      bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4)); 
      bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2)); 
    } else { 
      b1 = decodingTable[data.charAt(data.length() - 4)]; 
      b2 = decodingTable[data.charAt(data.length() - 3)]; 
      b3 = decodingTable[data.charAt(data.length() - 2)]; 
      b4 = decodingTable[data.charAt(data.length() - 1)]; 
      bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4)); 
      bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2)); 
      bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4); 
    } 
    for(int i=0;i<bytes.length;i++) System.out.println(","+bytes[i]); 
    return bytes; 
  } 
  private static byte[] discardNonBase64Bytes(byte[] data) { 
    byte[] temp = new byte[data.length]; 
    int bytesCopied = 0; 
    for (int i = 0; i < data.length; i++) { 
      if (isValidBase64Byte(data[i])) { 
        temp[bytesCopied++] = data[i]; 
      } 
    } 
    byte[] newData = new byte[bytesCopied]; 
    System.arraycopy(temp, 0, newData, 0, bytesCopied); 
    return newData; 
  } 
  private static String discardNonBase64Chars(String data) { 
    StringBuffer sb = new StringBuffer(); 
    int length = data.length(); 
    for (int i = 0; i < length; i++) { 
      if (isValidBase64Byte((byte) (data.charAt(i)))) { 
        sb.append(data.charAt(i)); 
      } 
    } 
    return sb.toString(); 
  } 
  private static boolean isValidBase64Byte(byte b) { 
    if (b == '=') { 
      return true; 
    } else if ((b < 0) || (b >= 128)) { 
      return false; 
    } else if (decodingTable[b] == -1) { 
      return false; 
    } 
    return true; 
  } 
  public static String encode(String data,String charset)throws Exception 
  { 
    // byte[] result = (data.getBytes("Unicode")); 
     if(data==null || data.length()==0) return data; 
     int offset=0; 
     // getBytes("unicode") And when you're done, you put two bytes in front of it." FE "  
     byte[] result=encode (data.getBytes(charset),offset); 
     StringBuffer sb=new StringBuffer(result.length); 
     for (int i=0;i<result.length;i++)  sb.append((char)result[i]); 
     return sb.toString(); 
  } 
  public static String decode(String data,String charset)throws Exception 
  { 
    if(data==null || data.length()==0) return data; 
    return new String(Base64.decode(data),charset); 
  } 
  public static void main(String[] args) throws Exception { 
    String data = " we "; 
    String data1=encode(data,"Unicode"); 
    String data2=decode(data1,"Unicode"); 
    System.out.println(data); 
    System.out.println(data1); 
    System.out.println(data2); 
  } 
}

Annex 2: Base64 encoder in JavaScript


<html> 
<head> 
<title>base64 Encoding/Decoding</title> 
</head> 
<script type="text/javascript"><!-- 
var keyStr = "ABCDEFGHIJKLMNOP" + 
       "QRSTUVWXYZabcdef" + 
       "ghijklmnopqrstuv" + 
       "wxyz0123456789+/" + 
       "=";

function encode64(input) { 
  input = unicodetoBytes(input); 
  var output = ""; 
  var chr1, chr2, chr3 = ""; 
  var enc1, enc2, enc3, enc4 = ""; 
  var i = 0;

  do { 
   chr1 = input[i++]; 
   chr2 = input[i++]; 
   chr3 = input[i++];

   enc1 = chr1 >> 2; 
   enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 
   enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 
   enc4 = chr3 & 63;

   if (isNaN(chr2)) { 
     enc3 = enc4 = 64; 
   } else if (isNaN(chr3)) { 
     enc4 = 64; 
   }

   output = output + 
     keyStr.charAt(enc1) + 
     keyStr.charAt(enc2) + 
     keyStr.charAt(enc3) + 
     keyStr.charAt(enc4); 
   chr1 = chr2 = chr3 = ""; 
   enc1 = enc2 = enc3 = enc4 = ""; 
  } while (i < input.length);

  return output; 
}

function decode64(input) { 
  var output = ""; 
  var chr1, chr2, chr3 = ""; 
  var enc1, enc2, enc3, enc4 = ""; 
  var i = 0;

  // remove all characters that are not A-Z, a-z, 0-9, +, /, or = 
  var base64test = /[^A-Za-z0-9/+///=]/g; 
  if (base64test.exec(input)) { 
   alert("There were invalid base64 characters in the input text./n" + 
      "Valid base64 characters are A-Z, a-z, 0-9, '+', '/', and '='/n" + 
      "Expect errors in decoding."); 
  } 
  input = input.replace(/[^A-Za-z0-9/+///=]/g, ""); 
  output=new Array(); 
  do { 
   enc1 = keyStr.indexOf(input.charAt(i++)); 
   enc2 = keyStr.indexOf(input.charAt(i++)); 
   enc3 = keyStr.indexOf(input.charAt(i++)); 
   enc4 = keyStr.indexOf(input.charAt(i++));

   chr1 = (enc1 << 2) | (enc2 >> 4); 
   chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 
   chr3 = ((enc3 & 3) << 6) | enc4;

   output.push(chr1); 
   if (enc3 != 64) { 
     output.push(chr2); 
   } 
   if (enc4 != 64) { 
     output.push(chr3); 
   }

   chr1 = chr2 = chr3 = ""; 
   enc1 = enc2 = enc3 = enc4 = "";

  } while (i < input.length); 
  return bytesToUnicode(output); 
}

 function unicodetoBytes(s) 
  { 
   var result=new Array(); 
   if(s==null || s=="") return result; 
   result.push(255); // add "FE" to head 
   result.push(254); 
   for(var i=0;i<s.length;i++) 
   { 
    var c=s.charCodeAt(i).toString(16); 
    if(c.length==1) i="000"+c; 
    else if(c.length==2) c="00"+c; 
    else if(c.length==3) c="0"+c; 
    var var1=parseInt( c.substring(2),16); 
    var var2=parseInt( c.substring(0,2),16); 
    result.push( var1); 
    result.push(var2) ; 
   } 
   return result; 
  }

  function bytesToUnicode(bs) 
  { 
   var result=""; 
   var offset=0; 
   if(bs.length>=2 && bs[0]==255 && bs[1]==254) offset=2; // delete "FE" 
   for(var i=offset;i<bs.length;i+=2) 
   { 
      var code=bs[i]+(bs[i+1]<<8); 
      result+=String.fromCharCode(code); 
   } 
   return result; 
  } 
//--> 
</script> 
<body> 
<form name="base64Form"> 
  Type in the message you want to encode in base64, or paste<br> 
  base64 encoded text into the text field, select Encode or Decode, <br> 
  and click the button!<br>

  <textarea name="theText" cols="40" rows="6"></textarea><br>

  <input type="button" name="encode" value="Encode to base64" 
   onClick="document.base64Form.theText.value=encode64(document.base64Form.theText.value);"> 
  <input type="button" name="decode" value="Decode from base64" 
   onClick="document.base64Form.theText.value=decode64(document.base64Form.theText.value);"> 
</form> 
</body> 
</html>

Thank you for reading, I hope to help you, thank you for your support of this site!


Related articles: