From cc2d94f3bc7ec5979ee147e728eebe3457233331 Mon Sep 17 00:00:00 2001
From: Jean-Laurent DUZANT <jean-laurent.duzant@xelians.fr>
Date: Wed, 27 Sep 2023 12:47:34 +0200
Subject: [PATCH] FEAT #25796 TIME 0:20 rapatriement du code

---
 bin/notification/process_email_stack.php      |   2 +-
 config/config.json.default                    |   3 +-
 config/mc_secret.key                          |   1 +
 .../PreProcessActionController.php            |   4 +-
 src/app/action/controllers/ShippingTrait.php  |   4 +-
 .../controllers/ConfigurationController.php   |   4 +-
 .../contact/controllers/ContactController.php |   6 +-
 src/app/email/controllers/EmailController.php |   4 +-
 .../controllers/AlfrescoController.php        |  16 +-
 .../SendMessageExchangeController.php         |   4 +-
 .../controllers/MultigestController.php       |  10 +-
 .../outlook/controllers/OutlookController.php |  20 +--
 .../ShippingTemplateController.php            |   8 +-
 .../controllers/AuthenticationController.php  |   4 +-
 src/core/controllers/PasswordController.php   | 147 ++++++++++++++++++
 src/core/models/CoreConfigModel.php           |  50 +++++-
 src/core/models/PasswordModel.php             |  30 +++-
 .../unitTests/core/PasswordControllerTest.php | 144 +++++++++++++++++
 18 files changed, 412 insertions(+), 49 deletions(-)
 create mode 100644 config/mc_secret.key

diff --git a/bin/notification/process_email_stack.php b/bin/notification/process_email_stack.php
index af326e8c074..4fae0897cd7 100644
--- a/bin/notification/process_email_stack.php
+++ b/bin/notification/process_email_stack.php
@@ -98,7 +98,7 @@ foreach ($emails as $key => $email) {
         if ($configuration['auth']) {
             $phpmailer->Username = $configuration['user'];
             if (!empty($configuration['password'])) {
-                $phpmailer->Password = \SrcCore\models\PasswordModel::decrypt(['cryptedPassword' => $configuration['password']]);
+                $phpmailer->Password = \SrcCore\controllers\PasswordController::decrypt(['encryptedData' => $configuration['password']]);
             }
         }
     } elseif ($configuration['type'] == 'sendmail') {
diff --git a/config/config.json.default b/config/config.json.default
index db85718bc82..ed44e2c6f6f 100644
--- a/config/config.json.default
+++ b/config/config.json.default
@@ -8,7 +8,8 @@
         "maarchDirectory" : "/var/www/html/MaarchCourrier/",
         "customID" : "",
         "maarchUrl" : "https://demo.maarchcourrier.com/",
-        "lockAdvancedPrivileges" : true
+        "lockAdvancedPrivileges" : true,
+        "privateKeyPath": "/var/www/html/MaarchCourrier/config/mc_secret.key"
     },
     "database": [
         {
diff --git a/config/mc_secret.key b/config/mc_secret.key
new file mode 100644
index 00000000000..5af040ba12d
--- /dev/null
+++ b/config/mc_secret.key
@@ -0,0 +1 @@
+Security Key Maarch Courrier 2008
\ No newline at end of file
diff --git a/src/app/action/controllers/PreProcessActionController.php b/src/app/action/controllers/PreProcessActionController.php
index 5f5e4985eab..267ed2662b8 100755
--- a/src/app/action/controllers/PreProcessActionController.php
+++ b/src/app/action/controllers/PreProcessActionController.php
@@ -57,7 +57,7 @@ use User\models\UserEntityModel;
 use User\models\UserModel;
 use ExportSeda\controllers\PreProcessActionSEDATrait;
 use Multigest\controllers\MultigestController;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SignatureBook\controllers\SignatureBookController;
 
 class PreProcessActionController
@@ -1844,7 +1844,7 @@ class PreProcessActionController
         $accountCheck = MultigestController::checkAccountWithCredentials([
             'sasId' => $entityInformations['multigest']['sasId'],
             'login' => $entityInformations['multigest']['login'],
-            'password' => empty($entityInformations['multigest']['password']) ? '' : PasswordModel::decrypt(['cryptedPassword' => $entityInformations['multigest']['password']])
+            'password' => empty($entityInformations['multigest']['password']) ? '' : PasswordController::decrypt(['encryptedData' => $entityInformations['multigest']['password']])
         ]);
         if (!empty($accountCheck['errors'])) {
             return $response->withStatus(400)->withJson(['errors' => $accountCheck['errors'], 'lang' => ($accountCheck['lang'] ?? null)]);
diff --git a/src/app/action/controllers/ShippingTrait.php b/src/app/action/controllers/ShippingTrait.php
index 7baa4ba736f..c5dca8d1a6b 100644
--- a/src/app/action/controllers/ShippingTrait.php
+++ b/src/app/action/controllers/ShippingTrait.php
@@ -28,7 +28,7 @@ use Shipping\models\ShippingModel;
 use Shipping\models\ShippingTemplateModel;
 use SrcCore\models\CoreConfigModel;
 use SrcCore\models\CurlModel;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\ValidatorModel;
 use User\models\UserModel;
 
@@ -197,7 +197,7 @@ trait ShippingTrait
             'queryParams'   => [
                 'grant_type'    => 'password',
                 'username'      => $shippingTemplate['account']['id'],
-                'password'      => PasswordModel::decrypt(['cryptedPassword' => $shippingTemplate['account']['password']])
+                'password'      => PasswordController::decrypt(['encryptedData' => $shippingTemplate['account']['password']])
             ]
         ]);
         if ($curlAuth['code'] != 200) {
diff --git a/src/app/configuration/controllers/ConfigurationController.php b/src/app/configuration/controllers/ConfigurationController.php
index b41cc006e5b..d233f250723 100755
--- a/src/app/configuration/controllers/ConfigurationController.php
+++ b/src/app/configuration/controllers/ConfigurationController.php
@@ -28,7 +28,7 @@ use Respect\Validation\Validator;
 use Slim\Psr7\Request;
 use SrcCore\http\Response;
 use SrcCore\models\CoreConfigModel;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use Status\models\StatusModel;
 use ContentManagement\controllers\DocumentEditorController;
 
@@ -90,7 +90,7 @@ class ConfigurationController
                     $data['password'] = $configuration['value']['password'];
                 }
             } elseif ($data['auth'] && !empty($data['password'])) {
-                $data['password'] = PasswordModel::encrypt(['password' => $data['password']]);
+                $data['password'] = PasswordController::encrypt(['dataToEncrypt' => $data['password']]);
             }
             $check = ConfigurationController::checkMailer($data);
             if (!empty($check['errors'])) {
diff --git a/src/app/contact/controllers/ContactController.php b/src/app/contact/controllers/ContactController.php
index 1726d4abd83..f7e1d70dc9e 100755
--- a/src/app/contact/controllers/ContactController.php
+++ b/src/app/contact/controllers/ContactController.php
@@ -36,7 +36,7 @@ use SrcCore\http\Response;
 use SrcCore\controllers\AutoCompleteController;
 use SrcCore\models\CoreConfigModel;
 use SrcCore\models\DatabaseModel;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\TextFormatModel;
 use SrcCore\models\ValidatorModel;
 use User\models\UserModel;
@@ -164,7 +164,7 @@ class ContactController
                 $contactBody['login'] = $body['communicationMeans']['login'];
             }
             if (!empty($body['communicationMeans']['password'])) {
-                $contactBody['password'] = PasswordModel::encrypt(['password' => $body['communicationMeans']['password']]);
+                $contactBody['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['communicationMeans']['password']]);
             }
         }
 
