Encrypt and decrypt data with JavaScript using the Web Crypto API - Chiffrer et déchiffrer des données avec javaScript avec l'API Web Crypto
Publié le .
[EN] I thought that the https protocol was sufficient to protect data exchanged between a server and a user but a proxy placed between the two can have access to sensitive user data. Here I offer you two functions that allow you to encrypt and decrypt data between a server and a client with javaScript using the Web Crypto API. Be careful though, cryptographic features can be misused and it's easy to make design errors that will expose your users' sensitive data. These functions are shown as examples and are in no way guaranteed. We will also give an example of code in PHP which allows you to decrypt data encrypted in JavaScript and decrypted in PHP on the server side. [FR] Je pensais que le protocole https était suffisant pour protéger les données échangées entre un serveur et un utilisateur mais un proxy placé entre les deux peut avoir accès à des données sensibles des utilisateurs. Je vous propose ici deux fonctions qui permettent de chiffrer et de déchiffrer des données entre un serveur et un client avec javaScript à l'aide de l'API Web Crypto. Attention cependant, les fonctionnalités cryptographiques peuvent être mal utilisées et il est facile de commettre des erreurs de conception qui exposeront les données sensibles de vos utilisateurs. Ces fonctions sont exposées à titre d'exemple et ne sont en aucun cas garanties. Nous donnerons aussi un exemple de code en PHP qui permet de déchiffrer des données chiffrées en javascript coté serveur.
// i. Here we are going to encrypt and decrypt a javascript object,
// but you can only encrypt a string of characters,
// you will simply not have to use the JSON.stringify() function.
//
// Sources :
// https://stackoverflow.com/questions/72201327/decrypt-aes-256-cbc-using-openssl-decrypt-in-php-from-subtle-crypto-javascript-p
// https://stackoverflow.com/a/11562550/9014097
// create an object to encrypt
var My_Object = {
name: 'Raf',
password: 'abcd1234'
}
/**
* async function encrypt_datas( Object );
*
* @param {object} Object
* @return {object} object of datas encrypted { key: '...', iv: '...', cipher: '...'}
*/
async function encrypt_datas( Object ){
/* function array buffer to Base 64 */
function ab2b64( arrayBuffer ){
var binary = '';
var bytes = new Uint8Array( arrayBuffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
// alternative: return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
/* end function array buffer to Base 64 */
const encoder = new TextEncoder();
const encoded = encoder.encode( JSON.stringify(My_Object) );
// we use the AES-CGM algorithm
const encryptionKey = await window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 256,
},
true,
['encrypt', 'decrypt'],
);
const iv = window.crypto.getRandomValues( new Uint8Array(12) );
const cipher = await window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv,
},
encryptionKey,
encoded,
);
const exportedKey = await window.crypto.subtle.exportKey(
'jwk',
encryptionKey,
);
return {
key: exportedKey.k,
iv: ab2b64(iv),
cipher: ab2b64(cipher)
};
}
/**
* end async function encrypt_datas( Object );
*/
/**
* async function decrypt_datas( iv, key, cipher );
*
* @param {string} iv initialization vector defined by encrypt_datas()
* @param {string} key key for decrypt defined by encrypt_datas()
* @param {string} cipher ciphertext defined by encrypt_datas()
* @return {string/object} string or Object decrypted
*/
async function decrypt_datas( iv, key, cipher ){
/* function 64 binary to array buffer */
function b642ab(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
/* end function 64 binary to array buffer */
// IMPORTED KEY
const Key_imported = await window.crypto.subtle.importKey(
'jwk',
{ kty: "oct", k: key, alg: "A256GCM", ext: true },
{ name: "AES-GCM", length: 256 },
true,
["decrypt"]
);
// DECRYPT
const Decrypted = await window.crypto.subtle.decrypt(
{ name: "AES-GCM", length: 256, iv: b642ab(iv) },
Key_imported,
b642ab(cipher)
);
// use TextDecoder()
var Decoder = new TextDecoder();
// return datas decrypted
return Decoder.decode( Decrypted );
}
/**
* end async function decrypt_datas( iv, key, cipher );
*/
// sample :
var Encrypt = encrypt_datas( My_Object );
Encrypt.then( ( datas_encrypted ) => {
console.log( datas_encrypted );
// output :
// {
// cipher: "qs8wFLdt7HLHTAB0qvwujcv7u2PvlkfmPL0OI1j2fv8UjfXUgHdl+zNqyrHcctuCiJja"
// iv: "6MCjhm8yjQ/N46AT"
// key: "hBpiBqMCQQvvUkvLp4nPJMPeeKQbF25SVZ71ckZRVjs"
// }
var Decrypt = decrypt_datas(
datas_encrypted.iv,
datas_encrypted.key,
datas_encrypted.cipher
);
Decrypt.then( ( datas_decrypted ) => {
console.log( JSON.parse( datas_decrypted ) );
// output :
// {
// name: 'Raf',
// password: 'abcd1234'
// }
});
});
[EN] Decode encrypted data sent to server with PHP : [FR] Décoder des données cryptées envoyées au serveur avec PHP :
[PHP]
// datas recived by server
$key_64 = 'qs8wFLdt7HLHTAB0qvwujcv7u2PvlkfmPL0OI1j2fv8UjfXUgHdl+zNqyrHcctuCiJja';
$iv_64 = '6MCjhm8yjQ/N46AT';
$cipher_64 = 'hBpiBqMCQQvvUkvLp4nPJMPeeKQbF25SVZ71ckZRVjs';
$keyB64url = $key_64;
$keyB64 = str_replace(['-','_'], ['+','/'], $keyB64url );
$key = base64_decode($keyB64);
$iv = base64_decode($iv_64);
$payload = base64_decode($cipher_64);
$payloadLen = strlen($payload);
$ciphertext = substr($payload, 0, $payloadLen - 16);
$tag = substr($payload, $payloadLen - 16, 16);
$dec = openssl_decrypt($ciphertext, 'AES-256-GCM', $key, OPENSSL_RAW_DATA, $iv, $tag);
$decrypted = json_decode( $dec, true );
var_dump($decrypted);
// output :
// array(
// 'name' => 'Raf',
// 'password' => 'abcd1234'
// );
Mots clés : javascript, js, Web Crypto API, encrypt and decrypt data with JavaScript, encrypt / decrypt js, encrypt / decrypt object js, javascript, js, API Web Crypto JS, encrypt client side with js and decrypt in server side with PHP, chiffrer et déchiffrer les données avec JavaScript, chiffrer/déchiffrer js, chiffrer/déchiffrer un objet js, chiffrer côté client avec js et déchiffrer côté serveur avec PHP