来自 Base64 解码值的字符串已损坏 (Java),

String from Base64 decoded value corrupt (Java),

我正在将 base64 encoded stringClassic ASP 页面发送到 JSP 页面。该字符串在编码之前已 RC4 加密。

现在,我观察到,在 ASP 页面中,使用 base64 对字符串进行编码和解码工作正常。但是,JSP 页面上的 base64 decoded string 不正确。我还尝试在 Eclipse 中解码字符串并得到相同的结果。这似乎与字符编码类型有关,但我正在努力确定问题所在。

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 时使用的字符集中的有效字符。