@@ -356,7 +356,7 @@ class ContactController
                 $contactBody['login'] = $body['communicationMeans']['login'];
             }
             if (!empty($body['communicationMeans']['password'])) {
-                $contactBody['password'] = PasswordModel::encrypt(['password' => $body['communicationMeans']['password']]);
+                $contactBody['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['communicationMeans']['password']]);
             }
         }
 
diff --git a/src/app/email/controllers/EmailController.php b/src/app/email/controllers/EmailController.php
index 091d2a7ed44..bda7d08bae8 100644
--- a/src/app/email/controllers/EmailController.php
+++ b/src/app/email/controllers/EmailController.php
@@ -39,7 +39,7 @@ use Respect\Validation\Validator;
 use Slim\Psr7\Request;
 use SrcCore\http\Response;
 use SrcCore\models\CoreConfigModel;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\TextFormatModel;
 use SrcCore\models\ValidatorModel;
 use User\models\UserModel;
@@ -669,7 +669,7 @@ class EmailController
             if ($configuration['auth']) {
                 $phpmailer->Username = $configuration['user'];
                 if (!empty($configuration['password'])) {
-                    $phpmailer->Password = PasswordModel::decrypt(['cryptedPassword' => $configuration['password']]);
+                    $phpmailer->Password = PasswordController::decrypt(['encryptedData' => $configuration['password']]);
                 }
             }
         } elseif ($configuration['type'] == 'sendmail') {
diff --git a/src/app/external/alfresco/controllers/AlfrescoController.php b/src/app/external/alfresco/controllers/AlfrescoController.php
index 513fc188bcf..66290a6e2ee 100644
--- a/src/app/external/alfresco/controllers/AlfrescoController.php
+++ b/src/app/external/alfresco/controllers/AlfrescoController.php
@@ -33,7 +33,7 @@ use Slim\Psr7\Request;
 use SrcCore\http\Response;
 use SrcCore\models\CoreConfigModel;
 use SrcCore\models\CurlModel;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\ValidatorModel;
 use User\models\UserModel;
 
@@ -186,7 +186,7 @@ class AlfrescoController
             'id'        => $id,
             'label'     => $body['label'],
             'login'     => $body['login'],
-            'password'  => PasswordModel::encrypt(['password' => $body['password']]),
+            'password'  => PasswordController::encrypt(['dataToEncrypt' => $body['password']]),
             'nodeId'    => $body['nodeId']
         ];
         $account = json_encode($account);
@@ -238,7 +238,7 @@ class AlfrescoController
             'id'        => $args['id'],
             'label'     => $body['label'],
             'login'     => $body['login'],
