diff --git a/config-templates/config.php b/config-templates/config.php
index c27decf2ca22267f2a3549033d38d2019bb7055f..77dbc3531504888a030248cb7ce8a4379cfb0fdd 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -56,7 +56,6 @@ $config = [
     /*
      * The following settings are *filesystem paths* which define where
      * SimpleSAMLphp can find or write the following things:
-     * - 'certdir': The base directory for certificate and key material.
      * - 'loggingdir': Where to write logs.
      * - 'datadir': Storage of general data.
      * - 'tempdir': Saving temporary files. SimpleSAMLphp will attempt to create
@@ -64,11 +63,61 @@ $config = [
      * When specified as a relative path, this is relative to the SimpleSAMLphp
      * root directory.
      */
-    'certdir' => 'cert/',
     'loggingdir' => 'log/',
     'datadir' => 'data/',
     'tempdir' => '/tmp/simplesaml',
 
+    /*
+     * Certificate and key material can be loaded from different possible
+     * locations. Currently two locations are supported, the local filesystem
+     * and the database via pdo using the global database configuration. Locations
+     * are specified by a URL-link prefix before the file name/path or database
+     * identifier.
+     */
+
+    /* To load a certificate or key from the filesystem, it should be specified
+     * as 'file://<name>' where <name> is either a relative filename or a fully
+     * qualified path to a file containing the certificate or key in PEM
+     * format, such as 'cert.pem' or '/path/to/cert.pem'. If the path is
+     * relative, it will be searched for in the directory defined by the
+     * 'certdir' parameter below. When 'certdir' is specified as a relative
+     * path, it will be interpreted as relative to the SimpleSAMLphp root
+     * directory. Note that locations with no prefix included will be treated
+     * as file locations for backwards compatibility.
+     */
+    'certdir' => 'cert/',
+
+    /* To load a certificate or key from the database, it should be specified
+     * as 'pdo://<id>' where <id> is the identifier in the database table that
+     * should be matched. While the certificate and key tables are expected to
+     * be in the simplesaml database, they are not created or managed by
+     * simplesaml. The following parameters control how the pdo location
+     * attempts to retrieve certificates and keys from the database:
+     *
+     * - 'cert.pdo.table': name of table where certificates are stored
+     * - 'cert.pdo.keytable': name of table where keys are stored
+     * - 'cert.pdo.apply_prefix': whether or not to prepend the database.prefix
+     *                            parameter to the table names; if you are using
+     *                            database.prefix to separate multiple SSP instances
+     *                            in the same database but want to share certificate/key
+     *                            data between them, set this to false
+     * - 'cert.pdo.id_column': name of column to use as identifier
+     * - 'cert.pdo.data_column': name of column where PEM data is stored
+     *
+     * Basically, the query executed will be:
+     *
+     *   SELECT cert.pdo.data_column FROM cert.pdo.table WHERE cert.pdo.id_column = :id
+     *
+     * Defaults are shown below, to change them, uncomment the line and update as
+     * needed
+     */
+
+    //'cert.pdo.table' => 'certificates',
+    //'cert.pdo.keytable' => 'private_keys',
+    //'cert.pdo.apply_prefix' => true,
+    //'cert.pdo.id_column' => 'id',
+    //'cert.pdo.data_column' => 'data',
+
     /*
      * Some information about the technical persons running this installation.
      * The email address will be used as the recipient address for error reports, and
diff --git a/docs/simplesamlphp-advancedfeatures.md b/docs/simplesamlphp-advancedfeatures.md
index 90df9f49e30d5f9c9ea68205f1ecbdd48f543ae8..73ef877e10ba2ecf12a5f1740833e6913f0914a4 100644
--- a/docs/simplesamlphp-advancedfeatures.md
+++ b/docs/simplesamlphp-advancedfeatures.md
@@ -90,9 +90,9 @@ Metadata signing
 SimpleSAMLphp supports signing of the metadata it generates. Metadata signing is configured by four options:
 
 - `metadata.sign.enable`: Whether metadata signing should be enabled or not. Set to `TRUE` to enable metadata signing. Defaults to `FALSE`.
-- `metadata.sign.privatekey`: Name of the file with the private key which should be used to sign the metadata. This file must exist in in the `cert` directory.
+- `metadata.sign.privatekey`: Location of the private key data which should be used to sign the metadata.
 - `metadata.sign.privatekey_pass`: Passphrase which should be used to open the private key. This parameter is optional, and should be left out if the private key is unencrypted.
-- `metadata.sign.certificate`: Name of the file with the certificate which matches the private key. This file must exist in in the `cert` directory.
+- `metadata.sign.certificate`: Location of certificate data which matches the private key.
 - `metadata.sign.algorithm`: The algorithm to use when signing metadata for this entity. Defaults to RSA-SHA256. Possible values:
 
     * `http://www.w3.org/2000/09/xmldsig#rsa-sha1`
diff --git a/docs/simplesamlphp-changelog.md b/docs/simplesamlphp-changelog.md
index 6360f81ddf7141dbd1f31371bb5919c5a445bd0d..0e2b20adb79d303cdf09fc32caae4d6c24b83014 100644
--- a/docs/simplesamlphp-changelog.md
+++ b/docs/simplesamlphp-changelog.md
@@ -17,6 +17,7 @@ See the upgrade notes for specific information about upgrading.
   * core:PairwiseID and core:SubjectID authprocs no longer support the 'scope' config-setting.
     Use 'scopeAttribute' instead to identify the attribute holding the scope.
   * Accepting unsolicited responses can be disabled by setting `enable_unsolicited` to `false` in the SP authsource.
+  * Certificates and private keys can now be retrieved from a database
 
 ## Version 1.19.1
 
diff --git a/docs/simplesamlphp-idp.md b/docs/simplesamlphp-idp.md
index a4f623fdf1a4bf8983659b9828dbdc62ba7a0505..367cf9e2c6ba3a7740866fafd09f6f0deb300549 100644
--- a/docs/simplesamlphp-idp.md
+++ b/docs/simplesamlphp-idp.md
@@ -152,7 +152,8 @@ This is a minimal configuration:
 
         /*
          * The private key and certificate to use when signing responses.
-         * These are stored in the cert-directory.
+         * These can be stored as files in the cert-directory or retrieved
+         * from a database.
          */
         'privatekey' => 'example.org.pem',
         'certificate' => 'example.org.crt',
diff --git a/docs/simplesamlphp-reference-idp-hosted.md b/docs/simplesamlphp-reference-idp-hosted.md
index bd82ad1bc3e60c8d31f511e5858eac8ddca21ab4..ea64aa3b30424d87a294546bd1efb9e4cb907e83 100644
--- a/docs/simplesamlphp-reference-idp-hosted.md
+++ b/docs/simplesamlphp-reference-idp-hosted.md
@@ -40,8 +40,7 @@ Common options
     the [authentication processing filter manual](simplesamlphp-authproc).
 
 `certificate`
-:   Certificate file which should be used by this IdP, in PEM format.
-    The filename is relative to the `cert/`-directory.
+:   Location of certificate data which should be used by this IdP, in PEM format.
 
 `contacts`
 :	Specify contacts in addition to the technical contact configured through config/config.php.
@@ -106,8 +105,7 @@ Common options
         ],
 
 `privatekey`
