I have a base64 encoded string that was encoded in Java:
string s = "x8QeoAdVOAwpKHAeXIxEticayZLMx7RP_baVdSpDSLLea5TZMxRT-IX93lA05MEUzmwtOvd6WLRBluLchZz2EJSHsFfxxtPQF1VEFv4rA5w="
I'm trying to decode it in C# using the following statement.
string s = "x8QeoAdVOAwpKHAeXIxEticayZLMx7RP_baVdSpDSLLea5TZMxRT-IX93lA05MEUzmwtOvd6WLRBluLchZz2EJSHsFfxxtPQF1VEFv4rA5w="
var decodedBytes = System.Convert.FromBase64String(s);
But I get the error:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
What am I doing wrong? I can clearly see there are no illegal characters. Can anyone point me in the right direction?
Update : Here is the java code that generated that string.
private static String encrypt(byte[] iv, String salt, String password, String clearText) throws Exception {
byte[] encryptedBytes = encryptDecrypt(true, iv, salt, password, clearText.getBytes("UTF-8"));
return Base64.encodeBytes(encryptedBytes, 16);
}
private static byte[] encryptDecrypt(boolean encrypt, byte[] iv, String salt, String password, byte[] bytesToEncryptDecrypt) throws Exception {
SecretKeySpec secretKeySpec = null;
MessageDigest digester = MessageDigest.getInstance("SHA-1");
digester.update((salt + password).getBytes("UTF-8"));
byte[] key = digester.digest();
secretKeySpec = new SecretKeySpec(key, 2, 16, "AES");
IvParameterSpec ivps = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(encrypt ? 1 : 2, secretKeySpec, ivps);
return cipher.doFinal(bytesToEncryptDecrypt, 0, bytesToEncryptDecrypt.length);
}
Here is the Base64.class file (decompiled). It has both encode and decode methods from Java.
public class Base64
{
public static final int NO_OPTIONS = 0;
public static final int ENCODE = 1;
public static final int DECODE = 0;
public static final int GZIP = 2;
public static final int DONT_GUNZIP = 4;
public static final int DO_BREAK_LINES = 8;
public static final int URL_SAFE = 16;
public static final int ORDERED = 32;
private static final int MAX_LINE_LENGTH = 76;
private static final byte EQUALS_SIGN = 61;
private static final byte NEW_LINE = 10;
private static final String PREFERRED_ENCODING = "US-ASCII";
private static final byte WHITE_SPACE_ENC = -5;
private static final byte EQUALS_SIGN_ENC = -1;
private static final byte[] _STANDARD_ALPHABET = {
65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85,
86, 87, 88, 89, 90,
97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122,
48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 43, 47 };
private static final byte[] _STANDARD_DECODABET = {
-9, -9, -9, -9, -9, -9, -9, -9, -9,
-5, -5,
-9, -9,
-5,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9,
-5,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
62,
-9, -9, -9,
63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-9, -9, -9,
-1,
-9, -9, -9,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-9, -9, -9, -9, -9, -9,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
-9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 };
private static final byte[] _URL_SAFE_ALPHABET = {
65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85,
86, 87, 88, 89, 90,
97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122,
48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 45, 95 };
private static final byte[] _URL_SAFE_DECODABET = {
-9, -9, -9, -9, -9, -9, -9, -9, -9,
-5, -5,
-9, -9,
-5,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9,
-5,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9,
-9,
62,
-9,
-9,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-9, -9, -9,
-1,
-9, -9, -9,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-9, -9, -9, -9,
63,
-9,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
-9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 };
private static final byte[] _ORDERED_ALPHABET = {
45,
48, 49, 50, 51, 52,
53, 54, 55, 56, 57,
65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85,
86, 87, 88, 89, 90,
95,
97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122 };
private static final byte[] _ORDERED_DECODABET = {
-9, -9, -9, -9, -9, -9, -9, -9, -9,
-5, -5,
-9, -9,
-5,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9,
-5,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9,
-9,
0, -9,
-9,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
-9, -9, -9,
-1,
-9, -9, -9,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
-9, -9, -9, -9,
37,
-9,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 };
private static final byte[] getAlphabet(int options)
{
if ((options & 0x10) == 16)
return _URL_SAFE_ALPHABET;
if ((options & 0x20) == 32) {
return _ORDERED_ALPHABET;
}
return _STANDARD_ALPHABET;
}
private static final byte[] getDecodabet(int options)
{
if ((options & 0x10) == 16)
return _URL_SAFE_DECODABET;
if ((options & 0x20) == 32) {
return _ORDERED_DECODABET;
}
return _STANDARD_DECODABET;
}
private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options)
{
encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
return b4;
}
private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset, int options)
{
byte[] ALPHABET = getAlphabet(options);
int inBuff = (numSigBytes > 0 ? source[srcOffset] << 24 >>> 8 : 0) | (
numSigBytes > 1 ? source[(srcOffset + 1)] << 24 >>> 16 : 0) | (
numSigBytes > 2 ? source[(srcOffset + 2)] << 24 >>> 24 : 0);
switch (numSigBytes)
{
case 3:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[(destOffset + 1)] = ALPHABET[(inBuff >>> 12 & 0x3F)];
destination[(destOffset + 2)] = ALPHABET[(inBuff >>> 6 & 0x3F)];
destination[(destOffset + 3)] = ALPHABET[(inBuff & 0x3F)];
return destination;
case 2:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[(destOffset + 1)] = ALPHABET[(inBuff >>> 12 & 0x3F)];
destination[(destOffset + 2)] = ALPHABET[(inBuff >>> 6 & 0x3F)];
destination[(destOffset + 3)] = 61;
return destination;
case 1:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[(destOffset + 1)] = ALPHABET[(inBuff >>> 12 & 0x3F)];
destination[(destOffset + 2)] = 61;
destination[(destOffset + 3)] = 61;
return destination;
}
return destination;
}
public static void encode(ByteBuffer raw, ByteBuffer encoded)
{
byte[] raw3 = new byte[3];
byte[] enc4 = new byte[4];
while (raw.hasRemaining()) {
int rem = Math.min(3, raw.remaining());
raw.get(raw3, 0, rem);
encode3to4(enc4, raw3, rem, 0);
encoded.put(enc4);
}
}
public static void encode(ByteBuffer raw, CharBuffer encoded)
{
byte[] raw3 = new byte[3];
byte[] enc4 = new byte[4];
int i;
for (; raw.hasRemaining();
i < 4)
{
int rem = Math.min(3, raw.remaining());
raw.get(raw3, 0, rem);
encode3to4(enc4, raw3, rem, 0);
i = 0; continue;
encoded.put((char)(enc4[i] & 0xFF));
i++;
}
}
public static String encodeObject(Serializable serializableObject)
throws IOException
{
return encodeObject(serializableObject, 0);
}
public static String encodeObject(Serializable serializableObject, int options)
throws IOException
{
if (serializableObject == null) {
throw new NullPointerException("Cannot serialize a null object.");
}
ByteArrayOutputStream baos = null;
OutputStream b64os = null;
GZIPOutputStream gzos = null;
ObjectOutputStream oos = null;
try
{
baos = new ByteArrayOutputStream();
b64os = new OutputStream(baos, 0x1 | options);
if ((options & 0x2) != 0)
{
gzos = new GZIPOutputStream(b64os);
oos = new ObjectOutputStream(gzos);
}
else {
oos = new ObjectOutputStream(b64os);
}
oos.writeObject(serializableObject);
}
catch (IOException e)
{
throw e;
} finally {
try {
oos.close(); } catch (Exception localException) {
}try { gzos.close(); } catch (Exception localException1) {
}try { b64os.close(); } catch (Exception localException2) {
}try { baos.close();
} catch (Exception localException3) {
}
}
try {
return new String(baos.toByteArray(), "US-ASCII");
}
catch (UnsupportedEncodingException uue) {
}
return new String(baos.toByteArray());
}
public static String encodeBytes(byte[] source)
{
String encoded = null;
try {
encoded = encodeBytes(source, 0, source.length, 0);
} catch (IOException ex) {
if (!$assertionsDisabled) throw new AssertionError(ex.getMessage());
}
assert (encoded != null);
return encoded;
}
public static String encodeBytes(byte[] source, int options)
throws IOException
{
return encodeBytes(source, 0, source.length, options);
}
public static String encodeBytes(byte[] source, int off, int len)
{
String encoded = null;
try {
encoded = encodeBytes(source, off, len, 0);
} catch (IOException ex) {
if (!$assertionsDisabled) throw new AssertionError(ex.getMessage());
}
assert (encoded != null);
return encoded;
}
public static String encodeBytes(byte[] source, int off, int len, int options)
throws IOException
{
byte[] encoded = encodeBytesToBytes(source, off, len, options);
try
{
return new String(encoded, "US-ASCII");
} catch (UnsupportedEncodingException uue) {
}
return new String(encoded);
}
public static byte[] encodeBytesToBytes(byte[] source)
{
byte[] encoded = (byte[])null;
try {
encoded = encodeBytesToBytes(source, 0, source.length, 0);
} catch (IOException ex) {
if (!$assertionsDisabled) throw new AssertionError("IOExceptions only come from GZipping, which is turned off: " + ex.getMessage());
}
return encoded;
}
public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options)
throws IOException
{
if (source == null) {
throw new NullPointerException("Cannot serialize a null array.");
}
if (off < 0) {
throw new IllegalArgumentException("Cannot have negative offset: " + off);
}
if (len < 0) {
throw new IllegalArgumentException("Cannot have length offset: " + len);
}
if (off + len > source.length) {
throw new IllegalArgumentException(
String.format("Cannot have offset of %d and length of %d with array of length %d", new Object[] { Integer.valueOf(off), Integer.valueOf(len), Integer.valueOf(source.length) }));
}
if ((options & 0x2) != 0) {
ByteArrayOutputStream baos = null;
GZIPOutputStream gzos = null;
OutputStream b64os = null;
try
{
baos = new ByteArrayOutputStream();
b64os = new OutputStream(baos, 0x1 | options);
gzos = new GZIPOutputStream(b64os);
gzos.write(source, off, len);
gzos.close();
}
catch (IOException e)
{
throw e;
} finally {
try {
gzos.close(); } catch (Exception localException) {
}try { b64os.close(); } catch (Exception localException1) {
}try { baos.close(); } catch (Exception localException2) {
}
}
return baos.toByteArray();
}
boolean breakLines = (options & 0x8) != 0;
int encLen = len / 3 * 4 + (len % 3 > 0 ? 4 : 0);
if (breakLines) {
encLen += encLen / 76;
}
byte[] outBuff = new byte[encLen];
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for (; d < len2; e += 4) {
encode3to4(source, d + off, 3, outBuff, e, options);
lineLength += 4;
if ((breakLines) && (lineLength >= 76))
{
outBuff[(e + 4)] = 10;
e++;
lineLength = 0;
}
d += 3;
}
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e, options);
e += 4;
}
if (e <= outBuff.length - 1)
{
byte[] finalOut = new byte[e];
System.arraycopy(outBuff, 0, finalOut, 0, e);
return finalOut;
}
return outBuff;
}
private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset, int options)
{
if (source == null) {
throw new NullPointerException("Source array was null.");
}
if (destination == null) {
throw new NullPointerException("Destination array was null.");
}
if ((srcOffset < 0) || (srcOffset + 3 >= source.length)) {
throw new IllegalArgumentException(String.format(
"Source array with length %d cannot have offset of %d and still process four bytes.", new Object[] { Integer.valueOf(source.length), Integer.valueOf(srcOffset) }));
}
if ((destOffset < 0) || (destOffset + 2 >= destination.length)) {
throw new IllegalArgumentException(String.format(
"Destination array with length %d cannot have offset of %d and still store three bytes.", new Object[] { Integer.valueOf(destination.length), Integer.valueOf(destOffset) }));
}
byte[] DECODABET = getDecodabet(options);
if (source[(srcOffset + 2)] == 61)
{
int outBuff = (DECODABET[source[srcOffset]] & 0xFF) << 18 |
(DECODABET[source[(srcOffset + 1)]] & 0xFF) << 12;
destination[destOffset] = ((byte)(outBuff >>> 16));
return 1;
}
if (source[(srcOffset + 3)] == 61)
{
int outBuff = (DECODABET[source[srcOffset]] & 0xFF) << 18 |
(DECODABET[source[(srcOffset + 1)]] & 0xFF) << 12 |
(DECODABET[source[(srcOffset + 2)]] & 0xFF) << 6;
destination[destOffset] = ((byte)(outBuff >>> 16));
destination[(destOffset + 1)] = ((byte)(outBuff >>> 8));
return 2;
}
int outBuff = (DECODABET[source[srcOffset]] & 0xFF) << 18 |
(DECODABET[source[(srcOffset + 1)]] & 0xFF) << 12 |
(DECODABET[source[(srcOffset + 2)]] & 0xFF) << 6 |
DECODABET[source[(srcOffset + 3)]] & 0xFF;
destination[destOffset] = ((byte)(outBuff >> 16));
destination[(destOffset + 1)] = ((byte)(outBuff >> 8));
destination[(destOffset + 2)] = ((byte)outBuff);
return 3;
}
public static byte[] decode(byte[] source)
throws IOException
{
byte[] decoded = (byte[])null;
decoded = decode(source, 0, source.length, 0);
return decoded;
}
public static byte[] decode(byte[] source, int off, int len, int options)
throws IOException
{
if (source == null) {
throw new NullPointerException("Cannot decode null source array.");
}
if ((off < 0) || (off + len > source.length)) {
throw new IllegalArgumentException(String.format(
"Source array with length %d cannot have offset of %d and process %d bytes.", new Object[] { Integer.valueOf(source.length), Integer.valueOf(off), Integer.valueOf(len) }));
}
if (len == 0)
return new byte[0];
if (len < 4) {
throw new IllegalArgumentException(
"Base64-encoded string must have at least four characters, but length specified was " + len);
}
byte[] DECODABET = getDecodabet(options);
int len34 = len * 3 / 4;
byte[] outBuff = new byte[len34];
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiDecode = 0;
for (i = off; i < off + len; i++)
{
sbiDecode = DECODABET[(source[i] & 0xFF)];
if (sbiDecode >= -5)
{
byte[] out;
if (sbiDecode >= -1) {
b4[(b4Posn++)] = source[i];
if (b4Posn > 3) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
b4Posn = 0;
if (source[i] == 61) {
break;
}
}
}
}
else
{
throw new IOException(String.format(
"Bad Base64 input character decimal %d in array position %d", new Object[] { Integer.valueOf(source[i] & 0xFF), Integer.valueOf(i) }));
}
}
out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
}
public static byte[] decode(String s)
throws IOException
{
return decode(s, 0);
}
public static byte[] decode(String s, int options)
throws IOException
{
if (s == null) {
throw new NullPointerException("Input string was null.");
}
try
{
bytes = s.getBytes("US-ASCII");
}
catch (UnsupportedEncodingException uee)
{
byte[] bytes;
bytes = s.getBytes();
}
byte[] bytes = decode(bytes, 0, bytes.length, options);
boolean dontGunzip = (options & 0x4) != 0;
if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip))
{
int head = bytes[0] & 0xFF | bytes[1] << 8 & 0xFF00;
if (35615 == head) {
ByteArrayInputStream bais = null;
GZIPInputStream gzis = null;
ByteArrayOutputStream baos = null;
byte[] buffer = new byte[2048];
int length = 0;
try
{
baos = new ByteArrayOutputStream();
bais = new ByteArrayInputStream(bytes);
gzis = new GZIPInputStream(bais);
while ((length = gzis.read(buffer)) >= 0) {
baos.write(buffer, 0, length);
}
bytes = baos.toByteArray();
}
catch (IOException e)
{
e.printStackTrace();
try
{
baos.close(); } catch (Exception localException) {
}try { gzis.close(); } catch (Exception localException1) {
}try { bais.close(); }
catch (Exception localException2)
{
}
}
finally
{
try
{
baos.close(); } catch (Exception localException3) {
}try { gzis.close(); } catch (Exception localException4) {
}try { bais.close();
} catch (Exception localException5) {
}
}
}
}
return bytes;
}
public static Object decodeToObject(String encodedObject)
throws IOException, ClassNotFoundException
{
return decodeToObject(encodedObject, 0, null);
}
}
It should be as simple as replacing the -
and _
that Java uses with the +
and /
that C# uses:
string s = "Vv7JKbNHOvHxC4L89be4vNw-J0KKPkMlq4iZb_T-35u-kjYhilF_ECJAPVufv2-2_ynUORVkIPPqd1H7MC-mrWW7Fu4ZcmsHG2Q1gJOU=";
string corrected = s.Replace('-', '+').Replace('_', '/');
var decodedBytes = System.Convert.FromBase64String(corrected);
However, your example string also seems to be incorrect; it's 105 characters long but (as it's padded) the length should be a multiple of 4. If you remove the trailing =
from your input then this code works, but I'd be worried about how it got there. If it's meant to be there, you'll need to programatically trim/pad your input and then double-check that the results you get are what you're expecting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With