-            'password'  => empty($body['password']) ? $alfresco['alfresco']['password'] : PasswordModel::encrypt(['password' => $body['password']]),
+            'password'  => empty($body['password']) ? $alfresco['alfresco']['password'] : PasswordController::encrypt(['dataToEncrypt' => $body['password']]),
             'nodeId'    => $body['nodeId']
         ];
         $account = json_encode($account);
@@ -312,7 +312,7 @@ class AlfrescoController
             if (empty($alfresco['alfresco']['password'])) {
                 return $response->withStatus(400)->withJson(['errors' => 'Account has no password']);
             }
-            $body['password'] = PasswordModel::decrypt(['cryptedPassword' => $alfresco['alfresco']['password']]);
+            $body['password'] = PasswordController::decrypt(['encryptedData' => $alfresco['alfresco']['password']]);
         }
 
         $configuration = ConfigurationModel::getByPrivilege(['privilege' => 'admin_alfresco']);
@@ -390,7 +390,7 @@ class AlfrescoController
         if (empty($entityInformations['alfresco'])) {
             return $response->withStatus(400)->withJson(['errors' => 'User primary entity has not enough alfresco informations']);
         }
-        $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]);
+        $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]);
 
         $curlResponse = CurlModel::exec([
             'url'           => "{$alfrescoUri}/alfresco/versions/1/nodes/{$entityInformations['alfresco']['nodeId']}/children",
@@ -440,7 +440,7 @@ class AlfrescoController
         if (empty($entityInformations['alfresco'])) {
             return $response->withStatus(400)->withJson(['errors' => 'User primary entity has not enough alfresco informations']);
         }
-        $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]);
+        $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]);
 
         $curlResponse = CurlModel::exec([
             'url'           => "{$alfrescoUri}/alfresco/versions/1/nodes/{$args['id']}/children",
@@ -497,7 +497,7 @@ class AlfrescoController
         if (empty($entityInformations['alfresco'])) {
             return $response->withStatus(400)->withJson(['errors' => 'User primary entity has not enough alfresco informations']);
         }
-        $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]);
+        $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]);
 
         $search = addslashes($queryParams['search']);
         $body = [
@@ -559,7 +559,7 @@ class AlfrescoController
         if (empty($entityInformations['alfresco'])) {
             return ['errors' => 'User primary entity has not enough alfresco informations'];
         }
-        $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]);
+        $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]);
 
         $document = ResModel::getById([
             'select'    => [
diff --git a/src/app/external/messageExchange/controllers/SendMessageExchangeController.php b/src/app/external/messageExchange/controllers/SendMessageExchangeController.php
index 17bf6633c0e..1d662868f9d 100755
--- a/src/app/external/messageExchange/controllers/SendMessageExchangeController.php
+++ b/src/app/external/messageExchange/controllers/SendMessageExchangeController.php
@@ -28,7 +28,7 @@ use Note\models\NoteModel;
 use Resource\controllers\ResController;
 use Resource\models\ResModel;
 use Respect\Validation\Validator;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\TextFormatModel;
 use Status\models\StatusModel;
 use User\models\UserModel;
@@ -247,7 +247,7 @@ class SendMessageExchangeController
                     }
                     $url = str_replace($prefix, '', $ArchivalAgencyCommunicationType['value']);
                     $login = $aArchivalAgencyCommunicationType['login'] ?? '';
-                    $password = !empty($aArchivalAgencyCommunicationType['password']) ? PasswordModel::decrypt(['cryptedPassword' => $aArchivalAgencyCommunicationType['password']]) : '';
+                    $password = !empty($aArchivalAgencyCommunicationType['password']) ? PasswordController::decrypt(['encryptedData' => $aArchivalAgencyCommunicationType['password']]) : '';
                     $ArchivalAgencyCommunicationType['value'] = $prefix;
                     if (!empty($login) && !empty($password)) {
                         $ArchivalAgencyCommunicationType['value'] .= $login . ':' . $password . '@';
diff --git a/src/app/external/multigest/controllers/MultigestController.php b/src/app/external/multigest/controllers/MultigestController.php
index 263862796ed..385d2a7f8f5 100644
--- a/src/app/external/multigest/controllers/MultigestController.php
+++ b/src/app/external/multigest/controllers/MultigestController.php
@@ -32,7 +32,7 @@ use Resource\models\ResourceContactModel;
 use Respect\Validation\Validator;
 use Slim\Psr7\Request;
 use SrcCore\http\Response;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\ValidatorModel;
 use SrcCore\models\CoreConfigModel;
 use User\models\UserModel;
@@ -162,7 +162,7 @@ class MultigestController
             'id'       => $id,
             'label'    => $body['label'],
             'login'    => $body['login'],
-            'password' => PasswordModel::encrypt(['password' => $body['password']]),
+            'password' => PasswordController::encrypt(['dataToEncrypt' => $body['password']]),
             'sasId'    => $body['sasId']
         ];
         $account = json_encode($account);
@@ -237,7 +237,7 @@ class MultigestController
             'id'        => $args['id'],
             'label'     => $body['label'],
             'login'     => $body['login'],
-            'password'  => empty($body['password']) ? $externalId['multigest']['password'] : PasswordModel::encrypt(['password' => $body['password']]),
+            'password'  => empty($body['password']) ? $externalId['multigest']['password'] : PasswordController::encrypt(['dataToEncrypt' => $body['password']]),
             'sasId'     => $body['sasId']
         ];
         $account = json_encode($account);
@@ -306,7 +306,7 @@ class MultigestController
         $result = MultigestController::checkAccountWithCredentials([
             'sasId' => $body['sasId'],
             'login' => $body['login'],
-            'password' => empty($body['password']) ? '' : PasswordModel::decrypt(['cryptedPassword' => $body['password']])
+            'password' => empty($body['password']) ? '' : PasswordController::decrypt(['encryptedData' => $body['password']])
         ]);
 
         if (!empty($result['errors'])) {
@@ -531,7 +531,7 @@ class MultigestController
         try {
             $soapClient = new \SoapClient($multigestUri, [
                 'login'          => $configuration['login'],
-                'password'       => empty($configuration['password']) ? '' : PasswordModel::decrypt(['cryptedPassword' => $configuration['password']]),
+                'password'       => empty($configuration['password']) ? '' : PasswordController::decrypt(['encryptedData' => $configuration['password']]),
                 'authentication' => SOAP_AUTHENTICATION_BASIC
             ]);
         } catch (\SoapFault $e) {
diff --git a/src/app/external/outlook/controllers/OutlookController.php b/src/app/external/outlook/controllers/OutlookController.php
index aac921a127a..14d82af6727 100644
--- a/src/app/external/outlook/controllers/OutlookController.php
+++ b/src/app/external/outlook/controllers/OutlookController.php
@@ -24,7 +24,7 @@ use Slim\Psr7\Request;
 use SrcCore\http\Response;
 use SrcCore\controllers\LanguageController;
 use SrcCore\models\CoreConfigModel;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use Status\models\StatusModel;
 use User\models\UserModel;
 
@@ -113,9 +113,9 @@ class OutlookController
             }
         }
 
-        $configuration['value']['tenantId']     = !empty($configuration['value']['tenantId']) ? PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['tenantId']]) : '';
-        $configuration['value']['clientId']     = !empty($configuration['value']['clientId']) ? PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientId']]) : '';
-        $configuration['value']['clientSecret'] = !empty($configuration['value']['clientSecret']) ? PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientSecret']]) : '';
+        $configuration['value']['tenantId']     = !empty($configuration['value']['tenantId']) ? PasswordController::decrypt(['encryptedData' => $configuration['value']['tenantId']]) : '';
+        $configuration['value']['clientId']     = !empty($configuration['value']['clientId']) ? PasswordController::decrypt(['encryptedData' => $configuration['value']['clientId']]) : '';
+        $configuration['value']['clientSecret'] = !empty($configuration['value']['clientSecret']) ? PasswordController::decrypt(['encryptedData' => $configuration['value']['clientSecret']]) : '';
 
         $configuration['value']['outlookConnectionSaved'] = false;
         if (!empty($configuration['value']['tenantId']) && !empty($configuration['value']['clientId']) && !empty($configuration['value']['clientSecret'])) {
@@ -171,9 +171,9 @@ class OutlookController
             'statusId'          => $body['statusId'],
             'attachmentTypeId'  => $body['attachmentTypeId'],
             'version'           => $body['version'],
-            'tenantId'          => !empty($body['tenantId']) ? PasswordModel::encrypt(['password' => $body['tenantId']]) : '',
-            'clientId'          => !empty($body['clientId']) ? PasswordModel::encrypt(['password' => $body['clientId']]) : '',
-            'clientSecret'      => !empty($body['clientSecret']) ? PasswordModel::encrypt(['password' => $body['clientSecret']]) : ''
+            'tenantId'          => !empty($body['tenantId']) ? PasswordController::encrypt(['password' => $body['tenantId']]) : '',
+            'clientId'          => !empty($body['clientId']) ? PasswordController::encrypt(['password' => $body['clientId']]) : '',
+            'clientSecret'      => !empty($body['clientSecret']) ? PasswordController::encrypt(['password' => $body['clientSecret']]) : ''
         ], JSON_UNESCAPED_SLASHES);
         if (empty(ConfigurationModel::getByPrivilege(['privilege' => 'admin_addin_outlook', 'select' => [1]]))) {
             ConfigurationModel::create(['value' => $data, 'privilege' => 'admin_addin_outlook']);
@@ -221,9 +221,9 @@ class OutlookController
             'ewsHost'        => explode('/', $body['ewsUrl'])[0],
             'email'          => $body['userId'],
             'version'        => $configuration['value']['version'],
-            'tenantId'       => PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['tenantId']]),
-            'clientId'       => PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientId']]),
-            'clientSecret'   => PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientSecret']]),
+            'tenantId'       => PasswordController::decrypt(['encryptedData' => $configuration['value']['tenantId']]),
+            'clientId'       => PasswordController::decrypt(['encryptedData' => $configuration['value']['clientId']]),
+            'clientSecret'   => PasswordController::decrypt(['encryptedData' => $configuration['value']['clientSecret']]),
             'attachmentType' => $attachmentType['type_id']
         ];
 
