C# Encrypt/Decrypt from Java AES/CBC/PKCS5Padding

主宰稳场 提交于 2021-02-20 02:14:11

问题


I'm facing an issue trying to Decrypt a string which has been encrypted in Java with the following properties (Java code)

public static Builder getDefaultBuilder(String key, String salt, byte[] iv) {
        return new Builder()
                .setIv(iv)
                .setKey(key)
                .setSalt(salt)
                .setKeyLength(128)
                .setKeyAlgorithm("AES")
                .setCharsetName("UTF8")
                .setIterationCount(1)
                .setDigestAlgorithm("SHA1")
                .setBase64Mode(Base64.DEFAULT)
                .setAlgorithm("AES/CBC/PKCS5Padding")
                .setSecureRandomAlgorithm("SHA1PRNG")
                .setSecretKeyType("PBKDF2WithHmacSHA1");
    }

This is my code so far (C#)

public string DecryptText(string encryptedString)
    {
        using (myRijndael = new RijndaelManaged())
        {
            myRijndael.Key = Convert.FromBase64String(encryptionKey);
            myRijndael.IV = new byte[16];
            myRijndael.Mode = CipherMode.CBC;
            myRijndael.Padding = PaddingMode.PKCS7;

            Byte[] ourEnc = Convert.FromBase64String(encryptedString);
            string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);

            return ourDec;
        }
    }

protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
    {
        // Check arguments. 
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("Key");

        // Declare the string used to hold 
        // the decrypted text. 
        string plaintext = null;

        // Create an RijndaelManaged object 
        // with the specified key and IV. 
        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Key = Key;
            rijAlg.IV = IV;

            // Create a decrytor to perform the stream transform.
            ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

            // Create the streams used for decryption. 
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {

                        // Read the decrypted bytes from the decrypting stream 
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }

        }

        return plaintext;

    }

But when I try to decrypt I get the following exception "System.Security.Cryptography.CryptographicException: 'Specified key is not a valid size for this algorithm.' ".

The origin of the Java code resides here https://github.com/simbiose/Encryption/blob/master/Encryption/main/se/simbio/encryption/Encryption.java

This is the Java code when encrypting

public String encrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException {
    if (data == null) return null;
    SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
    byte[] dataBytes = data.getBytes(mBuilder.getCharsetName());
    Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
    return Base64.encodeToString(cipher.doFinal(dataBytes), mBuilder.getBase64Mode());
}

private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
    SecretKeyFactory factory = SecretKeyFactory.getInstance(mBuilder.getSecretKeyType());
    KeySpec spec = new PBEKeySpec(key, mBuilder.getSalt().getBytes(mBuilder.getCharsetName()), mBuilder.getIterationCount(), mBuilder.getKeyLength());
    SecretKey tmp = factory.generateSecret(spec);
    return new SecretKeySpec(tmp.getEncoded(), mBuilder.getKeyAlgorithm());
}


private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
    MessageDigest messageDigest = MessageDigest.getInstance(mBuilder.getDigestAlgorithm());
    messageDigest.update(key.getBytes(mBuilder.getCharsetName()));
    return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING).toCharArray();
}

I've been struggling with this for two day since I haven't really worked a lot with encryption, so any help would be greatly appreciated.

Thanks!

Update: Whole class

public sealed class MyCryptoClass
{
    protected RijndaelManaged myRijndael;

    private static string encryptionKey = "random";

    // Singleton pattern used here with ensured thread safety
    protected static readonly MyCryptoClass _instance = new MyCryptoClass();
    public static MyCryptoClass Instance
    {
        get { return _instance; }
    }

    public MyCryptoClass()
    {

    }

