A short description: A JavaScript receives from a PHP-Script a RSA-Public-Key, the private-key will be stored.
In the JavaScript script, some data is encrypted and then passed to a PHP script that processes it.
There the encrypted data must be decrypted, but at this point my problem starts, my code doesn't decrypt the data.
My Code:
PHP:
header('Content-Type: application/json');
/*...*/
if(/*Conditions...*/){ /* When the Public Key is requested this is true */
die(json_encode(["EncryptionCode" => generateEncryptionCode()]));
} else if (
/*Conditions...*/ //When the encoded data will be passed this is true
&& !empty($_POST["key_id"]
&& !empty($_POST["encrypted_data"])
){
$data = decryptData(
ciphertext: $_POST["username"],
key_id: $_POST["key_id"]
);
// Now when the $data is correctly decoded (Here, when the decoded data is equal to the given text "This is some text that has to be encrypted and decrypted.")
$test_text = 'This is some text that has to be encrypted and decrypted.';
if($data == $test_text){
die(json_encode(["success" => true]));
} else {
die(json_encode(["Error"=>"Data not correctly decoded"]));
}
} else {
die(json_encode(["Error" => "Permission denied"]);
}
/********************************
* Functions that will be used *
********************************/
/**
* Generates an RSA encryption key pair and stores the private key in the database.
*
* @return array An array containing the public key and the ID of the stored private key.
*/
function generateEncryptionCode() : array{
global $database;
$config = array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
$privateKey = openssl_pkey_new($config);
$publicKey = openssl_pkey_get_details($privateKey)['key'];
$id = $database->add_encryption_key($privateKey);
return ["key"=>$publicKey, "id"=>$id];
}
/**
* Decrypts ciphertext using a private key retrieved from the database.
*
* @param string $ciphertext The base64-encoded ciphertext to decrypt.
* @param int $key_id The ID of the private key stored in the database.
* @return string The decrypted data.
*/
function decryptData($ciphertext, $key_id){
global $database;
$b64_ciphertext = base64_decode($ciphertext);
$private_key_string = $database->get_encryption_key($key_id);
$decrypted_data = '';
if(
!openssl_private_decrypt(
$b64_ciphertext,
$decrypted_data,
$private_key_string,
OPENSSL_PKCS1_OAEP_PADDING
)
){
$error = openssl_error_string();
die(json_encode(["OpenSSL Fehler: " . $error]));
}
return $decrypted_data;
}
JavaScript
const url = 'url_to_the_php_script';
let formData = new FormData()
/*formData.append(Data_for_permission)*/ /*The data that give permission in the PHP script*/
let text = "This is some text that has to be encrypted and decrypted."
// Call the PHP-Script to get the public_key
call_api(url, formData)
.then((data) => {
// get the public key of the data
encryption_key = data['EncryptionCode'];
// encrypt the data with the key
(async function() => {
var encrypted_text = await encrypt_data(text, await import_public_key(encryption_key['key']))
// now pass the encrypted data to the PHP file
formData = new FormData()
formData.append('key_id', encryption_key['id'])
formData.append('encrypted_data', encrypted_text)
/*formData.append(Data_for_permission)*/ /*The data that give permission in the PHP script*/
call_api(url, formData)
.then((data) {
// If the encrypted data was successfully passed to the PHP script and no error occurred in the PHP script while it was processing the data
if(data['success']){} //Success!
else {
console.error('Error', 'An error occured.')
}
})
})
})
.catch((error) => {
console.error('Error', error)
})
/********************************
* Functions that will be used *
********************************/
/**
* Encrypts the given value using RSA-OAEP encryption.
*
* @param {string} value The value to encrypt.
* @param {CryptoKey} encryption_key The encryption key.
* @returns {Promise<string>} A promise that resolves to the encrypted value.
*/
async function encrypt_data(value, encryption_key){
const enc = new TextEncoder();
const encoded = enc.encode(value)
const ciphertext = await window.crypto.subtle.encrypt(
{
name: 'RSA-OAEP'
},
encryption_key,
encoded
)
const encoder = new TextEncoder()
const data_view = new DataView(ciphertext)
const u_int_8_array = new Uint8Array(ciphertext.byteLength)
for (let i=0; i<ciphertext.byteLength; i++){
u_int_8_array[i] = data_view.getUint8(i);
}
return btoa(String.fromCharCode.apply(null, u_int_8_array))
}
/**
* Imports a public key from a PEM-encoded string.
*
* @param {string} pem The PEM-encoded public key string.
* @returns {Promise<CryptoKey>} A promise that resolves to the imported public key.
*/
function import_public_key(pem){
// fetch the part of the PEM string between header and footer
var pem_contents = pem
.replace(/-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----/g, '')
.replace(/\s/g, '');
// base64 decode the string to get the binary data
const binary_key_string = window.atob(pem_contents)
// convert from a binary string to an ArrayBuffer
const binary_key = (function(str){
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
})(binary_key_string);
return window.crypto.subtle.importKey(
'spki',
binary_key,
{
name: 'RSA-OAEP',
hash: 'SHA-256'
},
true,
["encrypt"]
);
}
/**
* Makes a POST request to the specified URL with the provided form data.
*
* @param {string} url The URL to make the request to.
* @param {FormData} formData The form data to include in the request body.
* @returns {Promise} A promise that resolves to the response data or an error.
*/
function call_api(url, formData) {
return new Promise((resolve, reject) => {
fetch(url, {
method: 'POST',
body: formData
})
.then(response => {
if(!response.ok){ throw new Error('Error fetching data.') }
return response.json()
})
.then(data => {
resolve(data)
})
.catch(error => {
resolve(error)
})
})
}
In a test run I got this Keys:
-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudG/Hbb3YDYSgd6IqR22 Vk9AOHhAmuR11zbFh1Sbetq8kdFXnYP2nj/fCaTemr63tfd8FY8LVnmuU6BbFL0Z CmpGTGuOtnYlyVd7NuNGdkwAi98apK2g0Sozl4ez4+KzqNy+B7+EPOknuR5tWQ8q 21DMmZU8RFXaiHrdWWONegiWyDc45jDXpWurnTDTC4BD3AC9fD3tbmbAxmuW4MDX aK6+nkGnv7R8ZCrr1BpV2HnYpikDiW0+1ISKjd1gEb37l1fb+VPH2vDi9cEmqubj ugNnvT2Eb9BEIsAV63Jqs60rQZOC74e9NXDr6QMW3x2qBexQQIS4K3rFCuPZb2AR pQIDAQAB \n-----END PUBLIC KEY-----
-----BEGIN PRIVATE KEY-----\n MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC50b8dtvdgNhKB 3oipHbZWT0A4eECa5HXXNsWHVJt62ryR0Vedg/aeP98JpN6avre193wVjwtWea5T oFsUvRkKakZMa462diXJV3s240Z2TACL3xqkraDRKjOXh7Pj4rOo3L4Hv4Q86Se5 Hm1ZDyrbUMyZlTxEVdqIet1ZY416CJbINzjmMNela6udMNMLgEPcAL18Pe1uZsDG a5bgwNdorr6eQae/tHxkKuvUGlXYedimKQOJbT7UhIqN3WARvfuXV9v5U8fa8OL1 wSaq5uO6A2e9PYRv0EQiwBXrcmqzrStBk4Lvh701cOvpAxbfHaoF7FBAhLgresUK 49lvYBGlAgMBAAECggEATf6P7XUC2EtZ8VDqo7Fo+0lAd3NiCqGiJLdEqc0FhceS JtJrqB3fwgSlJXMiTGmIysQaPSJRa/afCVLhaA8HF6wL3b+3ozZsHdquSReUV8sG 367BjCWkvqasCQpYo2pgZpxg1ve4Faj3l2gCFcOcBXogpsZRCY3PsEGB7ycuFu4U NFioP6EtN3XczXSCKbYt1KYdj2ndFHyGMfIL0LKmwNPamU40qJs09xMluIr/qUds YrxBe5JWWuODCfR0FUhOeMbVxRaZzZi6qH1WAkZCYjbF4eFhFN3oG5neEAO5T+8j TapdB32YF6kDajdVIX0NFVLNBbOPJ2ihlviQ5BlqcQKBgQDtz8sr1bpbMuGczfL+ ALKe1WHuoXmrIzH/jBiPo19+LJAR3L5xT3lcXPmNZaVLGYenp5cJvudhxrIOR82X EzyC/4Ia/RkP8uWGUbVkPQF1NtXReU2AjxzZar7TUFGY7oCd9oZOOtvOnXsCbJiF 1u2yAJ6JK0DTR/Mw+El4CdPYfwKBgQDIB/ltlmMVcD1odHk14eOmMNWobG996D7k dRnobcAhQKMXZXJBupJUdSfxgy9kHy696QK62CYtCk4+DmCCI0fsNJ3kUCHcrvQ+ ETp904T5ZX+hk2vDMjJr29Ww2bmUpik1bvVuUEmC9oDPIzLFgykFXNJ3AZrOyVMn 0xODfTOj2wKBgQDlCo0T2vVxgL/q1jCCkwl2EO4Rd1RHj85H4haFwUPnsePQUFrb pz+rxaBUnuFkQ2J0BuVhbYxMj6JOPrm0F8LgKFaWx82rnrWReIDL2jXdPsMQzVPn ze5rOHQx8dmlAZC+kwEnt2icxvAClbUQssCcAByw4Ae/djyznW6lPlHa4QKBgQCt KzokRS1CQgjnhO3qV8Rc+6n8ROPAfG72GOp07Y6HOw32Ezz26i4EL+iEjK1aYCR3 BGH4n2dtVp6l2oxyHVkGhAaswTKPema31PJuO8/CmLwFhTqloa9E8OvuTo76wV6r g4O2HIuHdR/OMwqhMwswOUt6+0ip/GCg+XrLOniaQQKBgFnXxe0QXN28bwnMRiGW BiEkOiq26sgueJuOApajsgXsFH/5O5covv6Qh7exe1PbeMboGArv5O1i5Zkc9xcQ phCZ21kEKQGNWkOc9AXK3MPnpuxrbfmPP2LY4qu7K9Zzn/iO/BPWfkoaXmB6GFb3 U5RSViL6D7Gb/Ht5UER44MGR \n-----END PRIVATE KEY-----
When the encryption is done the encrypted data (ciphertext) look like this:
AIoQF+ZIRqm/JkDEyZZhN7YoLZSSUIDd+DxN67b2mQFBmTWDj5BJ0Hb+ZFYYsobnrqBzyEEir0EI4Mg/lZv8CapkFmN0MXfe/Rov2JQ5YA6eNmm8WzWhi7AT9FhNBEBs03tbSWo92sfCAC1M0s6oEYiuwV7YTobxDgA6tWaDWzaulkMbaXNg02nteSXMIxG8ZAQrvxr4PzeLT0t9PMYCNIKv3+xr0Xswox7K2yg0eRlKYL9xQ3pGGObFU4LWPSVDTEM7LnCs4qlUz3RxfVc4Bpdpfr1ZMN+FL3qB6df9gV/nx2l3L2gIbPMYosqvHhhI6QDPZk8VqfHqYhq9tv8/4A==
But these decryptData function of the PHP-Script doesn't decrypt the data:
if(!openssl_private_decrypt(
$b64_ciphertext,
$decrypted_data,
$private_key_string,
OPENSSL_PKCS1_OAEP_PADDING
)){
$error = openssl_error_string();
die(json_encode(["OpenSSL Fehler: " . $error]));
}
After the check if the openssl_private_decrypt is true an error occured:
The error:
error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
I tryed to change the padding to OPENSSL_NO_PADDING. But nothing happened, the error was gone but the $decrypted_data wasn't set to the decrypted data.
At this point I really don't know what I am doing wrong.
If you know of anything I can do, I would be very grateful.