diff --git a/src/app/shipping/controllers/ShippingTemplateController.php b/src/app/shipping/controllers/ShippingTemplateController.php
index 11f65b35e54..1a3e9d36add 100755
--- a/src/app/shipping/controllers/ShippingTemplateController.php
+++ b/src/app/shipping/controllers/ShippingTemplateController.php
@@ -31,7 +31,7 @@ use SrcCore\controllers\LogsController;
 use SrcCore\models\CoreConfigModel;
 use Slim\Psr7\Request;
 use SrcCore\http\Response;
-use SrcCore\models\PasswordModel;
+use SrcCore\controllers\PasswordController;
 use SrcCore\models\ValidatorModel;
 use Firebase\JWT\JWT;
 
@@ -147,7 +147,7 @@ class ShippingTemplateController
         }
 
         if (!empty($body['account']['password'])) {
-            $body['account']['password'] = PasswordModel::encrypt(['password' => $body['account']['password']]);
+            $body['account']['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['account']['password']]);
         }
 
         $body['options']  = !empty($body['options']) ? json_encode($body['options']) : '{}';
@@ -218,7 +218,7 @@ class ShippingTemplateController
         }
 
         if (!empty($body['account']['password'])) {
-            $body['account']['password'] = PasswordModel::encrypt(['password' => $body['account']['password']]);
+            $body['account']['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['account']['password']]);
         } else {
             $shippingInfo = ShippingTemplateModel::getById(['id' => $args['id'], 'select' => ['account']]);
             $shippingInfo['account'] = json_decode($shippingInfo['account'], true);
@@ -922,7 +922,7 @@ class ShippingTemplateController
             'queryParams'   => [
                 'grant_type'    => 'password',
                 'username'      => $shippingTemplateAccount['id'],
-                'password'      => PasswordModel::decrypt(['cryptedPassword' => $shippingTemplateAccount['password']])
+                'password'      => PasswordController::decrypt(['encryptedData' => $shippingTemplateAccount['password']])
             ]
         ]);
         if ($curlAuth['code'] != 200) {
diff --git a/src/core/controllers/AuthenticationController.php b/src/core/controllers/AuthenticationController.php
index dc1fa744b24..fa7ae98c4f4 100755
--- a/src/core/controllers/AuthenticationController.php
+++ b/src/core/controllers/AuthenticationController.php
@@ -56,7 +56,7 @@ class AuthenticationController
 
         $parameter = ParameterModel::getById(['id' => 'loginpage_message', 'select' => ['param_value_string']]);
 
-        $encryptKey = CoreConfigModel::getEncryptKey();
+        $encryptKeyChanged = CoreConfigModel::hasEncryptKeyChanged();
 
         $loggingMethod = CoreConfigModel::getLoggingMethod();
         $authUri = null;
@@ -112,7 +112,7 @@ class AuthenticationController
             'instanceId'                => $hashedPath,
             'applicationName'           => $appName,
             'loginMessage'              => $parameter['param_value_string'] ?? null,
-            'changeKey'                 => $encryptKey == 'Security Key Maarch Courrier #2008',
+            'changeKey'                 => !$encryptKeyChanged,
             'authMode'                  => $loggingMethod['id'],
             'authUri'                   => $authUri,
             'lang'                      => CoreConfigModel::getLanguage(),
diff --git a/src/core/controllers/PasswordController.php b/src/core/controllers/PasswordController.php
index 8722c32bfa0..8e55eec387c 100755
--- a/src/core/controllers/PasswordController.php
+++ b/src/core/controllers/PasswordController.php
@@ -21,6 +21,10 @@ use Slim\Psr7\Request;
 use SrcCore\http\Response;
 use SrcCore\models\PasswordModel;
 use SrcCore\models\ValidatorModel;
+use Exception;
+use SrcCore\models\CoreConfigModel;
+use SrcCore\controllers\LogsController;
+
 
 class PasswordController
 {
@@ -100,4 +104,147 @@ class PasswordController
 
         return true;
     }
+
+    /**
+     * Encrypt data with the old vhost or new file encryption key
+     *
+     * @return string
+     */
+    public static function encrypt(array $args): string
+    {
+        ValidatorModel::notEmpty($args, ['dataToEncrypt']);
+        ValidatorModel::stringType($args, ['dataToEncrypt']);
+
+        if (CoreConfigModel::useVhostEncryptKey()) {
+            return PasswordModel::encrypt(['password' => $args['dataToEncrypt']]);
+        } else {
+            return PasswordController::newEncrypt(['dataToEncrypt' => $args['dataToEncrypt']]);
+        }
+    }
+
+    /**
+     * Decrypt encrypted data with the old vhost or new file encryption key
+     *
+     * @return string
+     */
+    public static function decrypt(array $args): string
+    {
+        ValidatorModel::notEmpty($args, ['encryptedData']);
+        ValidatorModel::stringType($args, ['encryptedData']);
+
+        if (CoreConfigModel::useVhostEncryptKey()) {
+            return PasswordModel::decrypt(['cryptedPassword' => $args['encryptedData']]);
+        } else {
+            return PasswordController::newDecrypt(['encryptedData' => $args['encryptedData']]);
+        }
+    }
+
+    /**
+     * @deprecated This function logic will be moved to PasswordController::encrypt() in future major versions.
+     * Please use PasswordController::encrypt() instead.
+     * 
+     * @return string
+     */
+    public static function newEncrypt(array $args): string
+    {
+        ValidatorModel::notEmpty($args, ['dataToEncrypt']);
+        ValidatorModel::stringType($args, ['dataToEncrypt']);
+
+        $encryptedResult = null;
+        $encryptKey      = CoreConfigModel::getEncryptKey();
+        $cipherMethod    = 'AES-256-CTR';
+
+        try {
+            $encryptKeyHash  = openssl_digest($encryptKey, 'sha256');
+
+            $initialisationVector = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipherMethod));
+            $encrypted = openssl_encrypt($args['dataToEncrypt'], $cipherMethod, $encryptKeyHash, OPENSSL_RAW_DATA, $initialisationVector);
+
+            if ($encrypted === false) {
+                LogsController::add([
+                    'isTech'    => true,
+                    'moduleId'  => 'Encryption/Decryption',
+                    'level'     => 'ERROR',
+                    'eventType' => 'Decrypt',
+                    'eventId'   => 'Encryption failed: ' . openssl_error_string()
+                ]);
+                throw new Exception('Encryption failed: ' . openssl_error_string());
+            }
+
+            $encryptedResult = $initialisationVector . $encrypted;
+        } catch (Exception $e) {
+            LogsController::add([
+                'isTech'    => true,
+                'moduleId'  => 'Encryption/Decryption',
+                'level'     => 'ERROR',
+                'eventType' => 'Decrypt',
+                'eventId'   => 'Encryption Exception: ' . $e->getMessage()
+            ]);
+            throw new Exception('Encryption Exception: ' . $e->getMessage());
+        }
+
+        return base64_encode($encryptedResult);
+    }
+
+    /**
+     * @deprecated This function logic will be moved to PasswordController::decrypt() in future major versions.
+     * Please use PasswordController::decrypt() instead.
+     * 
+     * @return string
+     */
+    public static function newDecrypt(array $args): string
+    {
+        ValidatorModel::notEmpty($args, ['encryptedData']);
+        ValidatorModel::stringType($args, ['encryptedData']);
+
+        $decryptedResult = null;
+        $encryptKey      = CoreConfigModel::getEncryptKey();
+        $cipherMethod    = 'AES-256-CTR';
+        $encryptedData   = base64_decode($args['encryptedData']);
+
+        try {
+            $initialisationVectorLength = openssl_cipher_iv_length($cipherMethod);
+
+            // encrypted data integrity check on size of data
+            if (strlen($encryptedData) < $initialisationVectorLength) {
+                LogsController::add([
+                    'isTech'    => true,
+                    'moduleId'  => 'Encryption/Decryption',
+                    'level'     => 'ERROR',
+                    'eventType' => 'Decrypt',
+                    'eventId'   => 'Decryption failed: data length '. strlen($encryptedData). ' is less than iv length ' . $initialisationVectorLength
+                ]);
+                throw new Exception('Decryption failed: data length '. strlen($encryptedData). ' is less than iv length ' . $initialisationVectorLength);
+            }
+
+            // Extract the initialisation vector and encrypted data
+            $initialisationVector   = substr($encryptedData, 0, $initialisationVectorLength);
+            $encryptedData          = substr($encryptedData, $initialisationVectorLength);
+            $encryptKeyHash         = openssl_digest($encryptKey, 'sha256');
+
+            $decryptedResult = openssl_decrypt($encryptedData, $cipherMethod, $encryptKeyHash, OPENSSL_RAW_DATA, $initialisationVector);
+
+            if ($decryptedResult === false) {
+                LogsController::add([
+                    'isTech'    => true,
+                    'moduleId'  => 'Encryption/Decryption',
+                    'level'     => 'ERROR',
+                    'eventType' => 'Decrypt',
+                    'eventId'   => 'Decryption failed: ' . openssl_error_string()
+                ]);
+                throw new Exception('Decryption failed: ' . openssl_error_string());
+            }
+        } catch (Exception $e) {
+            LogsController::add([
+                'isTech'    => true,
+                'moduleId'  => 'Encryption/Decryption',
+                'level'     => 'ERROR',
+                'eventType' => 'Decrypt',
+                'eventId'   => 'Decryption Exception: ' . $e->getMessage()
+            ]);
+            throw new Exception('Decryption Exception: ' . $e->getMessage());
+        }
+
+        return $decryptedResult;
+    }
 }
