在C#中解码Java编码的Base64字符串

我有一个用Java编码的base64编码字符串:

string s = "x8QeoAdVOAwpKHAeXIxEticayZLMx7RP_baVdSpDSLLea5TZMxRT-IX93lA05MEUzmwtOvd6WLRBluLchZz2EJSHsFfxxtPQF1VEFv4rA5w=" 

我正在尝试使用以下语句在C#中对其进行解码。

 string s = "x8QeoAdVOAwpKHAeXIxEticayZLMx7RP_baVdSpDSLLea5TZMxRT-IX93lA05MEUzmwtOvd6WLRBluLchZz2EJSHsFfxxtPQF1VEFv4rA5w=" var decodedBytes = System.Convert.FromBase64String(s); 

但我得到错误:

输入不是有效的Base-64字符串,因为它包含非基本64个字符,两个以上的填充字符或填充字符中的非法字符。

我究竟做错了什么? 我可以清楚地看到没有非法字符。 谁能指出我正确的方向?

更新:这是生成该字符串的java代码。

  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); 

}

这是Base64.class文件(反编译)。 它具有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] <>> 8 : 0) | ( numSigBytes > 1 ? source[(srcOffset + 1)] <>> 16 : 0) | ( numSigBytes > 2 ? source[(srcOffset + 2)] <>> 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  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 = 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 = 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 = 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) <>> 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) <>> 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) <> 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  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 = -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] <= 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); } } 

它应该像替换Java使用的-_以及C#使用的那样简单:

 string s = "Vv7JKbNHOvHxC4L89be4vNw-J0KKPkMlq4iZb_T-35u-kjYhilF_ECJAPVufv2-2_ynUORVkIPPqd1H7MC-mrWW7Fu4ZcmsHG2Q1gJOU="; string corrected = s.Replace('-', '+').Replace('_', '/'); var decodedBytes = System.Convert.FromBase64String(corrected); 

但是,您的示例字符串似乎也不正确; 它的长度为105个字符但是(因为它是填充的)长度应该是4的倍数。如果你从输入中删除了尾随=那么这个代码可以工作,但是我会担心它是如何到达那里的。 如果它意味着在那里,你需要以编程方式修剪/填充你的输入,然后仔细检查你得到的结果是你所期望的。

试试这段代码:(注意:此代码尚未针对性能进行优化,因为它使用字符串连接)

 using System; namespace Crypto { public class Base64 { private const String STANDARD_FIRST_62_CHARS= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ "abcdefghijklmnopqrstuvwxyz"+ "0123456789"; public const String STANDARD_RADIX_STRING= STANDARD_FIRST_62_CHARS + "+/"; public const String RFC4648_RADIX_STRING= STANDARD_FIRST_62_CHARS + "-_"; private const char padding='='; private char[]radix_chars; public Base64(String radix_string) { radix_chars=radix_string.ToCharArray(); } public Base64():this(STANDARD_RADIX_STRING) { } public String decode(String str) { String binary_str = ""; char[]chars = str.ToCharArray(); int i=0; for(;i 0 ) { bin_string = "0"+bin_string; --length; } return bin_string; } private int indexOf(char c) { for(int index=0; index < radix_chars.Length ; ++ index) if(c == radix_chars[index]) return index; return -1; } } } public class Program { public static void Main() { Crypto.Base64 b = new Crypto.Base64(Crypto.Base64.RFC4648_RADIX_STRING); Console.WriteLine(b.decode("x8QeoAdVOAwpKHAeXIxEticayZLMx7RP_baVdSpDSLLea5TZMxRT-IX93lA05MEUzmwtOvd6WLRBluLchZz2EJSHsFfxxtPQF1VEFv4rA5w=")); } } 

如果您希望decode方法返回byte[]而不是String则将decode方法更改为:

  public byte[] decode(String str) { String binary_str = ""; char[]chars = str.ToCharArray(); int i=0; for(;i