1
votes

I'm currently stuck here right now, this is from client side:

$scope.encryptFormData = function() {
    // Get payment data
    var paymentData = JSON.stringify($scope.payment);

    // Generate AES secret key
    var secret_key = $scope.generateKey(paymentData);
    var iv  = CryptoJS.lib.WordArray.random(16);

    // Encrypt form data using AES secret key
    var cipherPaymentData = CryptoJS.AES.encrypt(paymentData, secret_key, {iv: iv});

    // Encrypt AES secret key with RSA public key from server
    var secret_key_rsa = null;
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey($scope.public_key);
    secret_key_rsa = encrypt.encrypt(secret_key);

    // Assign values to form object
    var iv_hex = CryptoJS.enc.Hex.stringify(iv);
    var payment_data_hex = CryptoJS.enc.Hex.stringify(cipherPaymentData.ciphertext);

    $scope.payment_data.secret_key = secret_key_rsa;
    $scope.payment_data.iv = iv_hex;
    $scope.payment_data.payment = payment_data_hex;

    // Send form data to server
};

$scope.generateKey = function(p) {
    var salt = CryptoJS.lib.WordArray.random(128/8);

    return CryptoJS.PBKDF2(p, salt, { keySize: 512/32, iterations: 1000 });     
}

Now, I send this payload to the server for decryption:

{
    iv: "ae3bafa370bdc0c8a6b47ab1b792ec58"
    payment: "e12f9db635984ec2f146bd34e433ef912580c4b3c7c1efe0a8e3fa6981abefa860630752539a9af88a9db0c198a63b804855d4b56357f75456785d7313908c6e"
    secret_key: "emMKQ2QOWtzQAHhur4FLQCyBSK9zzPzomjWyDbeOM3VpjIKb1aNs9SL4P1nhuizEwuM2os/FXsN6MJz/cwxQWakK3tnVFvmt..."
}

This is my controller function in the server:

public function storePayment(Request $request) {
    $postData = $request->data;

    // Decrypt AES secret key, using private key
    $iv = $postData['iv'];
    $secretKey = $this->EncryptionService->decryptData($postData['secret_key']);

    // Decrypt AES client form data with secret key
    $paymentData = $this->EncryptionService->decryptSecretData($postData['payment'], $secretKey, $iv);

    // Print decrypted data
}

From my EncryptionService:

For RSA decryption:

public function decryptData($data) 
{
    $privateKey = $this->getPrivateKey();

    if (empty($privateKey)) {
        $this->regenerateKeypair();
    }

    $privateKey = $this->getPrivateKey();
    $binaryData = hex2bin($data);
    openssl_private_decrypt($binaryData, $decrypted, $privateKey);

    return $decrypted;
}

For AES decryption:

public function decryptSecretData($data, $secretKey, $iv) 
{
    $cipher = "aes-256-cbc";
    if (in_array($cipher, openssl_get_cipher_methods()))
    {
        $iv = hex2bin($iv);
        $originalData = openssl_decrypt($data, $cipher, $secretKey, $options=0, $iv);

        return $originalData;
    }
}

But right now I'm just getting this error:

file: "...\Services\EncryptionService.php"
line: 101
message: "hex2bin(): Input string must be hexadecimal string"

It points to this line inside decryptData():

$binaryData = hex2bin($data);

How do I properly send the data from the client so the server can properly decrypt it?

EDIT:

I'm using these libraries:

https://github.com/travist/jsencrypt

https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js

1
Is the data arriving at the server intact? What Content-Type does the POST request use? - georgeawg
@georgeawg For Content-Type, the value is text/html; charset=UTF-8. Data seem to arrive intact. - herondale
I am surprised the POST is text/html. The AngularJS framework normally uses application/json. Also the nomenclature in the code is confusing. There is a var named secret_key but the secret_key property of the sent object is different. It makes the code difficult to understand, debug, test, and maintain. - georgeawg
@georgeawg sorry for causing that confusion, did you mean this part? $scope.payment_data.secret_key = secret_key_rsa; The AES secret key has to be encrypted in RSA using the public key from the server so only the server can decrypt it using its private key. (That RSA encryption part on the client side is already included in the question, I already had that part commented.) - herondale

1 Answers

0
votes

It looks like the data you're getting from JavaScript for $secretKey is base64 encoded, rather than hex. Perhaps this change on the PHP side will work (but only for $secretKey):

$binaryData = base64_decode($data);