所以我一直在使用 Bouncycastle 库来尝试连接远程服务器。这个过程从一开始就存在问题,现在我接近让一切正常,但发生了一些奇怪的事情。
当我第一次开始构建加密过程时,我被告知要使用 AES 256 和 PKCS7Padding。经过一番唠叨,我得到了一个服务器代码的 c++ 示例。原来 IV 是 256 位的,所以我不得不改用 RijndaelEngine。此外,为了使其正常工作,我必须使用 ZeroBytePadding。
这是我的代码:
socket = new Socket(remoteIP, port);
outputStream = new PrintWriter(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
byte[] base_64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes("UTF-8");
Security.addProvider(new BouncyCastleProvider());
public String AESEncrypt(String out) throws IOException, DataLengthException, IllegalStateException, InvalidCipherTextException {
byte[] EncKey = key;
byte randKey;
Random randNumber = new Random();
randKey = base_64[randNumber.nextInt(base_64.length)];
EncKey[randKey&0x1f] = randKey;
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine), new ZeroBytePadding());
ParametersWithIV keyParameter = new ParametersWithIV(new KeyParameter(EncKey), iv);
cipher.init(true, keyParameter);
byte[] txt = out.getBytes();
byte[] encoded = new byte[cipher.getOutputSize(txt.length)];
int len = cipher.processBytes(txt, 0, txt.length, encoded, 0);
cipher.doFinal(encoded, len);
char keyChar = (char) randKey;
String encString = new String(Base64.encode(encoded));
encString = encString.substring(0, encString.length()-1) + randKey;
return encString;
}
public void AESDecrypt(String in) throws DataLengthException, IllegalStateException, IOException, InvalidCipherTextException {
byte[] decKey = key;
byte[] msg = in.getBytes();
byte randKey = msg[msg.length-1];
decKey[randKey&0x1f] = randKey;
byte[] trimMsg = new byte[msg.length-1];
System.arraycopy(msg, 0, trimMsg, 0, trimMsg.length);
in = new String(trimMsg);
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine), new ZeroBytePadding());
ParametersWithIV keyParameter = new ParametersWithIV(new KeyParameter(decKey), iv);
cipher.init(false, keyParameter);
byte[] encoded = Base64.decode(in.trim());
byte[] decoded = new byte[cipher.getOutputSize(encoded.length)];
int len = cipher.processBytes(encoded, 0, encoded.length, decoded, 0);
cipher.doFinal(decoded, len);
String decString = new String(decoded);
}
这是我用来发送和接收消息的测试函数:
public void serverTest() throws DataLengthException, IllegalStateException, InvalidCipherTextException, IOException {
//out = AESEncrypt(out);
outputStream.write(out + "\n");
outputStream.flush();
String msg = "";
while ((msg = inputStream.readLine()) != null) {
AESDecrypt(msg);
}
}
除了密钥中的最后一个字节外,密钥和 iv 不会改变。如果我正在加密,我会得到一个随机的 base64 字符并将最后一个字节更改为该字符。如果它被解密,我会从消息中获取最后一个字节,并将密钥的最后一个值设置给它以进行解密。
在 c++ 示例中,有一条未加密的消息和两条加密的消息。我可以处理那些罚款。
这是问题所在,当我将消息“加密”发送到远程服务器时,应用程序会等待响应,直到连接超时但永远不会得到响应。如果我发送未加密的消息,我会收到 7 个可以成功解密的响应,最后
org.bouncycastle.util.encoders.DecoderException: unable to decode base64 string:
String index out of range: -4 at org.bouncycastle.util.encoders.Base64.decode(Unknown Source)
或者我在错误之前的最后一行将如下所示:
?"??n?i???el????s???!_S=??ah????CR??l6??]?{?l??Y?????Gn???+?????9!'??gU&4>??{X????G?.$c=??0?5??GP???_Q5????8??Z\?~???<Kr?????[2\ ???a$?C??z%?W???{?.?????eR?j????~?B"$??"z??W;???<?Yu??Y*???Z?K?e!?????f?;O(?Zw0B??g<???????????,)?L>???A"?????<?????W??@\???f%??j ?EhY/?? ?5R?34r???@?1??I??????M
如果我将加密/解密设置为使用 PKCS7Padding,当我的消息仍然被加密但从服务器解密时我没有得到任何响应,我得到 2 到 6 个响应,然后
org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted
我对此不知所措。我不知道我可能做错了什么,所以我来到了这里。我希望社区可以指出我的错误并引导我朝着正确的方向前进。
我有一些更新,我发现我的加密错误。我没有正确地将随机 base64 值放在加密字符串的末尾,所以现在我正在这样做。
encString += (char)randKey;
我现在可以得到服务器的响应。现在的问题是我有时会得到一两行可读的行,但其余的都是垃圾。我询问了运行服务器的人,他们在一些 c# 代码中说他们引用了
return UTF8Encoding.UTF8.GetString(resultArray);
这就是我所要做的。我在任何我做 getBytes 或 new String 的地方都试过 UTF-8 编码,我也试过让 BurrferReader 流 UTF-8,但它仍然是垃圾。