diff --git a/src/core/models/CoreConfigModel.php b/src/core/models/CoreConfigModel.php
index 0ae565ebd5f..84b5bbfaf3a 100755
--- a/src/core/models/CoreConfigModel.php
+++ b/src/core/models/CoreConfigModel.php
@@ -178,9 +178,12 @@ class CoreConfigModel
     /**
      * Get the Encrypt Key
      *
+     * @deprecated This function is deprecated and will be removed in future major versions.
+     * Please use getEncryptKey() instead.
+     *
      * @return string
      */
-    public static function getEncryptKey()
+    public static function getOldEncryptKey()
     {
         if (isset($_SERVER['MAARCH_ENCRYPT_KEY'])) {
             $encryptKey = $_SERVER['MAARCH_ENCRYPT_KEY'];
@@ -193,6 +196,51 @@ class CoreConfigModel
         return $encryptKey;
     }
 
+        /**
+     * @deprecated This function will be removed in future major versions.
+     * Please use getEncryptKey() instead.
+     *
+     * @return bool
+     */
+    public static function useVhostEncryptKey(): bool
+    {
+        $configPath = CoreConfigModel::getConfigPath();
+        $config = json_decode(file_get_contents($configPath), true)['config'];
+
+        return !isset($config['privateKeyPath']) || empty($config['privateKeyPath']);
+    }
+
+    /**
+     * Get the Encrypt Key
+     *
+     * @return string
+     */
+    public static function getEncryptKey(): string
+    {
+        $configPath = CoreConfigModel::getConfigPath();
+        $config = json_decode(file_get_contents($configPath), true)['config'];
+
+        $encryptKeyPath = $config['privateKeyPath'] ?? null;
+
+        if (empty($encryptKeyPath)) {
+            $encryptKey = CoreConfigModel::getOldEncryptKey();
+        } elseif (!empty($encryptKeyPath) && is_file($encryptKeyPath) && is_readable($encryptKeyPath)) {
+            $encryptKey = file_get_contents($encryptKeyPath);
+            $encryptKey = trim($encryptKey);
+        } else {
+            $encryptKey = "Security Key Maarch Courrier 2008";
+        }
+
+        return $encryptKey;
+    }
+
+    public static function hasEncryptKeyChanged(): bool
+    {
+        $encryptKey = CoreConfigModel::getEncryptKey();
+
+        return $encryptKey !== "Security Key Maarch Courrier #2008" && $encryptKey !== "Security Key Maarch Courrier 2008";
+    }
+
     public static function getLibrariesDirectory()
     {
         if (isset($_SERVER['LIBRARIES_DIR'])) {
diff --git a/src/core/models/PasswordModel.php b/src/core/models/PasswordModel.php
index 71d6b343947..6c812212630 100755
--- a/src/core/models/PasswordModel.php
+++ b/src/core/models/PasswordModel.php
@@ -152,13 +152,19 @@ class PasswordModel
         return true;
     }
 
+    /**
+     * @deprecated This function is deprecated and will be removed in future major versions.
+     * Please use PasswordController::encrypt() instead.
+     *
+     * @return string
+     */
     public static function encrypt(array $aArgs)
     {
         ValidatorModel::notEmpty($aArgs, ['password']);
         ValidatorModel::stringType($aArgs, ['password']);
 
         $enc_key = CoreConfigModel::getEncryptKey();
-        
+
         $cipher_method = 'AES-128-CTR';
         $enc_iv        = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));
         $crypted_token = openssl_encrypt($aArgs['password'], $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
@@ -166,17 +172,33 @@ class PasswordModel
         return $crypted_token;
     }
 
+    /**
+     * @deprecated This function is deprecated and will be removed in future major versions.
+     * Please use PasswordController::decrypt() instead.
+     *
+     * @return string
+     */
     public static function decrypt(array $aArgs)
     {
         ValidatorModel::notEmpty($aArgs, ['cryptedPassword']);
         ValidatorModel::stringType($aArgs, ['cryptedPassword']);
 
         $enc_key = CoreConfigModel::getEncryptKey();
-        
+
         $cipher_method = 'AES-128-CTR';
 
-        list($crypted_token, $enc_iv) = explode("::", $aArgs['cryptedPassword']);
-        $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
+        $token = null;
+        try {
+            $cryptedPasswordParts = explode("::", $aArgs['cryptedPassword']);
+            if (count($cryptedPasswordParts) !== 2) {
+                throw new \Exception("Invalid format: cryptedPassword should contain two parts separated by '::'");
+            }
+            list($crypted_token, $enc_iv) = $cryptedPasswordParts;
+
+            $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
+        } catch (\Throwable $th) {
+            throw new \Exception($th->getMessage());
+        }
 
         return $token;
     }
diff --git a/test/unitTests/core/PasswordControllerTest.php b/test/unitTests/core/PasswordControllerTest.php
index d28be0b166a..1677afd1dbb 100755
--- a/test/unitTests/core/PasswordControllerTest.php
+++ b/test/unitTests/core/PasswordControllerTest.php
@@ -11,10 +11,30 @@ namespace MaarchCourrier\Tests\core;
 
 use SrcCore\controllers\PasswordController;
 use SrcCore\http\Response;
+use SrcCore\models\CoreConfigModel;
 use MaarchCourrier\Tests\CourrierTestCase;
 
 class PasswordControllerTest extends CourrierTestCase
 {
+    private static ?string $generalConfigPath = null;
+    private static $generalConfigOriginal = null;
+    private static bool $restoreOriginalConfig = false;
+
+    protected function setUp(): void
+    {
+        self::$generalConfigPath = (file_exists("config/config.json") ? "config/config.json" : "config/config.json.default");
+
+        $generalConfig = file_get_contents(self::$generalConfigPath);
+        $generalConfig = json_decode($generalConfig, true);
+        $generalConfig['config']['privateKeyPath'] = getcwd() . '/config/mc_secret.key';
+        self::$generalConfigOriginal = $generalConfig;
+    }
+
+    public function testCheckOriginalConfigIsNotEmpty()
+    {
+        $this->assertNotEmpty(self::$generalConfigOriginal);
+    }
+
     public function testGetRules()
     {
         $passwordController = new PasswordController();
@@ -153,4 +173,128 @@ class PasswordControllerTest extends CourrierTestCase
         $isPasswordValid = $passwordController->isPasswordValid(['password' => 'maarch']);
         $this->assertSame($isPasswordValid, true);
     }
+
+    public function provideDataToEncrypt()
+    {
+        return [
+            'lower case letters' => [
+                "dataToEncrypt" => "abcdefghijklmnopqrstuvwxyz"
+            ],
+            'upper case letters' => [
+                "dataToEncrypt" => "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            ],
+            'numbers' => [
+                "dataToEncrypt" => "0123456789"
+            ],
+            'special characters' => [
+                "dataToEncrypt" => "`~!@#$%^&*()-_=+[{]}\|;:'\",<.>/? "
+            ],
+            'lower and upper case letters' => [
+                "dataToEncrypt" => "Maarch Courrier"
+            ],
+            'lower, upper case letters and numbers' => [
+                "dataToEncrypt" => "Maarch Courrier 2301"
+            ],
+            'lower, upper case letters, numbers and special characters' => [
+                "dataToEncrypt" => "Wêl¢om€ Tô Määrc|-| Cøμrr¡€r 2301"
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider provideDataToEncrypt
+     */
+    public function testEncryptAndDecrypt($dataToEncrypt)
+    {
+        $encryptedData = PasswordController::encrypt(['dataToEncrypt' => $dataToEncrypt]);
+        $decryptedData = PasswordController::decrypt(['encryptedData' => $encryptedData]);
+
+        $this->assertNotEmpty($encryptedData);
+        $this->assertNotEmpty($decryptedData);
+        $this->assertSame($decryptedData, $dataToEncrypt);
+    }
+
+    /**
+     * @deprecated This test function is deprecated and will be removed in future major versions.
+     */
+    public function testGetEncryptKeyExpectUsingVhostKey()
+    {
+        self::removePrivateKeyPath();
+
+        $useVhostEncryptKey = CoreConfigModel::useVhostEncryptKey();
+
+        $this->assertNotEmpty($useVhostEncryptKey);
+        $this->assertSame(true, $useVhostEncryptKey);
+    }
+
+    /**
+     * @deprecated This test function is deprecated and will be removed in future major versions.
+     */
+    public function testGetEncryptKeyExpectUsingFileKey()
+    {
+        $usePrivateKey = CoreConfigModel::useVhostEncryptKey();
+
+        $this->assertEmpty($usePrivateKey);
+        $this->assertSame(false, $usePrivateKey);
+    }
+
+    /**
+     * @deprecated This test function is deprecated and will be removed in future major versions.
+     * @dataProvider provideDataToEncrypt
+     */
+    public function testEncryptAndDecryptUsingOldCipherMethod($dataToEncrypt)
+    {
+        self::removePrivateKeyPath();
+
+        $encryptedData = PasswordController::encrypt(['dataToEncrypt' => $dataToEncrypt]);
+        $decryptedData = PasswordController::decrypt(['encryptedData' => $encryptedData]);
+
+        $this->assertNotEmpty($encryptedData);
+        $this->assertNotEmpty($decryptedData);
+        $this->assertSame($decryptedData, $dataToEncrypt);
+    }
+
+    /**
+     * @deprecated This test function is deprecated and will be removed in future major versions.
+     * @dataProvider provideDataToEncrypt
+     */
+    public function testEncryptUsingNewCipherMethodAndDecryptUsingOldCipherMethod($dataToEncrypt)
+    {
+        $encryptedData = PasswordController::encrypt(['dataToEncrypt' => $dataToEncrypt]);
+
+        self::removePrivateKeyPath();
+
+        $decryptedData = null;
+        $exceptionError = null;
+
+        try {
+            $decryptedData = PasswordController::decrypt(['encryptedData' => $encryptedData]);
+        } catch (\Exception $e) {
+            $exceptionError = $e->getMessage();
+        }
+
+        $this->assertNotEmpty($encryptedData);
+        $this->assertNotEmpty($exceptionError);
+        $this->assertEmpty($decryptedData);
+    }
+
+    /**
+     * @deprecated This test function is deprecated and will be removed in future major versions.
+     */
+    private function removePrivateKeyPath()
+    {
+        $configPath = \SrcCore\models\CoreConfigModel::getConfigPath();
+        $coreConfig = json_decode(file_get_contents($configPath), true);
+
+        unset($coreConfig['config']['privateKeyPath']);
+        file_put_contents($configPath, json_encode($coreConfig));
+        self::$restoreOriginalConfig = true;
+    }
+
+    protected function tearDown(): void
+    {
+        if (!empty(self::$restoreOriginalConfig)) {
+            file_put_contents(self::$generalConfigPath, json_encode(self::$generalConfigOriginal, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+        }
+    }
 }
-- 
GitLab