PHP and JavaScript Data Encryption and Decryption

457 Views Asked by At

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.

0

There are 0 best solutions below