Reputation: 197
I use Laravel 5.7 and have two apps running on different servers. I want to communicate with encryption. So both ends need to have a certain key. Default laravel encryption is set to AES-256-CBC.
So I thought, I need a 32 byte key like this:
$key = bin2hex(openssl_random_pseudo_bytes(32, $cstrong));
I get a 64 long hex string, that I would like to save on both ends, so both have access to on one side encrypt and the other side decrypt.
But somehow this key is not accepted. Laravel throws: The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths
I also tried to set it to 16 byte, with a 32 character key. Also doesn't work.
However, it does work with setting it to 8 bit, with a 16 character key. But that doesn't make any sense to me?
Laravel itself uses a key something similar like this: base64:X, where X=44 character string.
I read somewhere that AES-256-CBC needs a key of 64 characters of which 44 characters should be base64. I am not sure if this is right but I have a hard time getting this. How does one read a key with this prefix of base64:? How to get this back to a regular string.
For now I have settled with this 16 character string, but doesn't seem right. So how does one generate a valid AES-256-CBC key, and how does one store it? If base64 is required, how does that work? Encoding a 64 generated hex key gives me back a 88 character string back.
Any help is appreciated thanks.
Upvotes: 2
Views: 7127
Reputation: 6683
Laravel uses Illuminate\Support\Facades\Crypt which is designed for end-to-end encryption. Thus meaning you can encrypt and decrypt your data from the database securely but it is not designed for mass communication encryption.
PHP 7.2+ came compact with LibSodium: it is frowned upon to use your own homestead encryption methods. You can enable the extension in your php.ini
configuration or by compiling PHP with the configuration. You can read more on the docs.
Lets say Bob wants to send Alice a message. Bob and Alice both need key pairs to encrypt and decrypt messages, or data.
sodium_crypto_box_keypair();
These should be stored in the database and can be done so using a mutual translator, or latter, like base64*
. Each user should hold a unique keypair which can be issued upon account registration.
You can achieve this by appending your Laravel \App\User.php
file and adding a new fillable, keypair
. Creating a new migration on the table users, appending the column keypair like so:
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('key_pair')->unique()->nullable();
});
}
And then heading over to your App\Http\Controllers\Auth\RegisterController.php
file and appending the create
method.
When you now want to encrypt, you can reference the users keypair. Lets take Bob: Bob has an id of 1. Lets take Alice: Alice has an id of 2.
$bob = User::find(1);
$alice = User::find(2);
We can now encrypt a message from Bob and send it to Alice by creating a sodium crypto box like so:
$kp = sodium_crypto_box_keypair_from_secretkey_and_publickey(
sodium_crypto_box_secretkey($bob->keypair),
sodium_crypto_box_publickey($alice->keypair) # Encrypt with Alices Public Key
);
$cbox = sodium_crypto_box(base64_encode('Hello, Alice. This is Bob.'), ($nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES)), $kp);
We can now send the IV, or known better as a nonce, and the cyprto box to Alice. If Alice now wants to read the message, she can use her private key and Bobs public key to read.
$kp = sodium_crypto_box_keypair_from_secretkey_and_publickey(
sodium_crypto_box_secretkey($alice->keypair),
sodium_crypto_box_publickey($bob->keypair) # Decrypt with Bobs public key
);
echo base64_decode(sodium_crypto_box_open($cbox, $nonce, $kp));
I built a container to make encryption, and signing messages easier with LibSodium. I hope this helps understand the concept and how to use encryption and store with base64*
latter.
Upvotes: 1
Reputation: 17383
I am not in the position to try this myself, but check out the source code here:
/**
* Create a new encryption key for the given cipher.
*
* @param string $cipher
* @return string
*/
public static function generateKey($cipher)
{
return random_bytes($cipher === 'AES-128-CBC' ? 16 : 32);
}
So invoking random_bytes()
(or its openssl equivalent) is all you need to do, either asking for 16 bytes (in case of AES-128-CBC) or 32 bytes (in case of AES-256-CBC). This makes sense, because in the end, keys for AES are nothing but a bunch of random bytes.
What may confuse you is the usage of the term string
. Unlike the common usage of the type string
, in this case it is not a printable string but just a collection of bytes, 16 or 32 of them.
That is why the Base64
translation comes in. It allows you to represent a collection of bytes by means of ASCII characters only. I suspect that the "base64:X, where X=44 character string" value that you are referring to is found in some configuration file, which typically should contain readable characters only. Indeed, translating 32 bytes into Base64 format will yield 44 characters. The base64:
prefix in that case is only used to indicate that the value is stored in Base64 format.
An example illustrating all this:
$key = random_bytes(32);
var_dump($key);
var_dump(base64_encode($key));
gives
string(32) "?9???֔e?N??Y?&[??b?4@O|?\?"
string(44) "45U5nvetGyfWlGWOF06N+VnIJlvwx2L3fzRAT3z5XPY="
The latter is a convenient format to store the key in.
PS: The Encryption - Configuration section of the docuemntation mentions
Before using Laravel's encrypter, you must set a key option in your config/app.php configuration file. You should use the php artisan key:generate command to generate this key
Maybe that command does everything that you are trying to do yourself?
Upvotes: 1