发送的密文和收到的密文不匹配
Ciphertext sent and ciphertext received do not match
我有两个 Socket
,它们通过环回相互连接,并由两个同步方法 运行 在不同的线程上控制,以模拟用于 JUnit 测试目的的服务器-客户端设置。服务器端套接字有一个BufferedWriter
,客户端套接字有一个BufferedReader
.
目的是通过Java内置的Cipher
class使用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
),但希望有第二意见来确认并指出我可能的解决方案。这可能是一个编码问题,但我很困惑会发生什么:密文的 byte
s 变成 char
s,通过发送,读出为 int
s,类型转换回char
s 然后被类型转换回 byte
s。我的意思是,如果一个 UTF-16 字符被写入缓冲区,然后被最初将它写入缓冲区的相同环境读回为 int
,那么 int
是不是包含原始字符的 UTF-16 代码?或者类型转换会将其解释为 ASCII?
现代密码方案的密文是二进制的。如果您假设二进制数据可以解释为具有某种字符编码(ASCII、UTF-8 等)的字符,那么您会感到失望。并非所有字节都构成有效的可打印字符。一些控制字符甚至以某种特殊方式解释,从而破坏密文。
这就是为什么您不使用基于字符串的 "streams"(Java 中的 ...Reader
和 ...Writer
)并使用实际流 (...InputStream
和 ...OutputStream
) 而不是。
如果您确实需要使用 Writers 和 Readers,那么您需要将 byte[]
编码为可打印的形式,例如使用 Hex 或 Base64。
我有两个 Socket
,它们通过环回相互连接,并由两个同步方法 运行 在不同的线程上控制,以模拟用于 JUnit 测试目的的服务器-客户端设置。服务器端套接字有一个BufferedWriter
,客户端套接字有一个BufferedReader
.
目的是通过Java内置的Cipher
class使用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
),但希望有第二意见来确认并指出我可能的解决方案。这可能是一个编码问题,但我很困惑会发生什么:密文的 byte
s 变成 char
s,通过发送,读出为 int
s,类型转换回char
s 然后被类型转换回 byte
s。我的意思是,如果一个 UTF-16 字符被写入缓冲区,然后被最初将它写入缓冲区的相同环境读回为 int
,那么 int
是不是包含原始字符的 UTF-16 代码?或者类型转换会将其解释为 ASCII?
现代密码方案的密文是二进制的。如果您假设二进制数据可以解释为具有某种字符编码(ASCII、UTF-8 等)的字符,那么您会感到失望。并非所有字节都构成有效的可打印字符。一些控制字符甚至以某种特殊方式解释,从而破坏密文。
这就是为什么您不使用基于字符串的 "streams"(Java 中的 ...Reader
和 ...Writer
)并使用实际流 (...InputStream
和 ...OutputStream
) 而不是。
如果您确实需要使用 Writers 和 Readers,那么您需要将 byte[]
编码为可打印的形式,例如使用 Hex 或 Base64。