Recent

Author Topic: [Solved] AES Decrypt  (Read 4445 times)

lainz

  • Hero Member
  • *****
  • Posts: 4460
    • https://lainz.github.io/
[Solved] AES Decrypt
« on: May 11, 2021, 05:20:25 pm »
Hi, I'm using CryptoJS to encrypt a JSON with AES in the browser.

Now I need to decrypt that with FPC.

I know there is TDPC_Rijndael with Dcpcrypt package, but I don't know how to use it to decrypt.

I have this code working in Java:

Code: Pascal  [Select][+][-]
  1. package ...;
  2.  
  3. import android.os.Build;
  4.  
  5. import androidx.annotation.RequiresApi;
  6.  
  7. import java.nio.charset.StandardCharsets;
  8. import java.security.DigestException;
  9. import java.security.InvalidAlgorithmParameterException;
  10. import java.security.InvalidKeyException;
  11. import java.security.MessageDigest;
  12. import java.security.NoSuchAlgorithmException;
  13. import java.util.Arrays;
  14. import java.util.Base64;
  15.  
  16. import javax.crypto.BadPaddingException;
  17. import javax.crypto.Cipher;
  18. import javax.crypto.IllegalBlockSizeException;
  19. import javax.crypto.NoSuchPaddingException;
  20. import javax.crypto.spec.IvParameterSpec;
  21. import javax.crypto.spec.SecretKeySpec;
  22.  
  23. public class DecryptAES {
  24.  
  25.     public static String decrypt(String secret, String cipherText) throws NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
  26.  
  27.         byte[] cipherData = android.util.Base64.decode(cipherText, 0);
  28.         byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
  29.  
  30.         MessageDigest md5 = MessageDigest.getInstance("MD5");
  31.         final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
  32.         SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
  33.         IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
  34.  
  35.         byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
  36.         Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
  37.         aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
  38.         byte[] decryptedData = aesCBC.doFinal(encrypted);
  39.         String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);
  40.  
  41.         return decryptedText;
  42.     }
  43.  
  44.     /**
  45.      * Generates a key and an initialization vector (IV) with the given salt and password.
  46.      * <p>
  47.      * This method is equivalent to OpenSSL's EVP_BytesToKey function
  48.     * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
  49.     * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
  50.     * </p>
  51.     * @param keyLength the length of the generated key (in bytes)
  52.     * @param ivLength the length of the generated IV (in bytes)
  53.     * @param iterations the number of digestion rounds
  54.     * @param salt the salt data (8 bytes of data or <code>null</code>)
  55.     * @param password the password data (optional)
  56.     * @param md the message digest algorithm to use
  57.     * @return an two-element array with the generated key and IV
  58.     */
  59.    public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
  60.  
  61.        int digestLength = md.getDigestLength();
  62.        int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
  63.        byte[] generatedData = new byte[requiredLength];
  64.        int generatedLength = 0;
  65.  
  66.        try {
  67.            md.reset();
  68.  
  69.            // Repeat process until sufficient data has been generated
  70.            while (generatedLength < keyLength + ivLength) {
  71.  
  72.                // Digest data (last digest if available, password data, salt if available)
  73.                if (generatedLength > 0)
  74.                    md.update(generatedData, generatedLength - digestLength, digestLength);
  75.                md.update(password);
  76.                if (salt != null)
  77.                    md.update(salt, 0, 8);
  78.                md.digest(generatedData, generatedLength, digestLength);
  79.  
  80.                // additional rounds
  81.                for (int i = 1; i < iterations; i++) {
  82.                    md.update(generatedData, generatedLength, digestLength);
  83.                    md.digest(generatedData, generatedLength, digestLength);
  84.                }
  85.  
  86.                generatedLength += digestLength;
  87.            }
  88.  
  89.            // Copy key and IV into separate byte arrays
  90.            byte[][] result = new byte[2][];
  91.            result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
  92.            if (ivLength > 0)
  93.                result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
  94.  
  95.            return result;
  96.  
  97.        } catch (DigestException e) {
  98.            throw new RuntimeException(e);
  99.  
  100.        } finally {
  101.            // Clean out temporary data
  102.            Arrays.fill(generatedData, (byte)0);
  103.        }
  104.    }
  105.  
  106.    public static void main(String[] args) {
  107.  
  108.    }
  109. }