    public string DecryptText(string encryptedString)
    {
        using (myRijndael = new RijndaelManaged())
        {
            myRijndael.Key = Convert.FromBase64String(encryptionKey);
            myRijndael.IV = new byte[16];
            myRijndael.Mode = CipherMode.CBC;
            myRijndael.Padding = PaddingMode.PKCS7;

            Byte[] ourEnc = Convert.FromBase64String(encryptedString);
            string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);

            return ourDec;
        }
    }


    public string EncryptText(string plainText)
    {
        using (myRijndael = new RijndaelManaged())
        {

            myRijndael.Key = HexStringToByte(encryptionKey);
            myRijndael.IV = HexStringToByte(initialisationVector);
            myRijndael.Mode = CipherMode.CBC;
            myRijndael.Padding = PaddingMode.PKCS7;

            byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
            string encString = Convert.ToBase64String(encrypted);

            return encString;
        }
    }


    protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
    {
        // Check arguments. 
        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;
        // Create an RijndaelManaged object 
        // with the specified key and IV. 
        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Key = Key;
            rijAlg.IV = IV;

            // Create a decrytor to perform the stream transform.
            ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

            // Create the streams used for encryption. 
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {

                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }


        // Return the encrypted bytes from the memory stream. 
        return encrypted;

    }

    protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
    {
        // Check arguments. 
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("Key");

        // Declare the string used to hold 
        // the decrypted text. 
        string plaintext = null;

        // Create an RijndaelManaged object 
        // with the specified key and IV. 
        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Key = Key;
            rijAlg.IV = IV;

            // Create a decrytor to perform the stream transform.
            ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

            // Create the streams used for decryption. 
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {

                        // Read the decrypted bytes from the decrypting stream 
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }

        }

        return plaintext;

    }

    public static void GenerateKeyAndIV()
    {
        // This code is only here for an example
        RijndaelManaged myRijndaelManaged = new RijndaelManaged();
        myRijndaelManaged.Mode = CipherMode.CBC;
        myRijndaelManaged.Padding = PaddingMode.PKCS7;

        myRijndaelManaged.GenerateIV();
        myRijndaelManaged.GenerateKey();
        string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
        string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
    }

    protected static byte[] HexStringToByte(string hexString)
    {
        try
        {
            int bytesCount = (hexString.Length) / 2;
            byte[] bytes = new byte[bytesCount];
            for (int x = 0; x < bytesCount; ++x)
            {
                bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
            }
            return bytes;
        }
        catch
        {
            throw;
        }
    }

    public static string ByteArrayToHexString(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:x2}", b);
        return hex.ToString();
    }
}

