使用C#加密AES以匹配Java加密

我已经获得了加密的Java实现,但不幸的是我们是一个.net商店,我无法将Java整合到我们的解决方案中。 可悲的是,我也不是一个爪哇人,所以我已经和它斗争了几天,并且以为我终于转到这里求助了。

我已经搜索了高低,以寻找与Java加密工作方式相匹配的方法,并且我已经达到了在c#中使用RijndaelManaged所需的分辨率。 我其实很亲密。 我在c#中返回的字符串与前半部分匹配,但后半部分不同。

这是java实现的一个片段:

private static String EncryptBy16( String str, String theKey) throws Exception { if ( str == null || str.length() > 16) { throw new NullPointerException(); } int len = str.length(); byte[] pidBytes = str.getBytes(); byte[] pidPaddedBytes = new byte[16]; for ( int x=0; x<16; x++ ) { if ( x 16){ str = s.substring(16); s = s.substring(0,16); }else { str = null; } result.append(EncryptBy16(s,key)); }while(str != null); return result.toString(); } 

我不完全确定为什么他们一次只传递16个字符,但是没有。 我使用字符串构建器尝试了同样的c#实现,并且一次只发送16个字符,并且得到了与我一次传入整个字符串时得到的结果相同的结果。

这是我的c#实现的片段,主要是来自MS’网站的RijndaelManaged的复制和粘贴:

 public static string Encrypt(string stringToEncrypt, string key) { using (RijndaelManaged myRijndael = new RijndaelManaged()) { myRijndael.Key = StringToByte(key); myRijndael.IV = new byte[16]; return EncryptStringToBytes(stringToEncrypt, myRijndael.Key, myRijndael.IV); } } static string EncryptStringToBytes(string plainText, byte[] Key, byte[] IV) { if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key"); byte[] encrypted; using (RijndaelManaged rijAlg = new RijndaelManaged()) { rijAlg.Key = Key; rijAlg.IV = IV; ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { swEncrypt.Write(plainText); } encrypted = msEncrypt.ToArray(); } } } return ByteToString(encrypted); } 

如上所述,加密字符串的前半部分是相同的(参见下面的示例),但后半部分是关闭的。 我在下面的输出中添加了空格,以更好地说明差异的位置。 我对加密和Java知之甚少,不知道下一步该转向何处。 任何指导将不胜感激

Java输出:

49a85367ec8bc387bb44963b54528c97 8026d7eaeff9e4cb7cf74f8227f80752

C#输出:

49a85367ec8bc387bb44963b54528c97 718f574341593be65034627a6505f13c

根据克里斯的建议更新如下:

 static string EncryptStringToBytes(string plainText, byte[] Key, byte[] IV) { if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key"); byte[] encrypted; using (RijndaelManaged rijAlg = new RijndaelManaged()) { rijAlg.Key = Key; rijAlg.IV = IV; rijAlg.Padding = PaddingMode.None; rijAlg.Mode = CipherMode.ECB; ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { swEncrypt.Write(plainText); if (plainText.Length < 16) { for (int i = plainText.Length; i < 16; i++) { swEncrypt.Write((byte)0x0); } } } encrypted = msEncrypt.ToArray(); } } } return ByteToString(encrypted); } 

你的C#翻译在大多数情况下看起来都是正确的,因为第一个块匹配。 不匹配的是最后一个块,这是因为Java代码填充了最后一个块以填充它,而你的C#代码没有这样做,所以它默认使用PKCS#5填充。

当然,PKCS#5填充比零填充要好得多,但由于后者是Java代码所使用的,所以你必须做同样的事情。 (这意味着,再调用swEncrypt.Write((byte) 0)几次,直到字节数是16的倍数。)

还有另一个微妙之处。 Java代码使用String.getBytes()将字符串转换为字节, String.getBytes()使用Java运行时的“默认编码”。 这意味着如果您的字符串包含非ASCII字符,则会遇到互操作性问题。 最佳做法是使用UTF-8,但看到你无法改变Java代码,我想你无能为力。

很好的问题,这是使用相同的加密算法但在不同语言中的常见错误。 算法细节的实现需要注意。 我没有测试过代码,但在你的情况下,两个实现的填充选项是不同的,尝试对c#和java实现使用相同的填充选项 。 您可以从此处阅读评论以及有关实施的更多信息。 请注意填充默认值。

  • Padding = PaddingMode.PKCS7,
  • private final String cipherTransformation =“AES / CBC / PKCS5Padding”;

c#实现:

 public RijndaelManaged GetRijndaelManaged(String secretKey) { var keyBytes = new byte[16]; var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey); Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length)); return new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7, KeySize = 128, BlockSize = 128, Key = keyBytes, IV = keyBytes }; } public byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged) { return rijndaelManaged.CreateEncryptor() .TransformFinalBlock(plainBytes, 0, plainBytes.Length); } public byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged) { return rijndaelManaged.CreateDecryptor() .TransformFinalBlock(encryptedData, 0, encryptedData.Length); } // Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string public String Encrypt(String plainText, String key) { var plainBytes = Encoding.UTF8.GetBytes(plainText); return Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(key))); } public String Decrypt(String encryptedText, String key) { var encryptedBytes = Convert.FromBase64String(encryptedText); return Encoding.UTF8.GetString(Decrypt(encryptedBytes, GetRijndaelManaged(key))); } 

Java实现:

 private final String characterEncoding = "UTF-8"; private final String cipherTransformation = "AES/CBC/PKCS5Padding"; private final String aesEncryptionAlgorithm = "AES"; public byte[] decrypt(byte[] cipherText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { Cipher cipher = Cipher.getInstance(cipherTransformation); SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm); IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec); cipherText = cipher.doFinal(cipherText); return cipherText; } public byte[] encrypt(byte[] plainText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { Cipher cipher = Cipher.getInstance(cipherTransformation); SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm); IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); plainText = cipher.doFinal(plainText); return plainText; } private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{ byte[] keyBytes= new byte[16]; byte[] parameterKeyBytes= key.getBytes(characterEncoding); System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length)); return keyBytes; } public String encrypt(String plainText, String key) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{ byte[] plainTextbytes = plainText.getBytes(characterEncoding); byte[] keyBytes = getKeyBytes(key); return Base64.encodeToString(encrypt(plainTextbytes,keyBytes, keyBytes), Base64.DEFAULT); } public String decrypt(String encryptedText, String key) throws KeyException, GeneralSecurityException, GeneralSecurityException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException{ byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT); byte[] keyBytes = getKeyBytes(key); return new String(decrypt(cipheredBytes, keyBytes, keyBytes), characterEncoding); }