diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index ca98f73..1f57a6f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1' ] + php-versions: [ '8.3' ] dependency-version: [ prefer-lowest, prefer-stable ] steps: - uses: actions/checkout@master @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1' ] + php-versions: [ '8.3' ] steps: - uses: actions/checkout@master - name: Setup PHP, with composer and extensions @@ -90,7 +90,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1' ] + php-versions: [ '8.3' ] steps: - uses: actions/checkout@master - name: Setup PHP, with composer and extensions @@ -114,6 +114,35 @@ jobs: run: | ./scripts/code-analysis + php-unit-tests: + name: PHP unit tests + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: [ '8.3' ] + steps: + - uses: actions/checkout@master + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json + coverage: none + tools: composer:v2 + # https://github.com/shivammathur/setup-php#cache-composer-dependencies + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + - name: Unit tests + run: | + ./scripts/unit-tests + coding-standards-markdown: name: Markdown coding standards runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dcae0a..5271aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* [PR-4](https://github.com/OS2web/os2web_key/pull/4) + Added HashiCorp Vault key provider * [PR-2](https://github.com/OS2web/os2web_key/pull/2) Updated documentation * [PR-1](https://github.com/OS2web/os2web_key/pull/1) diff --git a/README.md b/README.md index 002f7b9..fb7c6e5 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ Key types and providers for OS2Web built on the [Key module](https://www.drupal.org/project/key). The OS2Web key module provides two _key types_, [Certificate](#certificate) and [OpenID Connect -(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and [Infisical](#infisical), are -planned, but not yet implemented. +(OIDC)](#openid-connect-oidc). It also comes with two _key providers_, +[Azure Key Vault](#azure-key-vault) and [HashiCorp Vault](#hashicorp-vault). See [the Key Developer Guide](https://www.drupal.org/docs/contributed-modules/key/developer-guide) for details in how to use keys in Drupal. @@ -106,13 +106,24 @@ $key = $repository->getKey('openid_connect_ad'); ## Providers +The module comes with two key providers. + ### Azure Key Vault -`@todo` +Used for fetching certificate from Azure Key vault. + +### HashiCorp Vault -### Infisical +Used to fetch any sort of secret string from HashiCorp vault. Note that +this can only provide string values, i.e. no binary files. -`@todo` +To use this provider you must configure the following in `settings.local.php`: + +``` php +$settings['os2web_vault_role_id'] = '{ROLE_ID}'; +$settings['os2web_vault_secret_id'] = '{SECRET_ID}'; +$settings['os2web_vault_url'] = '{VAULT_URL}'; +``` ## Coding standards @@ -122,11 +133,11 @@ below to run the checks locally. ### PHP ```shell -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer install +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer install # Fix (some) coding standards issues -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer coding-standards-apply +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-apply # Check that code adheres to the coding standards -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer coding-standards-check +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-check ``` ### Markdown @@ -140,9 +151,69 @@ docker run --rm --volume $PWD:/md peterdavehello/markdownlint markdownlint --ign We use [PHPStan](https://phpstan.org/) for static code analysis. -Running statis code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the +Running static code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the +analysis: + +```shell +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/code-analysis +``` + +## Unit tests + +We use [PHPUnit](https://phpunit.de/documentation.html) for unit testing. + +Testing mostly centers around the conversion and parsing of certificates. For this purpose a bunch of test +certificates has been generated. See [Test certificates](#test-certificates) for how this is done. + +Running PHPUnit tests in a standalone Drupal module is a bit tricky, so we use a helper script to run the analysis: ```shell -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm ./scripts/code-analysis +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/unit-tests +``` + +### Test certificates + +Certificates have been generated in the follow way + +```shell +# p12 with password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:test -keyout test.key -out test.crt +openssl pkcs12 -export -out test_with_passphrase.p12 -passin pass:test -passout pass:test -inkey test.key -in test.crt +openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -noenc + +# p12 without password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:'' -keyout test_without_passphrase.key -out test_without_passphrase.crt +openssl pkcs12 -export -out test_without_passphrase.p12 -passin pass:'' -passout pass:'' -inkey test_without_passphrase.key -in test_without_passphrase.crt +openssl pkcs12 -in test_without_passphrase.p12 -passin pass:'' -noenc + +# PEM with password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:test -keyout test.key -out test.crt +cat test.crt test.key > test_with_passphrase.pem +openssl x509 -in test_with_passphrase.pem + +# PEM without password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:'' -keyout test_without_passphrase.key -out test_without_passphrase.crt -noenc +cat test_without_passphrase.crt test_without_passphrase.key > test_without_passphrase.pem +openssl x509 -in test_without_passphrase.pem +``` + +Extraction of certificate and private key parts in the following way + +```shell +# P12 with passphrase +openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -clcerts -nokeys | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > p12_with_passphrase_cert.txt +openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -nocerts -nodes | sed -ne '/-----BEGIN PRIVATE KEY-----/,/-----END PRIVATE KEY-----/p' > p12_with_passphrase_pkey.txt + +# P12 without passphrase +openssl pkcs12 -in test_without_passphrase.p12 -passin pass: -clcerts -nokeys | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > p12_without_passphrase_cert.txt +openssl pkcs12 -in test_without_passphrase.p12 -passin pass: -nocerts -nodes | sed -ne '/-----BEGIN PRIVATE KEY-----/,/-----END PRIVATE KEY-----/p' > p12_without_passphrase_pkey.txt + +# PEM with passphrase +openssl x509 -in test_with_passphrase.pem -passin pass:test -out pem_with_passphrase_cert.txt +openssl pkey -in test_with_passphrase.pem -passin pass:test -out pem_with_passphrase_pkey.txt + +# PEM without passphrase +openssl x509 -in test_without_passphrase.pem -passin pass: -out pem_without_passphrase_cert.txt +openssl pkey -in test_without_passphrase.pem -passin pass: -out pem_without_passphrase_pkey.txt ``` diff --git a/composer.json b/composer.json index cb11b37..013a5c7 100644 --- a/composer.json +++ b/composer.json @@ -13,15 +13,19 @@ "php": "^8.1", "ext-openssl": "*", "drupal/core": "^9 || ^10", - "drupal/key": "^1.17" + "drupal/key": "^1.17", + "itk-dev/serviceplatformen": "^1.6", + "itk-dev/vault": "^0.1" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "drupal/coder": "^8.3", + "drupal/core-dev": "^9 || ^10", "ergebnis/composer-normalize": "^2.42", "mglaman/phpstan-drupal": "^1.2", "phpstan/extension-installer": "^1.3", - "phpstan/phpstan-deprecation-rules": "^1.1" + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpunit/phpunit": "^9.6" }, "repositories": [ { @@ -29,6 +33,11 @@ "url": "https://packages.drupal.org/8" } ], + "autoload-dev": { + "psr-4": { + "Drupal\\Tests\\os2web_key\\": "tests/src/" + } + }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, diff --git a/os2web_key.services.yml b/os2web_key.services.yml index eca6b32..b7ba945 100644 --- a/os2web_key.services.yml +++ b/os2web_key.services.yml @@ -4,5 +4,11 @@ services: arguments: [ 'os2web_key' ] Drupal\os2web_key\KeyHelper: + autowire: true arguments: - - '@logger.channel.os2web_key' + $logger: '@logger.channel.os2web_key' + + Drupal\os2web_key\Services\Psr16CacheAdapter: + autowire: true + arguments: + $cacheBackend: '@cache.default' diff --git a/scripts/code-analysis b/scripts/code-analysis index 5a3c1e2..0435ebd 100755 --- a/scripts/code-analysis +++ b/scripts/code-analysis @@ -11,37 +11,34 @@ drupal_composer() { composer --working-dir="$drupal_dir" --no-interaction "$@" } -# Create new Drupal 9 project +# Create new Drupal 10 project if [ ! -f "$drupal_dir/composer.json" ]; then - composer --no-interaction create-project drupal/recommended-project:^9 "$drupal_dir" + composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir" fi # Copy our code into the modules folder - -# Clean up -rm -fr "${drupal_dir:?}/$module_path" - +mkdir -p "$drupal_dir/$module_path" # https://stackoverflow.com/a/15373763 -# rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path" - -# The rsync command in not available in itkdev/php8.1-fpm - -git config --global --add safe.directory /app -# Copy module files into module path -for f in $(git ls-files); do - mkdir -p "$drupal_dir/$module_path/$(dirname "$f")" - cp "$f" "$drupal_dir/$module_path/$f" -done +rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path" drupal_composer config minimum-stability dev +drupal_composer --append repositories.os2web/os2web_key path "$module_path" + # Allow ALL plugins # https://getcomposer.org/doc/06-config.md#allow-plugins drupal_composer config --no-plugins allow-plugins true +# Making Drupal 10 compatible +drupal_composer require psr/http-message:^1.0 +drupal_composer require mglaman/composer-drupal-lenient +drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]' + drupal_composer require wikimedia/composer-merge-plugin drupal_composer config extra.merge-plugin.include "$module_path/composer.json" # https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 drupal_composer require --dev symfony/phpunit-bridge +drupal_composer --no-interaction install + # Run PHPStan (cd "$drupal_dir/$module_path" && ../../../../vendor/bin/phpstan) diff --git a/scripts/unit-tests b/scripts/unit-tests new file mode 100755 index 0000000..9fca774 --- /dev/null +++ b/scripts/unit-tests @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +script_dir=$(pwd) +module_name=$(basename "$script_dir") +drupal_dir=vendor/drupal-module-unit-tests-analysis +# Relative to $drupal_dir +module_path=web/modules/contrib/$module_name + +cd "$script_dir" || exit + +drupal_composer() { + composer --working-dir="$drupal_dir" --no-interaction "$@" +} + +# Create new Drupal 10 project +if [ ! -f "$drupal_dir/composer.json" ]; then + composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir" +fi +# Copy our code into the modules folder +mkdir -p "$drupal_dir/$module_path" +# https://stackoverflow.com/a/15373763 +rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path" + +drupal_composer config minimum-stability dev + +drupal_composer --append repositories.os2web/os2web_key path "$module_path" + +# Allow ALL plugins +# https://getcomposer.org/doc/06-config.md#allow-plugins +drupal_composer config --no-plugins allow-plugins true + +# Making Drupal 10 compatible +drupal_composer require psr/http-message:^1.0 +drupal_composer require mglaman/composer-drupal-lenient +drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]' + +drupal_composer require wikimedia/composer-merge-plugin +drupal_composer config extra.merge-plugin.include "$module_path/composer.json" +# https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 +drupal_composer require --dev symfony/phpunit-bridge +drupal_composer --no-interaction install + +# Run PHPUnit +(cd "$drupal_dir" && vendor/bin/phpunit --configuration web/core/phpunit.xml.dist "$module_path/tests/src/Unit/KeyHelperUnitTest.php") diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php new file mode 100644 index 0000000..74c7571 --- /dev/null +++ b/src/Exception/Exception.php @@ -0,0 +1,10 @@ +createSslRuntimeException(sprintf('Invalid key type: %s', $type::class), $key); } - $contents = $key->getKeyValue(); - return $this->parseCertificates( - $contents, - $type->getInputFormat(), - $type->getPassphrase(), - $key - ); + return $this->parseCertificates($key->getKeyValue(), $type->getInputFormat(), $type->getPassphrase(), $key); + } /** @@ -85,7 +80,7 @@ public function getOidcValues(KeyInterface $key): array { * Parse certificates. * * @return array{cert: string, pkey: string} - * The certificates. + * The certificates, note that the certificate has no passphrase. */ public function parseCertificates( string $contents, @@ -97,6 +92,7 @@ public function parseCertificates( CertificateKeyType::CERT => NULL, CertificateKeyType::PKEY => NULL, ]; + switch ($format) { case CertificateKeyType::FORMAT_PFX: if (!openssl_pkcs12_read($contents, $certificates, $passphrase)) { @@ -109,30 +105,42 @@ public function parseCertificates( if (FALSE === $certificate) { throw $this->createSslRuntimeException('Error reading certificate', $key); } - if (!@openssl_x509_export($certificate, $certificates['cert'])) { + if (!@openssl_x509_export($certificate, $certificates[CertificateKeyType::CERT])) { throw $this->createSslRuntimeException('Error exporting x509 certificate', $key); } $pkey = @openssl_pkey_get_private($contents, $passphrase); if (FALSE === $pkey) { throw $this->createSslRuntimeException('Error reading private key', $key); } - if (!@openssl_pkey_export($pkey, $certificates['pkey'])) { + if (!@openssl_pkey_export($pkey, $certificates[CertificateKeyType::PKEY])) { throw $this->createSslRuntimeException('Error exporting private key', $key); } break; } if (!isset($certificates[CertificateKeyType::CERT], $certificates[CertificateKeyType::PKEY])) { - throw $this->createRuntimeException("Cannot read certificate parts 'cert' and 'pkey'", $key); + throw $this->createRuntimeException("Cannot read certificate parts CertificateKeyType::CERT and CertificateKeyType::PKEY", $key); } return $certificates; } /** - * Create a passwordless certificate. + * Converts certificates to format. + * + * @param array $certificates + * Output from parseCertificates. + * @param string $format + * Format to convert into. + * @param ?KeyInterface $key + * The key, for debugging purposes. + * + * @return string + * The converted certificate. + * + * @see self::parseCertificates() */ - public function createPasswordlessCertificate(array $certificates, string $format, ?KeyInterface $key): string { + public function convertCertificates(array $certificates, string $format, ?KeyInterface $key): string { $cert = $certificates[CertificateKeyType::CERT] ?? NULL; if (!isset($cert)) { throw $this->createRuntimeException('Certificate part "cert" not found', $key); @@ -191,7 +199,14 @@ public function createRuntimeException(string $message, ?KeyInterface $key, ?str * Create an SSL runtime exception. */ public function createSslRuntimeException(string $message, ?KeyInterface $key): RuntimeException { - return $this->createRuntimeException($message, $key, openssl_error_string() ?: NULL); + // @see https://www.php.net/manual/en/function.openssl-error-string.php. + $sslError = NULL; + + while ($errorMessage = openssl_error_string()) { + $sslError = $errorMessage; + } + + return $this->createRuntimeException($message, $key, $sslError); } } diff --git a/src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php b/src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php new file mode 100644 index 0000000..f4fe28a --- /dev/null +++ b/src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php @@ -0,0 +1,211 @@ +get('logger.channel.os2web_key'); + + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $logger + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + self::TENANT_ID => '', + self::APPLICATION_ID => '', + self::CLIENT_SECRET => '', + self::NAME => '', + self::SECRET => '', + self::VERSION => '', + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $configuration = $this->getConfiguration(); + + $settings = [ + self::TENANT_ID => ['title' => $this->t('Tenant id')], + self::APPLICATION_ID => ['title' => $this->t('Application id')], + self::CLIENT_SECRET => ['title' => $this->t('Client secret')], + self::NAME => ['title' => $this->t('Name')], + self::SECRET => ['title' => $this->t('Secret')], + self::VERSION => ['title' => $this->t('Version')], + ]; + + foreach ($settings as $key => $info) { + $form[$key] = [ + '#type' => 'textfield', + '#title' => $info['title'], + '#default_value' => $configuration[$key] ?? NULL, + '#required' => TRUE, + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { + try { + $this->getCertificate([ + self::TENANT_ID => $form_state->getValue(self::TENANT_ID), + self::APPLICATION_ID => $form_state->getValue(self::APPLICATION_ID), + self::CLIENT_SECRET => $form_state->getValue(self::CLIENT_SECRET), + self::NAME => $form_state->getValue(self::NAME), + self::SECRET => $form_state->getValue(self::SECRET), + self::VERSION => $form_state->getValue(self::VERSION), + ]); + } + catch (\Throwable $throwable) { + $form_state->setError($form, $this->t('Error getting certificate: %message', ['%message' => $throwable->getMessage()])); + } + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + $this->setConfiguration($form_state->getValues()); + } + + /** + * {@inheritdoc} + */ + public function getKeyValue(KeyInterface $key): ?string { + try { + return $this->getCertificate(); + } + catch (\Throwable $throwable) { + } + + return NULL; + } + + /** + * Get certificate. + * + * @return string + * The certificate. + * + * @throws \ItkDev\AzureKeyVault\Exception\TokenException + * @throws \ItkDev\Serviceplatformen\Certificate\Exception\AzureKeyVaultCertificateLocatorException + */ + private function getCertificate(?array $configuration = NULL): string { + try { + $httpClient = new GuzzleAdapter(new Client()); + $requestFactory = new RequestFactory(); + + $vaultToken = new VaultToken($httpClient, $requestFactory); + + $options = $configuration ?? $this->getConfiguration(); + + $token = $vaultToken->getToken( + $options[self::TENANT_ID], + $options[self::APPLICATION_ID], + $options[self::CLIENT_SECRET], + ); + + $vault = new VaultSecret( + $httpClient, + $requestFactory, + $options[self::NAME], + $token->getAccessToken() + ); + + $locator = new AzureKeyVaultCertificateLocator( + $vault, + $options[self::SECRET], + $options[self::VERSION], + ); + + return $locator->getCertificate(); + } + catch (\Exception $exception) { + // Log the exception and re-throw it. + $this->logger->error('Error getting certificate: @message', [ + '@message' => $exception->getMessage(), + 'throwable' => $exception, + ]); + throw $exception; + } + } + +} diff --git a/src/Plugin/KeyProvider/VaultKeyProvider.php b/src/Plugin/KeyProvider/VaultKeyProvider.php new file mode 100644 index 0000000..c71c919 --- /dev/null +++ b/src/Plugin/KeyProvider/VaultKeyProvider.php @@ -0,0 +1,261 @@ +setLogger($logger); + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('logger.channel.default'), + $container->get('http_client'), + $container->get(Psr16CacheAdapter::class), + $container->get(KeyHelper::class), + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + 'vault_path' => 'secret', + 'vault_secret' => '', + 'vault_key' => '', + 'vault_version' => NULL, + 'vault_cache_duration' => 60 * 60, + ]; + } + + /** + * {@inheritdoc} + */ + public function getKeyValue(KeyInterface $key) { + $roleId = Settings::get('os2web_vault_role_id'); + $secretId = Settings::get('os2web_vault_secret_id'); + + $vault = $this->getVault(); + + try { + $token = $vault->login( + $roleId, + $secretId, + ); + } + catch (\DateMalformedStringException | \DateMalformedIntervalStringException | UnknownErrorException | VaultException | InvalidArgumentException $e) { + // Log the exception and re-throw it. + $this->logger->error('Error fetching login token for key (@key_id): @message', [ + '@key_id' => $key->id(), + '@message' => $e->getMessage(), + 'throwable' => $e, + ]); + + throw $e; + } + + $config = $this->configuration; + + $vaultPath = $config['vault_path']; + $vaultSecret = $config['vault_secret']; + $vaultKey = $config['vault_key']; + $vaultVersion = $config['vault_version']; + $vaultCacheDuration = $config['vault_cache_duration'] ?? 0; + $vaultUseCache = $vaultCacheDuration !== 0; + + try { + $secret = $vault->getSecret( + token: $token, + path: $vaultPath, + secret: $vaultSecret, + key: $vaultKey, + version: $vaultVersion, + useCache: $vaultUseCache, + expire: $vaultCacheDuration + ); + } + catch (\DateMalformedStringException | UnknownErrorException | VaultException | InvalidArgumentException $e) { + // Log the exception and re-throw it. + $this->logger->error('Error getting certificate for key (@key_id): @message', [ + '@key_id' => $key->id(), + '@message' => $e->getMessage(), + 'throwable' => $e, + ]); + + throw $e; + } + + $type = $key->getKeyType(); + if (!($type instanceof CertificateKeyType)) { + throw $this->keyHelper->createSslRuntimeException(sprintf('Invalid key type: %s', $type::class), $key); + } + + return $secret->value; + } + + /** + * {@inheritDoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $providerConfig = $this->getConfiguration(); + + $form['vault_path'] = [ + '#type' => 'textfield', + '#title' => $this->t('Vault path'), + '#description' => $this->t('The Vault path'), + '#required' => TRUE, + '#default_value' => $providerConfig['vault_path'] ?? $this->defaultConfiguration()['vault_path'], + ]; + + $form['vault_secret'] = [ + '#type' => 'textfield', + '#title' => $this->t('Vault secret'), + '#description' => $this->t('The vault secret that should be used'), + '#required' => TRUE, + '#default_value' => $providerConfig['vault_secret'] ?? $this->defaultConfiguration()['vault_secret'], + ]; + + $form['vault_key'] = [ + '#type' => 'textfield', + '#title' => $this->t('Vault key'), + '#description' => $this->t('The key that should be fetched'), + '#required' => TRUE, + '#default_value' => $providerConfig['vault_key'] ?? $this->defaultConfiguration()['vault_key'], + ]; + + $form['vault_version'] = [ + '#type' => 'number', + '#title' => $this->t('Vault version'), + '#description' => $this->t('The version of key that should be fetched'), + '#default_value' => $providerConfig['vault_version'] ?? $this->defaultConfiguration()['vault_version'], + '#required' => FALSE, + ]; + + $form['vault_cache_duration'] = [ + '#type' => 'number', + '#title' => $this->t('Vault cache expiration duration'), + '#description' => $this->t('The amount of seconds version of key that should be fetched. Set to 0 to disable caching.'), + '#default_value' => $providerConfig['vault_cache_duration'] ?? $this->defaultConfiguration()['vault_cache_duration'], + '#required' => FALSE, + '#min' => 0, + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { + if (empty($form_state->getValue('vault_version'))) { + $form_state->setValue('vault_version', NULL); + } + + $vaultCacheDuration = $form_state->getValue('vault_cache_duration'); + if (empty($vaultCacheDuration) || $vaultCacheDuration < 0) { + $form_state->setValue('vault_cache_duration', 0); + } + + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + $this->setConfiguration($form_state->getValues()); + } + + /** + * Helper function to create vault client. + * + * @return \ItkDev\Vault\Vault + * The client + */ + private function getVault(): VaultClient { + static $vaultClient = NULL; + + $httpFactory = new HttpFactory(); + + if (is_null($vaultClient)) { + $vaultClient = new VaultClient( + httpClient: $this->httpClient, + requestFactory: $httpFactory, + streamFactory: $httpFactory, + cache: $this->cache, + vaultUrl: Settings::get('os2web_vault_url'), + ); + } + + return $vaultClient; + } + +} diff --git a/src/Plugin/KeyType/CertificateKeyType.php b/src/Plugin/KeyType/CertificateKeyType.php index 4373fc4..865c1c6 100644 --- a/src/Plugin/KeyType/CertificateKeyType.php +++ b/src/Plugin/KeyType/CertificateKeyType.php @@ -143,7 +143,7 @@ public function validateKeyValue(array $form, FormStateInterface $form_state, $k } try { - $this->certificateHelper->createPasswordlessCertificate($certificates, $outputFormat, NULL); + $this->certificateHelper->convertCertificates($certificates, $outputFormat, NULL); } catch (RuntimeException $exception) { $form_state->setError($form, $this->t('Error creating passwordless certificate: @message', ['@message' => $exception->getMessage()])); diff --git a/src/Services/Psr16CacheAdapter.php b/src/Services/Psr16CacheAdapter.php new file mode 100644 index 0000000..64c4c6b --- /dev/null +++ b/src/Services/Psr16CacheAdapter.php @@ -0,0 +1,105 @@ +cacheBackend->get($key); + return $cache ? $cache->data : $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = NULL): bool { + try { + $this->cacheBackend->set($key, $value, $ttl); + return TRUE; + } + catch (\Exception $exception) { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function delete($key): bool { + try { + $this->cacheBackend->delete($key); + return TRUE; + } + catch (\Exception $exception) { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function clear(): bool { + try { + $this->cacheBackend->deleteAll(); + return TRUE; + } + catch (\Exception $exception) { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = NULL): iterable { + // Not implemented for simplicity. + throw new \Exception('Method not implemented.'); + } + + /** + * {@inheritdoc} + * + * @param iterable $values + * A list of key => value pairs for a multiple-set operation. + * @param \DateInterval|int|null $ttl + * Optional. The TTL value of this item. + */ + public function setMultiple($values, $ttl = NULL): bool { + // Not implemented for simplicity. + throw new \Exception('Method not implemented.'); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys): bool { + // Not implemented for simplicity. + throw new \Exception('Method not implemented.'); + } + + /** + * {@inheritdoc} + */ + public function has($key): bool { + return $this->cacheBackend->get($key) !== FALSE; + } + +} diff --git a/tests/src/Unit/KeyHelperUnitTest.php b/tests/src/Unit/KeyHelperUnitTest.php new file mode 100644 index 0000000..d8a2a1b --- /dev/null +++ b/tests/src/Unit/KeyHelperUnitTest.php @@ -0,0 +1,244 @@ +mockLogger = $this->createMock(LoggerChannelInterface::class); + $this->keyHelper = new KeyHelper($this->mockLogger); + + } + + /** + * Tests creation of KeyHelper. + */ + public function testKeyHelperCreation() { + $this->assertInstanceOf(KeyHelper::class, $this->keyHelper); + } + + /** + * Tests creation of RuntimeException no SSL message. + */ + public function testCreateRuntimeExceptionNoSslMessage() { + $this->mockLogger->expects($this->once())->method('error'); + $message = 'test'; + $result = $this->keyHelper->createRuntimeException($message, NULL); + $this->assertEquals($message, $result->getMessage()); + } + + /** + * Tests creation of RuntimeException no SSL message. + */ + public function testCreateRuntimeExceptionWithSslMessage() { + $this->mockLogger->expects($this->once())->method('error'); + $message = 'test'; + $expected = $message . ' (some SSL error)'; + $result = $this->keyHelper->createRuntimeException($message, NULL, 'some SSL error'); + $this->assertEquals($expected, $result->getMessage()); + } + + /** + * Tests creation of RuntimeException no SSL message. + */ + public function testCreateRuntimeExceptionWithKeyAndSslMessage() { + $mockMessage = 'test'; + $expectedMessage = $mockMessage . ' (some SSL error)'; + $mockKeyId = 'some_key_id'; + $this->mockLogger->expects($this->once())->method('error')->with('@key: @message', [ + '@key' => $mockKeyId, + '@message' => $expectedMessage, + ]); + $mockKey = $this->createMock('Drupal\key\KeyInterface'); + $mockKey->expects($this->once())->method('id')->willReturn($mockKeyId); + $result = $this->keyHelper->createRuntimeException($mockMessage, $mockKey, 'some SSL error'); + $this->assertEquals($expectedMessage, $result->getMessage()); + } + + /** + * Data provider for parse certificates test. + */ + public static function parseCertificatesDataProvider(): \Generator { + yield [ + 'test_without_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + '', + [ + CertificateKeyType::CERT => 'p12_without_passphrase_cert.txt', + CertificateKeyType::PKEY => 'p12_without_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_with_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + 'test', + [ + CertificateKeyType::CERT => 'p12_with_passphrase_cert.txt', + CertificateKeyType::PKEY => 'p12_with_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_without_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + '', + [ + CertificateKeyType::CERT => 'pem_without_passphrase_cert.txt', + CertificateKeyType::PKEY => 'pem_without_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + 'test', + [ + CertificateKeyType::CERT => 'pem_with_passphrase_cert.txt', + CertificateKeyType::PKEY => 'pem_with_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + 'wrong_passphrase', + // @command openssl pkey -in test_with_passphrase.pem -passin pass:wrong_passphrase + new RuntimeException('Error reading private key (error:11800074:PKCS12 routines::pkcs12 cipherfinal error)'), + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PFX, + 'some_non_important_passphrase', + // @command openssl pkcs12 -in test_with_passphrase.pem -passin pass:some_non_important_passphrase + new RuntimeException('Error reading certificate (error:0688010A:asn1 encoding routines::nested asn1 error)'), + ]; + } + + /** + * Tests parse certificates. + * + * @dataProvider parseCertificatesDataProvider + */ + public function testParseCertificates(string $certificate, string $format, string $passphrase, array|RuntimeException $expected) { + + if ($expected instanceof RuntimeException) { + $this->expectException($expected::class); + $this->expectExceptionMessage($expected->getMessage()); + } + + $certificates = $this->keyHelper->parseCertificates(file_get_contents(__DIR__ . '/certificates/' . $certificate), $format, $passphrase, NULL); + + // Assert certificate. + $this->assertEquals(file_get_contents(__DIR__ . '/certificates/' . $expected[CertificateKeyType::CERT]), $certificates[CertificateKeyType::CERT]); + // Assert private key. + $this->assertEquals(file_get_contents(__DIR__ . '/certificates/' . $expected[CertificateKeyType::PKEY]), $certificates[CertificateKeyType::PKEY]); + } + + /** + * Data provider for parse certificates test. + */ + public static function parsedAndConvertedCertificatesAreEqualProvider(): \Generator { + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PEM, + 'test', + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PFX, + 'test', + ]; + + yield [ + 'test_without_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PEM, + '', + ]; + + yield [ + 'test_without_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PFX, + '', + ]; + + yield [ + 'test_with_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PFX, + 'test', + ]; + + yield [ + 'test_with_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PEM, + 'test', + ]; + + yield [ + 'test_without_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PFX, + '', + ]; + + yield [ + 'test_without_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PEM, + '', + ]; + } + + /** + * Tests conversion of certificates. + * + * @dataProvider parsedAndConvertedCertificatesAreEqualProvider + */ + public function testParsedAndConvertedCertificatesAreEqual(string $certificate, string $inputFormat, string $outputFormat, string $passphrase) { + $certificates = $this->keyHelper->parseCertificates(file_get_contents(__DIR__ . '/certificates/' . $certificate), $inputFormat, $passphrase, NULL); + $converted = $this->keyHelper->convertCertificates($certificates, $outputFormat, NULL); + + // Test that converted (now passwordless) certificate yields same result as + // original parsed certificate. + $convertedParsedCertificates = $this->keyHelper->parseCertificates($converted, $outputFormat, '', NULL); + + $this->assertEquals($certificates, $convertedParsedCertificates); + } + +} diff --git a/tests/src/Unit/certificates/p12_with_passphrase_cert.txt b/tests/src/Unit/certificates/p12_with_passphrase_cert.txt new file mode 100644 index 0000000..807c385 --- /dev/null +++ b/tests/src/Unit/certificates/p12_with_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUKvQXv3tnDrA/aa2skX93GLIuY0UwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzNDM5WhcNMjYw +MTE0MDkzNDM5WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAOft02M4VwGemD5YbWldJbirf1vXTfkrJPTLkT4y +udUFjrksQ7Ar24JdW0jfOAxcZS1J5AYK+JdCpYqOJJvEUNO5nN14qiBiMde06le4 +61mAaM56U6ZrMLE2KwJeYORoMU4ZIW9+x5IeOk/rxZagk3Og1ZJlkV5s5ZGQRS+/ ++JQTcQKZ5ts46QHJLjpSEM1WYp24EaPjFUkNwpLAgU+YhSiQcVvVPa0Doawct9uO +QdKlym1vo2wsGevTnqbbaEZfQonjIkxnG2kpNkUXs7dw+Sc5zgJJc/E21fJMB6hc +UBBGcGC0yWDA3bbp77adE0nZAQUmk+Of4RmZCjriFUVsrKLEebxrBLzdEGF2KGjT +fmyF0ZmxVZlQRhLAx1ofRWTvrs7cySxGGpvqJjjASqdP11FvWy7pCt+TZ7t+FaMH +NFxsYmtHpWUek+saKr55HPJ5xzeuc99RCR8+FFxJ41XnFbjW0OaDcA7s7LS55uv+ +cOJoJ3QyfgZhS+kZ6OC++FBEnT+UTukrDkd73LYjF5R/tCXgL8ixBps5pNO4sJiJ +MpkCqwxKBuUmicxBvNsRar7fWJBdGQ2B1LdsBxPH42rRyKk82SZcpEr1iRmpCeeU +yZFfz1aDkb29idWGr70qBB3KSJ8eP+tu6q2Qn0J9s5nHrqKjMOnScFWSen0Clu2H +y/a3AgMBAAGjUzBRMB0GA1UdDgQWBBRbrAyINQ3uSX8Yz7jhlR6EPvPQhTAfBgNV +HSMEGDAWgBRbrAyINQ3uSX8Yz7jhlR6EPvPQhTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCUNDbywMGcSedFF7jM/1d2A10ZfKCNAPYCSPoNK8Pd +OstRM+7n7p4zRuT+2p8YjpTwxPpj/2o346lZ86WgAjwBgG2JQsu4IHyE4Hy6odYG +bbMGD5rQmRKF0haksIAAVEY9XjPtxnn8/0jOiMC1D1aLTNH6B+iG5XwPpTynd0bZ +Z4zR8AvbpjxViDifGeYbTmqYFqLqxJXD0eQAGIin5DkfXPVoaOaMoDLlJZs317px +xk3RfcQgII4i98mFauIIu0a40f358zT96M46s1kOAuVgXRtk8MbmdsLzRrMRingY +iKOegOlc6ImR3IRX7RByxBBF0J77OCX1aAniBJaKcjOgrzbhzrEz8bXTEThKqjhq +w/OM0I92GvG3L/CU7DXecRXmvuD014RO/IeCP5TgMjWxJH2teax8SoCKP2LY3sJw +P5NtcBbOtABGFFTNFhq7usjz/SkoqDoF2WNJvgGTQKWJBStGTVX7+8MLypE2wGRd +YONzzEy6YaxlPbcUXPlfCHLeXmKgO8oOP0vc81yFuWW/BpVnyWDlCWhZpzOT1Zy8 +SebM8atRPwy8TvyPLBOcd96521fvE+nklG0mFfAftwMGPVus9314vSsuchO0E3ww +BUB3WgLv+F0kus6TRokntQaKuu1SzJsAbEPCs6SfjPKXg6LQdXRSEb4RaYfo0VDJ +3w== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/p12_with_passphrase_pkey.txt b/tests/src/Unit/certificates/p12_with_passphrase_pkey.txt new file mode 100644 index 0000000..b5585ac --- /dev/null +++ b/tests/src/Unit/certificates/p12_with_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDn7dNjOFcBnpg+ +WG1pXSW4q39b1035KyT0y5E+MrnVBY65LEOwK9uCXVtI3zgMXGUtSeQGCviXQqWK +jiSbxFDTuZzdeKogYjHXtOpXuOtZgGjOelOmazCxNisCXmDkaDFOGSFvfseSHjpP +68WWoJNzoNWSZZFebOWRkEUvv/iUE3ECmebbOOkByS46UhDNVmKduBGj4xVJDcKS +wIFPmIUokHFb1T2tA6GsHLfbjkHSpcptb6NsLBnr056m22hGX0KJ4yJMZxtpKTZF +F7O3cPknOc4CSXPxNtXyTAeoXFAQRnBgtMlgwN226e+2nRNJ2QEFJpPjn+EZmQo6 +4hVFbKyixHm8awS83RBhdiho035shdGZsVWZUEYSwMdaH0Vk767O3MksRhqb6iY4 +wEqnT9dRb1su6Qrfk2e7fhWjBzRcbGJrR6VlHpPrGiq+eRzyecc3rnPfUQkfPhRc +SeNV5xW41tDmg3AO7Oy0uebr/nDiaCd0Mn4GYUvpGejgvvhQRJ0/lE7pKw5He9y2 +IxeUf7Ql4C/IsQabOaTTuLCYiTKZAqsMSgblJonMQbzbEWq+31iQXRkNgdS3bAcT +x+Nq0cipPNkmXKRK9YkZqQnnlMmRX89Wg5G9vYnVhq+9KgQdykifHj/rbuqtkJ9C +fbOZx66iozDp0nBVknp9Apbth8v2twIDAQABAoICABQzTcNceSnuC/+RVYVx55YU +Jdi0QeY7+kRYZXM5f5/Zeir2e0/6uewHjcRYLaClL+phmOE0qNGjErNxmigRBLzN +wI2cc8dNr8URMybNBEtvpXFA2UyE4iEezfpiVjKx2oPqKQd16OCSqAYlZTiV2tHq +EvMxzBc7CXgdgARexlu+h78v8802dNQpfbb2eXn3YxZ7akBQFOufrbASrhCZmnt3 +SgOAHEn99Ef/DL2sQCwEy0LiqguDADnFXRIJlfIownn4O2WhLPno7RrYEiVqyxPX +pot8P1A/Z1bRUj3S1uX1l9ZS2LlUtkFQuQa6qDi5FF9dxhxZmAVro9opR9+bzaG8 +xatoo0DELirbiZCZSMDGvI+nR9pDPdUQ2+qcKLSHgTnVz/VR+fMi8a/+Ch6HH6JU +dHG7Xc2OqtOr6gTrqmSD3PYE3S93ydKO5l8SoM35bhz2SfLUMtClWF4QFweF+SU/ +dbo0RLtZmhA+C1KenkVgCW0RZd+NUd053x1igN6wlZY8CsgOH9yIer3hW5DKmREZ +1Fl/OrsZFwJpNOm7BhpJ9YL1GmoltXZeascz5l6anKAQspCeC1jd21JsaRoDH6+U +7lbmMqLUmO+QmKFqN1NwlBpFLlHPFYN7eeR5vdaKsJMux0ks5IZ5hoiMVCYtCuW5 +qN25P7Bq8/O3yiCIkG/FAoIBAQD7MOQ1k0D/n/2HsLQj+UX7CDpA2kpSValUxdoP +zy8izXrc8ekgb4q4BROYVuF6wvz1/m2ck1LKM+DNBMd4QSTl5qe7TyHez2CsGY4Q +crA4nrzZYS6uqpl5zCq1wwa9WILq5D6dWLN1WpI+0uBFSPlwPrHnhbG5ISVbUt1i +qzK5+WyEw8wx0miDnsjFZafGkq94dqTUYscpuph/nnFFgeuI/EphY/hwBxS5xNRM +yz4qSyCDYNFuORN3TxEsM5epAFC7v3Oi064hNtjzQ4zud7evQvUgtNYz/DX37SKA +zZvFs7cDZq6NH+MrMqkiXPIJr1xyBDpGzOcdNkD6xsQXGY2lAoIBAQDsXoeaJd+d +msVB05uap30GKKdshXJmb7tlfqELCfyVA60DWAVhUpgHXYTEeSB95BxkNoEcaQNy +lAsvP2Zg+IKIMp8EltRoNmwtuLsl2cjiQlgld9FzysMzub7PAPR70Ddse+GdVPJs +/9rMivMr3Ghlbw2g3LTLaIcqexViZe9U6G7T0Dq50BMtEfR7xduQ1OkcJ9asieQn +JEY4FUqFzxIoVPO1OSo/onYzf73iuytu29S6L2/c4FteTmWwyppW7EF12fZntYXX +zrtLDIczPIyc26J0BJdkjn/T6Ka93XUVE4LH2CQfQKiWSnPaBbZeHwKt07Yg6Wfa +vygdq5iHzLwrAoIBAQCaN44khl+lkWhqYkZ2v5gDYy3Am0R+ADGR5lbW1fCugVEp +TSw/ybt090jsgoq0f/omXSqWsqxlkCw4MvL0Faq9qzrXeOHmlZNYlefumTsvx+VR +4KR0gDDx4jNmnHIK1ciqxa7bk5W6Ov0GrRkb8I2UKCm5Zw+SBZOV2e4pocnm/Adw +C3XWMMQR6u6AosS62Vv5rX3NF0gHipQN5gzNC9K7q4lO+b2RqgdDdmCRNUbZWH6B +qnNqJIia+2Qjqxk1nQfz/LrWAPgrVC0mTRa9YarYY1dz2RKZoALSUDias6ErZAa8 +g9IuuqER6GyGuGGxKIStS4Mr7KkH+kMys824EOwxAoIBAQDhP/xNo8YDtVhBsNxa +dz4nvlo+irVIdTXKNPBXSr8Pap9XzmviIQmMC0zYxu11ywpbXa2QW2rTp804y1xP +EMOj80mi3/GvFh3lPGPsYCyVoc/KdPJ/PDzlcB8ruadCzlvi3/NTMX6M7Q2fWtL9 +e19pOnlsUKdiKEyW6RCp4qY+7fmflJJCPM3pZ3Dq+tSQorU7BaQIbKelvWXFUFTi +S0Dsenh8vOtMFRA7Z4KXLPLpBEkm3MnnXU71AhTac1i9eQuwuXOdxVi1HedST7AP +3aXKXWvkz/NN+pZKZS1xRjerTBqXQ5y4w69k+bKJlGQEMd0ltdmcZ5F23i/mhn4m ++65dAoIBAHRb5qsc3euMoHRK3hSdQa4Rhcw5rUnNX3LfQ+QJGOmUKH61eRt0akx1 +0VygnBmULU8xN8cDDUsx44zsVCowe93pFiXzAOCzPRD2L8QIX+fcHeIDqUDTlPwb +mbhgzyQcUpqGCLGAU/VQphs7UxfLM4EfHbI3X7FV8k4ELJnz7X5UqmYoxblbLYSU +lzb6uj5jvu3xZg7qFAgOJculqaa7se0Qssq/0hvOwSVEJU1TEj+3oM/lIjSwYgA0 +uv5FkIZjlt1mO9fiHbbpS8f0lwmNI3qtco1+T8GWTEjiwDsleape0ZrntwMkx9O2 +7nZr/JN7tErXucFV+4c34sUTvgHOLsM= +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/p12_without_passphrase_cert.txt b/tests/src/Unit/certificates/p12_without_passphrase_cert.txt new file mode 100644 index 0000000..893f4c2 --- /dev/null +++ b/tests/src/Unit/certificates/p12_without_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUHr62vcO7axcfps7XoWV2DcL5l/YwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzNjE3WhcNMjYw +MTE0MDkzNjE3WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAMQL/GhI14ngaVWCT3RhUdzcxrw2q9fJRTcEwSX1 +AUjXukJaRTUmJp4qm8bSfUu5wzEb+Dqbe44d3yUykmpMk+ZVuylrWoREpJ/dyVgK +EthGRNU7svLRd8z1sCF4HQcLqfqnXqQAKDoyhugF3R/ED+n5S7agmMj2bL/4tncN +62mUThcsNGiJ/pkbchEtWyBb7xcxmtfoGOPTPUOuhi0lfxoux+fNy+m3g5/7DTky +xJ+M/9HUbCG1CMNNS3+w3Aeyb/CdvWOEI4pVxQxcd30t6s0mP27wDdwSB0PdAlcs +gdUL3GucnMjDDKtJUjCfDHcsVylzvMZuyjGFKFg90HBYsjSXBGmk8bb8LvZGuDjI +s2u3frz7kit/1/WBs+Na5qkl11uvzfbuFM/tYSSgi43vjELJIDd60YvVnEkV88O2 +MU0wQH7sQMAasVKV388eD+SsmHTx6yvHUL1mKXQ021ZJyo6toouDaFWCXX3gK/kx +gWOmvrytWupXw/v5JT07SWAEk14OaejFMsE5ydbgjRdeT3vAmCkrVv1ZA1dlOkQq +5tMYmAastIw7JUwQz4ACQtCSbrOOnFgbDt+sKmLAOKqmVFIhULOrZxqVmVT8OuUj +/KatvoXh6iYtSApJE7NgEaH8+KTUTscsqo9oIcQ/FvPwGZDJBq0eQH+V2eKcWMrx +jvllAgMBAAGjUzBRMB0GA1UdDgQWBBTy4BOkfWFkOZ5wjZTmsVjyLM+zmjAfBgNV +HSMEGDAWgBTy4BOkfWFkOZ5wjZTmsVjyLM+zmjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQAt21NBll90kV9Ithzg7GwD0AoxFZR4MpUVmk35QD26 +zR0rh/XjUTIcdhN5xn7+0tZ/k6GyrgiVTR61vgxDVM0AOKhiSLfnCW5fBG/yUX80 +L4rj1pHnhL04+/c9sUO0kbG+JJAN60DMwptkh6SS4R+PsD8b8u5ehEFfBedm6A3r +gqqF57190FoHyzkikG15+77HmXOO1kHU6NCZUEgFinvszYbjQTPK47pF5wiP9RG7 +eI3XDUGBnyu0+7Q+TBi4pq+NLpgTWSxRQ3ouj+JjNThQkTcdZaYgeQrN1xZ4Bmin +WGPO2zLNLdFD3Me34S/nzPzHnaB0s6bJCncym1v/zcktY7J52W5agwvpdTvbBZNk +CHV9peGcAppU5hlOdLbtI99fkaN/iPMKjc86PKAiU5W5vf9BgRNIxQPzxJ3R63Yg +e6WJKCH3iCuvmzGeh5NkVG7+DJIIbQnoX6W+gD+xAIGdSh8sJ0rdaSVdhVEP4bT3 +seyDi4uJs6cLF4kodE1vQ6W5NMFtxFjrqVYvLaulWunEVLV/NsJiSdLSdMSs0+4P +u7E2zKKGQBDVnA7EkUaHbrDkeUrb4gTZIskcuhXrqD2DHz2vqfXegSsxi/Zmucnh +XYDVX9kPwCQ/KTsm3Ib+vF6zAtPwF13AoyIVoGqjrEXGwzmkuh5/f5mUZkMAgf5o +2w== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/p12_without_passphrase_pkey.txt b/tests/src/Unit/certificates/p12_without_passphrase_pkey.txt new file mode 100644 index 0000000..fe0f81f --- /dev/null +++ b/tests/src/Unit/certificates/p12_without_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +/cmQTxlq7+EoWysYrHsg9mvxBo6q81o= +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/pem_with_passphrase_cert.txt b/tests/src/Unit/certificates/pem_with_passphrase_cert.txt new file mode 100644 index 0000000..d2f437d --- /dev/null +++ b/tests/src/Unit/certificates/pem_with_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUOxTqD8szgQSYH8WVixrU3HYc3RAwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODA3WhcNMjYw +MTE0MDkzODA3WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANxXnV+sXqdkjm8GSucnrIhRerEk4eMjbJ3hg01R +tfcnZBzpj6i0RmVCAX4Ij/U+LKqLL2chblKKvG7vFwKSKGVCzpZAOuOxZaUtqjry +XDshdhE+ToegZbt+rxLsuFtED4A6zq5ZGuntadbbh57CuMpPpjAmcDWaZ6e9VF0g +ihVlfv4Psvx1Bi5zBjfKa6IKLfO3jU5g7i37ri6w3dWzmx19Dyb3u8LiMtDua2ph +VcfX3bwUA+UB+Ct0h3xe+FxMo+KxiXutuqMg/yWczJS5mKH4u8hSex64VhGP/Jxh +vsuZ1pmDBMKhGEK7jDdDWdCyvqY8pyPXfMXp9kFejjgw75Cv/83yknmeoI9OHNtc +77wsrAU5Xk6SnqaaJviiSHZIa4xbtRBekqz5T63ZT4D5wfSZcNhRhBDL5jbTZ4t0 +Fldwapf2kqHe4Y8YgzBNMJu04CCpFdzcpjeMHhmZe55nPOaZFj5fSgrSol6kwoMr +m3q8WaFOjeBIGOuiylvJj24YLYBvKH7+M34mgqfDJhlxpuyNpESu10p8+62PgXul +BLfHcekb84JjljHFcdc6331JFkBqofT6x5osWK2CcuJGRP8h6TaRIe8jy7uKbX+e +jlzT1+0rEd69Uy8e8UseZwEZ2eMZg8sW/hd8eEQfSkSDZtTAs7GIjx3ZkyUUzix2 +zU5PAgMBAAGjUzBRMB0GA1UdDgQWBBTfX/93AkTP0qCJtjM/wecomdjlVTAfBgNV +HSMEGDAWgBTfX/93AkTP0qCJtjM/wecomdjlVTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQBNFBjyk0M+PJwVmkEHm4Y34ShCVkicCV3gD96SvzDK +Oav/XZePruiCX9dGplxG9OnKmO+VCAwJNVyxkT1+sf2M7ir8amVXMoRu0y31m9pA +wQ7/2MD6qi8ae5FWz0J83SvmhoPWT2tXiRVfB4+gOGSRfHdKscbF+MMhsCMozTmP +rjjS4nI8rZUC9Hhr475Demk6zCqUqTz1+RnrtlNvK/dYSOgbe0G178Y/uEMJCqO5 +4i4OnDVdAuttixwfLvoCVQpCodec6xfa77U6v+/HvFLJDHBnhzeNQrNcsBVIBlog +FtPnNw05rH4eUJP4glmlM3OOkpVsBrkjVCHDxo2Up3fb7qbg6CYk/WQi/u01ueJ5 +fZAQXPAznKsCo7h7Gh0w5BhWb5GendOJw3nQOEU1+dE4Z20zjkOxGEGn5M4umf3q +JbJhOeOx+74fnzMNQokgIuESbAKfnd6VZbJzh4Sj3iEA20HN/vq+OQCLw/JQg6iM +Ki4Ry22oa6vWCbF4xtkVnmsYk8D4XqRQdDG8NFkFcsFOBkRjuK+LxLAnpsYCGo68 +0uzEU6JQila0Jav691oE4R3bHiAYLa97pr+VqCTh45/FkPcjX8o8vWkTgUhcmRXp +g6s+9WrysM7UN/yb4MTw+2FV4DMDFN+Jsl8dZpW7SF7/yXTtkcmTt294HWmfmAS3 +mg== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/pem_with_passphrase_pkey.txt b/tests/src/Unit/certificates/pem_with_passphrase_pkey.txt new file mode 100644 index 0000000..3186899 --- /dev/null +++ b/tests/src/Unit/certificates/pem_with_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +yeVmJ3Z5MregjUwQwbz4eRSpyhUuqg== +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/pem_without_passphrase_cert.txt b/tests/src/Unit/certificates/pem_without_passphrase_cert.txt new file mode 100644 index 0000000..8925696 --- /dev/null +++ b/tests/src/Unit/certificates/pem_without_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUKG+LSVZOsm0JLoxNlXf7d+vYEtQwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODU0WhcNMjYw +MTE0MDkzODU0WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAKr72psmqBTBhA6GlInwoGHpBolOGz+X+wMD/zsf +nsdH66MdyStEF5LXu8Va/MrsG18Nz4lIehNeFIGAz5w2E30cRyIB9xkqI674dDSa +mFz+ENGKKeVVMzMoahKue44z77aQXMJRDnuDgLw3G96L+t/+0WTmx2E+ym6TxJSq +JtFk2Gssi968wwO+3ifUzCOPcTdhmfrKyB09aFsl3xnpVEzkIieWHgbkWvr9odWJ +VDoFo2ti7UM6NMoKzaCK2NGSJKcFD1clpffVwz1o/OMKcaBB+WbzNbXLghEl96gE +4ZPDbI1KZLDS1dFhpfYTEvg3izJGbOCrjcFM/G1ZzfoC5S8X9U1OYbYKAuQTIC+E +JUPhBTOD+g7hKzyHojAyRP2ox8F2Z4RygNadaMHxmHJnOBPKf3CbE9HvUS0jpOEM +Dyu5x5sxVWsCaSt227XJBhWO0egXhN8d4iBHrLiJrfDO5mX6dep6U2Ts47/Ish7g +S3vGjm5v9PrMM37wq5runDH3kGOkLRVGbjdAu6eMp3NKaNxRxe1AxY5LBSq7lEqr +3Wiq8x3wzCJSYK0gJsaKoHCfM2NmMlV74P7oiBk9c7ruAq1nKLRv9t6ebtvQPcpP +2UjjZ6wnw/+sPvz2wNl52sBZ+GYr24Oey3WNLkrm48lv50DBLBTL2KuYv1KM62Ie +6K0HAgMBAAGjUzBRMB0GA1UdDgQWBBQSB8SGPGoqbKogypKN/y/xytuXJzAfBgNV +HSMEGDAWgBQSB8SGPGoqbKogypKN/y/xytuXJzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCE956GUHzpGasQfXp1n893qbRHXL3JuAA7KFvQUAGy +I5a8GQV6+COCWHE0YzPQjrWFpi/MZV5KsQV5WwWZL5CIirw5k2jk5og1VcQMdHpB +QQzIHe5/efUBFKM4u7etRwQqaVlsqRPTe+ChRMMH7Kj+V5C4d2Me0A7LI/fb5OhO +tSF9Gt/tDuFcNfYQtVIzia4vzbEfiktDF6/2uaz1RTBIcc6tAFI+0TCaBp5q2zet +BEjbb9rg8257Lqvfsd2SFZCr/PkbnFN1ZO/fZIfQWaaH8FqB2vFraQsX2t/Uy4eK +OqCudeJW3yHXRBAnwhz6OmDjIlX8BtUIyERovD4GqUcr61DvNb7Q99WZ0SL+kS8x +f+2sennQ9uUcEDXMmXmLwVsk7YVKYB177fCxRFOm6EusVscTKkbiNQMMntTVtUav +idCVk4hEG1zkf7UhEqKPQwOFvhGZCtqFMn38yovdNgld6vDJyBarbddHHF4MdWZU +tl3ywwq6kpdGrrrqt4truac6tbMIa2XILylXLTo9thW8y81E+58G1/OU2+fvADXw +z8qoDiBuLsyghW96SUtrlHz9P22bd6SjopwU2HOVNe2yB06wVmaB7MpXBbn0mQR9 +28lP38ecXys3wOucDxwAIZrx+CpMXM7WuTtENC75xmDSJM5TiKeM4QQcPJkFhUWd +bA== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/pem_without_passphrase_pkey.txt b/tests/src/Unit/certificates/pem_without_passphrase_pkey.txt new file mode 100644 index 0000000..832c20e --- /dev/null +++ b/tests/src/Unit/certificates/pem_without_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +1sy9cBx0ZARHTD27VxkpdyZOr8AAJw== +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/test_with_passphrase.p12 b/tests/src/Unit/certificates/test_with_passphrase.p12 new file mode 100644 index 0000000..adf744c Binary files /dev/null and b/tests/src/Unit/certificates/test_with_passphrase.p12 differ diff --git a/tests/src/Unit/certificates/test_with_passphrase.pem b/tests/src/Unit/certificates/test_with_passphrase.pem new file mode 100644 index 0000000..b386f47 --- /dev/null +++ b/tests/src/Unit/certificates/test_with_passphrase.pem @@ -0,0 +1,84 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUOxTqD8szgQSYH8WVixrU3HYc3RAwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODA3WhcNMjYw +MTE0MDkzODA3WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANxXnV+sXqdkjm8GSucnrIhRerEk4eMjbJ3hg01R +tfcnZBzpj6i0RmVCAX4Ij/U+LKqLL2chblKKvG7vFwKSKGVCzpZAOuOxZaUtqjry +XDshdhE+ToegZbt+rxLsuFtED4A6zq5ZGuntadbbh57CuMpPpjAmcDWaZ6e9VF0g +ihVlfv4Psvx1Bi5zBjfKa6IKLfO3jU5g7i37ri6w3dWzmx19Dyb3u8LiMtDua2ph +VcfX3bwUA+UB+Ct0h3xe+FxMo+KxiXutuqMg/yWczJS5mKH4u8hSex64VhGP/Jxh +vsuZ1pmDBMKhGEK7jDdDWdCyvqY8pyPXfMXp9kFejjgw75Cv/83yknmeoI9OHNtc +77wsrAU5Xk6SnqaaJviiSHZIa4xbtRBekqz5T63ZT4D5wfSZcNhRhBDL5jbTZ4t0 +Fldwapf2kqHe4Y8YgzBNMJu04CCpFdzcpjeMHhmZe55nPOaZFj5fSgrSol6kwoMr +m3q8WaFOjeBIGOuiylvJj24YLYBvKH7+M34mgqfDJhlxpuyNpESu10p8+62PgXul +BLfHcekb84JjljHFcdc6331JFkBqofT6x5osWK2CcuJGRP8h6TaRIe8jy7uKbX+e +jlzT1+0rEd69Uy8e8UseZwEZ2eMZg8sW/hd8eEQfSkSDZtTAs7GIjx3ZkyUUzix2 +zU5PAgMBAAGjUzBRMB0GA1UdDgQWBBTfX/93AkTP0qCJtjM/wecomdjlVTAfBgNV +HSMEGDAWgBTfX/93AkTP0qCJtjM/wecomdjlVTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQBNFBjyk0M+PJwVmkEHm4Y34ShCVkicCV3gD96SvzDK +Oav/XZePruiCX9dGplxG9OnKmO+VCAwJNVyxkT1+sf2M7ir8amVXMoRu0y31m9pA +wQ7/2MD6qi8ae5FWz0J83SvmhoPWT2tXiRVfB4+gOGSRfHdKscbF+MMhsCMozTmP +rjjS4nI8rZUC9Hhr475Demk6zCqUqTz1+RnrtlNvK/dYSOgbe0G178Y/uEMJCqO5 +4i4OnDVdAuttixwfLvoCVQpCodec6xfa77U6v+/HvFLJDHBnhzeNQrNcsBVIBlog +FtPnNw05rH4eUJP4glmlM3OOkpVsBrkjVCHDxo2Up3fb7qbg6CYk/WQi/u01ueJ5 +fZAQXPAznKsCo7h7Gh0w5BhWb5GendOJw3nQOEU1+dE4Z20zjkOxGEGn5M4umf3q +JbJhOeOx+74fnzMNQokgIuESbAKfnd6VZbJzh4Sj3iEA20HN/vq+OQCLw/JQg6iM +Ki4Ry22oa6vWCbF4xtkVnmsYk8D4XqRQdDG8NFkFcsFOBkRjuK+LxLAnpsYCGo68 +0uzEU6JQila0Jav691oE4R3bHiAYLa97pr+VqCTh45/FkPcjX8o8vWkTgUhcmRXp +g6s+9WrysM7UN/yb4MTw+2FV4DMDFN+Jsl8dZpW7SF7/yXTtkcmTt294HWmfmAS3 +mg== +-----END CERTIFICATE----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +OdntOpiFfWNOCIebk2Fgz9YTMisw+H1I +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/test_without_passphrase.p12 b/tests/src/Unit/certificates/test_without_passphrase.p12 new file mode 100644 index 0000000..a2a3a4f Binary files /dev/null and b/tests/src/Unit/certificates/test_without_passphrase.p12 differ diff --git a/tests/src/Unit/certificates/test_without_passphrase.pem b/tests/src/Unit/certificates/test_without_passphrase.pem new file mode 100644 index 0000000..e48af62 --- /dev/null +++ b/tests/src/Unit/certificates/test_without_passphrase.pem @@ -0,0 +1,82 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUKG+LSVZOsm0JLoxNlXf7d+vYEtQwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODU0WhcNMjYw +MTE0MDkzODU0WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAKr72psmqBTBhA6GlInwoGHpBolOGz+X+wMD/zsf +nsdH66MdyStEF5LXu8Va/MrsG18Nz4lIehNeFIGAz5w2E30cRyIB9xkqI674dDSa +mFz+ENGKKeVVMzMoahKue44z77aQXMJRDnuDgLw3G96L+t/+0WTmx2E+ym6TxJSq +JtFk2Gssi968wwO+3ifUzCOPcTdhmfrKyB09aFsl3xnpVEzkIieWHgbkWvr9odWJ +VDoFo2ti7UM6NMoKzaCK2NGSJKcFD1clpffVwz1o/OMKcaBB+WbzNbXLghEl96gE +4ZPDbI1KZLDS1dFhpfYTEvg3izJGbOCrjcFM/G1ZzfoC5S8X9U1OYbYKAuQTIC+E +JUPhBTOD+g7hKzyHojAyRP2ox8F2Z4RygNadaMHxmHJnOBPKf3CbE9HvUS0jpOEM +Dyu5x5sxVWsCaSt227XJBhWO0egXhN8d4iBHrLiJrfDO5mX6dep6U2Ts47/Ish7g +S3vGjm5v9PrMM37wq5runDH3kGOkLRVGbjdAu6eMp3NKaNxRxe1AxY5LBSq7lEqr +3Wiq8x3wzCJSYK0gJsaKoHCfM2NmMlV74P7oiBk9c7ruAq1nKLRv9t6ebtvQPcpP +2UjjZ6wnw/+sPvz2wNl52sBZ+GYr24Oey3WNLkrm48lv50DBLBTL2KuYv1KM62Ie +6K0HAgMBAAGjUzBRMB0GA1UdDgQWBBQSB8SGPGoqbKogypKN/y/xytuXJzAfBgNV +HSMEGDAWgBQSB8SGPGoqbKogypKN/y/xytuXJzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCE956GUHzpGasQfXp1n893qbRHXL3JuAA7KFvQUAGy +I5a8GQV6+COCWHE0YzPQjrWFpi/MZV5KsQV5WwWZL5CIirw5k2jk5og1VcQMdHpB +QQzIHe5/efUBFKM4u7etRwQqaVlsqRPTe+ChRMMH7Kj+V5C4d2Me0A7LI/fb5OhO +tSF9Gt/tDuFcNfYQtVIzia4vzbEfiktDF6/2uaz1RTBIcc6tAFI+0TCaBp5q2zet +BEjbb9rg8257Lqvfsd2SFZCr/PkbnFN1ZO/fZIfQWaaH8FqB2vFraQsX2t/Uy4eK +OqCudeJW3yHXRBAnwhz6OmDjIlX8BtUIyERovD4GqUcr61DvNb7Q99WZ0SL+kS8x +f+2sennQ9uUcEDXMmXmLwVsk7YVKYB177fCxRFOm6EusVscTKkbiNQMMntTVtUav +idCVk4hEG1zkf7UhEqKPQwOFvhGZCtqFMn38yovdNgld6vDJyBarbddHHF4MdWZU +tl3ywwq6kpdGrrrqt4truac6tbMIa2XILylXLTo9thW8y81E+58G1/OU2+fvADXw +z8qoDiBuLsyghW96SUtrlHz9P22bd6SjopwU2HOVNe2yB06wVmaB7MpXBbn0mQR9 +28lP38ecXys3wOucDxwAIZrx+CpMXM7WuTtENC75xmDSJM5TiKeM4QQcPJkFhUWd +bA== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +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 +1sy9cBx0ZARHTD27VxkpdyZOr8AAJw== +-----END PRIVATE KEY-----