I'm having a problem decrypting my message using AES, but first I'll explain my code so that you can help me solve the problem. I'll provide you with my public and private RSA keys, as I've modified it anyway and I'm in a local development space on my ubuntu server.
To start, on my graphic interface, I fill in 2 text entries, with a nickname and my e-mail (this is a registration form).
For the nickname, I used my first name: Quentin which corresponds to the variable inputOne in my code.
For the e-mail, I used an imaginary e-mail address : [email protected] which corresponds to the variable inputTwo in my code.
Then at validation time, a javascript function is called checkindb(1, [inputOne, inputTwo]); which returns the following function:
async function checkindb(num_check, data_check) {
console.log('hello');
var httpRequest_check = getHttpRequest();
httpRequest_check.onreadystatechange = function () {
if (httpRequest_check.readyState === 4){
console.log(httpRequest_check.responseText);
return httpRequest_check.responseText;
}
}
httpRequest_check.open('POST', '../includes/checkconditionregister.php', true);
httpRequest_check.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
if (num_check == 1) {
console.log(data_check);
username_check = await crypterEtEnvoyer(data_check[0]);
mail_check = await crypterEtEnvoyer(data_check[1]);
console.log(username_check);
console.log(mail_check);
httpRequest_check.send("username=" + encodeURIComponent(JSON.stringify(username_check)) + "&mail=" + encodeURIComponent(JSON.stringify(mail_check)) + "&type=" + encodeURIComponent(1));
}
}
[inputOne, inputTwo] corresponds to: Array [ "Quentin", "[email protected]" ]
You may notice the use of the num_check variable, but this will allow me to reuse the function for all the different pages of my form.
This function uses a function called crypterEtEnvoyer(); which encrypts the data before sending it via xhr (the function is named in French because I'm French). Here's the function:
async function crypterEtEnvoyer(data) {
try {
const publicKeyRSA=`<?= $publicKey ?>`; // Clé publique RSA du serveur
console.log(publicKeyRSA);
if (!publicKeyRSA) {
throw new Error("Clé publique RSA manquante ou invalide.");
}
const cleAES = CryptoJS.lib.WordArray.random(256/8).toString();
console.log(cleAES);
const iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex); // Générer un IV
// Lecture de la clé publique RSA
const publicKey = await openpgp.readKey({ armoredKey: publicKeyRSA });
// Cryptage de la clé AES avec la clé publique RSA
const cleAESCryptee = await openpgp.encrypt({
message: await openpgp.createMessage({ text: cleAES }),
encryptionKeys: publicKey
});
// Cryptage des données avec AES
const donneesCryptees = CryptoJS.AES.encrypt(JSON.stringify(data), cleAES, { iv: CryptoJS.enc.Hex.parse(iv) }).toString();
return { donnees: donneesCryptees, cleAES: cleAESCryptee, iv: iv };
} catch (error) {
console.error("Erreur dans le processus de cryptage : ", error);
return null;
}
}
This javascript function lets me encrypt data using the cryptoJS and OpenPGP libraries. As you can see, encryption takes place in 3 stages:
- creation of an AES 256 key and an IV
- encrypting the AES 256 key with RSA 4096
- data encryption with the unencrypted AES key Finally, the function returns an object with the encrypted data, the AES 256 key encrypted with RSA 4096 and the IV.
Here's my RSA 4096 public key: MediaFire Link
Here are the unencrypted AES keys :
For nickname : 3b14d637b7ec033c6274213d8faf3537e5e7b6367e4f841b6da9d9cedae0b74c
For e-mail : ca19b18ed47ffc7a9177d938b3e505ebdc777e4a22f9a8437753a573c38bb4b0
To finish the Javascript part, the data are sent by xhr to the PHP script, here is the data sent :
username: '{
donnees: "U2FsdGVkX1/SZGEirssXB6bhGgbitWniNDxlldDJ0hE=",
cleAES: "-----BEGIN PGP MESSAGE-----\n\nwcFMA+ocpFDu5ijIAQ/+NB7Ra27WE0VZy2yaPtueFNmD7D4otR/PJE9BgxnP\nOTvTkRM1elvLcGtm//e1PVxDjjdmrnK7lJNvOVIxrzcbMt7YefGIxEtOSgOe\nWPZ6eBY3By1yp6zkNY+HpGYtM0SBe94zziN2v7H3jCdy1xEu6ibz76Mo5K0d\n8d9q+Z7sQ+kN8uOZotd4euCIbSq4EDQrlyuJrkV9XBUEDa8xww0YMqIbucWL\nduAsnob6GB+Rw6Mv9M6X5AFMDdJRudE7jhxi0F7Rro0b1BFv0PMKbOW+2/SW\nwQd27IvheQ0mgA0CWdgF3WzRAxnrvBw1i1IZWdwLxRc/bD5FE0OneQyZQhlR\nD4aIQfWiwGojOV/J2f75GIGXsasanZXG/kBsWcpfgW8iFScryAJ6gfNF/8XO\nl8UFWPh7nY+c7U9sMqTB4Epshf1uNw3IS0bE7vmC706R5w1CkgEl0GKaMohI\nImay6lR81oApwcajyRis/m2qfFLyy3mbJTg0AyLEJyAszOWfDIJYrf0jIyT0\n6TmTHJTEV9bJb1jsZvmMRLGBkRkCccPXp2fqDYmCRi81U56ddLLlijyGFggm\nDMpqM2XqrglO3/usDQwAC+7rjf/J4JjcN0Xa2NnMYj0C6/2k1qniO6qc0Vwe\nYliQVgcOZhgm5sMX9tyvBqPupAuBQaY9kIo1i1XF6ZjScQE9Xemb0f0yYz8v\naxr0FPx0L0tof6AEM5adowGRBkbyIdKkeagj4/Hrk2gE+KAX3AG1Q+jAfFg+\nS7IhZmUybaBsmj96Ytl0hczPNo1oUjZ6JQezGHToCxHlyIH7YsD3ykgj/7q7\niLoVKUaITK4cVbiS\n=qdJ7\n-----END PGP MESSAGE-----\n",
iv: "aab855e68a51856d04f164bfb0c0ee83"
}'
mail: '{
donnees: "U2FsdGVkX1/mLi7KL+2NBx5I8YO5GMdw65TByeT2XIXLIr9h/uQd+GdnwwRrxZUc",
cleAES: "-----BEGIN PGP MESSAGE-----\n\nwcFMA+ocpFDu5ijIAQ//avdWvV/EFKDdgh3tu0uSradaK2Ic7SgqWpk0QdUg\nfZpXD2r48qXEcvrjnR2Gc2o9KJDRPmikHjigCeC/uhfr6ylZ6FYjmECwCEuk\ne4+NfAViG1rsD6zLR87H/VSPi0tWenRh8OWcQ49SJrdPJUGfTqH+ELHBKRQG\nlkBfQ33HoWVl1HTi6L9W+/w2L4cdBF1wObu81SlYYLw3XqrYcVqjDkk18xvC\nPBZm2hkTK7GDbyJwFbsMNl59hHR8tBuRCcvnHzofuYewWu1aNYcp/BRpihGY\nezp8t3q7HJcUvJfDjaqUwPpZwpyiq8kmZvWd/aJRWfrJHYa1eDVGc8ammVX2\nAGKCHdqARwNI2Mq3Z9YYvUZqRhchDOFfn29AZCF7Lh8bSlBCUfy12dnrclec\nce9KcPEpNiwIsSzIgv+gGnrmk5LIPi0/N0UAW+c72ViCyStxRD5oVs4KtRAz\nD03eO8aRR11WkGYAYe/QMFH40WUyONcnxt5lqm5tx729HUGXuncTtPax1jSt\nJX6ntm9+MoTSU3uPoGGzBDsK78cNxsurfo4qRVp/mz0QBu1vsfJ3ECTnuZqF\nKaglHdsQ2QNmPd7WsmhWtTrx5grp9CcFnPMLbF0jw5L+DMCgPCnfNyNUN3iA\nxJLIU4K1c6y2sRexS2+1gSJ3vz+RttX/aOet1oYNxU3ScQGRXAz9twPeU7kt\nZjS6paAyhE/Qrm6w48B5PDYGl2fyeeRqQmzXikmD6R3FkYCY3gf8sRf0qkS7\nMydpa24RJz9JoukadfTitL6Wp3pUHYbSi+X+ihTpfjPPKZkPOiQR1yOvqvCH\nuT0bmm8CREctgYMQ\n=Gsnx\n-----END PGP MESSAGE-----\n",
iv: "be5a63128b697147ba9a7a6864839581"
}'
type:'1'
Now we come to the heart of the matter, i.e. the PHP script. The aim of this script is to check the database to see if the nickname and email already exist; if there is no account with an email or nickname similar to the data entered, the script returns true, and if there is, it returns false. Here is the PHP script:
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
if (isset($_POST['type'])) {
echo 1;
if(!empty($_POST['type'])){
echo 2;
include 'database_profile.php';
include 'RSA4096AES256.php';
global $pro_bdd;
if ($_POST['type'] = 1) {
echo 3;
if (isset($_POST['username']) && !empty($_POST['username']) && isset($_POST['mail']) && !empty($_POST['mail'])) {
echo 4;
try {
echo 5;
echo "###A###".$_POST['username']."###</br>";
$pseudo = Decrypt($_POST['username']);
echo "###".$pseudo."###";
echo "Les données décryptées sont : " . $decryptedData;
} catch (Exception $e) {
echo "</br></br></br></br>Erreur lors du décryptage : " . $e->getMessage()."</br></br></br></br>";
}
try {
echo 6;
echo "###B###".$_POST['mail']."###</br>";
$mail = Decrypt($_POST['mail']);
echo "###".$mail."###";
echo "Les données décryptées sont : " . $decryptedData;
} catch (Exception $e) {
echo "</br></br></br></br>Erreur lors du décryptage : " . $e->getMessage()."</br></br></br></br>";
}
$q = $pro_bdd->prepare("SELECT id FROM users WHERE email = :email OR pseudo = :pseudo");
$q->execute(['email' => $mail,'pseudo' => $pseudo]);
if ($q->rowCount() == 0) {
echo true;
}else{
echo false;
}
}
}elseif ($_POST['type'] = 1) {
// code...
}elseif ($_POST['type'] = 2) {
// code...
}
}
}
In my PHP script, I also check the data I have received:
###A###
{
"donnees":"U2FsdGVkX1/SZGEirssXB6bhGgbitWniNDxlldDJ0hE=",
"cleAES":"-----BEGIN PGP MESSAGE-----\n\nwcFMA+ocpFDu5ijIAQ/+NB7Ra27WE0VZy2yaPtueFNmD7D4otR/PJE9BgxnP\nOTvTkRM1elvLcGtm//e1PVxDjjdmrnK7lJNvOVIxrzcbMt7YefGIxEtOSgOe\nWPZ6eBY3By1yp6zkNY+HpGYtM0SBe94zziN2v7H3jCdy1xEu6ibz76Mo5K0d\n8d9q+Z7sQ+kN8uOZotd4euCIbSq4EDQrlyuJrkV9XBUEDa8xww0YMqIbucWL\nduAsnob6GB+Rw6Mv9M6X5AFMDdJRudE7jhxi0F7Rro0b1BFv0PMKbOW+2/SW\nwQd27IvheQ0mgA0CWdgF3WzRAxnrvBw1i1IZWdwLxRc/bD5FE0OneQyZQhlR\nD4aIQfWiwGojOV/J2f75GIGXsasanZXG/kBsWcpfgW8iFScryAJ6gfNF/8XO\nl8UFWPh7nY+c7U9sMqTB4Epshf1uNw3IS0bE7vmC706R5w1CkgEl0GKaMohI\nImay6lR81oApwcajyRis/m2qfFLyy3mbJTg0AyLEJyAszOWfDIJYrf0jIyT0\n6TmTHJTEV9bJb1jsZvmMRLGBkRkCccPXp2fqDYmCRi81U56ddLLlijyGFggm\nDMpqM2XqrglO3/usDQwAC+7rjf/J4JjcN0Xa2NnMYj0C6/2k1qniO6qc0Vwe\nYliQVgcOZhgm5sMX9tyvBqPupAuBQaY9kIo1i1XF6ZjScQE9Xemb0f0yYz8v\naxr0FPx0L0tof6AEM5adowGRBkbyIdKkeagj4/Hrk2gE+KAX3AG1Q+jAfFg+\nS7IhZmUybaBsmj96Ytl0hczPNo1oUjZ6JQezGHToCxHlyIH7YsD3ykgj/7q7\niLoVKUaITK4cVbiS\n=qdJ7\n-----END PGP MESSAGE-----\n",
"iv":"aab855e68a51856d04f164bfb0c0ee83"
}
###
###B###
{
"donnees":"U2FsdGVkX1/mLi7KL+2NBx5I8YO5GMdw65TByeT2XIXLIr9h/uQd+GdnwwRrxZUc",
"cleAES":"-----BEGIN PGP MESSAGE-----\n\nwcFMA+ocpFDu5ijIAQ//avdWvV/EFKDdgh3tu0uSradaK2Ic7SgqWpk0QdUg\nfZpXD2r48qXEcvrjnR2Gc2o9KJDRPmikHjigCeC/uhfr6ylZ6FYjmECwCEuk\ne4+NfAViG1rsD6zLR87H/VSPi0tWenRh8OWcQ49SJrdPJUGfTqH+ELHBKRQG\nlkBfQ33HoWVl1HTi6L9W+/w2L4cdBF1wObu81SlYYLw3XqrYcVqjDkk18xvC\nPBZm2hkTK7GDbyJwFbsMNl59hHR8tBuRCcvnHzofuYewWu1aNYcp/BRpihGY\nezp8t3q7HJcUvJfDjaqUwPpZwpyiq8kmZvWd/aJRWfrJHYa1eDVGc8ammVX2\nAGKCHdqARwNI2Mq3Z9YYvUZqRhchDOFfn29AZCF7Lh8bSlBCUfy12dnrclec\nce9KcPEpNiwIsSzIgv+gGnrmk5LIPi0/N0UAW+c72ViCyStxRD5oVs4KtRAz\nD03eO8aRR11WkGYAYe/QMFH40WUyONcnxt5lqm5tx729HUGXuncTtPax1jSt\nJX6ntm9+MoTSU3uPoGGzBDsK78cNxsurfo4qRVp/mz0QBu1vsfJ3ECTnuZqF\nKaglHdsQ2QNmPd7WsmhWtTrx5grp9CcFnPMLbF0jw5L+DMCgPCnfNyNUN3iA\nxJLIU4K1c6y2sRexS2+1gSJ3vz+RttX/aOet1oYNxU3ScQGRXAz9twPeU7kt\nZjS6paAyhE/Qrm6w48B5PDYGl2fyeeRqQmzXikmD6R3FkYCY3gf8sRf0qkS7\nMydpa24RJz9JoukadfTitL6Wp3pUHYbSi+X+ihTpfjPPKZkPOiQR1yOvqvCH\nuT0bmm8CREctgYMQ\n=Gsnx\n-----END PGP MESSAGE-----\n",
"iv":"be5a63128b697147ba9a7a6864839581"
}
###
But to do the verification in the database, I need data and for that I use the PHP function Decrypt(); which I store in RSA4096AES256.php :
<?php
require '../vendor/autoload.php';
use phpseclib3\Crypt\AES;
use phpseclib3\Exception\NoKeyLoadedException;
function Decrypt($encryptedString)
{
// Convertir la chaîne JSON en tableau PHP
echo "###1###".$encryptedString."###</br>";
$input = json_decode($encryptedString, true);
echo "###2###";
var_dump($input);
echo "###</br>";
// Initialiser GnuPG
putenv('GNUPGHOME=/var/www/.gnupg'); // Assurez-vous que ce chemin est correct pour l'utilisateur www-data
$gpg = new gnupg();
// Supprimer les caractères d'échappement devant les caractères spéciaux
echo "###3###".$input['cleAES']."###</br>";
$input['cleAES'] = stripcslashes($input['cleAES']);
echo "###4###".$input['cleAES']."###</br>";
// Importer la clé privée et la passphrase
// Ceci est un exemple et devrait être fait en dehors du script avec une gestion sécurisée des clés
$privateKey = file_get_contents('/var/www/html/keys/private-key.asc');
$keyInfo = $gpg->import($privateKey);
$gpg->adddecryptkey($keyInfo['fingerprint'], ''); // Remplacer par votre passphrase réelle
// Décrypter la clé AES cryptée avec RSA
$cleAESDecrypteeHex = $gpg->decrypt($input['cleAES']);
echo "###5###".$cleAESDecrypteeHex."###</br>";
$cleAESDecrypteeBin = hex2bin($cleAESDecrypteeHex);
echo "###6###". $cleAESDecrypteeBin."###</br>";
echo "###7###". $input['iv']."###</br>";
echo "###8###". hex2bin($input['iv'])."###</br>";
echo "###9###". $input['donnees']."###</br>";
echo "###10###". base64_decode($input['donnees'])."###</br>";
// Décrypter les données avec AES
$aes = new \phpseclib3\Crypt\AES('cbc');
$aes->setKey($cleAESDecrypteeBin);
$aes->setIV(hex2bin($input['iv']));
$donneesDecryptees = $aes->decrypt(base64_decode($input['donnees']));
echo "###2.2.2.2.2.2.2.2.2.2.2###". $donneesDecryptees."###</br>";
if ($donneesDecryptees === false) {
throw new Exception("Décryptage des données échoué.");
}
// Renvoyer les données décryptées
return $donneesDecryptees;
}
I use the echo to inspect the data during the execution of the function, I know it's not very clean, but I prefer to do it this way. I didn't fill in the decryption key because it's very personal.
So to describe the function, at first I decode my object with json_decode to get a PHP array, this one (data of username object when execute of $pseudo = Decrypt($_POST['username']);) :
###2###
array(3) {
["donnees"]=> string(44) "U2FsdGVkX1/SZGEirssXB6bhGgbitWniNDxlldDJ0hE="
["cleAES"]=> string(932) "-----BEGIN PGP MESSAGE----- wcFMA+ocpFDu5ijIAQ/+NB7Ra27WE0VZy2yaPtueFNmD7D4otR/PJE9BgxnP OTvTkRM1elvLcGtm//e1PVxDjjdmrnK7lJNvOVIxrzcbMt7YefGIxEtOSgOe WPZ6eBY3By1yp6zkNY+HpGYtM0SBe94zziN2v7H3jCdy1xEu6ibz76Mo5K0d 8d9q+Z7sQ+kN8uOZotd4euCIbSq4EDQrlyuJrkV9XBUEDa8xww0YMqIbucWL duAsnob6GB+Rw6Mv9M6X5AFMDdJRudE7jhxi0F7Rro0b1BFv0PMKbOW+2/SW wQd27IvheQ0mgA0CWdgF3WzRAxnrvBw1i1IZWdwLxRc/bD5FE0OneQyZQhlR D4aIQfWiwGojOV/J2f75GIGXsasanZXG/kBsWcpfgW8iFScryAJ6gfNF/8XO l8UFWPh7nY+c7U9sMqTB4Epshf1uNw3IS0bE7vmC706R5w1CkgEl0GKaMohI Imay6lR81oApwcajyRis/m2qfFLyy3mbJTg0AyLEJyAszOWfDIJYrf0jIyT0 6TmTHJTEV9bJb1jsZvmMRLGBkRkCccPXp2fqDYmCRi81U56ddLLlijyGFggm DMpqM2XqrglO3/usDQwAC+7rjf/J4JjcN0Xa2NnMYj0C6/2k1qniO6qc0Vwe YliQVgcOZhgm5sMX9tyvBqPupAuBQaY9kIo1i1XF6ZjScQE9Xemb0f0yYz8v axr0FPx0L0tof6AEM5adowGRBkbyIdKkeagj4/Hrk2gE+KAX3AG1Q+jAfFg+ S7IhZmUybaBsmj96Ytl0hczPNo1oUjZ6JQezGHToCxHlyIH7YsD3ykgj/7q7 iLoVKUaITK4cVbiS =qdJ7 -----END PGP MESSAGE----- "
["iv"]=> string(32) "aab855e68a51856d04f164bfb0c0ee83"
}
###
Then we load the private key ($privateKey = file_get_contents();):
MediaFire Link
Then we start decrypting the AES key with gnupg, which I've installed on my ubuntu server:
Encrypted AES key:
###4###
-----BEGIN PGP MESSAGE----- wcFMA+ocpFDu5ijIAQ/+NB7Ra27WE0VZy2yaPtueFNmD7D4otR/PJE9BgxnP OTvTkRM1elvLcGtm//e1PVxDjjdmrnK7lJNvOVIxrzcbMt7YefGIxEtOSgOe WPZ6eBY3By1yp6zkNY+HpGYtM0SBe94zziN2v7H3jCdy1xEu6ibz76Mo5K0d 8d9q+Z7sQ+kN8uOZotd4euCIbSq4EDQrlyuJrkV9XBUEDa8xww0YMqIbucWL duAsnob6GB+Rw6Mv9M6X5AFMDdJRudE7jhxi0F7Rro0b1BFv0PMKbOW+2/SW wQd27IvheQ0mgA0CWdgF3WzRAxnrvBw1i1IZWdwLxRc/bD5FE0OneQyZQhlR D4aIQfWiwGojOV/J2f75GIGXsasanZXG/kBsWcpfgW8iFScryAJ6gfNF/8XO l8UFWPh7nY+c7U9sMqTB4Epshf1uNw3IS0bE7vmC706R5w1CkgEl0GKaMohI Imay6lR81oApwcajyRis/m2qfFLyy3mbJTg0AyLEJyAszOWfDIJYrf0jIyT0 6TmTHJTEV9bJb1jsZvmMRLGBkRkCccPXp2fqDYmCRi81U56ddLLlijyGFggm DMpqM2XqrglO3/usDQwAC+7rjf/J4JjcN0Xa2NnMYj0C6/2k1qniO6qc0Vwe YliQVgcOZhgm5sMX9tyvBqPupAuBQaY9kIo1i1XF6ZjScQE9Xemb0f0yYz8v axr0FPx0L0tof6AEM5adowGRBkbyIdKkeagj4/Hrk2gE+KAX3AG1Q+jAfFg+ S7IhZmUybaBsmj96Ytl0hczPNo1oUjZ6JQezGHToCxHlyIH7YsD3ykgj/7q7 iLoVKUaITK4cVbiS =qdJ7 -----END PGP MESSAGE-----
###
Decrypted AES key in hexadecimal format:
###5###
3b14d637b7ec033c6274213d8faf3537e5e7b6367e4f841b6da9d9cedae0b74c
###
Note that this is the same AES 256 key used in the javacript function, which means that decryption is correct in RSA 4096.
AES key decrypted in binary format (I have some problems when I copy the AES key in binary):
###6###
;Ö7·ì
###
Before decrypting the data, there are a few more steps:
IV original:
###7###
aab855e68a51856d04f164bfb0c0ee83
###
IV converts to binary (I have some problems when I copy the IV in binary):
###8###
ª¸UæQmñd¿°Àî
###
The length of the IV is 16, so it's correct.
Encrypted data:
###9###
U2FsdGVkX1/SZGEirssXB6bhGgbitWniNDxlldDJ0hE=
###
Decoded data base64_decode(); (I have some problems when I copy the data in binary):
###10###
Salted__Òda"®Ë¦áâµiâ4
###
But when I decrypt the data in AES 256 $donneesDecryptees = $aes->decrypt(base64_decode($input['donnees']));, I get an error, hence my long explanation:
Erreur lors du décryptage : The ciphertext has an invalid padding length (31) compared to the block size (16)
It's already been several days and I can't solve my problem, from what I understand, the problem comes from padding, after some research, I found that CrytoJS uses padding PKCS#7. But that's as far as I understand.
I've already been coding for several years in PHP and JS, I'm used to coding everything myself and not using any libraries, I think I learn more that way, but now I don't have much choice, I'm not going to reinvent RSA and AES.
If you have any suggestions regarding my error and my code, if you want, I won't refuse anything.
When my error is resolved, if other people want to use my code, there's no problem.
Thanks in advance to all those who reply.
What's more, for those who are interested, I'm making my code available, along with all the logs, as I've only given you the logs for the nickname. MediaFire Link