As well I have this example on how to use Rijndael:

Code: Pascal  [Select][+][-]
  1. var
  2.   Cipher : TDCP_rijndael;
  3.   Key    : AnsiString;
  4.   IV     : AnsiString;
  5.  
  6.   CBC    : AnsiString;
  7.   Buffer : AnsiString;
  8. begin
  9.   Result := '';
  10.   Key  := ??;
  11.   IV   := ??;
  12.   CBC :=DecodeStringBase64(sText);
  13.   Cipher := TDCP_rijndael.Create(nil);
  14.   try
  15.     Cipher.Init(Key[1], 128, @IV[1]);
  16.     SetLength(Buffer, Length(CBC));
  17.     Cipher.DecryptCBC(CBC[1], Buffer[1], 16);
  18.     Result := Buffer;
  19.   finally
  20.     Cipher.Free;
  21.   end;

Here the problem is how to fill the Key and IV, and use the salt as well like is done with the Java code.

Test data:
Code: Pascal  [Select][+][-]
  1. text: 'hello world'
  2. pass: 'o0t878'
  3. encrypted: 'U2FsdGVkX1/YzdOqaSV3F1JhH/80gyk7qK5VtJdpj5Y='

Any help is appreciated.
« Last Edit: May 15, 2021, 05:14:52 pm by lainz »

xinyiman

  • Hero Member
  • *****
  • Posts: 2256
    • Lazarus and Free Pascal italian community
Win10, Ubuntu and Mac
Lazarus: 2.1.0
FPC: 3.3.1

loaded

  • Hero Member
  • *****
  • Posts: 824
Re: AES Decrypt
« Reply #2 on: May 11, 2021, 06:02:42 pm »
In the webbrowser you use in fpc;
- Display an html that you can create by yourself, containing an 'input' object. (Alternatively, alert can also be used, but the above method is better for me)
- Run Javascript used by CryptoJS for encryption and decryption with execScript in the browser.
Write a javascript method that passes the -decrypt result to the input object and run it in the browser with execScript.
The solution will be in the palm of your hand.
« Last Edit: May 11, 2021, 06:10:51 pm by loaded »
Check out  loaded on Strava
https://www.strava.com/athletes/109391137

PierceNg

  • Sr. Member
  • ****
  • Posts: 369
    • SamadhiWeb
Re: AES Decrypt
« Reply #3 on: May 12, 2021, 03:48:18 am »
Code: Java  [Select][+][-]
  1. final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);

This is how the Java code is generating IV (initialization vector) and cipher key. The code requires secret (password 'o0t878' in your post presumably, passed in as a UTF8 string) and saltData. On the encrypting side, salt is usually randomly generated. The decrypting side must know what the salt is to be able to decrypt.

Code: Java  [Select][+][-]
  1. byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);

Ok, for decrypting, saltData is included in the ciphertext.

However, (without attempting to decode), looks like your cipher text  'U2FsdGVkX1/YzdOqaSV3F1JhH/80gyk7qK5VtJdpj5Y=' is base64- or something-encoded, so you'll need to decode it into a byte array.

Code: Java  [Select][+][-]
  1. Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");

And in the Java code the cipher used is AES/CBC/PKCS5Padding. CBC means cipher block chaining and is a standard cipher mode. PKCS5 is a padding mechanism. Does the Dcpcrypt package support this cipher combo?

For generating IV and key, you could port the Java code since it shows how it's done, but for PKCS5 padding, the crypto library needs to support.

If any of ciphertext format (usually byte array, and the string you posted looks encoded), salt, cipher IV and key, cipher mode, padding is wrong, then decryption will fail.

lainz

  • Hero Member
  • *****
  • Posts: 4460
    • https://lainz.github.io/
Re: AES Decrypt
« Reply #4 on: May 14, 2021, 10:30:03 pm »
Hi, I think I must learn first the basics of cryptography, else this can be done wrong.

I change the goal of this thread, still at cryptography: How I can start to learn cryptography? Any online documentation you recommend?

Xor-el

  • Sr. Member
  • ****
  • Posts: 404
Re: AES Decrypt
« Reply #5 on: May 15, 2021, 03:01:33 am »
Hi, I'm using CryptoJS to encrypt a JSON with AES in the browser.

Now I need to decrypt that with FPC.

I know there is TDPC_Rijndael with Dcpcrypt package, but I don't know how to use it to decrypt.

I have this code working in Java:

Code: Pascal  [Select][+][-]
  1. package ...;
  2.  
  3. import android.os.Build;
  4.  
  5. import androidx.annotation.RequiresApi;
  6.  
  7. import java.nio.charset.StandardCharsets;
  8. import java.security.DigestException;
  9. import java.security.InvalidAlgorithmParameterException;
  10. import java.security.InvalidKeyException;
  11. import java.security.MessageDigest;
  12. import java.security.NoSuchAlgorithmException;
  13. import java.util.Arrays;
  14. import java.util.Base64;
  15.  
  16. import javax.crypto.BadPaddingException;
  17. import javax.crypto.Cipher;
  18. import javax.crypto.IllegalBlockSizeException;
  19. import javax.crypto.NoSuchPaddingException;
  20. import javax.crypto.spec.IvParameterSpec;
  21. import javax.crypto.spec.SecretKeySpec;
  22.  
  23. public class DecryptAES {
  24.  
  25.     public static String decrypt(String secret, String cipherText) throws NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
  26.  
  27.         byte[] cipherData = android.util.Base64.decode(cipherText, 0);
  28.         byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
  29.  
  30.         MessageDigest md5 = MessageDigest.getInstance("MD5");
  31.         final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
  32.         SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
  33.         IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
  34.  
  35.         byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
  36.         Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
  37.         aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
  38.         byte[] decryptedData = aesCBC.doFinal(encrypted);
  39.         String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);
  40.  
  41.         return decryptedText;
  42.     }
  43.  
  44.     /**
  45.      * Generates a key and an initialization vector (IV) with the given salt and password.
  46.      * <p>
  47.      * This method is equivalent to OpenSSL's EVP_BytesToKey function
  48.     * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
  49.     * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
  50.     * </p>
  51.     * @param keyLength the length of the generated key (in bytes)
  52.     * @param ivLength the length of the generated IV (in bytes)
  53.     * @param iterations the number of digestion rounds
  54.     * @param salt the salt data (8 bytes of data or <code>null</code>)
  55.     * @param password the password data (optional)
  56.     * @param md the message digest algorithm to use
  57.     * @return an two-element array with the generated key and IV
  58.     */
  59.    public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
  60.  
  61.        int digestLength = md.getDigestLength();
  62.        int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
  63.        byte[] generatedData = new byte[requiredLength];
  64.        int generatedLength = 0;
  65.  
  66.        try {
  67.            md.reset();
  68.  
  69.            // Repeat process until sufficient data has been generated
  70.            while (generatedLength < keyLength + ivLength) {
  71.  
  72.                // Digest data (last digest if available, password data, salt if available)
  73.                if (generatedLength > 0)
  74.                    md.update(generatedData, generatedLength - digestLength, digestLength);
  75.                md.update(password);
  76.                if (salt != null)
  77.                    md.update(salt, 0, 8);
  78.                md.digest(generatedData, generatedLength, digestLength);
  79.  
  80.                // additional rounds
  81.                for (int i = 1; i < iterations; i++) {
  82.                    md.update(generatedData, generatedLength, digestLength);
  83.                    md.digest(generatedData, generatedLength, digestLength);
  84.                }
  85.  
  86.                generatedLength += digestLength;
  87.            }
  88.  
  89.            // Copy key and IV into separate byte arrays
  90.            byte[][] result = new byte[2][];
  91.            result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
  92.            if (ivLength > 0)
  93.                result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
  94.  
  95.            return result;
  96.  
  97.        } catch (DigestException e) {
  98.            throw new RuntimeException(e);
  99.  
  100.        } finally {
  101.            // Clean out temporary data
  102.            Arrays.fill(generatedData, (byte)0);
  103.        }
  104.    }
  105.  
  106.    public static void main(String[] args) {
  107.  
  108.    }
  109. }

