RSA加密/解密兼容Javascript和PHP

10

我想在Javascript中进行加密,然后在PHP中进行解密。虽然Javascript和PHP都有RSA实现,但它们不兼容。使用Javascript加密的内容无法在PHP中正确解密。

有没有谁知道一个可以在Javascript和PHP都使用的库/代码呢?

谢谢。


1
听起来你正在考虑在客户端和服务器之间加密敏感数据。但是请注意,这种方法无法防止中间人攻击,因为所有经过的数据都会被泄露。建议使用HTTPS进行安全、认证的端到端加密。 - deed02392
7个回答

9

试一下下面这个简单的例子。

它使用了一个开源的 JavaScript 库https://github.com/ziyan/javascript-rsa

HTML/JAVASCRIPT:

<script language="JavaScript" type="text/javascript" src="jsbn.js"></script>
<script language="JavaScript" type="text/javascript" src="rsa.js"></script>

<script language="JavaScript">

    function encryptData(){

        //Don't forget to escape the lines:
        var pem="-----BEGIN PUBLIC KEY-----\
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfmlc2EgrdhvakQApmLCDOgP0n\
NERInBheMh7J/r5aU8PUAIpGXET/8+kOGI1dSYjoux80AuHvkWp1EeHfMwC/SZ9t\
6rF4sYqV5Lj9t32ELbh2VNbE/7QEVZnXRi5GdhozBZtS1gJHM2/Q+iToyh5dfTaA\
U8bTnLEPMNC1h3qcUQIDAQAB\
-----END PUBLIC KEY-----";

        var key = RSA.getPublicKey(pem);

        element=document.getElementById('password');
        element.value=RSA.encrypt(element.value, key);
    }
</script>

<form method='POST' id='txtAuth' onsubmit='encryptData()'>
    <input type='text' name='username'/>
    <input type='password' name='password' id='password' placeholder="password"/>
    <input name='submit' type='submit' value='Submit'>
</form>

PHP:

<?php

if (isset($_POST['password'])) {

    //Load private key:
    $private = "-----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQDfmlc2EgrdhvakQApmLCDOgP0nNERInBheMh7J/r5aU8PUAIpG
    XET/8+kOGI1dSYjoux80AuHvkWp1EeHfMwC/SZ9t6rF4sYqV5Lj9t32ELbh2VNbE
    /7QEVZnXRi5GdhozBZtS1gJHM2/Q+iToyh5dfTaAU8bTnLEPMNC1h3qcUQIDAQAB
    AoGAcbh6UFqewgnpGKIlZ89bpAsANVckv1T8I7QT6qGvyBrABut7Z8t3oEE5r1yX
    UPGcOtkoRniM1h276ex9VtoGr09sUn7duoLiEsp8aip7p7SB3X6XXWJ9K733co6C
    dpXotfO0zMnv8l3O9h4pHrrBkmWDBEKbUeuE9Zz7uy6mFAECQQDygylLjzX+2rvm
    FYd5ejSaLEeK17AiuT29LNPRHWLu6a0zl923299FCyHLasFgbeuLRCW0LMCs2SKE
    Y+cIWMSRAkEA7AnzWjby8j8efjvUwIWh/L5YJyWlSgYKlR0zdgKxxUy9+i1MGRkn
    m81NLYza4JLvb8/qjUtvw92Zcppxb7E7wQJAIuQWC+X12c30nLzaOfMIIGpgfKxd
    jhFivZX2f66frkn2fmbKIorCy7c3TIH2gn4uFmJenlaV/ghbe/q3oa7L0QJAFP19
    ipRAXpKGX6tqbAR2N0emBzUt0btfzYrfPKtYq7b7XfgRQFogT5aeOmLARCBM8qCG
    tzHyKnTWZH6ff9M/AQJBAIToUPachXPhDyOpDBcBliRNsowZcw4Yln8CnLqgS9H5
    Ya8iBJilFm2UlcXfpUOk9bhBTbgFp+Bv6BZ2Alag7pY=
    -----END RSA PRIVATE KEY-----";
    if (!$privateKey = openssl_pkey_get_private($private)) die('Loading Private Key failed');

    //Decrypt
    $decrypted_text = "";
    if (!openssl_private_decrypt(base64_decode($_POST['password']), $decrypted_text, $privateKey)) die('Failed to decrypt data');

    //Decrypted :) 
    var_dump($decrypted_text);

    //Free key
    openssl_free_key($privateKey);
}
?>

祝您愉快!


