使用Java解密由.NET的RijndaelManaged加密的字节

5

我正在尝试解密一些东西,该东西使用.NET/C#的RijndaelManaged进行了加密,并使用Java进行解密。

C#程序不是我的,我无法更改它以实现更好的互操作性。但我知道它是如何加密的:

byte[] bytes = new UnicodeEncoding().GetBytes(password); // edit: built-in is 8chars
FileStream fileStream = new FileStream(outputFile, FileMode.Create);
RijndaelManaged rijndaelManaged = new RijndaelManaged();
CryptoStream cryptoStream = new CryptoStream((Stream) fileStream,
    rijndaelManaged.CreateEncryptor(bytes, bytes), CryptoStreamMode.Write);

我不知道如何在Java端解密这个内容。我找到了这篇博客文章,但它缺乏实际细节,我无法实现解密器。

编辑:我是个傻瓜,现在它可以工作了。

Unicode编码 是UTF-16LE,而我使用的是UTF-8。在插入密码时切换到正确的编码已经修复了程序。

我还需要获取BouncyCastle并执行Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");

最终编辑:以下是在Java中解密默认.NET RijndaelManaged流的代码,假设它是使用原始密码作为密钥创建的:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
String password = "kallisti"; // only 8, 12, or 16 chars will work as a key
byte[] key = password.getBytes(Charset.forName("UTF-16LE"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
    new IvParameterSpec(key));
return cipher; // then use CipherInputStream(InputStream, Cipher)

请记住:如果您控制C#端,请不要使用未派生的密码作为密钥!


1
非请求的建议:不要使用 new UnicodeEncoding().GetBytes(password); 来获取密码的字节。原因请参考 http://en.wikipedia.org/wiki/PBKDF2 和 http://tools.ietf.org/html/rfc2898,尤其是第3节。幸运的是,.NET和Java都有符合RFC2898标准(又名PKCS5,又名PBKDF2)的密钥派生类。你的应用程序应该使用它们。 - Cheeso
1
请参考 https://dev59.com/J3E95IYBdhLWcg3wSb_P 中的 PKBDF2 在 Java 中的问题。 - Cheeso
@Cheeso:这个C#应用程序不是我的,我知道使用原始密码作为密钥是一个可怕的想法。它也没有加密任何实际需要加密的内容,只是一个二进制块,应用程序编写者不希望其他应用程序能够读取。 - Aleksei Vasiliev
1个回答

3

可以使用标准AES解密。Rijndel只是AES的超集,它对特定选项更加宽松。有关详细信息,请参见Java中的Rijndael支持

从链接问题的答案中得到:

byte[] sessionKey = null; //Where you get this from is beyond the scope of this post
byte[] iv = null ; //Ditto
byte[] plaintext = null; //Whatever you want to encrypt/decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//You can use ENCRYPT_MODE or DECRYPT_MODE
cipher.calling init(Cipher.DECRYPT_MODE, new SecretKeySpec(sessionKey, "AES"), new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(plaintext);

3
你理解有些错误 - AES是Rijndael的一个子集,尤其是在块大小方面。AES严格使用128位的块大小(参考FIPS 197),而Rijndael则提供了一定的灵活性。 - vcsjones
"java.security.InvalidKeyException: 无效的AES密钥长度:8个字节""java.security.InvalidAlgorithmParameterException: 错误的IV长度:必须为16个字节长"我尝试了这段代码,但它不起作用。我正在使用与C#端相同的密码。C#端正在使用原始明文密码插入到密码器中;上述代码似乎希望接收从密码派生的字节数组。 - Aleksei Vasiliev
看看这个是否更好:http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-java/Rijndael.java。看起来AES支持需要特定的密钥长度(128、192、256等)。因此,您必须在两端制作与AES兼容的密钥。 - Chris Dennett
我知道我必须制作与AES兼容的密钥。但我需要以与C#程序相同的方式来完成。C#程序只需提供RijndaeManaged 8字节密码,然后显然会自动转换为适当的密钥。我不知道如何在Java中实现这一点。 - Aleksei Vasiliev
我的上面的评论可能是错误的,对我的情况肯定没有影响,但我还是保留它。dwi。 - Aleksei Vasiliev
显示剩余2条评论

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