Crypter en Javascript, décrypter en PHP, utiliser la cryptographie à clé publique

Je voudrais chiffrer en JavaScript, décrypter en PHP, en utilisant la cryptographie à clé publique. J'ai essayé de trouver des bibliothèques qui peuvent l'accomplir, mais j'ai des problèmes.

Je regarde actuellement openpgpjs , mais j'ai besoin de soutien dans tous les navigateurs, et même la page de test a des erreurs sur le seul navigateur pris en charge (Google Chrome).

Notes sur le but final:

La connexion TCP est déjà protégée par SSL. Le but principal de cette couche de protection est de défendre contre l'enregistrement intentionnel ou involontaire du serveur Web, les décharges accidentelles, etc.

Sur le côté PHP, une clé privée temporaire sera générée (elle expirera après un court laps de temps). L'appelant (en Javascript) est responsable de demander une nouvelle clé publique lorsqu'il expire. La raison de l'expiration de la clé privée est d'empêcher le décryptage des données chiffrées, au cas où le serveur qui stocke la clé privée est compromis ultérieurement.

Scénario compromis par les serveurs: quelqu'un reçoit des sauvegardes pour toutes les machines, à l'exception du serveur de base de données (et ne peut pas accéder à la base de données en cas de pare-feu, même s'il découvre l'utilisateur et le mot de passe). Étant donné que la clé privée qui a crypté les données enregistrées n'existe plus, il n'y a rien que l'attaquant puisse faire.

J'ai utilisé quelque chose de similaire pour ma page de connexion; Il crypte les informations d'identification de connexion en utilisant les informations clés de la clé publique (N, e) qui peuvent être déchiffrées en PHP.

Il utilise les fichiers suivants qui font partie de JSBN :

  • jsbn.js – pour travailler avec de gros nombres entiers
  • rsa.js – pour le chiffrement RSA uniquement (utilise jsbn.js)
  • rng.js – collecteur d'entropie basique
  • prng4.js – ARC4 RNG backend

Pour chiffrer les données:

 $pk = '-----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----'; $kh = openssl_pkey_get_private($pk); $details = openssl_pkey_get_details($kh); function to_hex($data) { return strtoupper(bin2hex($data)); } ?> <script> var rsa = new RSAKey(); rsa.setPublic('<?php echo to_hex($details['rsa']['n']) ?>', '<?php echo to_hex($details['rsa']['e']) ?>'); // encrypt using RSA var data = rsa.encrypt('hello world'); </script> 

Voici comment décoder les données envoyées:

 $kh = openssl_pkey_get_private($pk); $details = openssl_pkey_get_details($kh); // convert data from hexadecimal notation $data = pack('H*', $data); if (openssl_private_decrypt($data, $r, $kh)) { echo $r; } 

Vérifiez node-rsa .

C'est un module node.js

Ce module permet d'accéder aux routines de clé publique RSA d'OpenSSL. Le support est limité à RSAES-OAEP et au cryptage avec une clé publique, le décryptage avec une clé privée.

Peut-être que vous pouvez le transporter dans le navigateur.

METTRE À JOUR

Bibliothèque du côté client RSA pour javascript: (pidcrypt a été officiellement interrompu et le domaine du site Web a expiré – voir la réponse de @ jack qui contient les mêmes bibliothèques que pidcrypt contenues) . https://www.pidder.com/pidcrypt/?page=rsa

Composant côté serveur PHP: http://phpseclib.sourceforge.net/

Bonne chance!

Faites attention à la mise en œuvre de RSA. En fait, vous ne devriez probablement pas utiliser RSA du tout. ( Utilisez plutôt libsodium! )

Même si vous utilisez une bibliothèque (par exemple, l'extension OpenSSL de PHP directement ou, jusqu'à récemment, Zend\Crypt ), il y a encore beaucoup de choses qui peuvent aller mal. En particulier:

  • Le rembourrage PKCS1v1.5, qui est le mode de rembourrage par défaut (et souvent le seul mode de rembourrage pris en charge), est vulnérable à une classe d'attaques de texte chiffré choisies appelées oracle. Cela a été découvert par Daniel Bleichenbacher. En 1998.
  • RSA n'est pas adapté au cryptage de messages de grande taille, donc ce que les implémenteurs font souvent est de prendre un long message, de le diviser en blocs de taille fixe et de chiffrer chaque bloc séparément. Non seulement cela est lent, mais il est analogue au mode ECB redouté pour la cryptographie à clé symétrique.

