SSL Alternative - encrypt password with JavaScript submit to PHP to decrypt
http://www.jcryption.org/ -- Is the combination you are looking for.
Encryption/Decryption between PHP and Javascript
It's somewhat specific to the case of handling user login, but I proposed a protocol in this answer, and the asker ran with it and coded up an HTTP-sniffer-resistant PHP-to-JavaScript login form implementation.
The essentials of the scheme:
- Generate a random nonce value; this helps prevent replay attacks.
- Send that nonce value and the password salt to the browser along
with the rest of the login form.
- You are storing passwords in salted
and hashed form, right?- When the user enters a password, have the script on the form compute
and send back hash(hash(password, salt), nonce) instead.- When the server receives the form submission, have it compute hash(storedSaltedPassword, nonce) and verify that it equals the submitted
value.
- Retain the nonce value at
the server; don't trust the client to
echo it back to you, or your replay
protection is gone.
The weakness of this scheme is that
the password hashes in the database
are in some sense password-equivalent;
while it's likely infeasible to
extract the original password used to
produce those hashes, knowledge of the
stored hash is sufficient to
impersonate the user on your site.
Encrypt with CryptoJS and decrypt with PHP
You're not doing the same thing on both sides.
IV
You did parse the IV in CryptoJS, but forgot to do it in PHP:$iv_dec = pack('H*', "101112131415161718191a1b1c1d1e1f");
To fix that your IV is wrong, you probably noticed that the first 16 bytes are gibberish. That happens when the IV is wrong. Note that CryptoJS uses CBC mode by default, so the IV has only influence on the first block during decryption. Remove this:$ciphertext_dec = substr($ciphertext_dec, 16);
Padding
You probably noticed that most plaintexts don't come out right. They end with some strange repeated characters at the end. This is the PKCS#7 padding that is applied by default in CryptoJS. You have to remove the padding yourself in PHP. Good thing is that Maarten Bodewes has provided a proper copy paste solution for this here.trim()
might be appropriate for ZeroPadding, but not when a proper padding scheme like the one defined in PKCS#7 is used. You may remove the trim()
call altogether, because it is not useful and may result in unexpected plaintext, becauses zero bytes and whitespace is trimmed from the beginning and end.
OpenSSL Encrypt-Decrypt with CryptoJS
If the PHP code is executed with the plaintext $string = "test";
then the encryption part provides:
MWNjdVlVL1hBWGN2UFlpMG9yMGZBUT09
Regarding the PHP code the following should be noted:- The encryption process Base64 encodes twice. Once explicitly with
base64_encode
, once implicitly by default (s.openssl_encrypt
, 4th parameter). This redundancy is not necessary, i.e. one of the two Base64 encodings should be removed. Analogous for decryption. - The
hash
function returns by default the data hex encoded in lower case. Thus$key
is 64 bytes in size. For AES-256 OpenSSL implicitly uses only the first 32 bytes.
var Sha256 = CryptoJS.SHA256;
var Hex = CryptoJS.enc.Hex;
var Utf8 = CryptoJS.enc.Utf8;
var Base64 = CryptoJS.enc.Base64;
var AES = CryptoJS.AES;
var secret_key = 'thisIsK3y';
var secret_iv = 'tHis1s1v';
var key = Sha256(secret_key).toString(Hex).substr(0,32); // Use the first 32 bytes (see 2.)
var iv = Sha256(secret_iv).toString(Hex).substr(0,16);
// Encryption
var output = AES.encrypt("test", Utf8.parse(key), {
iv: Utf8.parse(iv),
}).toString(); // First Base64 encoding, by default (see 1.)
var output2ndB64 = Utf8.parse(output).toString(Base64); // Second Base64 encoding (see 1.)
console.log(output2ndB64); // MWNjdVlVL1hBWGN2UFlpMG9yMGZBUT09
// Decryption
var decrypted = AES.decrypt(output, Utf8.parse(key), {
iv: Utf8.parse(iv),
}).toString(Utf8);
console.log(decrypted); // test
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Seeking SLOW encrypt/decrypt method or library
You're mixing up the encryption algorithm with key derivation. You don't want your encryption algorithm to be slow, as AES isn't at risk for brute forcing with proper keys.
Presumably since you say "users create the encryption key", you mean it's a scheme using passwords for keys? PBKDF2 is the recommended approach, and is approved by NIST in SP800-132
Since you have this tagged Javascript, it looks like crypto-js supports PBKDF2
When you get a password/passphrase from the user, you'll want to generate a salt and derive the actual AES key from PBKDF2. It doesn't need to be bi-directional, in that the same inputs (password, salt, and number of rounds) will always generate the same key.
Note that not even this is a properly designed cryptosystem. You shouldn't use AES without CBC or CTR mode, and you still need to MAC your data (or use GCM, which will take care of both).
Related Topics
Regular Expression for French Characters
How to Sort Null Values Last Using Eloquent in Laravel
Get the Get Variables from a Url String
Yii Framework 2.0 Login with User Database
Type Hinting: Default Parameters
Are There Any PHP Docblock Parser Tools Available
Usort Issue with Decimal Numbers
PHP Fatal Error: Call to Undefined Function Imagettftext()
Magento Products by Categories
How to Get the Session Id in Laravel
How to Display HTML to the Browser Incrementally Over a Long Period of Time
Phpstorm: How to Add Method Stubs from a Pecl Library That PHPstorm Doesn't Currently Support
Display Custom Data on Woocommerce Admin Order Preview
Echo 'String' While Every Long Loop Iteration (Flush() Not Working)
Missing CSS File and Images After Url Rewrite
How to Sort a PHP Array by an Element Nested Inside