发送的密文和收到的密文不匹配

Ciphertext sent and ciphertext received do not match

我有两个 Socket,它们通过环回相互连接,并由两个同步方法 运行 在不同的线程上控制,以模拟用于 JUnit 测试目的的服务器-客户端设置。服务器端套接字有一个BufferedWriter,客户端套接字有一个BufferedReader.

目的是通过Java内置的Cipherclass使用RSA加密明文字符串,通过BufferedWriter发送生成的密文字节数组, 然后从 BufferedReader 读取它并解密回明文。

BufferedWriter bw;
BufferedReader br;
public void send(String plaintext){
    byte[] ciphertext = ... //encrypt plaintext here using Java's Cipher class
    bw.write(new String(ciphertext).toCharArray());
    bw.newLine();
    bw.flush();
}

public void receive(){
    byte[] ciphertext = br.readLine().getBytes();
}

问题是,从 BufferedReader 读取的字节数组与发送到 BufferedWriter 的字节数组不匹配。我将两者都打印到控制台,然后在记事本中将它们复制粘贴到彼此下方进行比较,但它们不匹配;尝试解密收到的密文自然会抛出 IllegalBlockSizeException.

现在,我终于意识到 BufferedReader.readLine() 在遇到 ASCII 换行和回车 return 字符(0A 和 0D)的字节值时立即停止,无论是否存在实际的换行符,或者接收到的数据由于被加密而恰好包含这些字节。然而,简单地重复 readLine() 并连接结果并没有帮助,因为无法知道两者中的哪一个导致 readLine() 停止,因此也无法知道将哪个插入回密文。除了这些丢失的字符弄乱了密文的长度,它还算顺利。

我尝试的下一件事是将 readLine() 替换为 read() 重复,直到实际的、故意的行终止:

ciphertext = new byte[0];     //to avoid a NullPointerException down the line
byte[] stream = new byte[0];
while(br.ready()){
    stream = Arrays.copyOf(stream, stream.length + 1);
    stream[stream.length-1] = (byte)(char)br.read();
}
ciphertext = Arrays.copyOf(stream, stream.length - 5);

这得到了正确的密文长度。现在的问题是,虽然收到了整个密文,但其中充满了误码。这是一个示例,没有使用填充的 RSA-1024(我想在放入填充之前先完成此操作,因为如果像这样保留它无论如何都会抛出异常)。

通过 BufferedWriter 发送的内容: 415EFB8FBBF54FB9BFC31C45E9BEA46035D744D0015E8C7A6B17D967FFCE5F18F5A4311BAC5BB572DA3488EE1DEC8018D611A9197C52B768896EF2FE9CFA0B057D5FE478BD85F13274FF3A59B6821F64A7089B7F470B83C010F263B5753202A7EC443E17617CA1D9516F3C57788A43F14A6FB3202317E9E11F35FF2696CE19EC

BufferedReader 的结果: 415E7179BB514F057C021C45E93EA46035D74410015E5A7A6B176E67D9CE5F1851A4311BAC5BB572DA343FEE1D1BAC18D611A9197C52B768306E48635BFA0B057D5FE478DD26443274D93A59B61A1F64A7083A7F470B3F54104863B5753202A71B443E17617CC76E516F3C57786043444A6F42202317E9E11F35D92613CE191B

如您所见,它很相似,但到处都有差异。我非常确定它是由双重类型转换引起的(因为read() returns an int,它不会隐式转换成byte),但希望有第二意见来确认并指出我可能的解决方案。这可能是一个编码问题,但我很困惑会发生什么:密文的 bytes 变成 chars,通过发送,读出为 ints,类型转换回chars 然后被类型转换回 bytes。我的意思是,如果一个 UTF-16 字符被写入缓冲区,然后被最初将它写入缓冲区的相同环境读回为 int,那么 int 是不是包含原始字符的 UTF-16 代码?或者类型转换会将其解释为 ASCII?

现代密码方案的密文是二进制的。如果您假设二进制数据可以解释为具有某种字符编码(ASCII、UTF-8 等)的字符,那么您会感到失望。并非所有字节都构成有效的可打印字符。一些控制字符甚至以某种特殊方式解释,从而破坏密文。

这就是为什么您不使用基于字符串的 "streams"(Java 中的 ...Reader...Writer)并使用实际流 (...InputStream...OutputStream) 而不是。

如果您确实需要使用 Writers 和 Readers,那么您需要将 byte[] 编码为可打印的形式,例如使用 Hex 或 Base64。