使用公钥加密在Javascript中加密,在PHP中解密

时间:2012-09-17 10:18:17

标签: javascript php rsa public-key pgp

我想用JavaScript加密,用PHP解密,使用公钥加密。我一直在努力寻找可以实现这一目标的库,但我遇到了问题。

我目前正在查看openpgpjs,但我需要在所有浏览器中提供支持,甚至测试页面也只列出了支持的浏览器(Google Chrome)。

关于最终目标的说明:

TCP连接已受SSL保护。这一保护层的主要目的是防止有意或无意的网络服务器日志记录,崩溃转储等。

在PHP端,将生成临时私钥(它将在短时间后过期)。调用者(在Javascript中)负责在新的公钥到期时要求它。私钥到期的原因是为了防止记录加密数据解密,以防后来存储私钥的服务器受到损害。

服务器受损的情况:有人获取除数据库服务器之外的所有计算机的备份(并且由于防火墙而无法访问数据库,即使他找到了用户和密码)。由于加密记录数据的私钥不再存在,攻击者无法做到这一点。

5 个答案:

答案 0 :(得分:32)

我在登录页面上使用了类似的东西;它使用给定的公钥信息(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;
}

答案 1 :(得分:24)

查看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/

祝你好运!

答案 2 :(得分:11)

小心实施RSA。实际上,您可能根本不应该使用RSA。 (Use libsodium instead!

即使你正在使用一个库(例如直接使用PHP的OpenSSL扩展,或者直到最近,Zend\Crypt),仍然有很多可能出错的地方。特别是:

  • PKCS1v1.5填充,默认(在许多情况下是唯一支持的填充模式),容易受到一类被称为填充oracle的选择密文攻击。这是Daniel Bleichenbacher首先发现的。 1998年。
  • RSA不适合加密大型消息,因此实现者经常做的是接收长消息,将其分解为固定大小的块,并分别加密每个块。这不仅速度慢,而且类似于the dreaded ECB mode对称密钥加密。

利用Libsodium做的最好的事情

您可能希望在沿着此路线前阅读JavaScript Cryptography Considered Harmful几次。但那说......

  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()和公钥的相应密钥来解密消息。
  5. 我需要使用RSA来解决这个问题。

    Please don't。椭圆曲线加密更快,更简单,并且更容易实现,没有侧通道。大多数图书馆已经为您做了这件事。 (Libsodium!)

    但我确实想要使用RSA!

    好的,请关注these recommendations to the letter,如果您犯了一个错误(如SaltStack did)而导致您的加密无用,请不要向StackOverflow哭泣。

    旨在提供简单易用的RSA加密的一个选项(没有补充的JavaScript实现,请不要求一个)paragonie/easyrsa

    EasyRSA加密协议

    1. EasyRSA为对称密钥加密(通过AES)生成随机128位密钥。
    2. 您的纯文本邮件已使用defuse/php-encryption加密。
    3. 您的AES密钥使用RSA加密,由phpseclib提供,使用正确的模式(如上所述)。
    4. 此信息作为简单字符串(带校验和)打包在一起。
    5. 但是,实际上,如果您找到公钥加密的有效用例,则需要libsodium。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

这是基于Tiny Encryption Algorithm,它是一个对称(私钥)加密系统。尽管如此,它可能对您有用,因为它重量轻。

现在位于:http://babelfish.nl/Projecten/JavascriptPhpEncryption

相关问题