-:   Name of private key file for this IdP, in PEM format. The filename
-    is relative to the `cert/`-directory.
+:   Location of private key data for this IdP, in PEM format.
 
 `privatekey_pass`
 :   Passphrase for the private key. Leave this option out if the
diff --git a/docs/simplesamlphp-reference-idp-remote.md b/docs/simplesamlphp-reference-idp-remote.md
index 8a4fb90b600bbd40e36fa2251c79c3f4991e8009..0fff6e88ceb558256e34b4d389f039eeedad2842 100644
--- a/docs/simplesamlphp-reference-idp-remote.md
+++ b/docs/simplesamlphp-reference-idp-remote.md
@@ -49,7 +49,7 @@ Options
 :   The base64 encoded certificate for this IdP. This is an alternative to storing the certificate in a file on disk and specifying the filename in the `certificate`-option.
 
 `certificate`
-:   The file with the certificate for this IdP. The path is relative to the `cert`-directory.
+:   Location of certificate data for this IdP.
 
 `description`
 :   A description of this IdP. Will be used by various modules when they need to show a description of the IdP to the user.
diff --git a/docs/simplesamlphp-reference-sp-remote.md b/docs/simplesamlphp-reference-sp-remote.md
index 77213e64911a2a2a49a4822920a1e0e5e176f1f1..ac44c1b2a0b02e71a9592f70516d74d69d8f868c 100644
--- a/docs/simplesamlphp-reference-sp-remote.md
+++ b/docs/simplesamlphp-reference-sp-remote.md
@@ -144,7 +144,7 @@ The following options can be set:
 :   The base64 encoded certificate for this SP. This is an alternative to storing the certificate in a file on disk and specifying the filename in the `certificate`-option.
 
 `certificate`
-:   Name of certificate file for this SP. The certificate is used to
+:   Location of certificate data for this SP. The certificate is used to
     verify the signature of messages received from the SP (if
     `redirect.validate`is set to `TRUE`), and to encrypting assertions
     (if `assertion.encryption` is set to TRUE and `sharedkey` is
@@ -221,7 +221,7 @@ The following options can be set:
 :    * `http://www.w3.org/2001/04/xmldsig-more#rsa-sha512`
 
 `signature.privatekey`
-:   Name of private key file for this IdP, in PEM format. The filename is relative to the cert/-directory.
+:   Location of private key data for this IdP, in PEM format.
 :   Note that this option also exists in the IdP-hosted metadata. This entry in the SP-remote metadata overrides the option `privatekey` in the IdP-hosted metadata.
 
 `signature.privatekey_pass`
@@ -229,7 +229,7 @@ The following options can be set:
 :   Note that this option only is used if `signature.privatekey` is present.
 
 `signature.certificate`
-:   Certificate file included by IdP for KeyInfo within the signature for the SP, in PEM format. The filename is relative to the cert/-directory.
+:   Location of certificate data included by IdP for KeyInfo within the signature for the SP, in PEM format.
 :   If `signature.privatekey` is present and `signature.certificate` is left blank, X509Certificate will not be included with the signature.
 
 `sign.logout`
diff --git a/src/SimpleSAML/Configuration.php b/src/SimpleSAML/Configuration.php
index 5284fadbedd803f3d39022383f2ac4f713674b9e..614684d66e21799d627b42f4d9a979f3f975aac8 100644
--- a/src/SimpleSAML/Configuration.php
+++ b/src/SimpleSAML/Configuration.php
@@ -1355,8 +1355,8 @@ class Configuration implements Utils\ClearableState
      *
      * @return array Public key data, or empty array if no public key or was found.
      *
-     * @throws \Exception If the certificate or public key cannot be loaded from a file.
-     * @throws \SimpleSAML\Error\Exception If the file does not contain a valid PEM-encoded certificate, or there is no
+     * @throws \Exception If the certificate or public key cannot be loaded from location.
+     * @throws \SimpleSAML\Error\Exception If the location does not contain a valid PEM-encoded certificate, or there is no
      * certificate in the metadata.
      */
     public function getPublicKeys(?string $use = null, bool $required = false, string $prefix = ''): array
@@ -1388,15 +1388,14 @@ class Configuration implements Utils\ClearableState
                 ],
             ];
         } elseif ($this->hasValue($prefix . 'certificate')) {
-            $configUtils = new Utils\Config();
+            $location = $this->getString($prefix . 'certificate');
 
-            $file = $this->getString($prefix . 'certificate');
-            $file = $configUtils->getCertPath($file);
-            $data = @file_get_contents($file);
+            $cryptoUtils = new Utils\Crypto();
+            $data = $cryptoUtils->retrieveCertificate($location);
 
-            if ($data === false) {
+            if ($data === null) {
                 throw new Exception(
-                    $this->location . ': Unable to load certificate/public key from file "' . $file . '".'
+                    $this->location . ': Unable to load certificate/public key from location "' . $location . '".'
                 );
             }
 
@@ -1404,7 +1403,7 @@ class Configuration implements Utils\ClearableState
             $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
             if (!preg_match($pattern, $data, $matches)) {
                 throw new Error\Exception(
-                    $this->location . ': Could not find PEM encoded certificate in "' . $file . '".'
+                    $this->location . ': Could not find PEM encoded certificate in "' . $location . '".'
                 );
             }
             $certData = preg_replace('/\s+/', '', $matches[1]);
diff --git a/src/SimpleSAML/Metadata/SAMLParser.php b/src/SimpleSAML/Metadata/SAMLParser.php
index 8e476e1282483e15a18a2392a86d0a7bc717e6c2..5ca445bbc9c816da6c1fad9aaf1c88a604cb9d2d 100644
--- a/src/SimpleSAML/Metadata/SAMLParser.php
+++ b/src/SimpleSAML/Metadata/SAMLParser.php
@@ -1277,22 +1277,21 @@ class SAMLParser
      *                      to do a key rollover.
      *
      * @return boolean True if it is possible to check the signature with the certificate, false otherwise.
-     * @throws \Exception If the certificate file cannot be found.
+     * @throws \Exception If the certificate location cannot be found.
      */
     public function validateSignature(array $certificates): bool
     {
-        $configUtils = new Utils\Config();
+        $cryptoUtils = new Utils\Crypto();
 
-        foreach ($certificates as $cert) {
-            Assert::string($cert);
-            $certFile = $configUtils->getCertPath($cert);
-            if (!$this->fileSystem->exists($certFile)) {
+        foreach ($certificates as $certLocation) {
+            Assert::string($certLocation);
+
+            $certData = $cryptoUtils->retrieveCertificate($certLocation);
+            if ($certData === null) {
                 throw new Exception(
-                    'Could not find certificate file [' . $certFile . '], which is needed to validate signature'
+                    'Could not find certificate location [' . $certLocation . '], which is needed to validate signature'
                 );
             }
-            $file = new File($certFile);
-            $certData = $file->getContent();
 
             foreach ($this->validators as $validator) {
                 $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'public']);
diff --git a/src/SimpleSAML/Metadata/Signer.php b/src/SimpleSAML/Metadata/Signer.php
index 7a35a33dcefc24206c8f1ed2b690c755d45793c1..4cdbfa12a4e7b59f82642e14d8211668b7ff1b0a 100644
--- a/src/SimpleSAML/Metadata/Signer.php
+++ b/src/SimpleSAML/Metadata/Signer.php
@@ -29,7 +29,7 @@ use function is_string;
 class Signer
 {
     /**
-     * This functions finds what key & certificate files should be used to sign the metadata
+     * This functions finds what key & certificate locations should be used to sign the metadata
      * for the given entity.
      *
      * @param \SimpleSAML\Configuration $config Our \SimpleSAML\Configuration instance.
@@ -235,7 +235,7 @@ class Signer
     public static function sign(string $metadataString, array $entityMetadata, string $type): string
     {
         $config = Configuration::getInstance();
-        $configUtils = new Utils\Config();
+        $cryptoUtils = new Utils\Crypto();
 
         // check if metadata signing is enabled
         if (!self::isMetadataSigningEnabled($config, $entityMetadata, $type)) {
@@ -243,27 +243,23 @@ class Signer
         }
 
         // find the key & certificate which should be used to sign the metadata
-        $keyCertFiles = self::findKeyCert($config, $entityMetadata, $type);
+        $keyCertLocations = self::findKeyCert($config, $entityMetadata, $type);
 
-        $keyFile = $configUtils->getCertPath($keyCertFiles['privatekey']);
-        $fileSystem = new Filesystem();
-        if (!$fileSystem->exists($keyFile)) {
+        $keyLocation = $keyCertLocations['privatekey'];
+        $keyData = $cryptoUtils->retrieveKey($keyLocation);
+        if ($keyData === null) {
             throw new Exception(
-                'Could not find private key file [' . $keyFile . '], which is needed to sign the metadata'
+                'Could not find private key location [' . $keyLocation . '], which is needed to sign the metadata'
             );
         }
 
-        $key = new File($keyFile);
-        $keyData = $key->getContent();
-
-        $certFile = $configUtils->getCertPath($keyCertFiles['certificate']);
-        $cert = new File($certFile);
-        if (!$fileSystem->exists($certFile)) {
+        $certLocation = $keyCertLocations['certificate'];
+        $certData = $cryptoUtils->retrieveCertificate($certLocation);
+        if ($certData === null) {
             throw new Exception(
-                'Could not find certificate file [' . $certFile . '], which is needed to sign the metadata'
+                'Could not find certificate location [' . $certLocation . '], which is needed to sign the metadata'
             );
         }
-        $certData = $cert->getContent();
 
         // convert the metadata to a DOM tree
         try {
@@ -276,8 +272,8 @@ class Signer
 
         // load the private key
         $objKey = new XMLSecurityKey($signature_cf['algorithm'], ['type' => 'private']);
-        if (array_key_exists('privatekey_pass', $keyCertFiles)) {
-            $objKey->passphrase = $keyCertFiles['privatekey_pass'];
+        if (array_key_exists('privatekey_pass', $keyCertLocations)) {
+            $objKey->passphrase = $keyCertLocations['privatekey_pass'];
         }
         $objKey->loadKey($keyData, false);
 
diff --git a/src/SimpleSAML/Utils/Crypto.php b/src/SimpleSAML/Utils/Crypto.php
index 5d741a4b1df67c80e0d165be5e953a36d587e175..ab55d9c659df409942b9bd2593f102cce907fbd8 100644
--- a/src/SimpleSAML/Utils/Crypto.php
+++ b/src/SimpleSAML/Utils/Crypto.php
@@ -8,6 +8,7 @@ use SimpleSAML\Assert\Assert;
 use InvalidArgumentException;
 use SimpleSAML\Configuration;
 use SimpleSAML\Error;
+use SimpleSAML\Logger;
 
 /**
  * A class for cryptography-related functions.
@@ -176,7 +177,7 @@ class Crypto
      * Load a private key from metadata.
      *
      * This function loads a private key from a metadata array. It looks for the following elements:
-     * - 'privatekey': Name of a private key file in the cert-directory.
+     * - 'privatekey': Location of a private key
      * - 'privatekey_pass': Password for the private key.
      *
      * It returns an array with the following elements:
@@ -188,8 +189,8 @@ class Crypto
      * missing key will cause an exception. Defaults to false.
      * @param string                    $prefix The prefix which should be used when reading from the metadata
      * array. Defaults to ''.
-     * @param bool                      $full_path Whether the filename found in the configuration contains the
-     * full path to the private key or not. Default to false.
+     * @param bool                      $full_path Whether the location found in the configuration contains the
+     * full path to the private key or not (only relevant for file locations). Default to false.
      *
      * @return array|NULL Extracted private key, or NULL if no private key is present.
      * @throws \InvalidArgumentException If $required is not boolean or $prefix is not a string.
@@ -203,8 +204,8 @@ class Crypto
         string $prefix = '',
         bool $full_path = false
     ): ?array {
-        $file = $metadata->getOptionalString($prefix . 'privatekey', null);
-        if ($file === null) {
+        $location = $metadata->getOptionalString($prefix . 'privatekey', null);
+        if ($location === null) {
             // no private key found
             if ($required) {
                 throw new Error\Exception('No private key found in metadata.');
@@ -213,14 +214,10 @@ class Crypto
             }
         }
 
-        if (!$full_path) {
-            $configUtils = new Config();
-            $file = $configUtils->getCertPath($file);
-        }
+        $data = $this->retrieveKey($location, $full_path);
 
-        $data = @file_get_contents($file);
-        if ($data === false) {
-            throw new Error\Exception('Unable to load private key from file "' . $file . '"');
+        if ($data === null) {
+            throw new Error\Exception('Unable to load private key from location "' . $location . '"');
         }
 
         $ret = [
@@ -239,7 +236,7 @@ class Crypto
      *
      * It will search for the following elements in the metadata:
      * - 'certData': The certificate as a base64-encoded string.
-     * - 'certificate': A file with a certificate or public key in PEM-format.
+     * - 'certificate': Location of a certificate or public key in PEM-format.
      *
      * This function will return an array with these elements:
      * - 'PEM': The public key/certificate in PEM-encoding.
@@ -377,4 +374,112 @@ class Crypto
         }
         return $hash === $password;
     }
+
+    /**
+     * Retrieve a certificate or private key from specified storage location
+     *
+     * @param string $data_type Type of data to retrieve, either "certificate" or "private_key"
+     * @param string $location Location of data to retrieve
+     * @param bool $full_path Whether the location found in the configuration contains the
+     *                        full path to the certificate or private key (only relevant to file locations)
+     *
+     * @return string The certificate or private key, or null if not found
+     *
+     */
+    private function retrieveCertOrKey(string $data_type, string $location, bool $full_path): ?string
+    {
+        if (strncmp($location, 'pdo://', 6) === 0) {
+            # Attempt to load data via pdo from database
+
+            $location = substr($location, 6);
+
+            $globalConfig = Configuration::getInstance();
+            $cert_table = $globalConfig->getOptionalString('cert.pdo.table', 'certificates');
+            $key_table = $globalConfig->getOptionalString('cert.pdo.keytable', 'private_keys');
+            $apply_prefix = $globalConfig->getOptionalBoolean('cert.pdo.apply_prefix', true);
+            $id_column = $globalConfig->getOptionalString('cert.pdo.id_column', 'id');
+            $data_column = $globalConfig->getOptionalString('cert.pdo.data_column', 'data');
+
+            try {
+                $db = \SimpleSAML\Database::getInstance();
+            } catch (\Exception $e) {
+                Logger::error('failed to instantiate database: ' . $e->getMessage());
+                return(null);
+            }
+
+            if ($apply_prefix) {
+                $cert_table = $db->applyPrefix($cert_table);
+                $key_table = $db->applyPrefix($key_table);
+            }
+
+            try {
+                $query = $db->read("select $data_column from " .
+                                    ($data_type == 'certificate' ? $cert_table : $key_table) .
+                                    " where $id_column = :id", ['id' => $location]);
+            } catch (\Exception $e) {
+                Logger::error('failed to query database: ' . $e->getMessage());
+                return(null);
+            }
+
+            $result = $query->fetch(\PDO::FETCH_NUM);
+
+            if ($result) {
+                return($result[0]);
+            }
+
+            return(null);
+        } elseif (strncmp($location, 'file://', 7) === 0) {
+            # For backwards compatibility, locations without a prefix are assumed to be file locations.
+            # So just remove prefix and fall through
+
+            $location = substr($location, 7);
+        }
+
+        # Attempt to load data from file
+        if (!$full_path) {
+            $configUtils = new Config();
+            $location = $configUtils->getCertPath($location);
+        }
+
+        $data = @file_get_contents($location);
+
+        if ($data === false) {
+            Logger::error("failed to read $data_type data from file $location");
+            return(null);
+        }
+
+        return($data);
+    }
+
+    /**
+     * Public wrapper around retrieveCertOrKey to retrieve a certificate
+     *
+     * @param string $location Location of certificate data to retrieve
+     * @param bool $full_path Whether the location found in the configuration contains the
+     *                        full path to the certificate (only relevant to file locations).
+     *                        Default to false.
+     *
+     * @return string The certificate or null if not found
+     *
+     */
+    public function retrieveCertificate(string $location, bool $full_path = false): ?string
+    {
+        return $this->retrieveCertOrKey('certificate', $location, $full_path);
+    }
+
+    /**
+     * Public wrapper around retrieveCertOrKey to retrieve a private key
+     *
+     * @param string $location Location of private key data to retrieve
+     * @param bool $full_path Whether the location found in the configuration contains the
+     *                        full path to the private key (only relevant to file locations).
+     *                        Default to false.
+     *
+     * @return string The private key or null if not found
+     *
+     */
+    public function retrieveKey(string $location, bool $full_path = false): ?string
+    {
+        return $this->retrieveCertOrKey('private_key', $location, $full_path);
+    }
 }
diff --git a/src/SimpleSAML/XML/Signer.php b/src/SimpleSAML/XML/Signer.php
index 4bc0876507f6c986bc361255ef93a2d0b812e982..17ac5b150aeb3a67dfcbe5aa5b11bd88c8480fc7 100644
--- a/src/SimpleSAML/XML/Signer.php
+++ b/src/SimpleSAML/XML/Signer.php
@@ -125,32 +125,23 @@ class Signer
      *
      * Will throw an exception if unable to load the private key.
      *
-     * @param string $file  The file which contains the private key. The path is assumed to be relative
-     *                      to the cert-directory.
+     * @param string $location  The location which contains the private key
      * @param string|null $pass  The passphrase on the private key. Pass no value or NULL if the private
      *                           key is unencrypted.
-     * @param bool $full_path  Whether the filename found in the configuration contains the
-     *                         full path to the private key or not. Default to false.
+     * @param bool $full_path  Whether the location found in the configuration contains the
+     *                         full path to the private key or not (only relevant to file locations).
+     *                         Default to false.
      * @throws \Exception
      */
-    public function loadPrivateKey(string $file, ?string $pass, bool $full_path = false): void
+    public function loadPrivateKey(string $location, ?string $pass, bool $full_path = false): void
     {
-        if (!$full_path) {
-            $configUtils = new Utils\Config();
-            $keyFile = $configUtils->getCertPath($file);
-        } else {
-            $keyFile = $file;
-        }
+        $cryptoUtils = new Utils\Crypto();
+        $keyData = $cryptoUtils->retrieveKey($location, $full_path);
 
-        if (!$this->fileSystem->exists($keyFile)) {
-            throw new Exception('Could not find private key file "' . $keyFile . '".');
+        if ($keyData === null) {
+            throw new Exception('Could not find private key location "' . $location . '".');
         }
 
-        $file = new File($keyFile);
-        $keyData = $file->getContent();
-        if ($keyData === false) {
-            throw new Exception('Unable to read private key file "' . $keyFile . '".');
-        }
 
         $privatekey = ['PEM' => $keyData];
         if ($pass !== null) {
@@ -232,31 +223,22 @@ class Signer
      * Extra certificates will be added to the certificate chain in the order they
      * are added.
      *
-     * @param string $file  The file which contains the certificate, relative to the cert-directory.
-     * @param bool $full_path  Whether the filename found in the configuration contains the
-     *                         full path to the private key or not. Default to false.
+     * @param string $location The location which contains the certificate
+     * @param bool $full_path  Whether the location found in the configuration contains the
+     *                         full path to the private key or not (only relevant to file locations).
+     *                         Default to false.
      * @throws \Exception
      */
-    public function addCertificate(string $file, bool $full_path = false): void
+    public function addCertificate(string $location, bool $full_path = false): void
     {
-        if (!$full_path) {
-            $configUtils = new Utils\Config();
-            $certFile = $configUtils->getCertPath($file);
-        } else {
-            $certFile = $file;
-        }
+        $cryptoUtils = new Utils\Crypto();
+        $certData = $cryptoUtils->retrieveCertificate($location, $full_path);
 
-        if (!$this->fileSystem->exists($certFile)) {
-            throw new Exception('Could not find extra certificate file "' . $certFile . '".');
-        }
-
-        $file = new File($certFile);
-        $certificate = $file->getContent();
-        if ($certificate === false) {
-            throw new Exception('Unable to read extra certificate file "' . $certFile . '".');
+        if ($certData === null) {
+            throw new Exception('Could not find extra certificate location "' . $location . '".');
         }
 
-        $this->extraCertificates[] = $certificate;
+        $this->extraCertificates[] = $certData;
     }