La meilleure chose à faire, avec Libsodium

Vous voudrez peut-être lire Cryptographie JavaScript Considérée Nocif quelques fois avant de parcourir cette route. Mais cela a dit …

  1. Utilisez TLSv1.2 avec HSTS et HPKP, de préférence avec ChaCha20-Poly1305 et / ou AES-GCM et un certificat ECDSA-P256 (important: lorsque l'IETF baptise Curve25519 et Ed25519, passez à cela).
  2. Ajoutez libsodium.js à votre projet.
  3. Utilisez crypto_box_seal() avec une clé publique pour chiffrer vos messages, côté client.
  4. En PHP, utilisez \Sodium\crypto_box_seal_open() avec la clé secrète correspondante pour la clé publique pour décrypter le message.

Je dois utiliser RSA pour résoudre ce problème.

Ne faites pas . La cryptographie de la courbe elliptique est plus rapide, plus simple et plus facile à mettre en œuvre sans canal latéral. La plupart des bibliothèques le font déjà pour vous. (Libsodium!)

Mais je veux vraiment utiliser RSA!

Bien, suivez ces recommandations à la lettre et ne venez pas à StackOverflow lorsque vous faites une erreur (comme SaltStack a fait ) qui rend votre cryptographie inutile.

Une option (qui ne vient pas avec une implémentation JavaScript complémentaire, et ne demande pas une) qui vise à fournir un chiffrement RSA simple et facile est paragonie / easyrsa .

  • Il évite les oracles de rembourrage en utilisant RSA-OAEP avec MGF1 + SHA256 au lieu de PKCS1v1.5.
  • Cela évite le mode ECB par une conception intelligente du protocole:

Le protocole de chiffrage EasyRSA

  1. EasyRSA génère une clé aléatoire de 128 bits pour la cryptographie à clé symétrique (via AES).
  2. Votre message en clair est crypté avec désactivation / cryptage php .
  3. Votre clé AES est cryptée avec RSA, fournie par phpseclib , en utilisant le mode correct (mentionné ci-dessus).
  4. Cette information est regroupée sous la forme d'une chaîne simple (avec une somme de contrôle).

Mais, en fait, si vous trouvez un cas d'utilisation valide pour la cryptographie à clé publique, vous souhaitez libsodium à la place.

Exemple d'utilisation de RSA pour pidCrypt (js) et phpseclib (php).

Ne réutilisez pas la clé privée dans cet exemple de travail.

Cryptage pidCrypt

 //From the pidCrypt example sandbox function certParser(cert) { var lines = cert.split('\n'); var read = false; var b64 = false; var end = false; var flag = ''; var retObj = { }; retObj.info = ''; retObj.salt = ''; retObj.iv; retObj.b64 = ''; retObj.aes = false; retObj.mode = ''; retObj.bits = 0; for (var i = 0; i < lines.length; i++) { flag = lines[i].substr(0, 9); if (i == 1 && flag != 'Proc-Type' && flag.indexOf('M') == 0)//unencrypted cert? b64 = true; switch (flag) { case '-----BEGI': read = true; break; case 'Proc-Type': if (read)retObj.info = lines[i]; break; case 'DEK-Info:': if (read) { var tmp = lines[i].split(','); var dek = tmp[0].split(': '); var aes = dek[1].split('-'); retObj.aes = (aes[0] == 'AES') ? true : false; retObj.mode = aes[2]; retObj.bits = parseInt(aes[1]); retObj.salt = tmp[1].substr(0, 16); retObj.iv = tmp[1]; } break; case '': if (read)b64 = true; break; case '-----END ': if (read) { b64 = false; read = false; } break; default : if (read && b64)retObj.b64 += pidCryptUtil.stripLineFeeds(lines[i]); } } return retObj; } var strCreditCardPublicKey="-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC\/tI7cw+gnUPK2LqWp50XboJ1i\njrLDn+4\/gPOe+pB5kz4VJX2KWwg9iYMG9UJ1M+AeN33qT7xt9ob2dxgtTh7Mug2S\nn1TLz4donuIzxCmW+SZdU1Y+WNDINds194hWsAVhMC1ClMQTfldUGzQnI5sXvZTF\nJWp\/9jheCNLDRIkAnQIDAQAB\n-----END PUBLIC KEY-----\n"; var objParams=certParser(strCreditCardPublicKey); var binaryPrivateKey=pidCryptUtil.decodeBase64(objParams.b64); var rsa=new pidCrypt.RSA(); var asn=pidCrypt.ASN1.decode(pidCryptUtil.toByteArray(key)); var tree=asn.toHexTree(); rsa.setPublicKeyFromASN(tree); var strHexSensitiveDataEncrypted=rsa.encrypt("4111111111111111"); var strBase64SensitiveDataEncrypted=pidCryptUtil.fragment(pidCryptUtil.encodeBase64(pidCryptUtil.convertFromHex(strHexSensitiveDataEncrypted)), 64)) console.log(strBase64SensitiveDataEncrypted); 

.

Décodage phpseclib

 require_once("Crypt/RSA.php"); function decrypt($strBase64CipherText) { //CRYPT_RSA_MODE_INTERNAL is slow //CRYPT_RSA_MODE_OPENSSL is fast, but requires openssl to be installed, configured and accessible. define("CRYPT_RSA_MODE", CRYPT_RSA_MODE_INTERNAL); $rsa=new Crypt_RSA(); //$strPrivateKey=file_get_contents("private.pem"); //This private key is for example purposes //DO NOT REUSE $strPrivateKey="-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDBNHK7R2CCYGqljipbPoj3Pwyz4cF4bL5rsm1t8S30gbEbMnKn 1gpzteoPlKp7qp0TnsgKab13Fo1d+Yy8u3m7JUd/sBrUa9knY6dpreZ9VTNul8Bs p2LNnAXOIA5xwT10PU4uoWOo1v/wn8eMeBS7QsDFOzIm+dptHYorB3DOUQIDAQAB AoGBAKgwGyxy702v10b1omO55YuupEU3Yq+NopqoQeCyUnoGKIHvgaYfiwu9sdsM ZPiwxnqc/7Eo6Zlw1XGYWu61GTrOC8MqJKswJvzZ0LrO3oEb8IYRaPxvuRn3rrUz K7WnPJyQ2FPL+/D81NK6SH1eHZjemb1jV9d8uGb7ifvha5j9AkEA+4/dZV+dZebL dRKtyHLfbXaUhJcNmM+04hqN1DUhdLAfnFthoiSDw3i1EFixvPSiBfwuWC6h9mtL CeKgySaOkwJBAMSdBhn3C8NHhsJA8ihQbsPa6DyeZN+oitiU33HfuggO3SVIBN/7 HmnuLibqdxpnDOtJT+9A+1D29TkNENlTWgsCQGjVIC8xtFcV4e2s1gz1ihSE2QmU JU9sJ3YeGMK5TXLiPpobHsnCK8LW16WzQIZ879RMrkeDT21wcvnwno6U6c8CQQCl dsiVvXUmyOE+Rc4F43r0VRwxN9QI7hy7nL5XZUN4WJoAMBX6Maos2Af7NEM78xHK SY59+aAHSW6irr5JR351AkBA+o7OZzHIhvJfaZLUSwTPsRhkdE9mx44rEjXoJsaT e8DYZKr84Cbm+OSmlApt/4d6M4YA581Os1eC8kopewpy -----END RSA PRIVATE KEY----- "; $strPrivateKey=preg_replace("/[ \t]/", "", $strPrivateKey);//this won't be necessary when loading from PEM $rsa->loadKey($strPrivateKey); $binaryCiphertext=base64_decode($strBase64CipherText); $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); $strBase64DecryptedData=$rsa->decrypt($binaryCiphertext); return base64_decode($strBase64DecryptedData); } //The pidCrypt example implementation will output a base64 string of an encrypted base64 string which contains the original data, like this one: $strBase64CipherText="JDlK7L/nGodDJodhCj4uMw0/LW329HhO2EvxNXNUuhe+C/PFcJBE7Gp5GWZ835fNekJDbotsUFpLvP187AFAcNEfP7VAH1xLhhlB2a9Uj/z4Hulr4E2EPs6XgvmLBS3MwiHALX2fES5hSKY/sfSUssRH10nBHHO9wBLHw5mRaeg="; $binaryDecrypted=decrypt($strBase64CipherText); //should output '4111111111111111' var_export($binaryDecrypted); 

Ceci est basé sur le Tiny Encryption Algorithm , qui est un système de cryptage symétrique (clé privée). Il peut néanmoins être utile pour vous en raison de sa légèreté.

Ceci est maintenant à: http://babelfish.nl/Projecten/JavascriptPhpEncryption