2
针对未来的读者:David.Ask在一个被拒绝的编辑中建议将以下代码添加到头部:<script language="JavaScript" type="text/javascript" src="jsbn2.js"></script> - Tieme
1
使用这个项目,PHP的加密解密工作对我来说非常完美。 https://github.com/travist/jsencrypt/ - Zakaria ASSANI

7

1
如果您设置服务器使用SSL,则可以通过https进行加密传输,从而实现ajax之间的数据加密。这可能是在JavaScript和PHP之间加密数据的最佳方式。如果您想自己制作它,那么很有可能会出现问题,系统将不安全。

请在Google上搜索如何为您的服务器设置https。


0

我发现了这个jsencrypt库(http://travistidwell.com/jsencrypt),经过两天的尝试,我终于找到了解决方案。

唯一的问题是当我发送一个很长的文本时。这是因为RSA在定义上只支持有限长度的字符串。

https://security.stackexchange.com/questions/33434/rsa-maximum-bytes-to-encrypt-comparison-to-aes-in-terms-of-security/33445#33445

RSA是一种由PKCS#1定义的加密算法,用于加密有限大小的“消息”。使用常用的“v1.5填充”和2048位RSA密钥,可以使用RSA加密的数据的最大大小为245字节。没有更多了。
也就是说,如果我使用1024位的私钥,我可以发送...
"José compró en Perú una vieja zampoña. Excusándose, Sofía tiró su whisky al desagüe de la banqueta."

内容与编程有关,只返回翻译文本:
如果我使用512位的私钥,我就可以发送。

"José compró en Perú una vieja zampoña. Excusánd"

不要太长。

在 JavaScript 控制台上报告长字符串:“RSA 的消息太长”

因此,如果您想加密长字符串,则必须在 JavaScript 加密之前压缩和拆分它们,在解密后加入并在 PHP 上进行解压缩,我认为 zlib 是拆分/连接的好解决方案,因为它在 JavaScript 和 PHP 上都受支持。

我的工作代码如下:

<?php
    //------------------------------------------------------------
    // Global Settings.
    //------------------------------------------------------------
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    $directorio = "/path/to/key/directory/apache/writable/";
    $nombre_base = "llaves_php";

    //------------------------------------------------------------
    // Initialization.
    //------------------------------------------------------------
    $encabezado_html = "";
    $cuerpo_html = "";

    //------------------------------------------------------------
    // Loading keys
    //------------------------------------------------------------
    list($privateKey, $pubKey) =
        cargar_llaves_RSA($directorio, $nombre_base);

    //------------------------------------------------------------
    // Form that uses javascript to encrypt data.
    // (it uses only the public key)
    //------------------------------------------------------------
    $librerias_html = "
        <script type='text/javascript'
                src='https://ajax.googleapis.com/ajax/libs/".
                    "jquery/3.2.1/jquery.min.js'></script>
        <script type='text/javascript'
                src='lib/jsencrypt.js'></script>
        ";

    $pubKey_html = htmlentities($pubKey);
    $datos_html = "
        <h2>Cifrando con Javascript</h2>
        <input type='text' id='mensaje' />
        <br />
        <button id='ENVIAR'>Enviar</button>
        <br />
        <textarea id='pubkey' style='display: none;'>".
        $pubKey_html.
        "</textarea>
        <script type='text/javascript'>
            $('#ENVIAR').click(function () {
                var codificador = new JSEncrypt();
                codificador.setKey($('#pubkey').val());
                var cifrado = codificador.encrypt($('#mensaje').val());
                window.open('?mensaje=' + encodeURIComponent(cifrado)
                           , '_top');
            });
        </script>
        ";

    //------------------------------------------------------------
    // Decrypting using php (it uses only the privateKey)
    //------------------------------------------------------------
    if (isset($_REQUEST['mensaje'])) {
        openssl_private_decrypt( base64_decode($_REQUEST['mensaje'])
                               , $descifrado
                               , $privateKey);
        $datos_html.= "
            <h2>Descifrando con PHP</h2>
            ".$descifrado."
            ";
    }

    //------------------------------------------------------------
    // HTML DISPLAY
    //------------------------------------------------------------
    $encabezado_html.= "<title>Receptor de mensaje cifrado</title>"
                     . $librerias_html;

    $cuerpo_html.= $datos_html;

    $contenido = "<head>$encabezado_html</head><body>$cuerpo_html</body>";
    $contenido = "<html>$contenido</html>";
    print $contenido;

//============================================================
//============================================================
// Functions
//============================================================
//============================================================

    //------------------------------------------------------------
    function cargar_llaves_RSA($directorio, $nombre_base) {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // PROPÓSITO: Genera o carga desde archivos las llaves RSA
    // ENTRADAS:
    // $directorio: Directorio donde se encuentran los archivos.
    // $nombre_base: Nombre, sin extensión, de los archivos con
    //               las llaves.
    // SALIDAS:
    //------------------------------------------------------------
        if (  !file_exists($directorio.$nombre_base.".crt")
           || !file_exists($directorio.$nombre_base.".pub")) {
            list($privateKey, $pubKey) = crear_llaves_RSA($directorio.$nombre_base);
        } else {
            //------------------------------------------------------------
            // CARGA DE LLAVES RSA ARCHIVADAS
            //------------------------------------------------------------
            $privateKey = file_get_contents($directorio.$nombre_base.".crt");
        if (!$privKey = openssl_pkey_get_private($privateKey))
            die('Loading Private Key failed');
            $pubKey  = file_get_contents($directorio.$nombre_base.".pub");
        }

    return array($privateKey, $pubKey);
    }

    //------------------------------------------------------------
    function crear_llaves_RSA($ruta_base) {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // PROPÓSITO:
    // generacion de llaves RSA en php
    // ENTRADAS:
    // $ruta_base: Ruta de los archivos a generar sin extensión.
    // SALIDAS:
    // Se generarán dos archivos, uno con la llave privada con
    // extensión .crt, el otro con llave pública con extensión
    // .pub; la función retorna tanto la llave pública como la
    // privada en un arreglo.
    //------------------------------------------------------------
        $config = array(
            "private_key_bits" => 1024,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        );

        $llavePrivadaCruda = openssl_pkey_new($config);
        openssl_pkey_export_to_file($llavePrivadaCruda, $ruta_base.".crt");
        $privateKey = file_get_contents($ruta_base.".crt");
        openssl_pkey_export($llavePrivadaCruda, $privKey);

        $pubKeyData = openssl_pkey_get_details($llavePrivadaCruda);
        $pubKey = $pubKeyData["key"];
        file_put_contents($ruta_base.".pub", $pubKey);
        openssl_free_key($llavePrivadaCruda);

    return array($privateKey, $pubKey);
    }

    //------------------------------------------------------------
    function Mostrar($valor) {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // PROPÓSITO: Genera el código HTML para presentar una
    // variable embebida en la página.
    // ENTRADAS:
    // $valor: el valor a presentar.
    // SALIDAS: código html que permite visualizar la variable.
    //------------------------------------------------------------
        $retorno = htmlentities(stripslashes(var_export($valor, true)));
        $retorno = "<pre>$retorno</pre>";
        return $retorno;
    }

?>

目录树必须如下所示:
├── script.php
└── lib
    └── jsencrypt.js

以及一个php可以写入的位于公共区域之外的目录

/path/to/key/directory/apache/writable/


0

我假设你有一个除了使用https之外的有效理由,所以我认为如果你遵循标准,你应该能够轻松地使用支持这些标准的任何技术进行解密:也就是说,它应该可以工作。

例如,如果你使用PKCS#7格式加密数据,请确保你的php库知道输入数据是PKCS#7格式。

同时确保你的加密密钥在服务器和客户端之间没有被篡改。你尝试使用javascript库解密数据了吗?

希望这可以帮到你...


0
也许您可以帮忙提供您同时在JS和PHP中使用的代码。
此外,您可能需要更具体地说明为什么需要同时使用JS和PHP。也许您可以只使用PHP,并使用AJAX(查询相同的PHP函数)来替代JS。

我认为OP试图以加密形式向服务器发送类似密码的内容。如果您使用AJAX将密码发送到php函数,则仍将以明文形式将密码发送到AJAX调用,然后在php方法中对其进行加密将不再有任何意义。 - lugte098

0

我不想自吹自擂,但我在 github.com 上有一个项目可以执行这个功能。

服务器上生成私钥,同时从私钥派生出公钥和 pkcs#7 证书。将公钥发送至客户端时,指定表单关联的每个表单元素在提交到服务器之前都会进行加密。

它与 OpenSSL 100% 兼容,因为它使用 PHP OpenSSL 扩展来生成、加密和解密数据。

https://github.com/jas-/jQuery.pidCrypt/

这个项目不像PGP那么安全,因为JavaScript只有在表单数据发送到服务器后才会签名和加密电子邮件,但要加密或签名的表单数据在发送到服务器之前已经使用RSA公钥加密。

同样,在身份验证和电子邮件签名方面,该项目还不完整,但对于使用公钥进行普通表单加密非常有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接