解密C#RIJNDAEL编码文本

我正在Java中实现与第三方应用程序的通信。 作为登录过程的一部分,第三方应用程序正在发送加密的字符串,我必须将其解码并发回。 我花了差不多2天googeling和阅读post,但我找不到正确的方法来实现这一点。

我有一个测试用例,其中加密的字符串是“c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY =”,用密码“GAT”解密必须返回“101714994”。

我所说的文档说明:授权字符串使用以下设置加密:

  • 填充输入数据:PKCS * 7
  • 密码字节数组长度为32个字节。 密码字符串转换为UTF-16编码的字节数组,然后用零填充字节数组,最长为32个字节。 更长的密码被截断。

这是如何解密授权字符串的C#示例:

///  /// Decrypts a string. ///  /// The string to decrypt. /// The password to use. /// The decrypted string. private static string DecryptString(string content, string password) { Rijndael aes; byte[] retVal = null; byte[] contentBytes; byte[] passwordBytes; byte[] ivBytes; try { //Get the content as byte[] contentBytes = Convert.FromBase64String(content); //Create the password and initial vector bytes passwordBytes = new byte[32]; ivBytes = new byte[16]; Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length); Array.Copy(passwordBytes, ivBytes, 16); //Create the cryptograpy object aes = Rijndael.Create(); aes.Key = passwordBytes; aes.IV = ivBytes; aes.Padding = PaddingMode.PKCS7; //Decrypt retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length); } catch { } aes = null; contentBytes = null; passwordBytes = null; ivBytes = null; return Encoding.Unicode.GetString(retVal) } 

这是我解密字符串的Java过程:

 private String decryptAuthorizationString(String authString, String password) { try { //Force the test string authString = "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY="; //Force the test password password = "GAT"; //Create the password and initial vector bytes byte[] passwordBytes= new byte[32]; byte[] b= password.getBytes("UTF-8"); int len= b.length; if (len > passwordBytes.length) len = passwordBytes.length; System.arraycopy(b, 0, passwordBytes, 0, len); byte[] ivBytes= new byte[16]; System.arraycopy(passwordBytes, 0, ivBytes, 0, 16); //Get the authString as byte[] byte[] authBytes = new BASE64Decoder().decodeBuffer(authString); InputStream inputStream = new ByteArrayInputStream(authBytes); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // If you have Bouncycastle library installed, you can use // Rijndael/CBC/PKCS7PADDING directly. Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING", "BC"); // convertedSecureString and initVector must be byte[] with correct length cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(passwordBytes, "AES"), new IvParameterSpec(ivBytes)); CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher); byte[] buffer = new byte[1024]; len = cryptoStream.read(buffer, 0, buffer.length); while (len > 0) { outputStream.write(buffer, 0, len); len = cryptoStream.read(buffer, 0, buffer.length); } outputStream.flush(); cryptoStream.close(); String resStr = outputStream.toString("UTF-8"); return resStr; //<<--- resStr must be "101714994" } catch (Throwable t) { } return null; } 

该过程运行没有任何错误,但结果不是我需要它。 任何帮助将非常感激。

您不需要BouncyCastleProvider,因为AES已包含在Java中。 但是PKCS#7填充由"PKCS5Padding" 错误地指示,因此如果没有Bouncy Castle,则不能指示"AES/CBC/PKCS7Padding"

.NET的默认Unicode编码实际上与UTF-16LE更兼容。 留给微软不要保留标准名称(尽管它们可能在它之前)。

Java JCE实际上并不像C#类那样围绕流构建,所以最好完全避免流式传输。

我已经重写了您的示例代码,以展示如何在Java中正确编写代码(但您需要兼容Java 7)。 不要在表下AssertErrorexception,将它们转换为AssertErrorRuntimeException

我使用了Bouncy Castle Base 64解码器,因为那个解码器可以为你和我提供一个解码器(但除此之外它与Bouncy无关)。 Java 8包含一个base 64类 。


所以不用多说:

 import static java.nio.charset.StandardCharsets.UTF_16LE; import java.security.GeneralSecurityException; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.util.encoders.Base64; public class AuthenticationStringDecrypter { private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING"; private static final int KEY_SIZE = 256; public static void main(final String[] args) throws Exception { System.out.println(decryptAuthorizationString( "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=", "GAT")); } private static String decryptAuthorizationString(final String authString, final String password) { try { // --- check if AES-256 is available if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) { throw new IllegalStateException("Unlimited crypto files not present in this JRE"); } // --- create cipher final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING); // --- create the key and initial vector bytes final byte[] passwordEncoded = password.getBytes(UTF_16LE); final byte[] keyData = Arrays.copyOf(passwordEncoded, KEY_SIZE / Byte.SIZE); final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize()); // --- init cipher cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"), new IvParameterSpec(ivBytes)); // --- decode & decrypt authentication string final byte[] authBytes = Base64.decode(authString); final byte[] decryptedData = cipher.doFinal(authBytes); // WARNING: may still decrypt to wrong string if // authString or password are incorrect - // BadPaddingException may *not* be thrown return new String(decryptedData, UTF_16LE); } catch (BadPaddingException | IllegalBlockSizeException e) { // failure to authenticate return null; } catch (final GeneralSecurityException e) { throw new IllegalStateException( "Algorithms or unlimited crypto files not available", e); } } }