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