RSA 加密、PHP 加密 (phpseclib) 和 JavaScript 解密 (crypto.subtle)

问题描述

我想要做的是在 javascript 中生成一个密钥对,并在 PHP 中使用这些加密,然后用 JS 解密。

我在附加的代码中有两个问题

  1. 它不会从装甲文本块重新加载私钥
  2. 它不会解密 PHP 加密的内容

两者都抛出错误 DOMException,而不是一个有用的错误

这是我的代码...

PHP/JAVASCRIPT

<?PHP
use PHPseclib3\Crypt\PublicKeyLoader;
use PHPseclib3\Crypt\RSA;
if ($_POST) {
    $public=$_POST['public'];
    $data='some text to encrypt';
    $key = PublicKeyLoader::load($public);
    $key = $key->withPadding(RSA::ENCRYPTION_OAEP);
    $encoded=base64_encode($key->encrypt($data));
    header('Content-Type: application/json');
    echo json_encode(array('encrypted'=>$encoded));
    exit;
}
?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script>
jQuery(document).ready(function($) {
    function ab2str(buf) {
        return String.fromCharCode.apply(null,new Uint8Array(buf));
    }
    function str2ab(str) {
        var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
        var bufView = new Uint16Array(buf);
        for (var i=0,strLen=str.length; i < strLen; i++) {
            bufView[i] = str.charCodeAt(i);
        }
        return buf;
    }
    function importPrivateKey(pem) {
        // fetch the part of the PEM string between header and footer
        const pemHeader = "-----BEGIN PRIVATE KEY-----\n";
        const pemFooter = "\n-----END PRIVATE KEY-----";
        const pemContents = pem.substring(pemHeader.length,pem.length - pemFooter.length);
        // base64 decode the string to get the binary data
        const binaryDerString = window.atob(pemContents);
        // convert from a binary string to an ArrayBuffer
        const binaryDer = str2ab(binaryDerString);

        return window.crypto.subtle.importKey(
            "pkcs8",binaryDer,{
                name: "RSA-OAEP",modulusLength: 1024,publicExponent: new Uint8Array([1,1]),hash: {name: "SHA-256"}
            },true,["decrypt"]
          );
    }
    (async() => {
        let keyPair = await window.crypto.subtle.generateKey(
          {
                name: "RSA-OAEP",hash: {name: "SHA-256"}
          },["encrypt","decrypt"]
        );
        
        var exported=await window.crypto.subtle.exportKey("pkcs8",keyPair.privateKey);
        var exportedAsstring = ab2str(exported);
        var exportedAsBase64 = window.btoa(exportedAsstring);
        var private = `-----BEGIN PRIVATE KEY-----\n${exportedAsBase64}\n-----END PRIVATE KEY-----`;
        
        var exported = await window.crypto.subtle.exportKey(
            "spki",keyPair.publicKey
        );
        var exportedAsstring = ab2str(exported);
        var exportedAsBase64 = window.btoa(exportedAsstring);
        var public = `-----BEGIN PUBLIC KEY-----\n${exportedAsBase64}\n-----END PUBLIC KEY-----`;

        console.log(public);
        console.log(private);
        
        $.ajax({
            url:window.location,type:'POST',data:{
                public:public
            },success:function(data) {
                (async() => {
                    console.log('*ENCRYPTED BY PHP*',data.encrypted);
                    // HELP!!! NEED TO BE ABLE TO RELOAD THE KEY FROM ARMORED STRING
                    var key=await importPrivateKey(private); // Error - Uncaught (in promise) DOMException
                    var buffer=str2ab(window.atob(data.encrypted));
                    // HELP!!! WONT DECRYPT WHAT PHP ENCODED USING THE PUBLIC KEY
                    var decrypted=await window.crypto.subtle.decrypt({name:"RSA-OAEP"},key,buffer);
                    
                    console.log('DECRYPTED',decrypted);
                })();
            }
        });
    })();
});
</script>

解决方法

错误在 str2ab() 函数中,该函数使用 Uint16Array 而不是 Uint8Array

如果修复了,就可以导入私钥,解密用PHP代码生成的密文:

function ab2str(buf) {
    return String.fromCharCode.apply(null,new Uint8Array(buf));
}

