簡體   English   中英

使用公共密鑰加密,以Javascript加密,以PHP解密

[英]Encrypt in Javascript, decrypt in PHP, using public-key cryptography

我想使用公共密鑰加密技術在JavaScript中加密,在PHP中解密。 我一直在嘗試找到可以完成此任務的庫,但是遇到了問題。

我目前正在使用 openpgpjs ,但是我需要所有瀏覽器的支持,甚至測試頁在唯一列為受支持的瀏覽器(Google Chrome)上都有錯誤。

關於最終目標的注意事項:

TCP連接已受SSL保護。 此保護層的主要目的是防御有意或無意的Web服務器日志記錄,崩潰轉儲等。

在PHP方面,將生成一個臨時私鑰(短時間后它將失效)。 調用方(使用Javascript)負責在過期時要求新的公共密鑰。 私鑰到期的原因是為了防止記錄的加密數據解密,以防存儲私鑰的服務器在以后遭到破壞。

服務器受損的情況:有人獲得了除數據庫服務器之外的所有計算機的備份(並且即使發現了用戶名和密碼,也由於防火牆無法訪問數據庫)。 由於加密記錄的數據的私鑰已不復存在,因此攻擊者無能為力。

我在登錄頁面上使用了類似的內容; 它使用給定的公共密鑰信息(N,e)加密登錄憑據,該信息可以在PHP中解密。

它使用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;
}

簽出node-rsa

這是一個node.js模塊

該模塊提供了從OpenSSL對RSA公鑰例程的訪問。 支持僅限於RSAES-OAEP以及使用公鑰加密,使用私鑰解密。

也許您可以移植它以在瀏覽器中運行。

更新

用於javascript的RSA客戶端庫:( pidcrypt已正式停產,並且網站域已過期-參見@jack的答案,其中包含與pidcrypt包含的庫相同的庫) https://www.pidder.com/pidcrypt/?page=rsa

PHP服務器端組件: http : //phpseclib.sourceforge.net/

祝好運!

實施RSA時要小心。 實際上,您可能根本不應該使用RSA。 改用libsodium!

即使您使用的是庫(例如,直接使用PHP的OpenSSL擴展,或者直到最近才使用Zend\\Crypt ),仍然有很多地方可能出問題。 尤其是:

  • PKCS1v1.5填充是默認設置 (在許多情況下是唯一受支持的填充模式),很容易受到稱為填充預言的一類選擇密文攻擊。 這是由Daniel Bleichenbacher首次發現的。 1998年。
  • RSA不適合加密大消息,因此實現者通常要做的是獲取一條長消息,將其分成固定大小的塊,然后分別加密每個塊。 這不僅慢,而且類似於對稱密鑰加密的可怕的ECB模式

最好的事情,用鋰鈉

在沿這條路線走之前,您可能需要閱讀幾次被認為有害JavaScript密碼學 但是那...

  1. 將TLSv1.2與HSTS和HPKP配合使用,最好將ChaCha20-Poly1305和/或AES-GCM與ECDSA-P256證書一起使用(重要:當IETF將Curve25519和Ed25519命名時,改為使用該證書)。
  2. libsodium.js添加到您的項目中。
  3. 在客戶端使用帶公共密鑰的crypto_box_seal()加密您的消息。
  4. 在PHP中,將\\Sodium\\crypto_box_seal_open()與對應的公鑰一起使用,以對消息進行解密。

我需要使用RSA解決此問題。

請不要 橢圓曲線密碼術更快,更簡單,並且無需側通道即可輕松實施。 大多數庫已經為您做到了。 (鈉!)

但是我真的很想使用RSA!

很好,請遵循這些建議 ,不要在您犯了使密碼學無用的錯誤(如SaltStack那樣 )時對StackOverflow哭泣。

旨在提供簡單易用的RSA加密的一種選擇(它沒有附帶的JavaScript實現,請不要提出要求)是paragonie / easyrsa

EasyRSA加密協議

  1. EasyRSA會為對稱密鑰加密生成一個隨機的128位密鑰(通過AES)。
  2. 您的純文本消息是使用defuse / php-encryption加密的
  3. 您的AES密鑰使用正確的模式(上述)由phpseclib提供的RSA加密。
  4. 此信息打包為一個簡單的字符串(帶有校驗和)。

但是,實際上,如果您找到了用於公鑰加密的有效用例,則需要libsodium。

獎勵:使用JavaScript加密,使用PHP解密

我們將使用鈉加鈉來實現此目標。 (從這篇文章中采用。)

const publicKey = X25519PublicKey.from('fb1a219011c1e0d17699900ef22723e8a2b6e3b52ddbc268d763df4b0c002e73', 'hex');

async function sendEncryptedMessage() {
    let key = await getExampleKey();
    let message = $("#user-input").val();
    let encrypted = await sodium.crypto_box_seal(message, publicKey);
    $.post("/send-message", {"message": encrypted.toString('hex')}, function (response) {
        console.log(response);
        $("#output").append("<li><pre>" + response.message + "</pre></li>");
    });
}

然后是一致的PHP代碼:

<?php
declare(strict_types=1);
require 'vendor/autoload.php'; // Composer
header('Content-Type: application/json');
$keypair = sodium_hex2bin(
    '0202040a9fbf98e1e712b0be8f4e46e73e4f72e25edb72e0cdec026b370f4787' .
    'fb1a219011c1e0d17699900ef22723e8a2b6e3b52ddbc268d763df4b0c002e73'
);

$encrypted = $_POST['message'] ?? null;
if (!$encrypted) {
    echo json_encode(
        ['message' => null, 'error' => 'no message provided'],
        JSON_PRETTY_PRINT
    );
    exit(1);
}
$plaintext = sodium_crypto_box_seal_open(sodium_hex2bin($encrypted), $keypair);

echo json_encode(
    ['message' => $plaintext, 'original' => $encrypted],
    JSON_PRETTY_PRINT
);

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

這基於Tiny Encryption Algorithm微型加密算法) ,該算法是對稱(私鑰)加密系統。 但是由於它的重量輕,它可能對您有用。

現在,它位於: http : //babelfish.nl/Projecten/JavascriptPhpEncryption

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM