简体   繁体   中英

How can I decode a Laravel encrypted String in VB.Net

I can't seem to find a valid answer to this issue...

I am encrypting a password in Laravel using the built in Crypt::encrypt() helper function and passing in a simple string value (let's say 'abcde' for the purpose of this example) I don't want to install any 3rd party libraries in Laravel if I can help it. I also have my Laravel APP_KEY which is a string value starting with Base64: , but I'm not sure where this comes into it.

This generates me an encrypted string to which I pass down to a VB.Net application. I cannot workout how to simply decode the encrypted string in my application to pluck out the raw password of 'abcde' .

I have tried many examples that I can find online, but I keep getting errors about "Not a valid key" or "Not a valid Base64 string", etc.

Does someone have an example of a code snippet that will decode a Laravel encrypted string in .Net?

The best I can get to is breaking the encrypted string down into iv , value and mac , but I can't seem to get any further.

Any help would be appreciated.

docs

Encrypting A Value You may encrypt a value using the encrypt helper. All encrypted values are encrypted using OpenSSL and the AES-256-CBC cipher. Furthermore, all encrypted values are signed with a message authentication code (MAC) to detect any modifications to the encrypted string

to begin let's look 'inside' Crypt facade in file vendor/laravel/framework/src/Illuminate /Encryption/Encrypter.php

class Encrypter implements EncrypterContract, StringEncrypter
{
     /**
     * The encryption key.
     *
     * @var string
     */
    protected $key;

    /**
     * The algorithm used for encryption.
     *
     * @var string
     */
    protected $cipher;
     /**
     * Create a new encrypter instance.
     *
     * @param  string  $key
     * @param  string  $cipher
     * @return void
     *
     * @throws \RuntimeException
     */
    public function __construct($key, $cipher = 'aes-128-cbc')
    {
        $key = (string) $key;

        if (! static::supported($key, $cipher)) {
            $ciphers = implode(', ', array_keys(self::$supportedCiphers));

            throw new RuntimeException("Unsupported cipher or incorrect key length. Supported ciphers are: {$ciphers}.");
        }

        $this->key = $key;
        $this->cipher = $cipher;
    }
...
}

param key in __construct method is base64 decoded value (somewhere deeper in calls) of APP_KEY from .env file after :

//APP_KEY=base64:uk1lwVRJIWHaMkiGRcFIzAcTzzGaauYtMSy4L+0ViDk=
$key = base64decode('uk1lwVRJIWHaMkiGRcFIzAcTzzGaauYtMSy4L+0ViDk=');
// and then it passes into __construct method

cipher also somewhere deeper is set to AES-256-CBC as mentioned in the docs ( need to mention that this part stays the same across all existing versions of laravel including v9 at the moment )

next looking for encryption to get actions order

/**
     * Encrypt the given value.
     *
     * @param  mixed  $value
     * @param  bool  $serialize
     * @return string
     *
     * @throws \Illuminate\Contracts\Encryption\EncryptException
     */
    public function encrypt($value, $serialize = true)
    {
        // openssl_cipher_iv_length for initialization vector (IV) is 16 bytes for AES-256-CBC
        $iv = random_bytes(openssl_cipher_iv_length(strtolower($this->cipher)));
        // not used in our case
        $tag = '';
        
        $value = self::$supportedCiphers[strtolower($this->cipher)]['aead']
            ? \openssl_encrypt(
                $serialize ? serialize($value) : $value,
                strtolower($this->cipher), $this->key, 0, $iv, $tag
            )
            : \openssl_encrypt(
                $serialize ? serialize($value) : $value,
                strtolower($this->cipher), $this->key, 0, $iv
            );

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        $iv = base64_encode($iv);
        $tag = base64_encode($tag);
        // not used in our case
        $mac = self::$supportedCiphers[strtolower($this->cipher)]['aead']
            ? '' // For AEAD-algoritms, the tag / MAC is returned by openssl_encrypt...
            : $this->hash($iv, $value);
        // here is a tricky part, pay attention that we didn't encode $value in base64
        $json = json_encode(compact('iv', 'value', 'mac', 'tag'), JSON_UNESCAPED_SLASHES);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);
    }

here we have base64 encoded json array with base64 encoded iv and not encoded manually value


c# part (to parse json newtonsoft json , .net472)

from microsoft docs (this part stays 'as is' because php uses same pkcs7 padding by default )

static string DecryptStringFromBytes_Aes(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("IV");

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

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Mode = CipherMode.CBC; // this is defaul value
                aesAlg.Padding = PaddingMode.PKCS7; // this is default value                

                aesAlg.Key = Key;
                aesAlg.IV = IV;
                
                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.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;
        }

and our working parse method to tie all this stuff

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key">app_key from .env after :</param>
        /// <param name="encodedText">result of Crypt::encrypt($value) or Crypt::encryptString($value) methods</param>
        /// <returns>decoded string</returns>
        private string parse(string key, string encodedText)
        {   
            // getting bytes from app_key same as in laravel
            byte[] keyBytes = Convert.FromBase64String(key);             
            
            // going back step by step                     
            // getting from base64 json and getting array of values 
            byte[] dataBytes = Convert.FromBase64String(encodedText);
            string base64decodedData = Encoding.UTF8.GetString(dataBytes);

            var parts = JsonConvert.DeserializeObject<Dictionary<string, string>>(base64decodedData);
            // iv in laravel was encoded with base64 before packing, so undo
            byte[] iv = Convert.FromBase64String(parts["iv"]);            
            
            // and here is the tricky part i've mentioned in encryption method
            // in laravel docs - there were no manual encoding $value to base64
            // but to get valid bytes for decryption we need to treat parts["value"] as 
            // it was encoded
            byte[] valueBytes = Convert.FromBase64String(parts["value"]);

            return DecryptStringFromBytes_Aes(valueBytes, keyBytes, iv);

        }

need to say that i didn't dig very hard into varians of encryption algos and didn't even try this code can deal with algos that uses $mac param, so if someone wants to add more info - you are welcome

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