I have a PHP class that handles encryption and decryption which stopped decrypting recently after a MySQL upgrade from 5.7 to 8 and migration to a new server. I need to decrypt the data in the database that was encrypted using the encrypt function and I can't for the life of me figure out what may have happened or what I need to fix. After searching around I decided to post for feedback. Thanks!
Quick clarification: While my main issue is retrieving and accessing the encrypted information in the database, this may be a 'red herring' since the decrypt function in the encryption class doesn't work at all for me. If I can solve this while preserving the encrypt function which works then it may result in a solution as to why I can't decrypt the database data. The encrypt function will run error-free and return an encrypted string, while the decrypt function gives the error below.
These are error I have received:
Decryption error: error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length
Decryption error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
This is the class:
class Openssl_EncryptDecrypt
{
private $encryption_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
public function encrypt($pure_string)
{
$cipher = 'AES-256-CBC';
$options = OPENSSL_RAW_DATA;
$hash_algo = 'sha256';
$sha2len = 32;
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($pure_string, $cipher, $this->encryption_key, $options, $iv);
$hmac = hash_hmac($hash_algo, $ciphertext_raw, $this->encryption_key, true);
return $iv . $hmac . $ciphertext_raw;
// return bin2hex($iv . $hmac . $ciphertext_raw);
}
public function decrypt($encrypted_string)
{
// $encrypted_string = hex2bin($encrypted_string);
$cipher = 'AES-256-CBC';
$options = OPENSSL_RAW_DATA;
$hash_algo = 'sha256';
$sha2len = 32;
$ivlen = openssl_cipher_iv_length($cipher);
$iv = substr($encrypted_string, 0, $ivlen);
$hmac = substr($encrypted_string, $ivlen, $sha2len);
$ciphertext_raw = substr($encrypted_string, $ivlen + $sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $this->encryption_key, $options, $iv);
$calcmac = hash_hmac($hash_algo, $ciphertext_raw, $this->encryption_key, true);
if ($original_plaintext === false) {
$errorMessage = 'Decryption error: ' . openssl_error_string() . ' for eid ' . $encrypted_string;
error_log($errorMessage);
return $errorMessage;
}
if (hash_equals($hmac, $calcmac)) {
return $original_plaintext;
}
}
}
Here is an example of retrieving data and passing it the decrypt function: (Aggregated from several files, trying to be succinct.)
// Grab employee data and decrypt the employee id:
$EmployeeData = getEmployeeDataBasedOnNetId($editNetId);
$EmployeeId = $OpensslEncryption->decrypt($EmployeeData['EmployeeId']);
// Get data from db as called above
function getEmployeeDataBasedOnNetId($NetId)
{
try {
$pdo = new pdoConnection('fasp');
$NetId = strtoupper(trim($NetId));
$sql = " SELECT NetId, EmployeeId, PrimaryRole, FirstName, MiddleName, LastName, Email, Phone, Mobile, Building, Room, MailStop, Picture, Gender, Ethnicity, CountryOfOrigin, UserName, Comment, OnWebsite
FROM Employee
WHERE NetId = :field1
";
$pdo->query($sql);
$row = $pdo->single(array(':field1' => $NetId));
return $row;
}
catch(Exception $e) {
echo $e;
}
finally {
$pdo->connectionClose();
$pdo = null;
}
}
// This is the query function from the pdo class called above:
public function query($query)
{
$this->stmt = $this->dbh->prepare($query);
}
// This is the single function from the pdo class called above:
public function single($arr = null)
{
$this->execute($arr);
return $this->stmt->fetch(PDO::FETCH_ASSOC);
}
// This is the execute function from the pdo class called above:
public function execute($arr = null)
{
if (is_array($arr) && count($arr)) {
return $this->stmt->execute($arr);
}
return $this->stmt->execute();
}
// This is the connectionClose funstion from the pdo class called above:
public function connectionClose()
{
$this->error = null;
$this->stmt = null;
$this->dbh = null;
}
Here is also the PDO settings I'm using for connecting to the DB, which may not be relevant but I'll add it in case it prompts anything I may not be considering:
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::MYSQL_ATTR_SSL_CA => true,
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
);
$dsn = "mysql:host=" . $host . ";port=" . $port . ";dbname=" . $db . ";charset=utf8mb4";
$dbh = new PDO($dsn, $user, $pass, $options);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Note about encryption class: You'll see a bin2hex
call in the encrypt function as well as a hex2bin
call in the decrypt function that are commented out, which was something I was testing and works! The issue is that it could be a solution moving forward, but it obviously doesn't help decrypt the current values in the database. Which is what I'm trying to figure out.
Thank you for the comments. I'm not 100% sure what caused this but I believe one of the changes I made on local that enabled me to decrypt the data was specifying the character encoding in PHP. I thought I tried this initially, so it may be some combination of this as well as some PHP configuration I did on my local vm. After I decrypted the data, I wrote a few scripts to update the db records (and encryption functions) to be more friendly with retrieval and updating. Cheers!