Шифрование в java и расшифровка в С# Для AES 256 бит

1. У меня есть функция java, которая шифрует xml файл и возвращает зашифрованную строку.

/// 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. У меня есть функция С#, которая расшифровывает сообщение, которое зашифровывается 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 работает хорошо. Но проблема в функции С#, когда я дешифруюсь, я становлюсь ниже сообщения об ошибке

CryptographicException: Length of the data to decrypt is invalid.

Я искал решения, используя ниже ref

но я все равно сталкиваюсь с той же ошибкой. Может ли кто-нибудь дать мне предложение, пожалуйста.

Обновление

Просто измените функцию С# crypto. Ниже перечислены мои списки изменений

  • Размер блока до 128
  • Размер ключа до 256
  • IV Размер до 16
  • Размер ключа до 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��\ ..........

Пожалуйста, позвольте мне снова предложить ваше предложение.

Ответ 1

Я считаю, что blockSize должен быть 128, а keyize - 256. KeyStr должен содержать 32 символа, а идентификатор должен составлять 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);

Ответ 2

После того, как я получил очень полезные предложения от @deathismyfriend и других, я узнал, чего мне не хватает в моей функции С# 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);
}

Используя функцию верхнего С#, теперь я могу расшифровать и прочитать зашифрованный текст.
Ниже то, что я узнал после того, как снова и снова получаю ошибку.

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. fooobar.com/questions/179681/...

Поэтому я снова изменяю его в Java и С#.

// Java code - Cipher mode CBC version.
// CBC version need Initialization vector IV.
// Reference from /questions/179681/why-does-my-aes-encryption-throws-an-invalidkeyexception6669812#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# 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.
Это тоже очень полезно.

Ответ 3

В одном из моих последних проектов мне было поручено создать URL-адрес с зашифрованной частью, чтобы перейти на другой веб-сайт. Они запускали java на своем сервере, тогда как мы развивались в С#.

Я знаю, что это не точное совпадение с тем, что вам было поручено создать, но, надеюсь, это может помочь другим, которые пытаются найти ответы:)

Я получил от своих разработчиков следующее: чтобы построить наше шифрование с

enter image description here

Чтобы выполнить это в С#, я сделал следующее:

    public String Encrypt(String plainText, String key)
    {
        var plainBytes = Encoding.UTF8.GetBytes(plainText);
        return Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(key)));
    }

    private RijndaelManaged GetRijndaelManaged(String secretKey)
    {
        var keyBytes = new byte[16];
        var secretKeyBytes = Encoding.ASCII.GetBytes(secretKey);
        Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
        return new RijndaelManaged
        {
            Mode = CipherMode.ECB,
            Padding = PaddingMode.PKCS7,
            KeySize = 128,
            BlockSize = 128,
            Key = keyBytes,
            IV = keyBytes

        };
    }

    private byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged)
    {
        return rijndaelManaged.CreateEncryptor()
            .TransformFinalBlock(plainBytes, 0, plainBytes.Length);
    }

Ответ 4

У меня была проблема с этим алгоритмом (только на Java), и я сделал несколько изменений.

Diff Image

// Java code - Cipher mode CBC version.
// CBC version need Initialization vector IV.
// Reference from https://stackoverflow.com/info/6669181/why-does-my-aes-encryption-throws-an-invalidkeyexception/6669812#6669812
public static String key = "FFClY170hLrhsDnKUEhJ4FhVOnrpNNFFClY170hLrhsDnKUE";
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_Array = Arrays.copyOf(key_Array, 32);

        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_Array = Arrays.copyOf(key_Array, 32);

        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;
}