回答1:


  • Since your MyCryptoClass.encryptionKey corresponds to your Encryption.Builder.mKey you have to generate the secret key on the C#-side i.e. you have to implement on the C# side a counterpart for each Java-method involved in this process. These Java-methods are getSecretKey(char[] key), hashTheKey(String key) and also third.part.android.util.Base64.encodeToString(byte[] input, int flags).

  • Possible C#-Counterpart of the Java-method getSecretKey(char[] key):

    private static byte[] GetSecretKey()
    {
         string hashedKey = GetHashedKey();
         byte[] saltBytes = Encoding.UTF8.GetBytes(salt);                                                // builder.mCharsetName = "UTF8";
         int iterations = 1;                                                                             // builder.mIterationCount = 1
         byte[] secretKey = null;
         using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(hashedKey, saltBytes, iterations))   // builder.mSecretKeyType = "PBKDF2WithHmacSHA1";
         {
              secretKey = rfc2898.GetBytes(16);                                                          // builder.mKeyLength = 128;
              //Console.WriteLine("Key: " + ByteArrayToHexString(secretKey));
         }
         return secretKey;
     }
    

    This method derives a secret key using PBKDF2WithHmacSHA1 with a key, salt, iterationcount and key length as input. The key (more precisely password) used here is a base64-encoded SHA1-hash from MyCryptoClass.encryptionKey provided by GetHashedKey() (see below).

  • Possible C#-Counterpart of the Java-method hashTheKey(String key):

    private static string GetHashedKey()
    {
         string hashBase64 = String.Empty;
         using (SHA1Managed sha1 = new SHA1Managed())                                  // builder.mDigestAlgorithm = "SHA1";
         {
              byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey));   // builder.mCharsetName = "UTF8";
              hashBase64 = Base64ThirdPartAndroid(hash, true);
              //Console.WriteLine("Hash (base64): " + hashBase64);
         }
         return hashBase64;
     }
    

    This method derives a SHA1-hash from MyCryptoClass.encryptionKey and base64-encodes that hash. For the base64-encoding the method Base64ThirdPartAndroid(byte[] arr, bool withoutPadding) (see below) is used.

  • Possible C#-Counterpart of the Java-method third.part.android.util.Base64.encodeToString(byte[] input, int flags) ( https://github.com/simbiose/Encryption/blob/master/Encryption/main/third/part/android/util/Base64.java):

    private static string Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
    {
         string base64String = System.Convert.ToBase64String(arr);
         if (withoutPadding) base64String = base64String.TrimEnd('='); // Remove trailing "="-characters
         base64String += "\n";                                         // Append LF (10)
         //Console.WriteLine("Array as base64 encoded string: " + base64String);
         return base64String;
    }
    

    In the Java code third.part.android.util.Base64.encodeToString(byte[] input, int flags) is used with flags = Base64.NO_PADDING which removes the "="-character at the end of the base64-encoded string. Additionally a line feed (LF, \n, ASCII value: 10) is appended. If a Base64-Encoding is used which doesn't remove the "="-characters or without a terminating line feed, the decryption will fail since the hash is the base of the later generated secret key which have to match on the encryption and on the decryption side. To the best of my knowledge there is no Base64-encoding on the C# side with the necessary characteristics. However, if there is such an encoding you can use it of course.

  • Add all three C#-counterparts to your MyCryptoClass class.

  • Additionally (to the static field encryptionKey) add the static fields initialisationVector, salt and secretKey to your MyCryptoClass-class and assign the following values for testing purposes:

    private static string encryptionKey = "A7zb534OPq59gU7q";
    private static string salt = "JV5k9GoH";
    private static byte[] initialisationVector = Encoding.UTF8.GetBytes("l4iG63jN9Dcg6537");
    private static byte[] secretKey = GetSecretKey();
    

    The type of the parameters corresponds to the type in the Java code (encryptionKey and salt are strings, the initialisationVector is a byte-array). The secret key generated by GetSecretKey() is stored in the byte-array secretKey.

  • In your C# DecryptText- and EncryptText-method set myRijndael.Key and myRijndael.IV to

    myRijndael.Key = secretKey;
    myRijndael.IV = initialisationVector;
    
  • Test the modifications as follows:

    • With your Java encrypt-method encrypt the following plain text:

      Test: The quick brown fox jumps over the lazy dog... 
      

      using the key/salt/iv above with

      mBuilder = Builder.getDefaultBuilder("A7zb534OPq59gU7q","JV5k9GoH","l4iG63jN9Dcg6537".getBytes("UTF-8"));
      
    • The encrypted text is:

      mL4ajZtdRgD8CtGSfJGkT24Ebw4SrGUGKQI6bvBw1ziCO/J7SeLiyIw41zumTHMMD9GOYK+kR79CVcpoaHT9TQ==
      
    • Decrypting this using the C# DecryptText-method gives again the plain text. Below are two test cases:

      static void Main(string[] args)
      {
           // Test 1: Encrypted text from C#
           MyCryptoClass mcc = MyCryptoClass.Instance;
           string encryptedText = mcc.EncryptText("This is a plain text which needs to be encrypted...");
           Console.WriteLine("Encrypted text (base64): " + encryptedText);
           string decryptedText = mcc.DecryptText(encryptedText);
           Console.WriteLine("Decrypted text: " + decryptedText);
      
           // Test 2: Encrypted text from Java
           string javaEncryptedText = "mL4ajZtdRgD8CtGSfJGkT24Ebw4SrGUGKQI6bvBw1ziCO/J7SeLiyIw41zumTHMMD9GOYK+kR79CVcpoaHT9TQ==";
           Console.WriteLine("Encrypted text from Java (base64): " + javaEncryptedText);
           string javaDecryptedText = mcc.DecryptText(javaEncryptedText);
           Console.WriteLine("Decrypted text from Java: " + javaDecryptedText);
      }
      



回答2:


