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.