web-dev-qa-db-ja.com

公開鍵暗号を使用して、Javascriptで暗号化、PHPで復号化

公開鍵暗号を使用して、JavaScriptで暗号化し、PHPで復号化したいと思います。私はこれを達成できるライブラリを探していましたが、問題があります。

現在、 openpgpjs を探していますが、すべてのブラウザーでサポートが必要であり、テストページでさえ、サポートされているブラウザー(Google Chrome)としてリストされているもののみにエラーがあります。

最終目標に関するメモ:

TCP接続は既にSSLで保護されています。この保護層の主な目的は、意図的または意図しないWebサーバーのロギング、クラッシュダンプなどに対する防御です]

PHP側では、一時的な秘密鍵が生成されます(短時間で期限切れになります)。呼び出し側(Javascriptで)は、期限切れになると新しい公開鍵を要求します。秘密鍵の有効期限が切れる理由は、秘密鍵を保存しているサーバーが後で侵害された場合に、ログに記録された暗号化されたデータの復号化を防ぐためです。

サーバーが侵害されたシナリオ:誰かがデータベースサーバー以外のすべてのマシンのバックアップを取得します(ユーザーとパスワードを見つけても、ファイアウォールのためにデータベースにアクセスできません)。ログに記録されたデータを暗号化した秘密鍵はもはや存在しないため、攻撃者ができることは何もありません。

32

私はログインページに似たようなものを使用しました。 PHPで復号化できる公開鍵情報(N、e)を使用してログイン資格情報を暗号化します。

JSBN の一部である次のファイルを使用します。

  • jsbn.js-大きな整数で動作する
  • rsa.js-RSA暗号化のみ(jsbn.jsを使用)
  • rng.js-基本的なエントロピーコレクター
  • prng4.js-ARC4 RNGバックエンド

データを暗号化するには:

$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>

これは、送信されたデータをデコードする方法です。

$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

node-rsa を確認してください。

Node.jsモジュールです

このモジュールは、OpenSSLからRSA公開鍵ルーチンへのアクセスを提供します。サポートはRSAES-OAEPと公開キーによる暗号化、秘密キーによる復号化に限定されます。

たぶん、あなたはそれを移植してブラウザで実行することができます。

[〜#〜] update [〜#〜]

JavascriptのRSAクライアント側ライブラリ:(pidcryptは公式に廃止され、ウェブサイトドメインの有効期限が切れています-含まれるpidcryptと同じライブラリを含む@jackの回答を参照してください)https://www.pidder.com/pidcrypt/?page=rsa

PHPサーバー側コンポーネント: http://phpseclib.sourceforge.net/

幸運を!

24
Vlad Balmos

RSAの実装には注意してください。実際、おそらくRSAはまったく使用すべきではありません。 ( 代わりにlibsodiumを使用してください!

ライブラリ(たとえば、PHPのOpenSSL拡張機能を直接使用している場合、または最近まで _Zend\Crypt_ )を使用している場合でも、まだ多くの問題が発生する可能性があります。特に:

  • PKCS1v1.5パディングは、default(および多くの場合、サポートされている唯一のパディングモード)であり、パディングOracleと呼ばれる選択された暗号文攻撃のクラスに対して脆弱です。これは、Daniel Bleichenbacherによって最初に発見されました。 1998年に。
  • RSAは大きなメッセージの暗号化には適していないため、実装者がよく行うことは、長いメッセージを受け取り、固定サイズのブロックに分割し、各ブロックを個別に暗号化することです。これは遅いだけでなく、対称キー暗号化の 恐ろしいECBモード に類似しています。

Libsodiumで行うべきこと

このルートを進む前に、 JavaScript暗号化は有害と見なされます を何度か読みたいかもしれません。しかし、それは言った...

  1. HSTSおよびHPKP、できればChaCha20-Poly1305および/またはAES-GCMおよびECDSA-P256証明書でTLSv1.2を使用してください(重要:IETFがCurve25519とEd25519を変更する場合、代わりに切り替えます)。
  2. libsodium.js をプロジェクトに追加します。
  3. crypto_box_seal()を公開鍵とともに使用して、クライアント側でメッセージを暗号化します。
  4. PHPでは、 \Sodium\crypto_box_seal_open() を公開鍵の対応する秘密鍵とともに使用して、メッセージを復号化します。

この問題を解決するには、RSAを使用する必要があります。

しないでください 。楕円曲線暗号化は、サイドチャネルを使用せずに実装するのがより速く、簡単で、はるかに簡単です。ほとんどのライブラリは既にこれを行っています。 (Libsodium!)

しかし、私は本当にRSAを使いたい!

レターへのこれらの推奨事項 に従ってください。あなたが暗号を役に立たなくするミス( SaltStackがしたように など)をStackOverflowに泣かないでください。

シンプルで簡単なRSA暗号化を提供することを目的とする1つのオプション(補完的なJavaScript実装は付属していませんので、お願いしません)は paragonie/easyrsa です。

  • PKCS1v1.5の代わりに RSA-OAEP with MGF1 + SHA256 を使用することにより、パディングオラクルを回避します。
  • 巧妙なプロトコル設計によりECBモードを回避します。

EasyRSA暗号化プロトコル

  1. EasyRSAは、対称キー暗号化用にランダムな128ビットキーを生成します(AES経由)。
  2. プレーンテキストメッセージは defuse/php-encryption で暗号化されます。
  3. AESキーは、正しいモード(上記)を使用して、 phpseclib で提供されるRSAで暗号化されます。
  4. この情報は、単純な文字列(チェックサム付き)としてまとめられます。

しかし、実際には、公開鍵暗号の有効なユースケースが見つかった場合、代わりにlibsodiumが必要です。

12

pidCrypt (js)および phpseclib (php)のRSAの使用例。

この実例では秘密鍵を再利用しないでください。

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);

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

これは、対称(秘密鍵)暗号化システムである Tiny Encryption Algorithm に基づいています。それにもかかわらず、軽量であるためにあなたの役に立つかもしれません。

これは次の場所にあります。 http://babelfish.nl/Projecten/JavascriptPhpEncryption

0
Pum Walters