繁体   English   中英

将C#加密转换为Java

[英]Converting C# cryptography to Java

我的任务是将C#加密方法转换为Java并且卡住了。 我知道C#代码可以工作,但是我无法让我的Java代码工作。

这是C#代码:

private const int Nb = 4; // Legal values:  4 = 128-bit blocks

public static void Decrypt(byte[] input, Stream output)
    { 
        var s1 = new MemoryStream(input);
        const int BufferSize = 1024;
        byte[] buffer = new byte[BufferSize];

        input.Read(buffer, 0, 4);
        int pad = buffer[3];

        RijndaelManaged rijndael = new RijndaelManaged();
        rijndael.BlockSize = Nb * 32;
        rijndael.KeySize = buffer[1] * 32;

        rijndael.Mode = CipherMode.ECB;
        rijndael.Padding = PaddingMode.None;

        byte[] key = GetKey(buffer[1]);
        ICryptoTransform decryptor = rijndael.CreateDecryptor(key, GetIV());

        int bytes;
        while ((bytes = input.Read(buffer, 0, BufferSize)) > 0)
        {
            for (int i = 0; i < bytes; i += rijndael.BlockSize)
            {
                decryptor.TransformBlock(buffer, i, rijndael.BlockSize, buffer, i);
            }
            output.Write(buffer, 0, bytes);
        }
        output.SetLength(output.Length - pad - 4);
    }

这是我到目前为止在Java中的尝试:

public static String decrypt(byte[] input) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
    byte[] key = getKey(input[1]);
    SecretKey secretKey = new SecretKeySpec(key, 0, key.length, "AES/ECB/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));
    // remove first 4 since C# code reads past those
    byte[] finalDecoded = Arrays.copyOfRange(input, 4, input.length);
    byte[] decryptedVal = cipher.doFinal(finalDecoded);
    return new String(decryptedVal);
}

更多信息

  • 对于GetIVGetKey ,我可以保证java中的结果是相同的(我比较了每个字节),但我不包括那些方法,因为我认为这是敏感信息。 我还可以保证输入字节[]是相同的并且(冗余地)相同的长度。

  • 调试尝试: Java中的当前错误是ECB mode cannot use IV

    • 当我删除此代码时: new IvParameterSpec(getIV())我收到此错误: Wrong algorithm: AES or Rijndael required
    • 如果我将算法更改为仅AES或仅Rijndael我得到此错误: Input length must be multiple of 16 when decrypting with padded cipher 读取过去/删除前4个字节后,输入长度开始为424420 我已经验证了Java和C#的输入字节是相同的。

我在Java代码中哪里出错了?

您收到错误ECB mode cannot use IV因为ECB不执行链接,因此IV无意义。 不同之处是Java会抛出错误而C#只会忽略IV。

当我删除此代码时: new IvParameterSpec(getIV())我收到此错误: Wrong algorithm: required AES or Rijndael

如果我将算法更改为仅AES或仅Rijndael我得到此错误: Input length must be multiple of 16 when decrypting with padded cipher. Input length must be multiple of 16 when decrypting with padded cipher.

你有正确的想法,但你走得太远了。 此错误仅与SecretKeySpec ,后者不关心模式,只关心算法。 Cipher是您指定模式的地方。 此外,Rijndael和AES并不完全相同。

首先,将前几行更改为:

Cipher cipher = Cipher.getInstance("Rijndael/ECB/NoPadding");
byte[] key = getKey(input[1]);
SecretKey secretKey = new SecretKeySpec(key, 0, key.length, "Rijndael");
cipher.init(Cipher.DECRYPT_MODE, secretKey);

请注意,由于您使用的是整个key ,因此您不需要offset和length参数,因此您可以这样做

SecretKey secretKey = new SecretKeySpec(key, "Rijndael");

原始的C#代码有一些不那么明显的行为:

while ((bytes = input.Read(buffer, 0, BufferSize)) > 0)
{
    for (int i = 0; i < bytes; i += rijndael.BlockSize)
    {
        decryptor.TransformBlock(buffer, i, rijndael.BlockSize, buffer, i);
    }
    output.Write(buffer, 0, bytes);
}

当循环到达input的末尾时,它将复制,但是它会留下多少进入buffer 除非最后一次Read恰好是1024个字节,否则在input结束后,前一个循环(或初始化,如果它通过一次Read操作得到整个input )将有残留。

内循环一次解密一个16字节的块。 对于420字节的示例,最后一个块将包含剩余的4个字节的输入和12个字节的垃圾 但它没关系,因为output.Write只写bytes数字节来截断垃圾。 您必须在Java代码中复制此行为。


旁注:你绝对必须使用ECB吗? 它不是很安全......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM