对于 AES 256 位,在 Java 中加密并在 C# 中解密

新手上路,请多包涵

1.我有加密xml文件并返回加密字符串的java函数。

 /// Java Class
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class Crypt {

    public static String key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    public static byte[] key_Array = Base64.decodeBase64(key);

    public static String encrypt(String strToEncrypt)
    {
        try
        {
            //Cipher _Cipher = Cipher.getInstance("AES");
            //Cipher _Cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            //Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");

            Key SecretKey = new SecretKeySpec(key_Array, "AES");

            Cipher _Cipher = Cipher.getInstance("AES");
            _Cipher.init(Cipher.ENCRYPT_MODE, SecretKey);

            return Base64.encodeBase64String(_Cipher.doFinal(strToEncrypt.getBytes()));
        }
        catch (Exception e)
        {
            System.out.println("[Exception]:"+e.getMessage());
        }
        return null;
    }

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("xml file string ...");

        String EncryptedString = encrypt(sb.toString());
        System.out.println("[EncryptedString]:"+EncryptedString);
    }
}

2.我有c#函数来解密由java函数加密的消息。

 /// C# Function
private static string Decrypt(string encryptedText)
{
    RijndaelManaged aesEncryption = new RijndaelManaged();
    aesEncryption.BlockSize = 256;
    //aesEncryption.KeySize = 256;
    //aesEncryption.Mode = CipherMode.CBC;
    //aesEncryption.Padding = PaddingMode.PKCS7;

    string keyStr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    //string ivStr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    byte[] keyArr = Convert.FromBase64String(keyStr);
    //byte[] ivArr = Convert.FromBase64String(ivStr);

    aesEncryption.Key = keyArr;
    //aesEncryption.IV = ivArr;

    ICryptoTransform decrypto = aesEncryption.CreateDecryptor();

    byte[] encryptedBytes = Convert.FromBase64CharArray(encryptedText.ToCharArray(), 0, encryptedText.Length);
    byte[] decryptedData = decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length); /// CryptographicException: Length of the data to decrypt is invalid.
    return ASCIIEncoding.UTF8.GetString(decryptedData);
}

Java 加密功能运行良好。但问题是 C# 函数,

当我解密时,我收到以下错误消息

CryptographicException: Length of the data to decrypt is invalid.

我使用以下参考搜索了解决方案

  1. Java中的AES加密和C#中的解密
  2. C# / Java | AES256加密/解密
  3. C#和Java中的加密/解密

但我仍然面临同样的错误。请问有人能给我建议吗?

更新

我只是更改了我的 C# 加密函数。以下是我的更改列表

  1. 块大小为 128
  2. 密钥大小为 256
  3. IV 尺寸至 16
  4. 密钥大小为 32
 /// Updated decrypt function
private static string Decrypt(string encryptedText)
{
    RijndaelManaged aesEncryption = new RijndaelManaged();
    aesEncryption.BlockSize = 128;
    aesEncryption.KeySize = 256;

    //aesEncryption.Mode = CipherMode.CBC;
    aesEncryption.Padding = PaddingMode.None;

    string keyStr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    string ivStr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    byte[] ivArr = Convert.FromBase64String(ivStr);
    byte[] IVkey16BytesValue = new byte[16];
    Array.Copy(ivArr, IVkey16BytesValue, 16);

    byte[] keyArr = Convert.FromBase64String(keyStr);
    byte[] KeyArr32BytesValue = new byte[32];
    Array.Copy(keyArr, KeyArr32BytesValue, 32);

    aesEncryption.IV = IVkey16BytesValue;
    aesEncryption.Key = KeyArr32BytesValue;

    ICryptoTransform decrypto = aesEncryption.CreateDecryptor();

    byte[] encryptedBytes = Convert.FromBase64CharArray(encryptedText.ToCharArray(), 0, encryptedText.Length);
    byte[] decryptedData = decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
    return ASCIIEncoding.UTF8.GetString(decryptedData);
}

在此期间,没有错误发生。但是我收到了我无法阅读的解密消息。

 g:�\0�\td��Y\\符O����\rL��W�wHm�>f�\au����%��0��\ ..........

请让我再次得到你的建议。

原文由 Frank Myat Thu 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

