简体   繁体   中英

Problems with AES encryption (encrypted string is not what it should be - Java & .NET)

I am trying to encrypt a plain text string to integrate with a third-party system using AES encryption. The receiver does not have any document to explain what their encryption algorithm is, and they've simply shared the below Java code to explain how the encryption works:

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
    
public class CipherData {
    private static final String SALT = "pa(MS";                               //Salt. Required for key generation
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";           //Encryption Algorithm/Cipher/Padding
    private static String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
    private static int KEY_SIZE = 256;  
    private static final String TOKEN = "Pre123454sk";        //Password. Used for Key Generation
    private static String initVector = "pre1234Init12345";    //IV. Required for Key generation
    private static int KEY_ITERATIONS = 22123;

    public static String encrypt(String value) throws Exception {    //Encryption Module
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        Key key = generateKey();
        cipher.init(1, key, iv);
        byte[] encrypted = cipher.doFinal(value.getBytes());
        return base64Encode(encrypted);
    }

    public static String decrypt(String value) throws Exception {   //Decryption module
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        Key key = generateKey();
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(2, key, iv);
        byte[] original = cipher.doFinal(base64Decode(value));
        return new String(original);
    }

    private static Key generateKey() throws Exception {   //AES Key Generation
        byte[] saltBytes = SALT.getBytes("UTF-8");
        SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
        PBEKeySpec spec = new PBEKeySpec(TOKEN.toCharArray(), saltBytes, KEY_ITERATIONS, KEY_SIZE);
        SecretKey secretKey = skf.generateSecret(spec);
        SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
        return key;
    }

    private static String base64Encode(byte[] token) {
        String encoded = DatatypeConverter.printBase64Binary(token);
        return encoded;
    }

    private static byte[] base64Decode(String token) {
        return DatatypeConverter.parseBase64Binary(token);
    }

    public static void main(String[] args) {
        String clearPAN="ABCDE1234F", encrPAN="", decrPAN="";
        try {
            encrPAN=encrypt(clearPAN);
            decrPAN=decrypt(encrPAN);
            System.out.println("Clear PAN: " + clearPAN);
            System.out.println("Encrypted PAN: " + encrPAN);
            System.out.println("Decrypted PAN: " + decrPAN);
        }
        catch (Exception e) {
            System.out.print("Exception Occured in main()");
            e.printStackTrace();
        }
    }
}

I am developing my application in .NET, and I'm unable to get the same string as the receiver gets by using the above Java code, and we're out of ideas on what we should be doing.

Here is my .NET algorithm (I have just inferred this logic from the Java code, and this is my first time with Java, so please be kind if I've made a stupid error):

private static String TOKEN = "Pre123454sk";
    private static String initVector = "pre1234Init12345";
    private static string Encryption(string plainText)
    {
        using (var aesProvider = new AesCryptoServiceProvider())
        {
            //String SALT = "pa(MS";

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
            pdb.IterationCount = 22123;
            aesProvider.KeySize = 256;
            aesProvider.Padding = PaddingMode.PKCS7;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Key = pdb.GetBytes(16);
            aesProvider.IV = Encoding.UTF8.GetBytes(initVector);

            Byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            ICryptoTransform encryptor = aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV);
            using (var memStream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                    cryptoStream.FlushFinalBlock();
                    Byte[] cipherTextBytes = memStream.ToArray();
                    memStream.Close();
                    memStream.Flush();
                    cryptoStream.Close();
                    return Convert.ToBase64String(cipherTextBytes);
                }
            }
        }
    }

    private static string Decryption(string plainText)
    {
        using (var aesProvider = new AesCryptoServiceProvider())
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
            pdb.IterationCount = 22123;
            aesProvider.KeySize = 256;
            aesProvider.Padding = PaddingMode.Zeros;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Key = pdb.GetBytes(16);
            aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
            byte[] cipherTextBytes1 = Convert.FromBase64String(plainText);
            ICryptoTransform decryptor = aesProvider.CreateDecryptor(aesProvider.Key, aesProvider.IV);
            using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes1))
            {
                using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
                    {
                        return streamReader.ReadToEnd();
                    }
                }
            }
        }
    }

I've already tried replacing the TOKEN value with the IV value in the PasswordDeriveBytes() function, but it's still giving me the same error (the receiving system is unable to decrypt this request).

  • Here is the plain text that I'm trying to encrypt: CXQPM4656P
  • Here is what I'm getting from the .NET code: pKjfaKu4AxBEbagiAWoLkg==
  • Here is what I should be getting: kY8lgWh97fqkm9gS8zgMHg==

I'm out of ideas at this point, and I have no support from the other end. Would be great if someone can help me figure it out.

Thanks to Topaco (first comment in the question), I was able to sort it out.

Here is Topaco's original answer:

The Java code uses PBKDF2 with HMAC/SHA1, the C# code an algorithm based on PBKDF1. For PBKDF2 in the C# code PasswordDeriveBytes has to be replaced by Rfc2898DeriveBytes (with HMAC/SHA1 as default). Note that the .NET implementation expects a minimum 8 bytes salt. Also, Java uses a 32 bytes key, the C# code a 16 bytes key. With consistent key derivation and key size, the generated ciphertexts are identical. Topaco

The only thing I had to change was to upgrade from .NET 5 to .NET 6.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM