diff --git a/docs/simplesamlphp-upgrade-notes-2.0.md b/docs/simplesamlphp-upgrade-notes-2.0.md
index 08c4f712cdb57d63781d4923adc935521c05b7aa..d90f914e8741a8e6716374f73802ab6e62c746fe 100644
--- a/docs/simplesamlphp-upgrade-notes-2.0.md
+++ b/docs/simplesamlphp-upgrade-notes-2.0.md
@@ -13,3 +13,20 @@ Upgrade notes for SimpleSAMLphp 2.0
 - If you're using the core:TargetedID authproc-filter, note that the `attributename` setting has been renamed to `identifyingAttribute`.
 - The default encryption algorithm is set from AES128_CBC to AES128_GCM. If you're upgrading from an existing implementation, you may want
     to manually switch back the `sharedkey_algorithm`. Note that CBC is vulnerable to the Padding oracle attack.
+- The following classes have been migrated to non-static:
+  + lib/SimpleSAMLphp\Utils\Arrays
+  + lib/SimpleSAMLphp\Utils\Attributes
+  + lib/SimpleSAMLphp\Utils\Auth
+  + lib/SimpleSAMLphp\Utils\Config
+  + lib/SimpleSAMLphp\Utils\Crypto
+
+  If you use any of these classes in your modules or themes, you will now have to instantiate them so that:
+
+  // Old style
+  $x = \SimpleSAML\Utils\Arrays::arrayize($someVar)
+
+  becomes:
+
+  // New style
+  $arrayUtils = new \SimpleSAML\Utils\Arrays();
+  $x = $arrayUtils->arrayize($someVar);
diff --git a/docs/simplesamlphp-upgrade-notes.md b/docs/simplesamlphp-upgrade-notes.md
index 58833c9eda846a691314563eee449c513e690f56..0f8e63ad97e2c5934fe60ba5d50473964ded41e7 100644
--- a/docs/simplesamlphp-upgrade-notes.md
+++ b/docs/simplesamlphp-upgrade-notes.md
@@ -4,6 +4,8 @@ SimpeSAMLphp Upgrade Notes
 See the following pages for important information for users upgrading
 from older versions of SimpleSAMLphp:
 
+* [Upgrade notes for version 2.0](simplesamlphp-upgrade-notes-2.0)
+* [Upgrade notes for version 1.19](simplesamlphp-upgrade-notes-1.19)
 * [Upgrade notes for version 1.18](simplesamlphp-upgrade-notes-1.18)
 * [Upgrade notes for version 1.17](simplesamlphp-upgrade-notes-1.17)
 * [Upgrade notes for version 1.16](simplesamlphp-upgrade-notes-1.16)
diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php
index 78e1cf80e067a0617b25b7ab058eb8eb6c35ee2e..0a8ebb8d1dc6bf58936ae5b9cf4233eea48cc3c9 100644
--- a/lib/SimpleSAML/Configuration.php
+++ b/lib/SimpleSAML/Configuration.php
@@ -233,7 +233,8 @@ class Configuration implements Utils\ClearableState
             if ($configSet !== 'simplesaml') {
                 throw new \Exception('Configuration set \'' . $configSet . '\' not initialized.');
             } else {
-                self::$configDirs['simplesaml'] = Utils\Config::getConfigDir();
+                $configUtils = new Utils\Config();
+                self::$configDirs['simplesaml'] = $configUtils->getConfigDir();
             }
         }
 
@@ -262,7 +263,8 @@ class Configuration implements Utils\ClearableState
             if ($configSet !== 'simplesaml') {
                 throw new \Exception('Configuration set \'' . $configSet . '\' not initialized.');
             } else {
-                self::$configDirs['simplesaml'] = Utils\Config::getConfigDir();
+                $configUtils = new Utils\Config();
+                self::$configDirs['simplesaml'] = $configUtils->getConfigDir();
             }
         }
 
@@ -1158,8 +1160,10 @@ class Configuration implements Utils\ClearableState
                 ],
             ];
         } elseif ($this->hasValue($prefix . 'certificate')) {
+            $configUtils = new Utils\Config();
+
             $file = $this->getString($prefix . 'certificate');
-            $file = Utils\Config::getCertPath($file);
+            $file = $configUtils->getCertPath($file);
             $data = @file_get_contents($file);
 
             if ($data === false) {
diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php
index 156e0bafc1a2636806d78f6e3557bfe760a9b43b..0a9dbf8288e9046002003f07ef700bb079296875 100644
--- a/lib/SimpleSAML/Memcache.php
+++ b/lib/SimpleSAML/Memcache.php
@@ -417,8 +417,9 @@ class Memcache
                 }
             }
 
+            $arrayUtils = new Utils\Arrays();
             /** @psalm-var array $stats */
-            $stats = Utils\Arrays::transpose($stats);
+            $stats = $arrayUtils->transpose($stats);
 
             $ret = array_merge_recursive($ret, $stats);
         }
diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php
index 2f79664813dc0c4054706cb0228d492010e8887c..a5961190a49b973fe9c9e828bb50d0841dbd82c9 100644
--- a/lib/SimpleSAML/Metadata/SAMLBuilder.php
+++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php
@@ -340,9 +340,11 @@ class SAMLBuilder
             return;
         }
 
-        $orgName = Utils\Arrays::arrayize($metadata['OrganizationName'], 'en');
-        $orgDisplayName = Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en');
-        $orgURL = Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en');
+        $arrayUtils = new Utils\Arrays();
+
+        $orgName = $arrayUtils->arrayize($metadata['OrganizationName'], 'en');
+        $orgDisplayName = $arrayUtils->arrayize($metadata['OrganizationDisplayName'], 'en');
+        $orgURL = $arrayUtils->arrayize($metadata['OrganizationURL'], 'en');
 
         $this->addOrganization($orgName, $orgDisplayName, $orgURL);
     }
diff --git a/lib/SimpleSAML/Metadata/SAMLParser.php b/lib/SimpleSAML/Metadata/SAMLParser.php
index e94509d12420e80848b8488ea5b748095dc00a29..a81a102801c68f3eb6e6637008e1dfd8250011ec 100644
--- a/lib/SimpleSAML/Metadata/SAMLParser.php
+++ b/lib/SimpleSAML/Metadata/SAMLParser.php
@@ -1298,9 +1298,11 @@ class SAMLParser
      */
     public function validateSignature(array $certificates): bool
     {
+        $configUtils = new Utils\Config();
+
         foreach ($certificates as $cert) {
             Assert::string($cert);
-            $certFile = Utils\Config::getCertPath($cert);
+            $certFile = $configUtils->getCertPath($cert);
             if (!file_exists($certFile)) {
                 throw new \Exception(
                     'Could not find certificate file [' . $certFile . '], which is needed to validate signature'
diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php
index 7d5d6a0a8cd9714939d3772a210dfc959afcd674..01c1cffc3596fcce924c6cffa83cda8ff7edcf55 100644
--- a/lib/SimpleSAML/Metadata/Signer.php
+++ b/lib/SimpleSAML/Metadata/Signer.php
@@ -226,6 +226,7 @@ class Signer
     public static function sign(string $metadataString, array $entityMetadata, string $type): string
     {
         $config = Configuration::getInstance();
+        $configUtils = new Utils\Config();
 
         // check if metadata signing is enabled
         if (!self::isMetadataSigningEnabled($config, $entityMetadata, $type)) {
@@ -235,7 +236,7 @@ class Signer
         // find the key & certificate which should be used to sign the metadata
         $keyCertFiles = self::findKeyCert($config, $entityMetadata, $type);
 
-        $keyFile = Utils\Config::getCertPath($keyCertFiles['privatekey']);
+        $keyFile = $configUtils->getCertPath($keyCertFiles['privatekey']);
         if (!file_exists($keyFile)) {
             throw new \Exception(
                 'Could not find private key file [' . $keyFile . '], which is needed to sign the metadata'
@@ -243,7 +244,7 @@ class Signer
         }
         $keyData = file_get_contents($keyFile);
 
-        $certFile = Utils\Config::getCertPath($keyCertFiles['certificate']);
+        $certFile = $configUtils->getCertPath($keyCertFiles['certificate']);
         if (!file_exists($certFile)) {
             throw new \Exception(
                 'Could not find certificate file [' . $certFile . '], which is needed to sign the metadata'
diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index 438abff785d89539c23de8e2cc160b216f740c4b..5bf2f3e9381ece49072d44502f10d4f38af77c33 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -368,7 +368,8 @@ class Session implements Serializable, Utils\ClearableState
                     Logger::warning('Missing AuthToken cookie.');
                     return null;
                 }
-                if (!Utils\Crypto::secureCompare($session->authToken, $_COOKIE[$authTokenCookieName])) {
+                $cryptoUtils = new Utils\Crypto();
+                if (!$cryptoUtils->secureCompare($session->authToken, $_COOKIE[$authTokenCookieName])) {
                     Logger::warning('Invalid AuthToken cookie.');
                     return null;
                 }
diff --git a/lib/SimpleSAML/Utils/Arrays.php b/lib/SimpleSAML/Utils/Arrays.php
index b667a7fe4d596dce1d4cf7225166e42f6fde015a..b736f130f185432a385515a0b2be84d43258474f 100644
--- a/lib/SimpleSAML/Utils/Arrays.php
+++ b/lib/SimpleSAML/Utils/Arrays.php
@@ -21,7 +21,7 @@ class Arrays
      *     array.
      *
      */
-    public static function arrayize($data, $index = 0): array
+    public function arrayize($data, $index = 0): array
     {
         return (is_array($data)) ? $data : [$index => $data];
     }
@@ -35,7 +35,7 @@ class Arrays
      * @return array|false The transposed array, or false if $array is not a valid two-dimensional array.
      *
      */
-    public static function transpose(array $array)
+    public function transpose(array $array)
     {
         $ret = [];
         foreach ($array as $k1 => $a2) {
diff --git a/lib/SimpleSAML/Utils/Attributes.php b/lib/SimpleSAML/Utils/Attributes.php
index 047efd52235d230e145f186c0fd5fe3ded070894..e778cb12bfb8ee8cefc55ef3ec49c7f687908aa0 100644
--- a/lib/SimpleSAML/Utils/Attributes.php
+++ b/lib/SimpleSAML/Utils/Attributes.php
@@ -27,7 +27,7 @@ class Attributes
      * @throws \InvalidArgumentException If $attributes is not an array or $expected is not a string.
      * @throws \SimpleSAML\Error\Exception If the expected attribute was not found in the attributes array.
      */
-    public static function getExpectedAttribute(array $attributes, string $expected, bool $allow_multiple = false)
+    public function getExpectedAttribute(array $attributes, string $expected, bool $allow_multiple = false)
     {
         if (!array_key_exists($expected, $attributes)) {
             throw new Error\Exception("No such attribute '" . $expected . "' found.");
@@ -66,7 +66,7 @@ class Attributes
      *     not strings.
      *
      */
-    public static function normalizeAttributesArray(array $attributes): array
+    public function normalizeAttributesArray(array $attributes): array
     {
         $newAttrs = [];
         foreach ($attributes as $name => $values) {
@@ -74,7 +74,8 @@ class Attributes
                 throw new \InvalidArgumentException('Invalid attribute name: "' . print_r($name, true) . '".');
             }
 
-            $values = Arrays::arrayize($values);
+            $arrayUtils = new Arrays();
+            $values = $arrayUtils->arrayize($values);
 
             foreach ($values as $value) {
                 if (!is_string($value)) {
@@ -103,7 +104,7 @@ class Attributes
      *
      * @return array The attribute name, split to the namespace and the actual attribute name.
      */
-    public static function getAttributeNamespace(string $name, string $defaultns): array
+    public function getAttributeNamespace(string $name, string $defaultns): array
     {
         $slash = strrpos($name, '/');
         if ($slash !== false) {
diff --git a/lib/SimpleSAML/Utils/Auth.php b/lib/SimpleSAML/Utils/Auth.php
index 614749aee98382b54d0f76667d10faa4f40f277c..9a1526f4ccdb68b87855ff145506df0ca6fa8eb0 100644
--- a/lib/SimpleSAML/Utils/Auth.php
+++ b/lib/SimpleSAML/Utils/Auth.php
@@ -24,7 +24,7 @@ class Auth
      * @return string A URL which can be used for admin authentication.
      * @throws \InvalidArgumentException If $returnTo is neither a string nor null.
      */
-    public static function getAdminLoginURL(?string $returnTo = null): string
+    public function getAdminLoginURL(?string $returnTo = null): string
     {
         if ($returnTo === null) {
             $returnTo = HTTP::getSelfURL();
@@ -42,7 +42,7 @@ class Auth
      * @return string A URL which can be used for logging out.
      * @throws \InvalidArgumentException If $returnTo is neither a string nor null.
      */
-    public static function getAdminLogoutURL(?string $returnTo = null): string
+    public function getAdminLogoutURL(?string $returnTo = null): string
     {
         $as = new Authentication\Simple('admin');
         return $as->getLogoutURL($returnTo);
@@ -55,7 +55,7 @@ class Auth
      * @return boolean True if the current user is an admin user, false otherwise.
      *
      */
-    public static function isAdmin(): bool
+    public function isAdmin(): bool
     {
         $session = Session::getSessionFromRequest();
         return $session->isValid('admin') || $session->isValid('login-admin');
@@ -71,9 +71,9 @@ class Auth
      * @throws \SimpleSAML\Error\Exception If no "admin" authentication source was configured.
      *
      */
-    public static function requireAdmin(): void
+    public function requireAdmin(): void
     {
-        if (self::isAdmin()) {
+        if ($this->isAdmin()) {
             return;
         }
 
diff --git a/lib/SimpleSAML/Utils/Config.php b/lib/SimpleSAML/Utils/Config.php
index 6e4c029f6e757adf3e90a39dd4d77013e5f188dd..354da68eee81107692cc90f09fec0fffbc0b5b8f 100644
--- a/lib/SimpleSAML/Utils/Config.php
+++ b/lib/SimpleSAML/Utils/Config.php
@@ -22,7 +22,7 @@ class Config
      * @throws \InvalidArgumentException If $path is not a string.
      *
      */
-    public static function getCertPath(string $path): string
+    public function getCertPath(string $path): string
     {
         $globalConfig = Configuration::getInstance();
         $base = $globalConfig->getPathValue('certdir', 'cert/');
@@ -44,7 +44,7 @@ class Config
      * @throws \InvalidArgumentException If the secret salt hasn't been configured.
      *
      */
-    public static function getSecretSalt(): string
+    public function getSecretSalt(): string
     {
         $secretSalt = Configuration::getInstance()->getString('secretsalt');
         if ($secretSalt === 'defaultsecretsalt') {
@@ -62,7 +62,7 @@ class Config
      *
      * @return string The path to the configuration directory.
      */
-    public static function getConfigDir(): string
+    public function getConfigDir(): string
     {
         $configDir = dirname(dirname(dirname(__DIR__))) . '/config';
         $configDirEnv = getenv('SIMPLESAMLPHP_CONFIG_DIR');
diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php
index efd2aba4320b8334211986bb343d4df8ee79bc84..d7c9bfbae2b35fab0d3b5ca1c729f5c7f9aebdba 100644
--- a/lib/SimpleSAML/Utils/Crypto.php
+++ b/lib/SimpleSAML/Utils/Crypto.php
@@ -29,8 +29,12 @@ class Crypto
      *
      * @see \SimpleSAML\Utils\Crypto::aesDecrypt()
      */
-    private static function aesDecryptInternal(string $ciphertext, string $secret): string
+    private function aesDecryptInternal(string $ciphertext, string $secret): string
     {
+        if (!extension_loaded('openssl')) {
+            throw new Error\Exception("The openssl PHP module is not loaded.");
+        }
+
         /** @var int $len */
         $len = mb_strlen($ciphertext, '8bit');
         if ($len < 48) {
@@ -38,9 +42,6 @@ class Crypto
                 'Input parameter "$ciphertext" must be a string with more than 48 characters.'
             );
         }
-        if (!function_exists("openssl_decrypt")) {
-            throw new Error\Exception("The openssl PHP module is not loaded.");
-        }
 
         // derive encryption and authentication keys from the secret
         $key  = openssl_digest($secret, 'sha512');
@@ -50,7 +51,7 @@ class Crypto
         $msg  = mb_substr($ciphertext, 48, $len - 48, '8bit');
 
         // authenticate the ciphertext
-        if (self::secureCompare(hash_hmac('sha256', $iv . $msg, substr($key, 64, 64), true), $hmac)) {
+        if ($this->secureCompare(hash_hmac('sha256', $iv . $msg, substr($key, 64, 64), true), $hmac)) {
             $plaintext = openssl_decrypt(
                 $msg,
                 'AES-256-CBC',
@@ -72,15 +73,22 @@ class Crypto
      * Decrypt data using AES-256-CBC and the system-wide secret salt as key.
      *
      * @param string $ciphertext The HMAC of the encrypted data, the IV used and the encrypted data, concatenated.
+     * @param string $secret The secret to use to decrypt the data.
+     *                       If not provided, the secret salt from the configuration will be used
      *
      * @return string The decrypted data.
      * @throws \InvalidArgumentException If $ciphertext is not a string.
      * @throws Error\Exception If the openssl module is not loaded.
      *
      */
-    public static function aesDecrypt(string $ciphertext): string
+    public function aesDecrypt(string $ciphertext, string $secret = null): string
     {
-        return self::aesDecryptInternal($ciphertext, Config::getSecretSalt());
+        if ($secret === null) {
+            $configUtils = new Config();
+            $secret = $configUtils->getSecretSalt();
+        }
+
+        return $this->aesDecryptInternal($ciphertext, $secret);
     }
 
 
@@ -96,9 +104,9 @@ class Crypto
      *
      * @see \SimpleSAML\Utils\Crypto::aesEncrypt()
      */
-    private static function aesEncryptInternal(string $data, string $secret): string
+    private function aesEncryptInternal(string $data, string $secret): string
     {
-        if (!function_exists("openssl_encrypt")) {
+        if (!extension_loaded('openssl')) {
             throw new Error\Exception('The openssl PHP module is not loaded.');
         }
 
@@ -130,15 +138,22 @@ class Crypto
      * Encrypt data using AES-256-CBC and the system-wide secret salt as key.
      *
      * @param string $data The data to encrypt.
+     * @param string $secret The secret to use to decrypt the data.
+     *                       If not provided, the secret salt from the configuration will be used
      *
      * @return string An HMAC of the encrypted data, the IV and the encrypted data, concatenated.
      * @throws \InvalidArgumentException If $data is not a string.
      * @throws Error\Exception If the openssl module is not loaded.
      *
      */
-    public static function aesEncrypt(string $data): string
+    public function aesEncrypt(string $data, string $secret = null): string
     {
-        return self::aesEncryptInternal($data, Config::getSecretSalt());
+        if ($secret === null) {
+            $configUtils = new Config();
+            $secret = $configUtils->getSecretSalt();
+        }
+
+        return $this->aesEncryptInternal($data, $secret);
     }
 
 
@@ -150,7 +165,7 @@ class Crypto
      * @return string The same data encoded in PEM format.
      * @see RFC7648 for known types and PEM format specifics.
      */
-    public static function der2pem(string $der, string $type = 'CERTIFICATE'): string
+    public function der2pem(string $der, string $type = 'CERTIFICATE'): string
     {
         return "-----BEGIN " . $type . "-----\n" .
             chunk_split(base64_encode($der), 64, "\n") .
@@ -183,7 +198,7 @@ class Crypto
      *     it.
      *
      */
-    public static function loadPrivateKey(
+    public function loadPrivateKey(
         Configuration $metadata,
         bool $required = false,
         string $prefix = '',
@@ -200,7 +215,8 @@ class Crypto
         }
 
         if (!$full_path) {
-            $file = Config::getCertPath($file);
+            $configUtils = new Config();
+            $file = $configUtils->getCertPath($file);
         }
 
         $data = @file_get_contents($file);
@@ -243,7 +259,7 @@ class Crypto
      *     it.
      *
      */
-    public static function loadPublicKey(Configuration $metadata, bool $required = false, string $prefix = ''): ?array
+    public function loadPublicKey(Configuration $metadata, bool $required = false, string $prefix = ''): ?array
     {
         $keys = $metadata->getPublicKeys(null, false, $prefix);
         if (!empty($keys)) {
@@ -283,7 +299,7 @@ class Crypto
      * @throws \InvalidArgumentException If $pem is not encoded in PEM format.
      * @see RFC7648 for PEM format specifics.
      */
-    public static function pem2der(string $pem): string
+    public function pem2der(string $pem): string
     {
         $pem   = trim($pem);
         $begin = "-----BEGIN ";
@@ -311,18 +327,15 @@ class Crypto
      * @param mixed $algorithm The algorithm to use. Defaults to the system default
      *
      * @return string The hashed password.
-     * @throws \InvalidArgumentException If the input parameter is not a string.
+     * @throws \Exception If the algorithm is not known ti PHP.
      * @throws Error\Exception If the algorithm specified is not supported.
      *
      * @see hash_algos()
      *
      */
-    public static function pwHash(string $password, $algorithm = PASSWORD_DEFAULT): string
+    public function pwHash(string $password, $algorithm = PASSWORD_DEFAULT): string
     {
-        if (!is_string($hash = password_hash($password, $algorithm))) {
-            throw new InvalidArgumentException('Error while hashing password.');
-        }
-        return $hash;
+        return password_hash($password, $algorithm);
     }
 
 
@@ -337,7 +350,7 @@ class Crypto
      *
      * @return bool True if both strings are equal, false otherwise.
      */
-    public static function secureCompare(string $known, string $user): bool
+    public function secureCompare(string $known, string $user): bool
     {
         return hash_equals($known, $user);
     }
@@ -354,7 +367,7 @@ class Crypto
      * @throws Error\Exception If the algorithm specified is not supported.
      *
      */
-    public static function pwValid(string $hash, string $password): bool
+    public function pwValid(string $hash, string $password): bool
     {
         if (!is_null(password_get_info($password)['algo'])) {
             throw new Error\Exception("Cannot use a hash value for authentication.");
diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php
index 7ebe99637c369220bc8f68ee2c30401a9b4c29cc..8dc6e0258abcfa7e265523ae9173101dea770a8c 100644
--- a/lib/SimpleSAML/Utils/HTTP.php
+++ b/lib/SimpleSAML/Utils/HTTP.php
@@ -87,7 +87,8 @@ class HTTP
         $session_id = $session->getSessionId();
 
         // encrypt the session ID and the random ID
-        $info = base64_encode(Crypto::aesEncrypt($session_id . ':' . $id));
+        $cryptoUtils = new Crypto();
+        $info = base64_encode($cryptoUtils->aesEncrypt($session_id . ':' . $id));
 
         $url = Module::getModuleURL('core/postredirect.php', ['RedirInfo' => $info]);
         return preg_replace('#^https:#', 'http:', $url);
diff --git a/lib/SimpleSAML/XML/Signer.php b/lib/SimpleSAML/XML/Signer.php
index 898eb639a120ef205967df5c9b76e121fbf32264..24f62d7c3af2004e852f35b7922a5b3c452d8c40 100644
--- a/lib/SimpleSAML/XML/Signer.php
+++ b/lib/SimpleSAML/XML/Signer.php
@@ -125,7 +125,8 @@ class Signer
     public function loadPrivateKey(string $file, ?string $pass, bool $full_path = false): void
     {
         if (!$full_path) {
-            $keyFile = Utils\Config::getCertPath($file);
+            $configUtils = new Utils\Config();
+            $keyFile = $configUtils->getCertPath($file);
         } else {
             $keyFile = $file;
         }
@@ -182,7 +183,8 @@ class Signer
     public function loadCertificate(string $file, bool $full_path = false): void
     {
         if (!$full_path) {
-            $certFile = Utils\Config::getCertPath($file);
+            $configUtils = new Utils\Config();
+            $certFile = $configUtils->getCertPath($file);
         } else {
             $certFile = $file;
         }
@@ -224,7 +226,8 @@ class Signer
     public function addCertificate(string $file, bool $full_path = false): void
     {
         if (!$full_path) {
-            $certFile = Utils\Config::getCertPath($file);
+            $configUtils = new Utils\Config();
+            $certFile = $configUtils->getCertPath($file);
         } else {
             $certFile = $file;
         }
diff --git a/modules/admin/lib/Controller/Config.php b/modules/admin/lib/Controller/Config.php
index e4823002060e6e5eb08a058654d0ddecf8edce07..a7058f8d19ab660f5292c65ac7e761615cc8a309 100644
--- a/modules/admin/lib/Controller/Config.php
+++ b/modules/admin/lib/Controller/Config.php
@@ -30,10 +30,9 @@ class Config
     protected Configuration $config;
 
     /**
-     * @var \SimpleSAML\Utils\Auth|string
-     * @psalm-var \SimpleSAML\Utils\Auth|class-string
+     * @var \SimpleSAML\Utils\Auth
      */
-    protected $authUtils = Utils\Auth::class;
+    protected $authUtils;
 
     /** @var \SimpleSAML\Module\admin\Controller\Menu */
     protected Menu $menu;
@@ -53,6 +52,7 @@ class Config
         $this->config = $config;
         $this->session = $session;
         $this->menu = new Menu();
+        $this->authUtils = new Utils\Auth();
     }
 
 
@@ -76,12 +76,12 @@ class Config
      */
     public function diagnostics(Request $request): Template
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         $t = new Template($this->config, 'admin:diagnostics.twig');
         $t->data = [
             'remaining' => $this->session->getAuthData('admin', 'Expire') - time(),
-            'logouturl' => Utils\Auth::getAdminLogoutURL(),
+            'logouturl' => $this->authUtils->getAdminLogoutURL(),
             'items' => [
                 'HTTP_HOST' => [$request->getHost()],
                 'HTTPS' => $request->isSecure() ? ['on'] : [],
@@ -111,7 +111,7 @@ class Config
      */
     public function main(/** @scrutinizer ignore-unused */ Request $request): Template
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         $t = new Template($this->config, 'admin:config.twig');
         $t->data = [
@@ -132,12 +132,11 @@ class Config
                 'saml20idp' => $this->config->getBoolean('enable.saml20-idp', false),
             ],
             'funcmatrix' => $this->getPrerequisiteChecks(),
-            'logouturl' => Utils\Auth::getAdminLogoutURL(),
+            'logouturl' => $this->authUtils->getAdminLogoutURL(),
         ];
 
         Module::callHooks('configpage', $t);
-        $this->menu->addOption('logout', Utils\Auth::getAdminLogoutURL(), Translate::noop('Log out'));
-        /** @psalm-var \SimpleSAML\XHTML\Template $t */
+        $this->menu->addOption('logout', $this->authUtils->getAdminLogoutURL(), Translate::noop('Log out'));
         return $this->menu->insert($t);
     }
 
@@ -151,7 +150,7 @@ class Config
      */
     public function phpinfo(/** @scrutinizer ignore-unused */ Request $request): RunnableResponse
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         return new RunnableResponse('phpinfo');
     }
diff --git a/modules/admin/lib/Controller/Federation.php b/modules/admin/lib/Controller/Federation.php
index 2b349d877c895026faf3b0b3ca870e661bb23880..531608d266da26e14383b4702eb82d615102b749 100644
--- a/modules/admin/lib/Controller/Federation.php
+++ b/modules/admin/lib/Controller/Federation.php
@@ -42,11 +42,11 @@ class Federation
      */
     protected $authSource = Auth\Source::class;
 
-    /**
-     * @var \SimpleSAML\Utils\Auth|string
-     * @psalm-var \SimpleSAML\Utils\Auth|class-string
-     */
-    protected $authUtils = Utils\Auth::class;
+    /** @var \SimpleSAML\Utils\Auth */
+    protected $authUtils;
+
+    /** @var \SimpleSAML\Utils\Crypto */
+    protected $cryptoUtils;
 
     /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */
     protected MetadataStorageHandler $mdHandler;
@@ -65,6 +65,8 @@ class Federation
         $this->config = $config;
         $this->menu = new Menu();
         $this->mdHandler = MetaDataStorageHandler::getMetadataHandler();
+        $this->authUtils = new Utils\Auth();
+        $this->cryptoUtils = new Utils\Crypto();
     }
 
 
@@ -111,7 +113,7 @@ class Federation
      */
     public function main(/** @scrutinizer ignore-unused */ Request $request): Template
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         // initialize basic metadata array
         $hostedSPs = $this->getHostedSP();
@@ -188,7 +190,7 @@ class Federation
                 'adfs-idp-remote' => Translate::noop('ADFS IdP metadata'),
                 'adfs-idp-hosted' => Translate::noop('ADFS IdP metadata'),
             ],
-            'logouturl' => Utils\Auth::getAdminLogoutURL(),
+            'logouturl' => $this->authUtils->getAdminLogoutURL(),
         ];
 
         Module::callHooks('federationpage', $t);
@@ -396,7 +398,7 @@ class Federation
      */
     public function metadataConverter(Request $request): Template
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
         if ($xmlfile = $request->files->get('xmlfile')) {
             $xmldata = trim(file_get_contents($xmlfile->getPathname()));
         } elseif ($xmldata = $request->request->get('xmldata')) {
@@ -425,7 +427,8 @@ class Federation
                 }
 
                 // transpose from $entities[entityid][type] to $output[type][entityid]
-                $output = Utils\Arrays::transpose($entities);
+                $arrayUtils = new Utils\Arrays();
+                $output = $arrayUtils->transpose($entities);
 
                 // merge all metadata of each type to a single string which should be added to the corresponding file
                 foreach ($output as $type => &$entities) {
@@ -461,7 +464,7 @@ class Federation
 
         $t = new Template($this->config, 'admin:metadata_converter.twig');
         $t->data = [
-            'logouturl' => Utils\Auth::getAdminLogoutURL(),
+            'logouturl' => $this->authUtils->getAdminLogoutURL(),
             'xmldata' => $xmldata,
             'output' => $output,
             'error' => $error,
@@ -481,7 +484,7 @@ class Federation
      */
     public function downloadCert(Request $request): Response
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         $set = $request->get('set');
         $prefix = $request->get('prefix', '');
@@ -500,7 +503,7 @@ class Federation
         }
 
         /** @var array $certInfo  Second param ensures non-nullable return-value */
-        $certInfo = Utils\Crypto::loadPublicKey($mdconfig, true, $prefix);
+        $certInfo = $this->cryptoUtils->loadPublicKey($mdconfig, true, $prefix);
 
         $response = new Response($certInfo['PEM']);
         $disposition = $response->headers->makeDisposition(
@@ -524,7 +527,7 @@ class Federation
      */
     public function showRemoteEntity(Request $request): Template
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         $entityId = $request->get('entityid');
         $set = $request->get('set');
diff --git a/modules/admin/lib/Controller/Test.php b/modules/admin/lib/Controller/Test.php
index 3be69e3d967213458d6e496681cfd47c3046e2d0..44ddc3d6f509b4cb59d8476968d6b373d971c653 100644
--- a/modules/admin/lib/Controller/Test.php
+++ b/modules/admin/lib/Controller/Test.php
@@ -31,10 +31,9 @@ class Test
     protected Configuration $config;
 
     /**
-     * @var \SimpleSAML\Utils\Auth|string
-     * @psalm-var \SimpleSAML\Utils\Auth|class-string
+     * @var \SimpleSAML\Utils\Auth
      */
-    protected $authUtils = Utils\Auth::class;
+    protected $authUtils;
 
     /**
      * @var \SimpleSAML\Auth\Simple|string
@@ -66,6 +65,7 @@ class Test
         $this->config = $config;
         $this->session = $session;
         $this->menu = new Menu();
+        $this->authUtils = new Utils\Auth();
     }
 
 
@@ -111,7 +111,7 @@ class Test
      */
     public function main(Request $request, string $as = null)
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
         if (is_null($as)) {
             $t = new Template($this->config, 'admin:authsource_list.twig');
             $t->data = [
@@ -161,8 +161,7 @@ class Test
         Module::callHooks('configpage', $t);
         Assert::isInstanceOf($t, Template::class);
 
-        $this->menu->addOption('logout', Utils\Auth::getAdminLogoutURL(), Translate::noop('Log out'));
-        /** @var \SimpleSAML\XHTML\Template $t */
+        $this->menu->addOption('logout', $this->authUtils->getAdminLogoutURL(), Translate::noop('Log out'));
         return $this->menu->insert($t);
     }
 
diff --git a/modules/core/lib/Auth/Process/TargetedID.php b/modules/core/lib/Auth/Process/TargetedID.php
index 04b6aefc2c59d78e24f259fb12d415484da7cca2..b4648d02d16b759a73cf1394236ff47f3522891f 100644
--- a/modules/core/lib/Auth/Process/TargetedID.php
+++ b/modules/core/lib/Auth/Process/TargetedID.php
@@ -47,10 +47,10 @@ class TargetedID extends Auth\ProcessingFilter
     private bool $generateNameId = false;
 
     /**
-     * @var \SimpleSAML\Utils\Config|string
-     * @psalm-var \SimpleSAML\Utils\Config|class-string
+     * @var \SimpleSAML\Utils\Config
      */
-    protected $configUtils = Utils\Config::class;
+    protected $configUtils;
+
 
     /**
      * Initialize this filter.
@@ -76,6 +76,8 @@ class TargetedID extends Auth\ProcessingFilter
                 throw new Exception('Invalid value of \'nameId\'-option to core:TargetedID filter.');
             }
         }
+
+        $this->configUtils = new Utils\Config();
     }
 
 
@@ -122,7 +124,7 @@ class TargetedID extends Auth\ProcessingFilter
             $dstID = '';
         }
 
-        $secretSalt = $this->configUtils::getSecretSalt();
+        $secretSalt = $this->configUtils->getSecretSalt();
         $uidData = 'uidhashbase' . $secretSalt;
         $uidData .= strlen($srcID) . ':' . $srcID;
         $uidData .= strlen($dstID) . ':' . $dstID;
diff --git a/modules/core/lib/Auth/Source/AdminPassword.php b/modules/core/lib/Auth/Source/AdminPassword.php
index 82ee499abb98e56e4a568a34ca49c483bfafa9a4..0af50a1ed782534d5c814bada7e7c90311135a59 100644
--- a/modules/core/lib/Auth/Source/AdminPassword.php
+++ b/modules/core/lib/Auth/Source/AdminPassword.php
@@ -8,6 +8,7 @@ use SimpleSAML\Assert\Assert;
 use SimpleSAML\Configuration;
 use SimpleSAML\Error;
 use SimpleSAML\Module\core\Auth\UserPassBase;
+use SimpleSAML\Utils;
 
 /**
  * Authentication source which verifies the password against
@@ -59,7 +60,8 @@ class AdminPassword extends UserPassBase
             throw new Error\Error('WRONGUSERPASS');
         }
 
-        if (!\SimpleSAML\Utils\Crypto::pwValid($adminPassword, $password)) {
+        $cryptoUtils = new Utils\Crypto();
+        if (!$cryptoUtils->pwValid($adminPassword, $password)) {
             throw new Error\Error('WRONGUSERPASS');
         }
         return ['user' => ['admin']];
diff --git a/modules/core/lib/Controller/Login.php b/modules/core/lib/Controller/Login.php
index a4941bc00fa12976abd3637e486405a52db31ef6..91bb4b6375d6cd1019c00de7869710c115dec3d7 100644
--- a/modules/core/lib/Controller/Login.php
+++ b/modules/core/lib/Controller/Login.php
@@ -139,8 +139,9 @@ class Login
 
         if ($as === null) { // no authentication source specified
             if (!$default) {
+                $authUtils = new Utils\Auth();
                 $t = new Template($this->config, 'core:login.twig');
-                $t->data['loginurl'] = Utils\Auth::getAdminLoginURL();
+                $t->data['loginurl'] = $authUtils->getAdminLoginURL();
                 $t->data['sources'] = $this->sources;
                 return $t;
             }
diff --git a/modules/core/lib/Controller/Redirection.php b/modules/core/lib/Controller/Redirection.php
index 942dd675c1a3369cd6930de8e110ffbd22c72b36..23141d4346c1d4936dd00624ffbb8a9eb5d38dbc 100644
--- a/modules/core/lib/Controller/Redirection.php
+++ b/modules/core/lib/Controller/Redirection.php
@@ -31,6 +31,9 @@ class Redirection
     /** @var \SimpleSAML\Session */
     protected Session $session;
 
+    /** @var \SimpleSAML\Utils\Crypto */
+    protected $cryptoUtils;
+
 
     /**
      * Controller constructor.
@@ -48,6 +51,7 @@ class Redirection
     ) {
         $this->config = $config;
         $this->session = $session;
+        $this->cryptoUtils = new Utils\Crypto();
     }
 
 
@@ -72,7 +76,7 @@ class Redirection
                 throw new Error\BadRequest('Invalid RedirInfo data.');
             }
 
-            list($sessionId, $postId) = explode(':', Utils\Crypto::aesDecrypt($encData));
+            list($sessionId, $postId) = explode(':', $this->cryptoUtils->aesDecrypt($encData));
 
             if (empty($sessionId) || empty($postId)) {
                 throw new Error\BadRequest('Invalid session info data.');
diff --git a/modules/core/www/login-admin.php b/modules/core/www/login-admin.php
index 3b6443432b2fac05f4464804856f8be71cd4f9b6..a51f6d9c21bec558128c260de2b4ffad3a871864 100644
--- a/modules/core/www/login-admin.php
+++ b/modules/core/www/login-admin.php
@@ -8,5 +8,6 @@ if (!array_key_exists('ReturnTo', $_REQUEST)) {
     throw new \SimpleSAML\Error\BadRequest('Missing ReturnTo parameter.');
 }
 
-\SimpleSAML\Utils\Auth::requireAdmin();
+$authUtils = new \SimpleSAML\Utils\Auth();
+$authUtils->requireAdmin();
 \SimpleSAML\Utils\HTTP::redirectUntrustedURL($_REQUEST['ReturnTo']);
diff --git a/modules/cron/lib/Controller/Cron.php b/modules/cron/lib/Controller/Cron.php
index 0b6542d7f5862d4070682595e59f641f50f6d8f7..b95cf9d51952171ed1d089ec118adad105652eca 100644
--- a/modules/cron/lib/Controller/Cron.php
+++ b/modules/cron/lib/Controller/Cron.php
@@ -38,10 +38,9 @@ class Cron
     protected Session $session;
 
     /**
-     * @var \SimpleSAML\Utils\Auth|string
-     * @psalm-var \SimpleSAML\Utils\Auth|class-string
+     * @var \SimpleSAML\Utils\Auth
      */
-    protected $authUtils = Utils\Auth::class;
+    protected $authUtils;
 
 
     /**
@@ -61,6 +60,7 @@ class Cron
         $this->config = $config;
         $this->cronconfig = Configuration::getConfig('module_cron.php');
         $this->session = $session;
+        $this->authUtils = new Utils\Auth();
     }
 
 
@@ -83,7 +83,7 @@ class Cron
      */
     public function info(): Template
     {
-        $this->authUtils::requireAdmin();
+        $this->authUtils->requireAdmin();
 
         $key = $this->cronconfig->getValue('key', 'secret');
         $tags = $this->cronconfig->getValue('allowed_tags', []);
diff --git a/modules/exampleauth/lib/Auth/Source/StaticSource.php b/modules/exampleauth/lib/Auth/Source/StaticSource.php
index fc047932230c450258c5eec79e4413887f09af35..fef70b66816a9b51f75e99a7d342e19eac1bf42b 100644
--- a/modules/exampleauth/lib/Auth/Source/StaticSource.php
+++ b/modules/exampleauth/lib/Auth/Source/StaticSource.php
@@ -37,9 +37,11 @@ class StaticSource extends Auth\Source
         // Call the parent constructor first, as required by the interface
         parent::__construct($info, $config);
 
+        $attrUtils = new Utils\Attributes();
+
         // Parse attributes
         try {
-            $this->attributes = Utils\Attributes::normalizeAttributesArray($config);
+            $this->attributes = $attrUtils->normalizeAttributesArray($config);
         } catch (Exception $e) {
             throw new Exception('Invalid attributes for authentication source ' .
                 $this->authId . ': ' . $e->getMessage());
diff --git a/modules/exampleauth/lib/Auth/Source/UserPass.php b/modules/exampleauth/lib/Auth/Source/UserPass.php
index e2d08094cdec50b788b5dbb21212147611b99873..b1867f8075971132f538e4c466f4dd6f7f9c528a 100644
--- a/modules/exampleauth/lib/Auth/Source/UserPass.php
+++ b/modules/exampleauth/lib/Auth/Source/UserPass.php
@@ -60,8 +60,10 @@ class UserPass extends UserPassBase
             $username = $userpass[0];
             $password = $userpass[1];
 
+            $attrUtils = new Utils\Attributes();
+
             try {
-                $attributes = Utils\Attributes::normalizeAttributesArray($attributes);
+                $attributes = $attrUtils->normalizeAttributesArray($attributes);
             } catch (Exception $e) {
                 throw new Exception('Invalid attributes for user ' . $username .
                     ' in authentication source ' . $this->authId . ': ' . $e->getMessage());
diff --git a/modules/saml/lib/Auth/Process/PersistentNameID.php b/modules/saml/lib/Auth/Process/PersistentNameID.php
index b7f825a69ded7d71e84afffb2f1be58d39ab8c66..be583d6d882eb9df35d6876167990338e2747c1f 100644
--- a/modules/saml/lib/Auth/Process/PersistentNameID.php
+++ b/modules/saml/lib/Auth/Process/PersistentNameID.php
@@ -93,7 +93,8 @@ class PersistentNameID extends BaseNameIDGenerator
             return null;
         }
 
-        $secretSalt = Utils\Config::getSecretSalt();
+        $configUtils = new Utils\Config();
+        $secretSalt = $configUtils->getSecretSalt();
 
         $uidData = 'uidhashbase' . $secretSalt;
         $uidData .= strlen($idpEntityId) . ':' . $idpEntityId;
diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php
index 9a159796a9978ab1cd53199b23693b94bf439968..4c6a185e7ff1670755d4af0702655ad6e5c4f4cb 100644
--- a/modules/saml/lib/Auth/Source/SP.php
+++ b/modules/saml/lib/Auth/Source/SP.php
@@ -205,8 +205,10 @@ class SP extends \SimpleSAML\Auth\Source
             $metadata['contacts'][] = Utils\Config\Metadata::getContact($contact);
         }
 
+        $cryptoUtils = new Utils\Crypto();
+
         // add certificate(s)
-        $certInfo = Utils\Crypto::loadPublicKey($this->metadata, false, 'new_');
+        $certInfo = $cryptoUtils->loadPublicKey($this->metadata, false, 'new_');
         $hasNewCert = false;
         if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
             $hasNewCert = true;
@@ -228,7 +230,7 @@ class SP extends \SimpleSAML\Auth\Source
             ];
         }
 
-        $certInfo = Utils\Crypto::loadPublicKey($this->metadata);
+        $certInfo = $cryptoUtils->loadPublicKey($this->metadata);
         if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
             $metadata['keys'][] = [
                 'type' => 'X509Certificate',
@@ -456,11 +458,13 @@ class SP extends \SimpleSAML\Auth\Source
             $ar->setRelayState($state['\SimpleSAML\Auth\Source.ReturnURL']);
         }
 
+        $arrayUtils = new Utils\Arrays();
+
         $accr = null;
         if ($idpMetadata->getString('AuthnContextClassRef', false)) {
-            $accr = Utils\Arrays::arrayize($idpMetadata->getString('AuthnContextClassRef'));
+            $accr = $arrayUtils->arrayize($idpMetadata->getString('AuthnContextClassRef'));
         } elseif (isset($state['saml:AuthnContextClassRef'])) {
-            $accr = Utils\Arrays::arrayize($state['saml:AuthnContextClassRef']);
+            $accr = $arrayUtils->arrayize($state['saml:AuthnContextClassRef']);
         }
 
         if ($accr !== null) {
diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php
index 74a6dc1a7c493130aa3dc5d34c162c24a433c6ae..eeb53c1a05629ebddf1fb9c2b5dcda7d7a5e427d 100644
--- a/modules/saml/lib/IdP/SAML2.php
+++ b/modules/saml/lib/IdP/SAML2.php
@@ -771,9 +771,11 @@ class SAML2
             'NameIDFormat' => $config->getArrayizeString('NameIDFormat', Constants::NAMEID_TRANSIENT),
         ];
 
+        $cryptoUtils = new Utils\Crypto();
+
         // add certificates
         $keys = [];
-        $certInfo = Utils\Crypto::loadPublicKey($config, false, 'new_');
+        $certInfo = $cryptoUtils->loadPublicKey($config, false, 'new_');
         $hasNewCert = false;
         if ($certInfo !== null) {
             $keys[] = [
@@ -787,7 +789,7 @@ class SAML2
         }
 
         /** @var array $certInfo */
-        $certInfo = Utils\Crypto::loadPublicKey($config, true);
+        $certInfo = $cryptoUtils->loadPublicKey($config, true);
         $keys[] = [
             'type' => 'X509Certificate',
             'signing' => true,
@@ -798,7 +800,7 @@ class SAML2
 
         if ($config->hasValue('https.certificate')) {
             /** @var array $httpsCert */
-            $httpsCert = Utils\Crypto::loadPublicKey($config, true, 'https.');
+            $httpsCert = $cryptoUtils->loadPublicKey($config, true, 'https.');
             $keys[] = [
                 'type' => 'X509Certificate',
                 'signing' => true,
diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php
index 2104296eec3461dd11d1fa69345b6c4c38733409..1115198678820e21522a2760ace2c8356f847ae6 100644
--- a/modules/saml/lib/Message.php
+++ b/modules/saml/lib/Message.php
@@ -44,15 +44,16 @@ class Message
         SignedElement $element
     ): void {
         $dstPrivateKey = $dstMetadata->getString('signature.privatekey', null);
+        $cryptoUtils = new Utils\Crypto();
 
         if ($dstPrivateKey !== null) {
             /** @var array $keyArray */
-            $keyArray = Utils\Crypto::loadPrivateKey($dstMetadata, true, 'signature.');
-            $certArray = Utils\Crypto::loadPublicKey($dstMetadata, false, 'signature.');
+            $keyArray = $cryptoUtils->loadPrivateKey($dstMetadata, true, 'signature.');
+            $certArray = $cryptoUtils->loadPublicKey($dstMetadata, false, 'signature.');
         } else {
             /** @var array $keyArray */
-            $keyArray = Utils\Crypto::loadPrivateKey($srcMetadata, true);
-            $certArray = Utils\Crypto::loadPublicKey($srcMetadata, false);
+            $keyArray = $cryptoUtils->loadPrivateKey($srcMetadata, true);
+            $certArray = $cryptoUtils->loadPublicKey($srcMetadata, false);
         }
 
         $algo = $dstMetadata->getString('signature.algorithm', null);
@@ -264,9 +265,10 @@ class Message
         }
 
         $keys = [];
+        $cryptoUtils = new Utils\Crypto();
 
         // load the new private key if it exists
-        $keyArray = Utils\Crypto::loadPrivateKey($dstMetadata, false, 'new_');
+        $keyArray = $cryptoUtils->loadPrivateKey($dstMetadata, false, 'new_');
         if ($keyArray !== null) {
             assert::keyExists($keyArray, 'PEM');
 
@@ -283,7 +285,7 @@ class Message
          *
          * @var array $keyArray  Because the second param is true
          */
-        $keyArray = Utils\Crypto::loadPrivateKey($dstMetadata, true);
+        $keyArray = $cryptoUtils->loadPrivateKey($dstMetadata, true);
         Assert::keyExists($keyArray, 'PEM');
 
         $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, ['type' => 'private']);
diff --git a/modules/saml/www/idp/certs.php b/modules/saml/www/idp/certs.php
index 5f11e7461db7d38fccabcf090d65fe5996ecfa35..ae3889819943befd4db91ea2f6e1acaa65180222 100644
--- a/modules/saml/www/idp/certs.php
+++ b/modules/saml/www/idp/certs.php
@@ -10,24 +10,26 @@ if (!$config->getBoolean('enable.saml20-idp', false)) {
 
 // Check if valid local session exists..
 if ($config->getBoolean('admin.protectmetadata', false)) {
-    \SimpleSAML\Utils\Auth::requireAdmin();
+    $authUtils = new \SimpleSAML\Utils\Auth();
+    $authUtils->requireAdmin();
 }
 
 $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
 $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted');
+$cryptoUtils = new \SimpleSAML\Utils\Crypto();
 
 switch ($_SERVER['PATH_INFO']) {
     case '/new_idp.crt':
         /** @var array $certInfo */
-        $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'new_');
+        $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true, 'new_');
         break;
     case '/idp.crt':
         /** @var array $certInfo */
-        $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true);
+        $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true);
         break;
     case '/https.crt':
         /** @var array $certInfo */
-        $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'https.');
+        $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true, 'https.');
         break;
     default:
         throw new \SimpleSAML\Error\NotFound('Unknown certificate.');
diff --git a/modules/saml/www/sp/metadata.php b/modules/saml/www/sp/metadata.php
index d54431233014bcf455d6f704dab250c3931062cf..3b4e847eb5759108e229c74e8cdcd1a36fb52630 100644
--- a/modules/saml/www/sp/metadata.php
+++ b/modules/saml/www/sp/metadata.php
@@ -18,7 +18,8 @@ if (!array_key_exists('PATH_INFO', $_SERVER)) {
 
 $config = Configuration::getInstance();
 if ($config->getBoolean('admin.protectmetadata', false)) {
-    Utils\Auth::requireAdmin();
+    $authUtils = new Utils\Auth();
+    $authUtils->requireAdmin();
 }
 $sourceId = substr($_SERVER['PATH_INFO'], 1);
 $source = Auth\Source::getById($sourceId);
@@ -123,7 +124,8 @@ foreach ($assertionsconsumerservices as $services) {
 $metaArray20['AssertionConsumerService'] = $spconfig->getArray('AssertionConsumerService', $eps);
 
 $keys = [];
-$certInfo = Utils\Crypto::loadPublicKey($spconfig, false, 'new_');
+$cryptoUtils = new Utils\Crypto();
+$certInfo = $cryptoUtils->loadPublicKey($spconfig, false, 'new_');
 if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
     $hasNewCert = true;
 
@@ -139,7 +141,7 @@ if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
     $hasNewCert = false;
 }
 
-$certInfo = Utils\Crypto::loadPublicKey($spconfig);
+$certInfo = $cryptoUtils->loadPublicKey($spconfig);
 if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
     $certData = $certInfo['certData'];
 
diff --git a/tests/lib/SimpleSAML/Utils/ArraysTest.php b/tests/lib/SimpleSAML/Utils/ArraysTest.php
index 7267b8ab3ca5875078208afc982241b18a021404..8d297cf6a31f062cb2f4e8dc7327db645adc1a31 100644
--- a/tests/lib/SimpleSAML/Utils/ArraysTest.php
+++ b/tests/lib/SimpleSAML/Utils/ArraysTest.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
 namespace SimpleSAML\Test\Utils;
 
 use PHPUnit\Framework\TestCase;
-use SimpleSAML\Utils\Arrays;
+use SimpleSAML\Utils;
 
 /**
  * Tests for SimpleSAML\Utils\Arrays.
@@ -14,6 +14,22 @@ use SimpleSAML\Utils\Arrays;
  */
 class ArraysTest extends TestCase
 {
+    /** @var \SimpleSAML\Utils\Arrays */
+    protected $arrayUtils;
+
+
+    /**
+     * Set up for each test.
+     * @return void
+     */
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->arrayUtils = new Utils\Arrays();
+    }
+
+
     /**
      * Test the arrayize() function.
      */
@@ -21,23 +37,23 @@ class ArraysTest extends TestCase
     {
         // check with empty array as input
         $array = [];
-        $this->assertEquals($array, Arrays::arrayize($array));
+        $this->assertEquals($array, $this->arrayUtils->arrayize($array));
 
         // check non-empty array as input
         $array = ['key' => 'value'];
-        $this->assertEquals($array, Arrays::arrayize($array));
+        $this->assertEquals($array, $this->arrayUtils->arrayize($array));
 
         // check indexes are ignored when input is an array
-        $this->assertArrayNotHasKey('invalid', Arrays::arrayize($array, 'invalid'));
+        $this->assertArrayNotHasKey('invalid', $this->arrayUtils->arrayize($array, 'invalid'));
 
         // check default index
         $expected = ['string'];
-        $this->assertEquals($expected, Arrays::arrayize($expected[0]));
+        $this->assertEquals($expected, $this->arrayUtils->arrayize($expected[0]));
 
         // check string index
         $index = 'key';
         $expected = [$index => 'string'];
-        $this->assertEquals($expected, Arrays::arrayize($expected[$index], $index));
+        $this->assertEquals($expected, $this->arrayUtils->arrayize($expected[$index], $index));
     }
 
 
@@ -48,11 +64,11 @@ class ArraysTest extends TestCase
     {
         // check bad arrays
         $this->assertFalse(
-            Arrays::transpose(['1', '2', '3']),
+            $this->arrayUtils->transpose(['1', '2', '3']),
             'Invalid two-dimensional array was accepted'
         );
         $this->assertFalse(
-            Arrays::transpose(['1' => 0, '2' => '0', '3' => [0]]),
+            $this->arrayUtils->transpose(['1' => 0, '2' => '0', '3' => [0]]),
             'Invalid elements on a two-dimensional array were accepted'
         );
 
@@ -77,7 +93,7 @@ class ArraysTest extends TestCase
         ];
         $this->assertEquals(
             $transposed,
-            Arrays::transpose($array),
+            $this->arrayUtils->transpose($array),
             'Unexpected result of transpose()'
         );
 
@@ -102,7 +118,7 @@ class ArraysTest extends TestCase
         ];
         $this->assertEquals(
             $transposed,
-            Arrays::transpose($array),
+            $this->arrayUtils->transpose($array),
             'Unexpected result of transpose()'
         );
 
@@ -129,7 +145,7 @@ class ArraysTest extends TestCase
         ];
         $this->assertEquals(
             $transposed,
-            Arrays::transpose($array),
+            $this->arrayUtils->transpose($array),
             'Unexpected result of transpose()'
         );
     }
diff --git a/tests/lib/SimpleSAML/Utils/AttributesTest.php b/tests/lib/SimpleSAML/Utils/AttributesTest.php
index 452fd46fbb80f8d4575ba411b59cb88a490773ca..4f99ee4c0411c3b460aaff3421f9f88499dbf213 100644
--- a/tests/lib/SimpleSAML/Utils/AttributesTest.php
+++ b/tests/lib/SimpleSAML/Utils/AttributesTest.php
@@ -16,6 +16,22 @@ use SimpleSAML\Utils\Attributes;
  */
 class AttributesTest extends TestCase
 {
+    /** @var \SimpleSAML\Utils\Attributes */
+    protected $attrUtils;
+
+
+    /**
+     * Set up for each test.
+     * @return void
+     */
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->attrUtils = new Attributes();
+    }
+
+
     /**
      * Test the getExpectedAttributeMethod() method with a non-normalized attributes array.
      */
@@ -30,7 +46,7 @@ class AttributesTest extends TestCase
         $this->expectExceptionMessage(
             'The attributes array is not normalized, values should be arrays.'
         );
-        Attributes::getExpectedAttribute($attributes, $expected);
+        $this->attrUtils->getExpectedAttribute($attributes, $expected);
     }
 
 
@@ -46,7 +62,7 @@ class AttributesTest extends TestCase
         $expected = 'missing';
         $this->expectException(Error\Exception::class);
         $this->expectExceptionMessage("No such attribute '" . $expected . "' found.");
-        Attributes::getExpectedAttribute($attributes, $expected);
+        $this->attrUtils->getExpectedAttribute($attributes, $expected);
     }
 
 
@@ -62,7 +78,7 @@ class AttributesTest extends TestCase
         $expected = 'attribute';
         $this->expectException(Error\Exception::class);
         $this->expectExceptionMessage("Empty attribute '" . $expected . "'.'");
-        Attributes::getExpectedAttribute($attributes, $expected);
+        $this->attrUtils->getExpectedAttribute($attributes, $expected);
     }
 
 
@@ -83,7 +99,7 @@ class AttributesTest extends TestCase
         $this->expectExceptionMessage(
             'More than one value found for the attribute, multiple values not allowed.'
         );
-        Attributes::getExpectedAttribute($attributes, $expected);
+        $this->attrUtils->getExpectedAttribute($attributes, $expected);
     }
 
 
@@ -98,7 +114,7 @@ class AttributesTest extends TestCase
             'attribute' => [$value],
         ];
         $expected = 'attribute';
-        $this->assertEquals($value, Attributes::getExpectedAttribute($attributes, $expected));
+        $this->assertEquals($value, $this->attrUtils->getExpectedAttribute($attributes, $expected));
 
         // check multiple (allowed) values
         $value = 'value';
@@ -106,7 +122,7 @@ class AttributesTest extends TestCase
             'attribute' => [$value, 'value2', 'value3'],
         ];
         $expected = 'attribute';
-        $this->assertEquals($value, Attributes::getExpectedAttribute($attributes, $expected, true));
+        $this->assertEquals($value, $this->attrUtils->getExpectedAttribute($attributes, $expected, true));
     }
 
 
@@ -116,7 +132,7 @@ class AttributesTest extends TestCase
     public function testNormalizeAttributesArrayBadKeys(): void
     {
         $this->expectException(InvalidArgumentException::class);
-        Attributes::normalizeAttributesArray(['attr1' => 'value1', 1 => 'value2']);
+        $this->attrUtils->normalizeAttributesArray(['attr1' => 'value1', 1 => 'value2']);
     }
 
 
@@ -126,7 +142,7 @@ class AttributesTest extends TestCase
     public function testNormalizeAttributesArrayBadValues(): void
     {
         $this->expectException(InvalidArgumentException::class);
-        Attributes::normalizeAttributesArray(['attr1' => 'value1', 'attr2' => 0]);
+        $this->attrUtils->normalizeAttributesArray(['attr1' => 'value1', 'attr2' => 0]);
     }
 
 
@@ -147,7 +163,7 @@ class AttributesTest extends TestCase
         ];
         $this->assertEquals(
             $expected,
-            Attributes::normalizeAttributesArray($attributes),
+            $this->attrUtils->normalizeAttributesArray($attributes),
             'Attribute array normalization failed'
         );
     }
@@ -161,13 +177,13 @@ class AttributesTest extends TestCase
         // test for only the name
         $this->assertEquals(
             ['default', 'name'],
-            Attributes::getAttributeNamespace('name', 'default')
+            $this->attrUtils->getAttributeNamespace('name', 'default')
         );
 
         // test for a given namespace and multiple '/'
         $this->assertEquals(
             ['some/namespace', 'name'],
-            Attributes::getAttributeNamespace('some/namespace/name', 'default')
+            $this->attrUtils->getAttributeNamespace('some/namespace/name', 'default')
         );
     }
 }
diff --git a/tests/lib/SimpleSAML/Utils/ConfigTest.php b/tests/lib/SimpleSAML/Utils/ConfigTest.php
index 3516d455483410ed1284af4b4c326efb9ff6d5f3..2e75b09d6117a6d20b44e3aaab278e29590cf7e3 100644
--- a/tests/lib/SimpleSAML/Utils/ConfigTest.php
+++ b/tests/lib/SimpleSAML/Utils/ConfigTest.php
@@ -6,7 +6,7 @@ namespace SimpleSAML\Test\Utils;
 
 use InvalidArgumentException;
 use PHPUnit\Framework\TestCase;
-use SimpleSAML\Utils\Config;
+use SimpleSAML\Utils;
 
 /**
  * Tests for SimpleSAML\Utils\Config
@@ -15,6 +15,22 @@ use SimpleSAML\Utils\Config;
  */
 class ConfigTest extends TestCase
 {
+    /** @var \SimpleSAML\Utils\Config */
+    protected $configUtils;
+
+
+    /**
+     * Set up for each test.
+     * @return void
+     */
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->configUtils = new Utils\Config();
+    }
+
+
     /**
      * Test default config dir with not environment variable
      */
@@ -22,7 +38,7 @@ class ConfigTest extends TestCase
     {
         // clear env var
         putenv('SIMPLESAMLPHP_CONFIG_DIR');
-        $configDir = Config::getConfigDir();
+        $configDir = $this->configUtils->getConfigDir();
 
         $this->assertEquals($configDir, dirname(dirname(dirname(dirname(__DIR__)))) . '/config');
     }
@@ -34,7 +50,7 @@ class ConfigTest extends TestCase
     public function testEnvVariableConfigDir(): void
     {
         putenv('SIMPLESAMLPHP_CONFIG_DIR=' . __DIR__);
-        $configDir = Config::getConfigDir();
+        $configDir = $this->configUtils->getConfigDir();
 
         $this->assertEquals($configDir, __DIR__);
     }
@@ -45,7 +61,7 @@ class ConfigTest extends TestCase
     public function testEnvRedirectVariableConfigDir(): void
     {
         putenv('REDIRECT_SIMPLESAMLPHP_CONFIG_DIR=' . __DIR__);
-        $configDir = Config::getConfigDir();
+        $configDir = $this->configUtils->getConfigDir();
 
         $this->assertEquals($configDir, __DIR__);
     }
@@ -58,7 +74,7 @@ class ConfigTest extends TestCase
     {
         putenv('SIMPLESAMLPHP_CONFIG_DIR=' . dirname(__DIR__));
         putenv('REDIRECT_SIMPLESAMLPHP_CONFIG_DIR=' . __DIR__);
-        $configDir = Config::getConfigDir();
+        $configDir = $this->configUtils->getConfigDir();
 
         $this->assertEquals($configDir, dirname(__DIR__));
     }
@@ -79,6 +95,6 @@ class ConfigTest extends TestCase
             'Given: "' . $invalidDir . '"'
         );
 
-        Config::getConfigDir();
+        $this->configUtils->getConfigDir();
     }
 }
diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php
index fa06c98ecc5273667c39d4753645ea22302bde41..dcc25aebe4a4882f37e2bb0f0ea1075031f44410 100644
--- a/tests/lib/SimpleSAML/Utils/CryptoTest.php
+++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php
@@ -4,13 +4,14 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Test\Utils;
 
+use InvalidArgumentException;
 use org\bovigo\vfs\vfsStream;
 use org\bovigo\vfs\vfsStreamDirectory;
 use PHPUnit\Framework\TestCase;
 use ReflectionMethod;
 use SimpleSAML\Configuration;
 use SimpleSAML\Error;
-use SimpleSAML\Utils\Crypto;
+use SimpleSAML\Utils;
 
 /**
  * Tests for SimpleSAML\Utils\Crypto.
@@ -32,11 +33,60 @@ class CryptoTest extends TestCase
     /** @var string */
     protected string $certdir;
 
+    /** @var \SimpleSAML\Utils\Crypto */
+    protected $cryptoUtils;
+
+    /** @var string */
+    protected $pem = <<<PHP
+-----BEGIN CERTIFICATE-----
+MIIF8zCCA9ugAwIBAgIJANSv0D4ZoP9iMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
+VQQGEwJFWDEQMA4GA1UECAwHRXhhbXBsZTEQMA4GA1UEBwwHRXhhbXBsZTEQMA4G
+A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBs
+ZS5jb20xIjAgBgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5jb20wHhcNMTcw
+MTEwMDk1MTIxWhcNMTgwMTEwMDk1MTIxWjCBjzELMAkGA1UEBhMCRVgxEDAOBgNV
+BAgMB0V4YW1wbGUxEDAOBgNVBAcMB0V4YW1wbGUxEDAOBgNVBAoMB0V4YW1wbGUx
+EDAOBgNVBAsMB0V4YW1wbGUxFDASBgNVBAMMC2V4YW1wbGUuY29tMSIwIAYJKoZI
+hvcNAQkBFhNzb21lb25lQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOC
+Ag8AMIICCgKCAgEA5Mp4xLdV41NtAI3YYr70G4gJYKegTHRwYhMeYAjudmZUng1/
+vbHLFGQybm8C6naEireQhHWzYfmDkOMU8dmdItwN4YLypYWwxYuWutWWIsDHHe0y
+CfjVz6nnTPSjZEq5PpJYY+2XTZOP+g8FmDo4nmhEchF+8eiGvHQzdBqh26EwJjQ3
+LMXyc2F2+9Cm/On+M6BQKvvXkg8FqggW8YwcOujZNWGbfG3LVJcZ0p39PbnNgJX2
+ExbscPHfjmv2RlXd5EjruRhW1oX35sB4ycIFfHGWbCl2HPc1VfouJMq/fxgkKJdb
+3RNxIBZnGpBdVJ25lCfk6t2dRdWKECrBHmcX/uR19of4H+hd4zOCPrej8IsCF2IS
+1umyUBIDyPE4WciWMUERyG1dxSjUI4DBMi4l+LRX1YUrADSthH/0jV1WDsGpHT26
++at2ZBgPy8tEvpLsITw/opUKWPCx3u5JVwFdduL8i0UF2yHmcsq44TUHVEoA1c55
+T+46ug7zHzhqFrPIwUN0DTKf33pg30xtL4d1rebc5K1KBNd9IDicd2iL8uD3HG6L
+dPdt+1OaSbGlMMKdOte31TdOp7WhqcFANkKxd6TzMUHMVmkbYh2NesaQmCgxJdv6
+/pD7L+sbMKdhlcSoJW+1wwtIo5+CzZxPA2ehZ/IWQg+Oh6djvUJzo0/84ncCAwEA
+AaNQME4wHQYDVR0OBBYEFOk6cEb397GMRCJe9xMIZ/y3yFvEMB8GA1UdIwQYMBaA
+FOk6cEb397GMRCJe9xMIZ/y3yFvEMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggIBACc1c8j7oZeVDd8O2k97kY/7pHypVZswLfmg1UqbUmYYqQ9lM6FD0J8R
+P+B8i7zST09pJ0FOsCsbyUKQmMIq/citTKmgk8NLK8otWHewHs5KTpsEvJm9XV4s
+QjF07GBECJdQWu93Rn8FdR9eJ+H0Y0oHbBu3OtSbHFHyDvaCI5bxM/5FPf4HkJil
+qIQunhO5gkz21ebukQUgiZ1YmFl0LjxGUDUDwnQ/3kOejlMUQv+ZXdQp/SaX1z5c
+dQlGl/8HDs1YAM3duvdMCXn2LP3QuhrphT/+2o+ZkY32I1p/Q0fDNaE4u7JjaxAd
+6+ijpmzZwgG5cFVU+sEeDqCI5MFn2JKiSCrHAHFMTnkpq687qBTLWoYTJ4coxtvs
+kmvdoZytKiSf7aDzGQK345BSZWJ+D5RJr2250PHMMeNkFBc+GdGiRsABhhHQAqtE
+7TVgdwvc8CYCfXlhRzdSowAVWibiftfPMmItM8Z0w5T/iPW0MsiCLGa5AvCHicN7
+pfajpJ9ZzdyLIo6dVjdQtl+S1rpFCx7ziVN8tCCX4fAVCqRqZJaG/UMLvguVqayb
+3Aw1B/fVvWoAnAzVN5ZEClZvuyjImnNZpnYSWHzCJ/9JTqB7rq93nf6Olp9QXD5y
+5iHKlJ6FlnuhcGCDsUCvG8qCw9FfoS0tuS4tKoQ5WHGQx3sKmr/D
+-----END CERTIFICATE-----
+PHP;
 
     /**
      */
     public function setUp(): void
     {
+        $this->config = Configuration::loadFromArray(
+            [
+                'module.enable' => [],
+                'secretsalt' => 'SUPER_SECRET_SALT'
+            ],
+            '[ARRAY]',
+            'simplesaml'
+        );
+
         $this->root = vfsStream::setup(
             self::ROOTDIRNAME,
             null,
@@ -46,6 +96,7 @@ class CryptoTest extends TestCase
         );
         $this->root_directory = vfsStream::url(self::ROOTDIRNAME);
         $this->certdir = $this->root_directory . DIRECTORY_SEPARATOR . self::DEFAULTCERTDIR;
+        $this->cryptoUtils = new Utils\Crypto();
     }
 
 
@@ -57,17 +108,58 @@ class CryptoTest extends TestCase
     public function testAesDecrypt(): void
     {
         if (!extension_loaded('openssl')) {
-            $this->expectException(Error\Exception::class);
+            $this->markTestSkipped('The openssl PHP module is not loaded.');
+        }
+
+        $plaintext = 'SUPER_SECRET_TEXT';
+        $ciphertext = <<<CIPHER
+uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY=
+CIPHER;
+
+        $decrypted = $this->cryptoUtils->aesDecrypt(base64_decode($ciphertext));
+        $this->assertEquals($plaintext, $decrypted);
+    }
+
+
+    /**
+     * @return void
+     */
+    public function testAesDecryptWithSmallCipherTextThrowsException(): void
+    {
+        if (!extension_loaded('openssl')) {
+            $this->markTestSkipped('The openssl PHP module is not loaded.');
         }
 
         $secret = 'SUPER_SECRET_SALT';
-        $m = new ReflectionMethod(Crypto::class, 'aesDecryptInternal');
-        $m->setAccessible(true);
+        $plaintext = 'SUPER_SECRET_TEXT';
+        // This is too small!
+        $ciphertext = 'AWcTN8tyO82hiSY=';
+
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Input parameter "$ciphertext" must be a string with more than 48 characters.');
+        $this->cryptoUtils->aesDecrypt(base64_decode($ciphertext), $secret);
+    }
 
+
+    /**
+     * @return void
+     */
+    public function testAesDecryptWithWrongSecretThrowsException(): void
+    {
+        if (!extension_loaded('openssl')) {
+            $this->markTestSkipped('The openssl PHP module is not loaded.');
+        }
+
+        // This is the wrong secret!
+        $secret = 'notsosecret';
         $plaintext = 'SUPER_SECRET_TEXT';
-        $ciphertext = 'uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4'
-            . 'vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY=';
-        $this->assertEquals($plaintext, $m->invokeArgs(null, [base64_decode($ciphertext), $secret]));
+        $ciphertext = <<<CIPHER
+uR2Yu0r4itInKx91D/l9y/08L5CIQyev9nAr27fh3Sshous4vbXRRcMcjqHDOrquD+2vqLyw7ygnbA9jA9TpB4hLZocvAWcTN8tyO82hiSY=
+CIPHER;
+
+        $this->expectException(Error\Exception::class);
+        $this->expectExceptionMessage('Failed to decrypt ciphertext.');
+        $this->cryptoUtils->aesDecrypt(base64_decode($ciphertext), $secret);
     }
 
 
@@ -78,18 +170,14 @@ class CryptoTest extends TestCase
     public function testAesEncrypt(): void
     {
         if (!extension_loaded('openssl')) {
-            $this->expectException(Error\Exception::class);
+            $this->markTestSkipped('The openssl PHP module is not loaded.');
         }
 
-        $secret = 'SUPER_SECRET_SALT';
-        $e = new ReflectionMethod(Crypto::class, 'aesEncryptInternal');
-        $d = new ReflectionMethod(Crypto::class, 'aesDecryptInternal');
-        $e->setAccessible(true);
-        $d->setAccessible(true);
-
         $original_plaintext = 'SUPER_SECRET_TEXT';
-        $ciphertext = $e->invokeArgs(null, [$original_plaintext, $secret]);
-        $decrypted_plaintext = $d->invokeArgs(null, [$ciphertext, $secret]);
+
+        $ciphertext = $this->cryptoUtils->aesEncrypt($original_plaintext);
+        $decrypted_plaintext = $this->cryptoUtils->aesDecrypt($ciphertext);
+
         $this->assertEquals($original_plaintext, $decrypted_plaintext);
     }
 
@@ -100,43 +188,29 @@ class CryptoTest extends TestCase
      */
     public function testFormatConversion(): void
     {
-        $pem = <<<PHP
------BEGIN CERTIFICATE-----
-MIIF8zCCA9ugAwIBAgIJANSv0D4ZoP9iMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
-VQQGEwJFWDEQMA4GA1UECAwHRXhhbXBsZTEQMA4GA1UEBwwHRXhhbXBsZTEQMA4G
-A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBs
-ZS5jb20xIjAgBgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5jb20wHhcNMTcw
-MTEwMDk1MTIxWhcNMTgwMTEwMDk1MTIxWjCBjzELMAkGA1UEBhMCRVgxEDAOBgNV
-BAgMB0V4YW1wbGUxEDAOBgNVBAcMB0V4YW1wbGUxEDAOBgNVBAoMB0V4YW1wbGUx
-EDAOBgNVBAsMB0V4YW1wbGUxFDASBgNVBAMMC2V4YW1wbGUuY29tMSIwIAYJKoZI
-hvcNAQkBFhNzb21lb25lQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOC
-Ag8AMIICCgKCAgEA5Mp4xLdV41NtAI3YYr70G4gJYKegTHRwYhMeYAjudmZUng1/
-vbHLFGQybm8C6naEireQhHWzYfmDkOMU8dmdItwN4YLypYWwxYuWutWWIsDHHe0y
-CfjVz6nnTPSjZEq5PpJYY+2XTZOP+g8FmDo4nmhEchF+8eiGvHQzdBqh26EwJjQ3
-LMXyc2F2+9Cm/On+M6BQKvvXkg8FqggW8YwcOujZNWGbfG3LVJcZ0p39PbnNgJX2
-ExbscPHfjmv2RlXd5EjruRhW1oX35sB4ycIFfHGWbCl2HPc1VfouJMq/fxgkKJdb
-3RNxIBZnGpBdVJ25lCfk6t2dRdWKECrBHmcX/uR19of4H+hd4zOCPrej8IsCF2IS
-1umyUBIDyPE4WciWMUERyG1dxSjUI4DBMi4l+LRX1YUrADSthH/0jV1WDsGpHT26
-+at2ZBgPy8tEvpLsITw/opUKWPCx3u5JVwFdduL8i0UF2yHmcsq44TUHVEoA1c55
-T+46ug7zHzhqFrPIwUN0DTKf33pg30xtL4d1rebc5K1KBNd9IDicd2iL8uD3HG6L
-dPdt+1OaSbGlMMKdOte31TdOp7WhqcFANkKxd6TzMUHMVmkbYh2NesaQmCgxJdv6
-/pD7L+sbMKdhlcSoJW+1wwtIo5+CzZxPA2ehZ/IWQg+Oh6djvUJzo0/84ncCAwEA
-AaNQME4wHQYDVR0OBBYEFOk6cEb397GMRCJe9xMIZ/y3yFvEMB8GA1UdIwQYMBaA
-FOk6cEb397GMRCJe9xMIZ/y3yFvEMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
-BQADggIBACc1c8j7oZeVDd8O2k97kY/7pHypVZswLfmg1UqbUmYYqQ9lM6FD0J8R
-P+B8i7zST09pJ0FOsCsbyUKQmMIq/citTKmgk8NLK8otWHewHs5KTpsEvJm9XV4s
-QjF07GBECJdQWu93Rn8FdR9eJ+H0Y0oHbBu3OtSbHFHyDvaCI5bxM/5FPf4HkJil
-qIQunhO5gkz21ebukQUgiZ1YmFl0LjxGUDUDwnQ/3kOejlMUQv+ZXdQp/SaX1z5c
-dQlGl/8HDs1YAM3duvdMCXn2LP3QuhrphT/+2o+ZkY32I1p/Q0fDNaE4u7JjaxAd
-6+ijpmzZwgG5cFVU+sEeDqCI5MFn2JKiSCrHAHFMTnkpq687qBTLWoYTJ4coxtvs
-kmvdoZytKiSf7aDzGQK345BSZWJ+D5RJr2250PHMMeNkFBc+GdGiRsABhhHQAqtE
-7TVgdwvc8CYCfXlhRzdSowAVWibiftfPMmItM8Z0w5T/iPW0MsiCLGa5AvCHicN7
-pfajpJ9ZzdyLIo6dVjdQtl+S1rpFCx7ziVN8tCCX4fAVCqRqZJaG/UMLvguVqayb
-3Aw1B/fVvWoAnAzVN5ZEClZvuyjImnNZpnYSWHzCJ/9JTqB7rq93nf6Olp9QXD5y
-5iHKlJ6FlnuhcGCDsUCvG8qCw9FfoS0tuS4tKoQ5WHGQx3sKmr/D
------END CERTIFICATE-----
-PHP;
-        $this->assertEquals(trim($pem), trim(Crypto::der2pem(Crypto::pem2der($pem))));
+        $this->assertEquals(trim($this->pem), trim($this->cryptoUtils->der2pem($this->cryptoUtils->pem2der($this->pem))));
+    }
+
+
+    /**
+     * @return void
+     */
+    public function testFormatConversionThrowsExceptionWhenNotPEMStart(): void
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('pem2der: input is not encoded in PEM format.');
+        $this->cryptoUtils->pem2der(substr($this->pem, 6));
+    }
+
+
+    /**
+     * @return void
+     */
+    public function testFormatConversionThrowsExceptionWhenNotPEMEnd(): void
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('pem2der: input is not encoded in PEM format.');
+        $this->cryptoUtils->pem2der(substr($this->pem, 0, -20));
     }
 
 
@@ -146,8 +220,8 @@ PHP;
     {
         $pw = "password";
 
-        $hash = Crypto::pwHash($pw);
-        $res = Crypto::pwValid($hash, $pw);
+        $hash = $this->cryptoUtils->pwHash($pw);
+        $res = $this->cryptoUtils->pwValid($hash, $pw);
 
         $this->assertTrue($res);
     }
@@ -160,8 +234,8 @@ PHP;
         $pw = "password";
         $pw2 = "password2";
 
-        $hash = Crypto::pwHash($pw);
-        $res = Crypto::pwValid($hash, $pw2);
+        $hash = $this->cryptoUtils->pwHash($pw);
+        $res = $this->cryptoUtils->pwValid($hash, $pw2);
 
         $this->assertFalse($res);
     }
@@ -173,9 +247,9 @@ PHP;
     {
         $pw = "password";
 
-        $hash = Crypto::pwHash($pw);
+        $hash = $this->cryptoUtils->pwHash($pw);
         $this->expectException(Error\Exception::class);
-        Crypto::pwValid($hash, $hash);
+        $this->cryptoUtils->pwValid($hash, $hash);
     }
 
 
@@ -183,7 +257,7 @@ PHP;
      */
     public function testSecureCompareEqual(): void
     {
-        $res = Crypto::secureCompare("string", "string");
+        $res = $this->cryptoUtils->secureCompare("string", "string");
 
         $this->assertTrue($res);
     }
@@ -193,7 +267,7 @@ PHP;
      */
     public function testSecureCompareNotEqual(): void
     {
-        $res = Crypto::secureCompare("string1", "string2");
+        $res = $this->cryptoUtils->secureCompare("string1", "string2");
 
         $this->assertFalse($res);
     }
@@ -207,7 +281,7 @@ PHP;
         $config = new Configuration([], 'test');
         $required = true;
 
-        Crypto::loadPrivateKey($config, $required);
+        $this->cryptoUtils->loadPrivateKey($config, $required);
     }
 
 
@@ -218,7 +292,7 @@ PHP;
         $config = new Configuration([], 'test');
         $required = false;
 
-        $res = Crypto::loadPrivateKey($config, $required);
+        $res = $this->cryptoUtils->loadPrivateKey($config, $required);
 
         $this->assertNull($res);
     }
@@ -231,7 +305,7 @@ PHP;
         $this->expectException(Error\Exception::class);
         $config = new Configuration(['privatekey' => 'nonexistant'], 'test');
 
-        Crypto::loadPrivateKey($config, false, '', true);
+        $this->cryptoUtils->loadPrivateKey($config, false, '', true);
     }
 
 
@@ -246,7 +320,7 @@ PHP;
 
         file_put_contents($filename, $data);
 
-        $res = Crypto::loadPrivateKey($config, false, '', $full_path);
+        $res = $this->cryptoUtils->loadPrivateKey($config, false, '', $full_path);
         $expected = ['PEM' => $data, 'password' => null];
 
         $this->assertEquals($expected, $res);
@@ -271,7 +345,7 @@ PHP;
 
         file_put_contents($filename, $data);
 
-        $res = Crypto::loadPrivateKey($config, false, '', $full_path);
+        $res = $this->cryptoUtils->loadPrivateKey($config, false, '', $full_path);
         $expected = ['PEM' => $data, 'password' => $password];
 
         $this->assertEquals($expected, $res);
@@ -297,7 +371,7 @@ PHP;
 
         file_put_contents($filename, $data);
 
-        $res = Crypto::loadPrivateKey($config, false, $prefix, $full_path);
+        $res = $this->cryptoUtils->loadPrivateKey($config, false, $prefix, $full_path);
         $expected = ['PEM' => $data, 'password' => $password];
 
         $this->assertEquals($expected, $res);
@@ -312,7 +386,7 @@ PHP;
         $config = new Configuration([], 'test');
         $required = true;
 
-        Crypto::loadPublicKey($config, $required);
+        $this->cryptoUtils->loadPublicKey($config, $required);
     }
 
 
@@ -323,7 +397,7 @@ PHP;
         $config = new Configuration([], 'test');
         $required = false;
 
-        $res = Crypto::loadPublicKey($config, $required);
+        $res = $this->cryptoUtils->loadPublicKey($config, $required);
 
         $this->assertNull($res);
     }
@@ -346,7 +420,7 @@ PHP;
             'test'
         );
 
-        $res = Crypto::loadPublicKey($config);
+        $res = $this->cryptoUtils->loadPublicKey($config);
 
         $this->assertNull($res);
     }
@@ -369,7 +443,7 @@ PHP;
             'test'
         );
 
-        $res = Crypto::loadPublicKey($config);
+        $res = $this->cryptoUtils->loadPublicKey($config);
 
         $this->assertNull($res);
     }
@@ -394,7 +468,7 @@ PHP;
         );
 
         /** @var array $pubkey */
-        $pubkey = Crypto::loadPublicKey($config);
+        $pubkey = $this->cryptoUtils->loadPublicKey($config);
         $res = $pubkey['certData'];
         $expected = $x509certificate;
 
diff --git a/tests/modules/admin/lib/Controller/ConfigTest.php b/tests/modules/admin/lib/Controller/ConfigTest.php
index e7e513dca95b52b5ef757a6cf85d964d63064c38..c10f17e79370049b8c15cab5aa875f223ee66660 100644
--- a/tests/modules/admin/lib/Controller/ConfigTest.php
+++ b/tests/modules/admin/lib/Controller/ConfigTest.php
@@ -61,7 +61,7 @@ class ConfigTest extends TestCase
         );
 
         $this->authUtils = new class () extends Utils\Auth {
-            public static function requireAdmin(): void
+            public function requireAdmin(): void
             {
                 // stub
             }
diff --git a/tests/modules/admin/lib/Controller/FederationTest.php b/tests/modules/admin/lib/Controller/FederationTest.php
index f15cb033c92c1d48b00b6eb9f9fb83b5789f7e4f..417834bd7946b8941ba6bd626ab74e79d1e6698e 100644
--- a/tests/modules/admin/lib/Controller/FederationTest.php
+++ b/tests/modules/admin/lib/Controller/FederationTest.php
@@ -79,7 +79,7 @@ class FederationTest extends TestCase
         );
 
         $this->authUtils = new class () extends Utils\Auth {
-            public static function requireAdmin(): void
+            public function requireAdmin(): void
             {
                 // stub
             }
diff --git a/tests/modules/admin/lib/Controller/TestTest.php b/tests/modules/admin/lib/Controller/TestTest.php
index 78743f128572c4cd039645395dcc83aa6a14bb5a..a27a83c4f3d967d1afc3c6a916063dbbc79f9995 100644
--- a/tests/modules/admin/lib/Controller/TestTest.php
+++ b/tests/modules/admin/lib/Controller/TestTest.php
@@ -51,7 +51,7 @@ class TestTest extends TestCase
         );
 
         $this->authUtils = new class () extends Utils\Auth {
-            public static function requireAdmin(): void
+            public function requireAdmin(): void
             {
                 // stub
             }
diff --git a/tests/modules/core/lib/Auth/Process/TargetedIDTest.php b/tests/modules/core/lib/Auth/Process/TargetedIDTest.php
index 0bccc514e0afbed8a2340224f2af1bf822f3470f..34a740d4171ecd4eb858cff96b86b3df3cea0bb2 100644
--- a/tests/modules/core/lib/Auth/Process/TargetedIDTest.php
+++ b/tests/modules/core/lib/Auth/Process/TargetedIDTest.php
@@ -33,7 +33,7 @@ class TargetedIDTest extends TestCase
         parent::setUp();
 
         self::$configUtils = new class () extends Utils\Config {
-            public static function getSecretSalt(): string
+            public function getSecretSalt(): string
             {
                 // stub
                 return 'secretsalt';
diff --git a/tests/modules/cron/lib/Controller/CronTest.php b/tests/modules/cron/lib/Controller/CronTest.php
index da032e241f859bf93df551b754d637c7ebd97110..e543a6dad2125b956e817032cb3b740309e68bc1 100644
--- a/tests/modules/cron/lib/Controller/CronTest.php
+++ b/tests/modules/cron/lib/Controller/CronTest.php
@@ -50,7 +50,7 @@ class CronTest extends TestCase
         $this->session = Session::getSessionFromRequest();
 
         $this->authUtils = new class () extends Utils\Auth {
-            public static function requireAdmin(): void
+            public function requireAdmin(): void
             {
                 // stub
             }
diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php
index 7bf640076a46786ea18b9e4135af9756ae115881..7bb1fa632ec75634f39235f46b54b0a25408066e 100644
--- a/www/saml2/idp/metadata.php
+++ b/www/saml2/idp/metadata.php
@@ -9,9 +9,7 @@ use SimpleSAML\Assert\Assert;
 use SimpleSAML\Configuration;
 use SimpleSAML\Error;
 use SimpleSAML\Module;
-use SimpleSAML\Utils\Auth as Auth;
-use SimpleSAML\Utils\Crypto as Crypto;
-use SimpleSAML\Utils\HTTP as HTTP;
+use SimpleSAML\Utils;
 use SimpleSAML\Utils\Config\Metadata as Metadata;
 
 $config = Configuration::getInstance();
@@ -21,7 +19,8 @@ if (!$config->getBoolean('enable.saml20-idp', false) || !Module::isModuleEnabled
 
 // check if valid local session exists
 if ($config->getBoolean('admin.protectmetadata', false)) {
-    Auth::requireAdmin();
+    $authUtils = new Utils\Auth();
+    $authUtils->requireAdmin();
 }
 
 $metadata = \SimpleSAML\Metadata\MetaDataStorageHandler::getMetadataHandler();
@@ -31,10 +30,11 @@ try {
         $_GET['idpentityid'] : $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
     $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted');
 
+    $cryptoUtils = new Utils\Crypto();
     $availableCerts = [];
-
     $keys = [];
-    $certInfo = Crypto::loadPublicKey($idpmeta, false, 'new_');
+
+    $certInfo = $cryptoUtils->loadPublicKey($idpmeta, false, 'new_');
     if ($certInfo !== null) {
         $availableCerts['new_idp.crt'] = $certInfo;
         $keys[] = [
@@ -48,7 +48,7 @@ try {
         $hasNewCert = false;
     }
 
-    $certInfo = Crypto::loadPublicKey($idpmeta, true);
+    $certInfo = $cryptoUtils->loadPublicKey($idpmeta, true);
     $availableCerts['idp.crt'] = $certInfo;
     $keys[] = [
         'type'            => 'X509Certificate',
@@ -58,7 +58,7 @@ try {
     ];
 
     if ($idpmeta->hasValue('https.certificate')) {
-        $httpsCert = Crypto::loadPublicKey($idpmeta, true, 'https.');
+        $httpsCert = $cryptoUtils->loadPublicKey($idpmeta, true, 'https.');
         Assert::notNull($httpsCert['certData']);
         $availableCerts['https.crt'] = $httpsCert;
         $keys[] = [
@@ -117,7 +117,7 @@ try {
         // Artifact sending enabled
         $metaArray['ArtifactResolutionService'][] = [
             'index'    => 0,
-            'Location' => HTTP::getBaseURL() . 'saml2/idp/ArtifactResolutionService.php',
+            'Location' => Utils\HTTP::getBaseURL() . 'saml2/idp/ArtifactResolutionService.php',
             'Binding'  => Constants::BINDING_SOAP,
         ];
     }
@@ -127,7 +127,7 @@ try {
         array_unshift($metaArray['SingleSignOnService'], [
             'hoksso:ProtocolBinding' => Constants::BINDING_HTTP_REDIRECT,
             'Binding'                => Constants::BINDING_HOK_SSO,
-            'Location'               => HTTP::getBaseURL() . 'saml2/idp/SSOService.php'
+            'Location'               => Utils\HTTP::getBaseURL() . 'saml2/idp/SSOService.php'
         ]);
     }
 
@@ -135,7 +135,7 @@ try {
         $metaArray['SingleSignOnService'][] = [
             'index' => 0,
             'Binding'  => Constants::BINDING_SOAP,
-            'Location' => HTTP::getBaseURL() . 'saml2/idp/SSOService.php',
+            'Location' => Utils\HTTP::getBaseURL() . 'saml2/idp/SSOService.php',
         ];
     }
 
@@ -238,7 +238,7 @@ try {
         $t->data['certdata'] = $certdata;
         $t->data['header'] = 'saml20-idp'; // TODO: Replace with headerString in 2.0
         $t->data['headerString'] = \SimpleSAML\Locale\Translate::noop('metadata_saml20-idp');
-        $t->data['metaurl'] = HTTP::getSelfURLNoQuery();
+        $t->data['metaurl'] = Utils\HTTP::getSelfURLNoQuery();
         $t->data['metadata'] = htmlspecialchars($metaxml);
         $t->data['metadataflat'] = htmlspecialchars($metaflat);
         $t->send();