Java中的Bouncycastle奇怪的加密和解密结果

4
所以我一直在尝试使用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编码,也尝试将BufferedReader流设置为UTF-8,但仍然是垃圾数据。


听起来你只是试图随机尝试直到找到有效的方法。为什么不从服务器操作员那里获取真正的规范呢? - President James K. Polk
“Random”是一个安全的PRNG吗?标准的“Random”类不安全,不应在加密代码中使用。 - CodesInChaos
@GregS,其中一个问题是服务器运营商的规范。他们一直告诉我必须使用256位AES,但初始化向量只有256位。AES的块大小不允许这样做。当我提到这一点并且我必须使用Rijndael才能使用提供的IV时,他们告诉我要使用的填充对解密从来没有起作用过。 - hswets
@CodesInChaos,我不相信Random是一个安全的PRNG,我会改变它,但不幸的是这并不能解决我的另一个问题。 - hswets
1个回答

1

您看过BCgit吗?这里有Bouncycastle的代码和示例。我在此存储库中使用Csharp版本。https://github.com/bcgit/bc-java

所有加密原语示例都存储在此处:https://github.com/bcgit/bc-java/tree/master/core/src/test/java/org/bouncycastle/crypto/test

尝试使用此代码测试Aes-CBC。

private void testNullCBC()
    throws InvalidCipherTextException
{
    BufferedBlockCipher b = new BufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
    KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));

    b.init(true, new ParametersWithIV(kp, new byte[16]));

    byte[] out = new byte[b.getOutputSize(tData.length)];

    int len = b.processBytes(tData, 0, tData.length, out, 0);

    len += b.doFinal(out, len);

    if (!areEqual(outCBC1, out))
    {
        fail("no match on first nullCBC check");
    }

    b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));

    len = b.processBytes(tData, 0, tData.length, out, 0);

    len += b.doFinal(out, len);

    if (!areEqual(outCBC2, out))
    {
        fail("no match on second nullCBC check");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接