我相信 blockSize 应该是 128,keysize 应该是 256。keyStr 应该是 32 个字符长,IVstr 应该是 16 个字符长。这可能会有所帮助,因为它描述了为什么必须将 128 位用于块大小以及密钥大小可以是多少。 csrc.nist.gov/publications/fips/fips197/fips-197.pdf

你在解密方法中有这个。

     aesEncryption.Padding = PaddingMode.None;

我相信你也需要把它放在加密方法中。

另外为什么不对密钥和 IV 使用此方法。

     aes.Key = ASCIIEncoding.ASCII.GetBytes(keyStr);
    aes.IV = ASCIIEncoding.ASCII.GetBytes(ivStr);

原文由 deathismyfriend 发布,翻译遵循 CC BY-SA 3.0 许可协议

在我从@deathismyfriend 和其他人那里得到非常有用的建议后,我发现我的 C# Decrypt 函数中缺少什么。所以我按如下方式更改我的函数。

 /// C# Error Fixed Version - CipherMode.ECB
public static string keyStr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

private static string Encrypt(string PlainText)
{
    RijndaelManaged aes = new RijndaelManaged();
    aes.BlockSize = 128;
    aes.KeySize = 256;

    /// In Java, Same with below code
    /// Cipher _Cipher = Cipher.getInstance("AES");  // Java Code
    aes.Mode = CipherMode.ECB;

    byte[] keyArr = Convert.FromBase64String(keyStr);
    byte[] KeyArrBytes32Value = new byte[32];
    Array.Copy(keyArr, KeyArrBytes32Value, 32);

    aes.Key = KeyArrBytes32Value;

    ICryptoTransform encrypto = aes.CreateEncryptor();

    byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(PlainText);
    byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
    return Convert.ToBase64String(CipherText);
}

private static string Decrypt(string CipherText)
{
    RijndaelManaged aes = new RijndaelManaged();
    aes.BlockSize = 128;
    aes.KeySize = 256;

    /// In Java, Same with below code
    /// Cipher _Cipher = Cipher.getInstance("AES");  // Java Code
    aes.Mode = CipherMode.ECB;

    byte[] keyArr = Convert.FromBase64String(keyStr);
    byte[] KeyArrBytes32Value = new byte[32];
    Array.Copy(keyArr, KeyArrBytes32Value, 32);

    aes.Key = KeyArrBytes32Value;

    ICryptoTransform decrypto = aes.CreateDecryptor();

    byte[] encryptedBytes = Convert.FromBase64CharArray(CipherText.ToCharArray(), 0, CipherText.Length);
    byte[] decryptedData = decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
    return ASCIIEncoding.UTF8.GetString(decryptedData);
}

通过使用 upper c# 函数,现在我可以解密和读取密文。

以下是我一次又一次出错后发现的。

 CryptographicException: Padding is invalid and cannot be removed.

 Solution:
_RijndaelManaged.Padding = CipherMode.xxx;    ///should toggle here
_RijndaelManaged.Padding = PaddingMode.xxx;   ///should toggle here

CryptographicException: Length of the data to decrypt is invalid.
CryptographicException: Specified initialization vector (IV) does not match the block size for this algorithm.

Solution
1. _RijndaelManaged.BlockSize = 128; /// Must be
2. _RijndaelManaged.KeySize = 256; /// Must be
3. _RijndaelManaged.Key = Byte Array Size must be 32 in length ( more detail 32*8 = 256 KeySize )
4. _RijndaelManaged.IV = Byte Array Size must be 16 in length ( more detail 16*8 = 128 BlockSize)

但是出于安全原因,我认为我不应该使用 ECB 模式。

根据

  1. ECB 模式不安全
  2. StackOverFlow 链接

所以我又用Java和C#修改了一遍。

 // Java code - Cipher mode CBC version.
// CBC version need Initialization vector IV.
// Reference from https://stackoverflow.com/questions/6669181/why-does-my-aes-encryption-throws-an-invalidkeyexception/6669812#6669812

