Ssl Alternative - Encrypt Password with JavaScript Submit to PHP to Decrypt

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:

  1. 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.
  2. 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.

The CryptoJS code you posted could be modified as follows to implement this functionality (JavaScript):

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



Leave a reply



Submit