function str2ab(str) {
    // Fix: Don't double the size 
    var buf = new ArrayBuffer(str.length);                  
    // Fix: Apply a Uint8Array!
    var bufView = new Uint8Array(buf);                      
    for (var i=0,strLen=str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

function importPrivateKey(pem) {

    const pemHeader = "-----BEGIN PRIVATE KEY-----\n";
    const pemFooter = "\n-----END PRIVATE KEY-----";
    const pemContents = pem.substring(pemHeader.length,pem.length - pemFooter.length);

    const binaryDerString = window.atob(pemContents);
    const binaryDer = str2ab(binaryDerString);

    return window.crypto.subtle.importKey(
        "pkcs8",binaryDer,{
            name: "RSA-OAEP",modulusLength: 1024,publicExponent: new Uint8Array([1,1]),hash: {name: "SHA-256"}
        },true,["decrypt"]
    );
}

(async function() {

    // Apply the private key from key pair generated with the posted JavaScript code
    var privateKey = 
    `-----BEGIN PRIVATE KEY-----                     
        MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKeQUdBu3zTX6QyfGfRWYxWWOnxd2xssTOIu6XczDByQEMfBbpQO9iM3u/Mn84zZFPFNvOKUNxcnftmrPiqUO9fBI2aAh77d2m65FBGsm4k/oUPzMNORGaDdBY4gg8FPMKo60kqBaMXAwzF8I4EUS/ot2fkBzSL0BGXT9o1NaO8bAgMBAAECgYAO2OPW8ywF86ervaFAHDN1YzVVdb+HXdqGJB/9tuE42q8R9BrHNbgrkLGvrveOoGGRrBCzhuyGubIsuVat0SqoI6qEnB9uahaIBfF5FZ7+bNW5OfkgerUUYP1S1MGFxUqINnUY1YHITmo6pUKHsiJtP7sihnCT6uEx8LqVNf1quQJBANs+VCZVUDq6eMy3E/u03HiAB8cyqLVMVQ4cLyoiWmFlnEFzZwMd20ZMjtcxICiizW3dlDvyxWYKH93irL0JyM0CQQDDp/VFsh83vKICVvM9IZHwE/Z8vZA3eTkGbWmgnr6qaxqge3FU02kUvIHHlvLmXYIt30lTq0Rn+Lz+TGV/jDeHAkBHYSaSiGojhLx5og1+gKbbEIv3vbWRuTVj76cnZ6HXXfaelIzwRdMzMw+6XgMjV8XcRCzTy7ma/Cbd3cPxk/LtAkEAwkehMVexz/KrHI+icG1JMI9iDnNdJPhmO4+hdzCqOyanBfwNiSF0Encslze4ci8f+NTjRwWlo2hGomzRzFk7OQJAPPd/o0azkg9nF+JxLiz7hF+/6MLVZgIfw04u05ANtOSVVQP4UTmJ/tNAe3OBUQVlRQAJ1m3jzUlir0ACPypC1Q==
    -----END PRIVATE KEY-----`;

    // Use the ciphertext generated with the PHP code
    var ciphertext = 'a8gEZ6/DymB8dTGPytQPNS8QiYFuUULK+/c0vtie1l722isC0Z/jSeC2ytA6MjVUuTdq7sPuNW850gEZ2XvKujLQzl9sjJ8pcsxznBzMK8v03YJCTBr2lbfHpEEtuSLaAR2UbovXDoCyIIvOnMjqlIS3Ug2PG4hALThn/aAUwE0=';

    var key = await importPrivateKey(privateKey);
    var decryptedBuffer = str2ab(window.atob(ciphertext));
    var decrypted = await window.crypto.subtle.decrypt(
        {name:"RSA-OAEP"},key,decryptedBuffer
    );
    
    console.log(ab2str(decrypted)); // some text to encrypt
})();

在这里,发布的 JavaScript 代码用于生成 RSA 密钥对。公钥:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnkFHQbt801+kMnxn0VmMVljp8XdsbLEziLul3MwwckBDHwW6UDvYjN7vzJ/OM2RTxTbzilDcXJ37Zqz4qlDvXwSNmgIe+3dpuuRQRrJuJP6FD8zDTkRmg3QWOIIPBTzCqOtJKgWjFwMMxfCOBFEv6Ldn5Ac0i9ARl0/aNTWjvGwIDAQAB
-----END PUBLIC KEY-----

用于使用已发布的PHP代码进行加密,并在上面的代码中应用私钥进行解密。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...