import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class CryptoSecurity {

    public static String key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    public static byte[] key_Array = Base64.decodeBase64(key);

    public static String encrypt(String strToEncrypt)
    {
        try
        {
            //Cipher _Cipher = Cipher.getInstance("AES");
            //Cipher _Cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");

            // Initialization vector.
            // It could be any value or generated using a random number generator.
            byte[] iv = { 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 7, 7, 7, 7 };
            IvParameterSpec ivspec = new IvParameterSpec(iv);

            Key SecretKey = new SecretKeySpec(key_Array, "AES");
            _Cipher.init(Cipher.ENCRYPT_MODE, SecretKey, ivspec);

            return Base64.encodeBase64String(_Cipher.doFinal(strToEncrypt.getBytes()));
        }
        catch (Exception e)
        {
            System.out.println("[Exception]:"+e.getMessage());
        }
        return null;
    }

    public static String decrypt(String EncryptedMessage)
    {
        try
        {
            //Cipher _Cipher = Cipher.getInstance("AES");
            //Cipher _Cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");

            // Initialization vector.
            // It could be any value or generated using a random number generator.
            byte[] iv = { 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 7, 7, 7, 7 };
            IvParameterSpec ivspec = new IvParameterSpec(iv);

            Key SecretKey = new SecretKeySpec(key_Array, "AES");
            _Cipher.init(Cipher.DECRYPT_MODE, SecretKey, ivspec);

            byte DecodedMessage[] = Base64.decodeBase64(EncryptedMessage);
            return new String(_Cipher.doFinal(DecodedMessage));

        }
        catch (Exception e)
        {
            System.out.println("[Exception]:"+e.getMessage());

        }
        return null;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        StringBuilder sb = new StringBuilder();

        sb.append("xml file string ...");

        String outputOfEncrypt = encrypt(sb.toString());
        System.out.println("[CryptoSecurity.outputOfEncrypt]:"+outputOfEncrypt);

        String outputOfDecrypt = decrypt(outputOfEncrypt);
        //String outputOfDecrypt = decrypt(sb.toString());
        System.out.println("[CryptoSecurity.outputOfDecrypt]:"+outputOfDecrypt);
    }

}

在 C# 中,我将其修改如下。

 // C# Code, CipherMode.CBC
// CBC version need Initialization vector IV.

public static string keyStr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
private static string Encrypt(string PlainText)
{
    RijndaelManaged aes = new RijndaelManaged();
    aes.BlockSize = 128;
    aes.KeySize = 256;

    // It is equal in java
    /// Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    aes.Mode = CipherMode.CBC;
    aes.Padding = PaddingMode.PKCS7;

    byte[] keyArr = Convert.FromBase64String(keyStr);
    byte[] KeyArrBytes32Value = new byte[32];
    Array.Copy(keyArr, KeyArrBytes32Value, 32);

    // Initialization vector.
    // It could be any value or generated using a random number generator.
    byte[] ivArr = { 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 7, 7, 7, 7 };
    byte[] IVBytes16Value = new byte[16];
    Array.Copy(ivArr, IVBytes16Value, 16);

    aes.Key = KeyArrBytes32Value;
    aes.IV = IVBytes16Value;

    ICryptoTransform encrypto = aes.CreateEncryptor();

    byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(PlainText);
    byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
    return Convert.ToBase64String(CipherText);

}

private static string Decrypt(string CipherText)
{
    RijndaelManaged aes = new RijndaelManaged();
    aes.BlockSize = 128;
    aes.KeySize = 256;

    aes.Mode = CipherMode.CBC;
    aes.Padding = PaddingMode.PKCS7;

    byte[] keyArr = Convert.FromBase64String(keyStr);
    byte[] KeyArrBytes32Value = new byte[32];
    Array.Copy(keyArr, KeyArrBytes32Value, 32);

    // Initialization vector.
    // It could be any value or generated using a random number generator.
    byte[] ivArr = { 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 7, 7, 7, 7 };
    byte[] IVBytes16Value = new byte[16];
    Array.Copy(ivArr, IVBytes16Value, 16);

    aes.Key = KeyArrBytes32Value;
    aes.IV = IVBytes16Value;

    ICryptoTransform decrypto = aes.CreateDecryptor();

    byte[] encryptedBytes = Convert.FromBase64CharArray(CipherText.ToCharArray(), 0, CipherText.Length);
    byte[] decryptedData = decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
    return ASCIIEncoding.UTF8.GetString(decryptedData);
}

现在一切正常。

有关 AES 的更多详细信息, 请单击我由 @deathismyfriend 提供的此链接

它也非常有用。

原文由 Frank Myat Thu 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题