Home > Back-end >  Java to C# Encryption Conversion
Java to C# Encryption Conversion

Time:10-21

I have tried several attempts to port this code over to C# but I can not seem to get the outputs to match. Can someone help point me in the direction of what I am missing. I have included the actual code as well as links to online tools to run the samples and see the results.

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
 
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
 
public class JavaEncryption {
 
    private static SecretKeySpec secretKey;
    private static byte[] key;

    private static void processEncryptionKey(String secret)
    {
        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(secret.getBytes("UTF-8"));
            byte[] base_hash = new byte[20];
            base_hash = crypt.digest();

            byte[] buffer = new byte[64];
            for (int i = 0; i < 64; i  ) buffer [i] = 0x36;
            for (int i = 0; i < 20; i  ) buffer [i] ^= base_hash[i];

            crypt.reset();
            crypt.update(buffer, 0, buffer.length);
            byte[] final_hash = new byte[20];
            final_hash = crypt.digest();
            key = Arrays.copyOf(final_hash, 16); 
             secretKey = new SecretKeySpec(key, "AES");
        }
        catch(NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch(UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
    }

    public static String encrypt(String strToEncrypt, String secret) 
    {
        try
        {
            processEncryptionKey(secret);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
        } 
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: "   e.toString());
        }
        return null;
    }

    public static String decrypt(String strToDecrypt, String secret) 
    {
        try
        {
            processEncryptionKey(secret);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
            return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } 
        catch (Exception e) 
        {
            System.out.println("Error while decrypting: "   e.toString());
        }
        return null;
    }

  public static void main(String[] args){
    final String secretKey = "YVr_wlp8_fFa";

    System.out.println("Secret = "   secretKey);

    String originalString = "testing";
    
    System.out.println("Clear text = "   originalString);

    String encryptedString = JavaEncryption.encrypt(originalString, secretKey) ;
    System.out.println("Encrypted = "   encryptedString);

    String decryptedString = JavaEncryption.decrypt(encryptedString, secretKey);
    System.out.println("Decrypted = "   decryptedString);
  }
}

Here is the C# code I am trying to use but not getting the same result. I'm hoping it is just something small that I am missing:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public class CSharpEncryption
{
    public static void Main()
    {
        Console.WriteLine(new CSharpEncryption().EncryptText());
    }
    
        public string EncryptText()
        {
            using (var aes = new AesManaged())
            {
                aes.Key = CreateSecretKey();
                aes.IV = new byte[16];
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;

                byte[] encrypted = GetEncryptedBytes("testing", aes.Key, aes.IV);
                string encString = Convert.ToBase64String(encrypted);

                return encString;
            }
        }

        protected byte[] GetEncryptedBytes(string textToEncrypt, byte[] Key, byte[] IV)
        {
            byte[] encrypted;

            using (var rij = new RijndaelManaged())
            {
                rij.Key = Key;
                rij.IV = IV;

                ICryptoTransform encryptor = rij.CreateEncryptor(rij.Key, rij.IV);

                using (var msEncrypt = new MemoryStream())
                {
                    using (var cs = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var sw = new StreamWriter(cs))
                        {
                            sw.Write(textToEncrypt);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            return encrypted;
        }

        private static byte[] CreateSecretKey()
        {
            var keyHash = CreateHashedKey();

            byte[] key = null;
            using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(keyHash, new byte[16], 1))
            {
                key = rfc2898.GetBytes(16);
            }

            return key;
        }

        private static string CreateHashedKey()
        {
            var hashBase64 = String.Empty;
            using (SHA1Managed sha1 = new SHA1Managed())
            {
                byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes("YVr_wlp8_fFa"));

                hashBase64 = CreateBase64Hash(hash, true);
            }
            return hashBase64;
        }

        private static string CreateBase64Hash(byte[] arr, bool withoutPadding)
        {
            var base64String = Convert.ToBase64String(arr);
            if (withoutPadding)
            {
                base64String = base64String.TrimEnd('=');
            }

            base64String  = "\n";

            return base64String;
        }
    }

https://dotnetfiddle.net/ktULKG

CodePudding user response:

Both codes differ in the key derivation. For instance, the C# code uses Rfc2898DeriveBytes, which implements PBKDF2. But there is nothing of this in the Java code. So you just have to port the logic of processEncryptionKey() from the Java code to C#, e.g.:

private static byte[] ProcessEncryptionKey(string secret)
{
    using (SHA1Managed sha1 = new SHA1Managed())
    {
        byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(secret));
        byte[] extHash = new byte[64];
        for (int i = 0; i < 64; i  ){
            extHash[i] = (byte)(i < 20 ? hash[i]^0x36 : 0x36);
        }
        byte[] finHash = sha1.ComputeHash(extHash);
        byte[] secretKey = new byte[16];
        Buffer.BlockCopy(finHash, 0, secretKey, 0, secretKey.Length);
        return secretKey;
    }
}

which is called with aes.Key = ProcessEncryptionKey("YVr_wlp8_fFa") in EncryptText(). This gives the same ciphertext as the Java code.

Be aware that the code has vulnerabilities:

  • As key derivation, a dedicated key derivation function such as PBKDF2 should be used.
  • A static IV (like a zero IV) is insecure.
  • Related