Reputation: 23
What I'm trying to do is generate a keypair within javascript, and use these with encrypting in PHP and then decrypt with JS.
I'm having two problems within the code attached
Both throw the error DOMException, instead of a useful error.
So this is my code...
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, 0, 1]),
hash: {name: "SHA-256"}
},
true,
["decrypt"]
);
}
(async() => {
let keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {name: "SHA-256"}
},
true,
["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>
Upvotes: 2
Views: 1492
Reputation: 49251
The bug is in the str2ab()
function, which uses a Uint16Array
instead of a Uint8Array
.
If this is fixed, the private key can be imported and the ciphertext generated with the PHP code can be decrypted:
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, 0, 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
})();
Here, the posted JavaScript code was applied to generate an RSA key pair. The public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnkFHQbt801+kMnxn0VmMVljp8XdsbLEziLul3MwwckBDHwW6UDvYjN7vzJ/OM2RTxTbzilDcXJ37Zqz4qlDvXwSNmgIe+3dpuuRQRrJuJP6FD8zDTkRmg3QWOIIPBTzCqOtJKgWjFwMMxfCOBFEv6Ldn5Ac0i9ARl0/aNTWjvGwIDAQAB
-----END PUBLIC KEY-----
was used to perform the encryption with the posted PHP code and the private key is applied in the code above for decryption.
Upvotes: 2