As well I have this example on how to use Rijndael:

Code: Pascal  [Select][+][-]
  1. var
  2.   Cipher : TDCP_rijndael;
  3.   Key    : AnsiString;
  4.   IV     : AnsiString;
  5.  
  6.   CBC    : AnsiString;
  7.   Buffer : AnsiString;
  8. begin
  9.   Result := '';
  10.   Key  := ??;
  11.   IV   := ??;
  12.   CBC :=DecodeStringBase64(sText);
  13.   Cipher := TDCP_rijndael.Create(nil);
  14.   try
  15.     Cipher.Init(Key[1], 128, @IV[1]);
  16.     SetLength(Buffer, Length(CBC));
  17.     Cipher.DecryptCBC(CBC[1], Buffer[1], 16);
  18.     Result := Buffer;
  19.   finally
  20.     Cipher.Free;
  21.   end;

Here the problem is how to fill the Key and IV, and use the salt as well like is done with the Java code.

Test data:
Code: Pascal  [Select][+][-]
  1. text: 'hello world'
  2. pass: 'o0t878'
  3. encrypted: 'U2FsdGVkX1/YzdOqaSV3F1JhH/80gyk7qK5VtJdpj5Y='

Any help is appreciated.

The demo attached below should solve your issue, However do note that this is a very bad approach to generate a salt and IV.
There are specialized KDF's for that such as PBKDF2, Scrypt, Argon2. all these KDF's mentioned are well supported by CryptoLib.
Please do note you need the following packages, they can be gotten in OPM.

CryptoLib4Pascal
SimpleBaseLib4Pascal
HashLib4Pascal
« Last Edit: May 15, 2021, 03:06:50 am by Xor-el »

Xor-el

  • Sr. Member
  • ****
  • Posts: 404
Re: AES Decrypt
« Reply #6 on: May 15, 2021, 03:04:37 am »
Hi, I think I must learn first the basics of cryptography, else this can be done wrong.

I change the goal of this thread, still at cryptography: How I can start to learn cryptography? Any online documentation you recommend?

Glad you agree that Cryptography is not something to be rushed.
that been said, I suggest you begin by understanding symmetric and asymmetric Cryptography for a start.
Then you proceed to proper practices in Crypto.

PierceNg

  • Sr. Member
  • ****
  • Posts: 369
    • SamadhiWeb
Re: AES Decrypt
« Reply #7 on: May 15, 2021, 05:08:38 am »
Hi, I think I must learn first the basics of cryptography, else this can be done wrong.

I change the goal of this thread, still at cryptography: How I can start to learn cryptography? Any online documentation you recommend?

You can pick something from this list to start: https://github.com/sobolevn/awesome-cryptography

Study and practice, basically.

lainz

  • Hero Member
  • *****
  • Posts: 4460
    • https://lainz.github.io/
Re: AES Decrypt
« Reply #8 on: May 15, 2021, 05:14:42 pm »
@PierceNg and @Xor-el thanks for the help, this thread is solved. Start to study and practice for me.

 

TinyPortal © 2005-2018