web-dev-qa-db-fra.com

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

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

Je regarde actuellement openpgpjs , mais j'ai besoin de support dans tous les navigateurs, et même la page de test a des erreurs sur le seul navigateur répertorié comme supporté (Google Chrome).

Notes sur l'objectif final:

La connexion TCP est déjà protégée par SSL. Le but principal de cette couche de protection est de se défendre contre la journalisation intentionnelle ou non du serveur Web, les vidages sur incident, etc.

Du côté PHP côté, une clé privée temporaire sera générée (elle expirera peu de temps après). L'appelant (en Javascript) est responsable de demander une nouvelle clé publique à son expiration. La raison de l'expiration de la clé privée est d'empêcher le déchiffrement des données chiffrées enregistrées, au cas où le serveur qui stocke la clé privée serait ultérieurement compromis.

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

32
Tiberiu-Ionuț Stan

J'ai utilisé quelque chose de similaire pour ma page de connexion; il crypte les identifiants de connexion en utilisant les informations de clé publique données (N, e) qui peuvent être décryptées en PHP.

Il utilise les fichiers suivants qui font partie de JSBN :

  • jsbn.js - pour travailler avec de grands entiers
  • rsa.js - pour le chiffrement RSA uniquement (utilise jsbn.js)
  • rng.js - collecteur d'entropie de base
  • prng4.js - Backend ARC4 RNG

Pour crypter 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;
}
33
Ja͢ck

Découvrez node-rsa .

C'est un module node.js

Ce module permet d'accéder aux routines à clé publique RSA à partir d'OpenSSL. La prise en charge est limitée à RSAES-OAEP et au chiffrement avec une clé publique, au déchiffrement avec une clé privée.

Vous pouvez peut-être le porter pour l'exécuter dans le navigateur.

[~ # ~] mise à jour [~ # ~]

Bibliothèque 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 contenue) . https://www.pidder.com/pidcrypt/?page=rsa

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

Bonne chance!

24
Vlad Balmos

Soyez prudent avec l'implémentation de RSA. En fait, vous ne devriez probablement pas utiliser du tout RSA. ( tilisez 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 mal tourner. En particulier:

  • Le remplissage PKCS1v1.5, qui est le par défaut (et dans de nombreux cas le seul mode de remplissage pris en charge), est vulnérable à une classe d'attaques de texte chiffré choisi appelée un remplissage Oracle. Cela a été découvert pour la première fois par Daniel Bleichenbacher. En 1998.
  • RSA n'est pas adapté au chiffrement de gros messages, 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 c'est lent, c'est analogue à le mode ECB redouté pour la cryptographie à clé symétrique.

La meilleure chose à faire, avec Libsodium

Vous voudrez peut-être lire La cryptographie JavaScript considérée comme nuisible quelques fois avant de suivre cette voie. Mais cela 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 à la place).
  2. Ajoutez libsodium.js à votre projet.
  3. Utilisez crypto_box_seal() avec une clé publique pour crypter 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échiffrer le message.

J'ai besoin d'utiliser RSA pour résoudre ce problème.

Veuillez ne pas le faire . La cryptographie à courbe elliptique est plus rapide, plus simple et beaucoup plus facile à mettre en œuvre sans canaux latéraux. La plupart des bibliothèques le font déjà pour vous. (Libsodium!)

Mais je vraiment je veux utiliser RSA!

Très bien, suivez ces recommandations à la lettre et ne venez pas pleurer sur StackOverflow lorsque vous faites une erreur (comme SaltStack l'a fait ) qui rend votre cryptographie inutile.

Une option (qui n'est pas livrée avec une implémentation JavaScript complémentaire, et n'en demandez pas) qui vise à fournir un chiffrement RSA simple et facile est paragonie/easyrsa .

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

Le protocole de cryptage 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 defuse/php-encryption .
  3. Votre clé AES est chiffrée avec RSA, fournie par phpseclib , en utilisant le mode correct (mentionné ci-dessus).
  4. Ces informations sont regroupées sous la forme d'une chaîne simple (avec une somme de contrôle).

Mais, vraiment, si vous trouvez un cas d'utilisation valide pour la cryptographie à clé publique, vous voulez plutôt libsodium.

12
Scott Arciszewski

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écryptage 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);
1
Tiberiu-Ionuț Stan

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

C'est maintenant à: http://babelfish.nl/Projecten/JavascriptPhpEncryption

0
Pum Walters