来自 Base64 解码值的字符串已损坏 (Java),
String from Base64 decoded value corrupt (Java),
我正在将 base64 encoded string
从 Classic ASP
页面发送到 JSP
页面。该字符串在编码之前已 RC4
加密。
现在,我观察到,在 ASP
页面中,使用 base64
对字符串进行编码和解码工作正常。但是,JSP
页面上的 base64 decoded string
不正确。我还尝试在 Eclipse 中解码字符串并得到相同的结果。这似乎与字符编码类型有关,但我正在努力确定问题所在。
- base64编码字符串:yOBIc4FY
- base64解码字符串(来自ASP页):ÈàHsX(正确)
- base64 解码字符串(来自 JSP 页面和 Eclipse):ÈàHs?X(不正确)
Java/JSP代码:
import org.apache.commons.codec.binary.Base64;
String base64String = "yOBIc4FY";
byte[] decodedBase64Byte = Base64.decodeBase64(base64String);
// ÈàHs?X
decodedBase64String = new String(decodedBase64Byte, "ISO-8859-1");
// ÈàHs?X
decodedBase64String = new String(decodedBase64Byte, "windows-1252");
// ??Hs?X
decodedBase64String = new String(decodedBase64Byte, "utf-8");
重申一下,正确的值应该是ÈàHsX
。我不明白这是什么问题。任何帮助将不胜感激。
谢谢。
更新
让我进一步解释一下。
Classic ASP 中的 RC4 密码算法被广泛使用,所以我不会浪费房地产在这里发布它。但是,我将在下面展示我用于 `Classic ASP 的 base64 encoder/decoder
。
对于 RC4,我使用的明文值是 foobar。我使用的密钥是 test。从表面上看,解码 base64 字符串应该 return 密码。并且解密密码应该return明文值。
' Functions to provide encoding/decoding of strings with Base64.
'
' Encoding: myEncodedString = base64_encode( inputString )
' Decoding: myDecodedString = base64_decode( encodedInputString )
'
' Programmed by Markus Hartsmar for ShameDesigns in 2002.
' Email me at: mark@shamedesigns.com
' Visit our website at: http://www.shamedesigns.com/
'
Dim Base64Chars
Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" & _
"abcdefghijklmnopqrstuvwxyz" & _
"0123456789" & _
"+/"
' Functions for encoding string to Base64
Public Function base64_encode( byVal strIn )
Dim c1, c2, c3, w1, w2, w3, w4, n, strOut
For n = 1 To Len( strIn ) Step 3
c1 = Asc( Mid( strIn, n, 1 ) )
c2 = Asc( Mid( strIn, n + 1, 1 ) + Chr(0) )
c3 = Asc( Mid( strIn, n + 2, 1 ) + Chr(0) )
w1 = Int( c1 / 4 ) : w2 = ( c1 And 3 ) * 16 + Int( c2 / 16 )
If Len( strIn ) >= n + 1 Then
w3 = ( c2 And 15 ) * 4 + Int( c3 / 64 )
Else
w3 = -1
End If
If Len( strIn ) >= n + 2 Then
w4 = c3 And 63
Else
w4 = -1
End If
strOut = strOut + mimeencode( w1 ) + mimeencode( w2 ) + _
mimeencode( w3 ) + mimeencode( w4 )
Next
base64_encode = strOut
End Function
Private Function mimeencode( byVal intIn )
If intIn >= 0 Then
mimeencode = Mid( Base64Chars, intIn + 1, 1 )
Else
mimeencode = ""
End If
End Function
' Function to decode string from Base64
Public Function base64_decode( byVal strIn )
Dim w1, w2, w3, w4, n, strOut
For n = 1 To Len( strIn ) Step 4
w1 = mimedecode( Mid( strIn, n, 1 ) )
w2 = mimedecode( Mid( strIn, n + 1, 1 ) )
w3 = mimedecode( Mid( strIn, n + 2, 1 ) )
w4 = mimedecode( Mid( strIn, n + 3, 1 ) )
If w2 >= 0 Then _
strOut = strOut + _
Chr( ( ( w1 * 4 + Int( w2 / 16 ) ) And 255 ) )
If w3 >= 0 Then _
strOut = strOut + _
Chr( ( ( w2 * 16 + Int( w3 / 4 ) ) And 255 ) )
If w4 >= 0 Then _
strOut = strOut + _
Chr( ( ( w3 * 64 + w4 ) And 255 ) )
Next
base64_decode = strOut
End Function
Private Function mimedecode( byVal strIn )
If Len( strIn ) = 0 Then
mimedecode = -1 : Exit Function
Else
mimedecode = InStr( Base64Chars, strIn ) - 1
End If
End Function
从ASP内,明文值从密码正确实现:
plainText: foobar
cipherText: ÈàHsX
base64 String: yOBIc4FY
Decoded base64 String: ÈàHsX
Decrypted text: foobar
然而,将密码作为 base64 字符串传递给 JSP/Java,JSP/Java 看起来像这样:
plainText: foobar (from ASP)
cipherText: ÈàHsX (from ASP)
base64 String: yOBIc4FY
Decoded base64 String: ÈàHs?X
Decrypted text: foobßr
所以,这里有些事情没有加起来。事实上,对于 Java,我对 return 的解密方式做了一个更改 foobar
的正确解密文本。 Java代码中的RC4解密采用int[]
.
类型形式的密码
public int[] decrypt(int[] ciphertext, byte[] key) throws Exception {
return encrypt(ciphertext, key);
}
换句话说,我必须将密码从 String
类型转换为 int[]
类型。我使用下面的函数来做到这一点:
public static int[] convertToIntArray(byte[] input)
{
int[] ret = new int[input.length];
for (int i = 0; i < input.length; i++)
{
ret[i] = input[i] & 0xff; // Range 0 to 255
}
return ret;
}
我有两个选择。我可以将 base64 字符串解码为 byte[]
类型并解密,这将 return foobar
.
String base64String = "yOBIc4FY";
byte[] decodedBase64Byte = Base64.decodeBase64(base64String);
int[] cipheredText = convertToIntArray(decodedBase64Byte);
或者,我可以将 base64 字符串解码为 byte[]
类型,然后将其转换为 String
类型,然后再次返回类型 byte[]
进行解密,这将 return foobßr
.
String base64String = "yOBIc4FY";
byte[] decodedBase64Byte = Base64.decodeBase64(base64String);
// ÈàHs?X
String decodedBase64String = new String(decodedBase64Byte, "ISO-8859-1");
int[] cipheredText = convertToIntArray(decodedBase64String.getBytes());
我的猜测是原始字节序列是正确的,因为 RC4 解密功能成功 returns foobar
。但是,当我将字节序列转换为某个字符编码集的字符串时,它会更改值,最终解密值 foobßr
.
为什么 ASP 和 JSP/Java 报告的密码值略有不同? ASP 可以毫无问题地将 base64 字符串或密码解码回其明文值。我不知道问题出在 ASP、JSP 还是两者都有。
yOBIc4FY
正确解码为6字节,具体为:
c8 e0 48 73 81 58
ÈàHsX
值可能只是将字符 0x81
忽略为不可打印。
证明:
y O B I c 4 F Y
110010 001110 000001 001000 011100 111000 000101 011000
11001000 11100000 01001000 01110011 10000001 01011000
c8 e0 48 73 81 58
要解决您的后续问题 - 您应该使用从 base64 解码器获得的字节数组。如果需要,请将其转换为 int[]
,但不要从中创建 String
,因为编码会弄乱它:
static void printByteArray(byte[] bytes) {
for (byte b : bytes) {
System.out.print(Integer.toHexString(b & 0xff) + ", ");
}
System.out.println();
}
public static void main(String[] args) {
byte[] cipherBytes = Base64.getDecoder().decode("yOBIc4FY");
printByteArray(cipherBytes); // c8, e0, 48, 73, 81, 58 - correct
cipherBytes = new String(cipherBytes).getBytes();
printByteArray(cipherBytes); // c8, e0, 48, 73, 3f, 58 - wrong
// your results may vary depending on your default charset,
// these are for windows-1250
}
这里可以看到原来正确的字节0x81
变成了问号?
(字节0x3f
),因为0x81
不代表一个从字节数组创建 String
时使用的字符集中的有效字符。
我正在将 base64 encoded string
从 Classic ASP
页面发送到 JSP
页面。该字符串在编码之前已 RC4
加密。
现在,我观察到,在 ASP
页面中,使用 base64
对字符串进行编码和解码工作正常。但是,JSP
页面上的 base64 decoded string
不正确。我还尝试在 Eclipse 中解码字符串并得到相同的结果。这似乎与字符编码类型有关,但我正在努力确定问题所在。
- base64编码字符串:yOBIc4FY
- base64解码字符串(来自ASP页):ÈàHsX(正确)
- base64 解码字符串(来自 JSP 页面和 Eclipse):ÈàHs?X(不正确)
Java/JSP代码:
import org.apache.commons.codec.binary.Base64;
String base64String = "yOBIc4FY";
byte[] decodedBase64Byte = Base64.decodeBase64(base64String);
// ÈàHs?X
decodedBase64String = new String(decodedBase64Byte, "ISO-8859-1");
// ÈàHs?X
decodedBase64String = new String(decodedBase64Byte, "windows-1252");
// ??Hs?X
decodedBase64String = new String(decodedBase64Byte, "utf-8");
重申一下,正确的值应该是ÈàHsX
。我不明白这是什么问题。任何帮助将不胜感激。
谢谢。
更新
让我进一步解释一下。
Classic ASP 中的 RC4 密码算法被广泛使用,所以我不会浪费房地产在这里发布它。但是,我将在下面展示我用于 `Classic ASP 的 base64 encoder/decoder
。
对于 RC4,我使用的明文值是 foobar。我使用的密钥是 test。从表面上看,解码 base64 字符串应该 return 密码。并且解密密码应该return明文值。
' Functions to provide encoding/decoding of strings with Base64.
'
' Encoding: myEncodedString = base64_encode( inputString )
' Decoding: myDecodedString = base64_decode( encodedInputString )
'
' Programmed by Markus Hartsmar for ShameDesigns in 2002.
' Email me at: mark@shamedesigns.com
' Visit our website at: http://www.shamedesigns.com/
'
Dim Base64Chars
Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" & _
"abcdefghijklmnopqrstuvwxyz" & _
"0123456789" & _
"+/"
' Functions for encoding string to Base64
Public Function base64_encode( byVal strIn )
Dim c1, c2, c3, w1, w2, w3, w4, n, strOut
For n = 1 To Len( strIn ) Step 3
c1 = Asc( Mid( strIn, n, 1 ) )
c2 = Asc( Mid( strIn, n + 1, 1 ) + Chr(0) )
c3 = Asc( Mid( strIn, n + 2, 1 ) + Chr(0) )
w1 = Int( c1 / 4 ) : w2 = ( c1 And 3 ) * 16 + Int( c2 / 16 )
If Len( strIn ) >= n + 1 Then
w3 = ( c2 And 15 ) * 4 + Int( c3 / 64 )
Else
w3 = -1
End If
If Len( strIn ) >= n + 2 Then
w4 = c3 And 63
Else
w4 = -1
End If
strOut = strOut + mimeencode( w1 ) + mimeencode( w2 ) + _
mimeencode( w3 ) + mimeencode( w4 )
Next
base64_encode = strOut
End Function
Private Function mimeencode( byVal intIn )
If intIn >= 0 Then
mimeencode = Mid( Base64Chars, intIn + 1, 1 )
Else
mimeencode = ""
End If
End Function
' Function to decode string from Base64
Public Function base64_decode( byVal strIn )
Dim w1, w2, w3, w4, n, strOut
For n = 1 To Len( strIn ) Step 4
w1 = mimedecode( Mid( strIn, n, 1 ) )
w2 = mimedecode( Mid( strIn, n + 1, 1 ) )
w3 = mimedecode( Mid( strIn, n + 2, 1 ) )
w4 = mimedecode( Mid( strIn, n + 3, 1 ) )
If w2 >= 0 Then _
strOut = strOut + _
Chr( ( ( w1 * 4 + Int( w2 / 16 ) ) And 255 ) )
If w3 >= 0 Then _
strOut = strOut + _
Chr( ( ( w2 * 16 + Int( w3 / 4 ) ) And 255 ) )
If w4 >= 0 Then _
strOut = strOut + _
Chr( ( ( w3 * 64 + w4 ) And 255 ) )
Next
base64_decode = strOut
End Function
Private Function mimedecode( byVal strIn )
If Len( strIn ) = 0 Then
mimedecode = -1 : Exit Function
Else
mimedecode = InStr( Base64Chars, strIn ) - 1
End If
End Function
从ASP内,明文值从密码正确实现:
plainText: foobar
cipherText: ÈàHsX
base64 String: yOBIc4FY
Decoded base64 String: ÈàHsX
Decrypted text: foobar
然而,将密码作为 base64 字符串传递给 JSP/Java,JSP/Java 看起来像这样:
plainText: foobar (from ASP)
cipherText: ÈàHsX (from ASP)
base64 String: yOBIc4FY
Decoded base64 String: ÈàHs?X
Decrypted text: foobßr
所以,这里有些事情没有加起来。事实上,对于 Java,我对 return 的解密方式做了一个更改 foobar
的正确解密文本。 Java代码中的RC4解密采用int[]
.
public int[] decrypt(int[] ciphertext, byte[] key) throws Exception {
return encrypt(ciphertext, key);
}
换句话说,我必须将密码从 String
类型转换为 int[]
类型。我使用下面的函数来做到这一点:
public static int[] convertToIntArray(byte[] input)
{
int[] ret = new int[input.length];
for (int i = 0; i < input.length; i++)
{
ret[i] = input[i] & 0xff; // Range 0 to 255
}
return ret;
}
我有两个选择。我可以将 base64 字符串解码为 byte[]
类型并解密,这将 return foobar
.
String base64String = "yOBIc4FY";
byte[] decodedBase64Byte = Base64.decodeBase64(base64String);
int[] cipheredText = convertToIntArray(decodedBase64Byte);
或者,我可以将 base64 字符串解码为 byte[]
类型,然后将其转换为 String
类型,然后再次返回类型 byte[]
进行解密,这将 return foobßr
.
String base64String = "yOBIc4FY";
byte[] decodedBase64Byte = Base64.decodeBase64(base64String);
// ÈàHs?X
String decodedBase64String = new String(decodedBase64Byte, "ISO-8859-1");
int[] cipheredText = convertToIntArray(decodedBase64String.getBytes());
我的猜测是原始字节序列是正确的,因为 RC4 解密功能成功 returns foobar
。但是,当我将字节序列转换为某个字符编码集的字符串时,它会更改值,最终解密值 foobßr
.
为什么 ASP 和 JSP/Java 报告的密码值略有不同? ASP 可以毫无问题地将 base64 字符串或密码解码回其明文值。我不知道问题出在 ASP、JSP 还是两者都有。
yOBIc4FY
正确解码为6字节,具体为:
c8 e0 48 73 81 58
ÈàHsX
值可能只是将字符 0x81
忽略为不可打印。
证明:
y O B I c 4 F Y
110010 001110 000001 001000 011100 111000 000101 011000
11001000 11100000 01001000 01110011 10000001 01011000
c8 e0 48 73 81 58
要解决您的后续问题 - 您应该使用从 base64 解码器获得的字节数组。如果需要,请将其转换为 int[]
,但不要从中创建 String
,因为编码会弄乱它:
static void printByteArray(byte[] bytes) {
for (byte b : bytes) {
System.out.print(Integer.toHexString(b & 0xff) + ", ");
}
System.out.println();
}
public static void main(String[] args) {
byte[] cipherBytes = Base64.getDecoder().decode("yOBIc4FY");
printByteArray(cipherBytes); // c8, e0, 48, 73, 81, 58 - correct
cipherBytes = new String(cipherBytes).getBytes();
printByteArray(cipherBytes); // c8, e0, 48, 73, 3f, 58 - wrong
// your results may vary depending on your default charset,
// these are for windows-1250
}
这里可以看到原来正确的字节0x81
变成了问号?
(字节0x3f
),因为0x81
不代表一个从字节数组创建 String
时使用的字符集中的有效字符。