0

I have been running Alpine Linux image version 3.15 with PHP 8.0 with openssl 1.1.x version on my server.

Using this version I have encrypted some data using openssl_pkcs7_encrypt() function, stored the encrypted data in DB and I was able to then successfully decrypt the encrypted data from DB when needed using openssl_pkcs7_decrypt() function.

After updating Alpine Linux to 3.17 with PHP to 8.1 which uses openssl 3.0, the openssl_pkcs7_decrypt() function is unable to decrypt my previously encrypted data which were encrypted using PHP 8.0 with OpenSSL 1.1.x.

I have used openssl_error_string() function to get all errors reported by OpenSSL library and here's the list of errors reported:

  1. error:10800077:PKCS7 routines::decrypt error
  2. error:0308010C:digital envelope routines::unsupported
  3. last 8 errors are.: error:12000079:random number generator::Cannot open file

I have tried installing openssl1.1-compat using apk add openssl1.1-compat as referred in this link as well as I have tried to comment out RANDFILE = $ENV::HOME/.rnd /etc/ssl/openssl.cnf as referred to in this post, but unfortunately none of the changes led to any success.

Has anyone faced a similar issue and have you been able to fix this?

Does anyone has any suggestions on what could help me to fix the issue?

Simple code snippet with amended code for data encryption / decryption using the above functions is here:

<?php

function GetOpenSSLErrors()
{
    $openSSLErrors = "";
    $errorCounter = 0;
    while ( ($currentError = openssl_error_string()) !== false) 
    {
        ++$errorCounter;
        $openSSLErrors = $openSSLErrors . "$errorCounter. - $currentError ";
    }

    return $openSSLErrors;
}

function Decrypt($encryptedMessage, $privateKey, $certificate, &$error = "")
{
    $decrypted              = NULL;
    $fileEncryptedMessage   = "EncryptedMessage.txt";
    $fileDecryptedMessage   = "DecryptedMessage.txt";

    if(file_put_contents($fileEncryptedMessage, $encryptedMessage) === false) {
        $error = "Failed to write received message for RSA decryption to the file '$fileEncryptedMessage'";
    }
    else if(openssl_pkcs7_decrypt($fileEncryptedMessage, $fileDecryptedMessage, $certificate, $privateKey)) {
        $decrypted = file_get_contents($fileDecryptedMessage,true);
        if (!$decrypted) {
            $error = "Failed to get content decrypted by openssl_pkcs7_decrypt from file [$fileDecryptedMessage]";
        }
    } else {
        $error = "Failed to openssl_pkcs7_decrypt content of file [$fileEncryptedMessage], openSSL errors [" . GetOpenSSLErrors() . "]";
    }

    // Delete any created files
    if (file_exists ($fileEncryptedMessage)) unlink($fileEncryptedMessage);
    if (file_exists ($fileDecryptedMessage)) unlink($fileDecryptedMessage);

    return $decrypted;
}

function Encrypt($messageToEncrypt, $certificate, &$error = "")
{
    $returnValue = NULL;
    $fileMessageToEncrypt = "MessageToEncrypt.txt";
    $fileEncryptedMessage = "EncryptedMessage.txt";

    if(file_put_contents($fileMessageToEncrypt, $messageToEncrypt) === false) {
        $error = "Failed to write received message for RSA encryption to the file '$fileEncryptedMessage'";
    }
    else if (openssl_pkcs7_encrypt($fileMessageToEncrypt, $fileEncryptedMessage, $certificate, NULL)) {
        $returnValue = file_get_contents($fileEncryptedMessage,true);

        if (!$returnValue) {
            $returnValue = NULL;
            $error = "Failed to get content encrypted by openssl_pkcs7_encrypt from file [$fileEncryptedMessage]";
        }
    } else {
        $error = "Failed to openssl_pkcs7_encrypt content of file [$fileMessageToEncrypt], openSSL errors [" . GetOpenSSLErrors() . "]";
    }
    
    // Delete any created files
    if (file_exists ($fileEncryptedMessage)) unlink($fileEncryptedMessage);
    if (file_exists ($fileMessageToEncrypt)) unlink($fileMessageToEncrypt);

    return $returnValue;
}

// *************** END OF FUNCTIONS ********************

// Get certificate and private key used for decryption / encryption
$certificate    = file_get_contents("../pathToSomeFolder/certificate.pem", true);
$privateKey     = file_get_contents("../pathToSomeFolder/private_key.pem", true);

$decrypted      = NULL;
$plainMessage   = "Some string that needs to be encrypted";
$cryptoError    = "";

// Attempt to encrypt the plain message and if encryption fails, print the error and exit the script
$encryptedMessage = Encrypt($plainMessage, $certificate, $cryptoError);

if ($encryptedMessage === NULL) {
    echo "Failed to encrypt the plain message, error = $cryptoError";
    exit(1);
} 

// Attempt to decrypt the encrypted message
$decryptedMessage = Decrypt($encryptedMessage, $privateKey, $certificate, $cryptoError);

if ($decryptedMessage === NULL) {
    echo "Failed to decrypt the encrypted message, error = $cryptoError";
    exit(1);
}

echo "Successfully encrypted and decrypted required message";
?>
mbuster
  • 170
  • 14
  • Obviously, OpenSSL 1.1 uses an algorithm that is not supported in OpenSSL 3 any more. Does [this](https://www.datainfinities.com/49/error-0308010C-digital-envelope-routines-unsupported) help? – not2savvy Aug 15 '23 at 07:35
  • Thanks @not2savvy for the link, yes I have came across that too as well as its PHP equivalent [here](https://stackoverflow.com/questions/73832854/php-openssl-pkcs12-read-error0308010cdigital-envelope-routinesunsupported), but my openssl.cnf doesn't have such sections where I'd add it (unless I try adding the whole sections to see what it does). Obviously I need to upgrade to newer versions of PHP and openssl going forward so running something in legacy mode is only a short term solution. – mbuster Aug 15 '23 at 08:40

1 Answers1

0

I have eventually managed to find a solution for this.

Enabling legacy provider on top of having the default provider enabled in openssl.cnf file that is used by openssl has helped in this case. There are some security considerations to be aware of when this is enabled and this solution is not recommended as a long term solution!

Enabling legacy provider is even mentioned on the manual page of official OpenSSL website here.

Is has helped me to check what's the latest openssl.cnf file configured to in the public github repo. And thanks to this post and this post I did add the required sections to my openssl.cnf file, reloaded / restarted the server and things started to work.

These are the changes that I did to my openssl.cnf file:

  1. Make sure openssl_conf = openssl_init config item is present
  2. Make sure [openssl_init] section is present below it and in that section the providers config item is configured accordingly. E.g. providers = provider_sect
  3. The rest is as per this post, which means making sure [default_sect] section is specified and activate config item is set (activate = 1), as well as making sure [legacy_sect] section is specified and activate config item is set (activate = 1) under that section too, and lastly that [provider_sect] section is specified and both default and the legacy providers are set as under their corresponding config items: default = default_sect and legacy = legacy_sect

Below is a short code snippet with all of the above changes I did to my openssl.cnf file :

# Use this in order to automatically load providers.
openssl_conf = openssl_init

[openssl_init]
providers = provider_sect

# List of providers to load
[provider_sect]
default = default_sect
legacy = legacy_sect

[default_sect]
activate = 1
[legacy_sect]
activate = 1

And here's an image with the changes if that would be of any help to anyone. enter image description here

mbuster
  • 170
  • 14