Following is Full C# class which i were able to work as it is like java class At the moment i have only checked encryption part

 public sealed class MyCryptoClass
    {
        protected AesManaged myRijndael;

        private static string encryptionKey = "MyKey";
        private static string salt = "Mysalt";
        private static byte[] initialisationVector = new byte[16];
        //private static byte[] initialisationVector = Encoding.UTF8.GetBytes("l4iG63jN9Dcg6537");
        private static byte[] secretKey = GetSecretKey();


        // Singleton pattern used here with ensured thread safety
        protected static readonly MyCryptoClass _instance = new MyCryptoClass();
        public static MyCryptoClass Instance
        {
            get { return _instance; }
        }

        public MyCryptoClass()
        {

        }
        public string DecryptText(string encryptedString)
        {
            using (myRijndael = new AesManaged())
            {
                myRijndael.Key = Convert.FromBase64String(encryptionKey);
                myRijndael.IV = new byte[16];
                myRijndael.Mode = CipherMode.CBC;
                myRijndael.Padding = PaddingMode.PKCS7;

                Byte[] ourEnc = Convert.FromBase64String(encryptedString);
                string ourDec = DecryptStringFromBytes(ourEnc, myRijndael.Key, myRijndael.IV);

                return ourDec;
            }
        }
        public string EncryptText(string plainText)
        {
            using (myRijndael = new AesManaged())
            {

                myRijndael.Key = secretKey;
                myRijndael.IV = initialisationVector;
                myRijndael.Mode = CipherMode.CBC;
                myRijndael.Padding = PaddingMode.PKCS7;

                byte[] encrypted = EncryptStringToBytes(plainText, myRijndael.Key, myRijndael.IV);
                string encString = Convert.ToBase64String(encrypted);

                return encString;
            }
        }
        protected byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments. 
            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;
            // Create an RijndaelManaged object 
            // with the specified key and IV. 
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for encryption. 
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }
            // Return the encrypted bytes from the memory stream. 
            return encrypted;

        }
        protected string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments. 
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");

            // Declare the string used to hold 
            // the decrypted text. 
            string plaintext = null;

            // Create an RijndaelManaged object 
            // with the specified key and IV. 
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for decryption. 
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream 
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
            return plaintext;
        }

        public static void GenerateKeyAndIV()
        {
            // This code is only here for an example
            AesManaged myRijndaelManaged = new AesManaged();
            myRijndaelManaged.Mode = CipherMode.CBC;
            myRijndaelManaged.Padding = PaddingMode.PKCS7;

            myRijndaelManaged.GenerateIV();
            myRijndaelManaged.GenerateKey();
            string newKey = ByteArrayToHexString(myRijndaelManaged.Key);
            string newinitVector = ByteArrayToHexString(myRijndaelManaged.IV);
        }

        protected static byte[] HexStringToByte(string hexString)
        {
            try
            {
                int bytesCount = (hexString.Length) / 2;
                byte[] bytes = new byte[bytesCount];
                for (int x = 0; x < bytesCount; ++x)
                {
                    bytes[x] = Convert.ToByte(hexString.Substring(x * 2, 2), 16);
                }
                return bytes;
            }
            catch
            {
                throw;
            }
        }

        public static string ByteArrayToHexString(byte[] ba)
        {
            StringBuilder hex = new StringBuilder(ba.Length * 2);
            foreach (byte b in ba)
                hex.AppendFormat("{0:x2}", b);
            return hex.ToString();
        }

        private static byte[] GetSecretKey()
        {
            string hashedKey = GetHashedKey();
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt); // builder.mCharsetName = "UTF8";
            int iterations = 1; // builder.mIterationCount = 1
            byte[] secretKey = null;
            using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(hashedKey, saltBytes, iterations)) // builder.mSecretKeyType = "PBKDF2WithHmacSHA1";
            {
                secretKey = rfc2898.GetBytes(16); // builder.mKeyLength = 128;
                //Console.WriteLine("Key: " + ByteArrayToHexString(secretKey));
            }
            return secretKey;
        }

        private static string GetHashedKey()
        {
            string hashBase64 = String.Empty;
            using (SHA1Managed sha1 = new SHA1Managed()) // builder.mDigestAlgorithm = "SHA1";
            {
                byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey)); // builder.mCharsetName = "UTF8";

                hashBase64 = Base64ThirdPartAndroid(hash, true);
                //hashBase64 = Base64ThirdPartAndroid(hash, true);
                //Console.WriteLine("Hash (base64): " + hashBase64);
            }
            return hashBase64;
        }

        private static string Base64ThirdPartAndroid(byte[] arr, bool withoutPadding)
        {
            string base64String = System.Convert.ToBase64String(arr);
            if (withoutPadding) base64String = base64String.TrimEnd('='); // Remove trailing "="-characters
            base64String += "\n"; // Append LF (10)
            //Console.WriteLine("Array as base64 encoded string: " + base64String);
            return base64String;
        }
    }


来源:https://stackoverflow.com/questions/53580563/c-sharp-encrypt-decrypt-from-java-aes-cbc-pkcs5padding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!