You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sm...@apache.org on 2022/02/07 22:41:15 UTC

[cassandra] branch trunk updated: Add support for PEM based key material for SSL

This is an automated email from the ASF dual-hosted git repository.

smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 3655b26  Add support for PEM based key material for SSL
3655b26 is described below

commit 3655b26adf8d3b94095924920d05cc1a16d0f4c0
Author: Maulin Vasavada <mv...@paypal.com>
AuthorDate: Fri Oct 8 23:51:47 2021 -0700

    Add support for PEM based key material for SSL
    
    patch by Maulin Vasavada; reviewed by Jon Meredith and Stefan Miklosovic for CASSANDRA-17031
---
 .gitignore                                         |   2 +-
 CHANGES.txt                                        |   1 +
 examples/ssl-factory/build.xml                     |  11 +-
 .../KubernetesSecretsPEMSslContextFactory.java     | 184 +++++++++
 .../KubernetesSecretsSslContextFactory.java        |   4 +-
 .../test/conf/cassandra_encrypted_private_key.pem  |  51 +++
 ...ssandra_encrypted_private_key_multiplecerts.pem |  71 ++++
 .../test/conf/cassandra_trusted_certificates.pem   |  22 +
 .../conf/cassandra_unencrypted_private_key.pem     |  50 +++
 ...KubernetesSecretsPEMSslContextFactoryTest.java} | 240 ++++++-----
 .../KubernetesSecretsSslContextFactoryTest.java    |  93 ++---
 .../security/FileBasedSslContextFactory.java       |  46 ++-
 .../security/PEMBasedSslContextFactory.java        | 372 +++++++++++++++++
 .../org/apache/cassandra/security/PEMReader.java   | 282 +++++++++++++
 test/conf/cassandra-pem-jks-sslcontextfactory.yaml | 150 +++++++
 ...pem-sslcontextfactory-invalidconfiguration.yaml | 147 +++++++
 test/conf/cassandra-pem-sslcontextfactory.yaml     | 151 +++++++
 test/conf/cassandra_ssl_test.keystore.pem          |  51 +++
 test/conf/cassandra_ssl_test.truststore.pem        |  22 +
 .../cassandra_ssl_test.unencrypted_keystore.pem    |  50 +++
 .../PEMBasedSslContextFactoryConfigTest.java       |  79 ++++
 ...PEMBasedSslContextFactoryInvalidConfigTest.java |  62 +++
 .../security/PEMBasedSslContextFactoryTest.java    | 449 ++++++++++++++++++++
 .../PEMJKSSslContextFactoryConfigTest.java         |  71 ++++
 .../apache/cassandra/security/PEMReaderTest.java   | 454 +++++++++++++++++++++
 .../apache/cassandra/security/SSLFactoryTest.java  |  42 ++
 26 files changed, 2990 insertions(+), 167 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3b01673..18f2204 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,7 +66,7 @@ target/
 .DS_Store
 Thumbs.db
 
-/.ant-targets-build.xml
+**/.ant-targets-build.xml
 .ant_targets
 
 # Generated files from the documentation
diff --git a/CHANGES.txt b/CHANGES.txt
index 66aae18..bf0de22 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.1
+ * Add support for PEM based key material for SSL (CASSANDRA-17031)
  * Standardize storage configuration parameters' names. Support unit suffixes. (CASSANDRA-15234)
  * Remove support for Windows (CASSANDRA-16956)
  * Runtime-configurable YAML option to prohibit USE statements (CASSANDRA-17318)
diff --git a/examples/ssl-factory/build.xml b/examples/ssl-factory/build.xml
index 9f150b0..d803aeb 100644
--- a/examples/ssl-factory/build.xml
+++ b/examples/ssl-factory/build.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
  ~ Licensed to the Apache Software Foundation (ASF) under one
  ~ or more contributor license agreements.  See the NOTICE file
@@ -51,6 +51,7 @@
 		</fileset>
 		<path refid="build.classpath"/>
 		<path location="${build.dir}/${final.name}.jar"/>
+		<pathelement location="${build.classes}"/>
 		<pathelement location="${test.build.classes}"/>
 		<pathelement location="${test.build.conf}"/>
 	</path>
@@ -73,9 +74,11 @@
 		</jar>
 	</target>
 
-	<target name="buildTests" depends="init">
-		<deltree dir="${test.build.dir}/conf"/>
-		<copydir src="test/conf" dest="${test.build.dir}/conf"/>
+	<target name="buildTests" depends="build">
+		<delete dir="${test.build.dir}/conf"/>
+		<copy todir="${test.build.dir}/conf">
+			<fileset dir="test/conf" includes="**"/>
+		</copy>
 		<javac destdir="${test.build.classes}" debug="true" includeantruntime="false">
 			<src path="${test.src}" />
 			<src path="${test.build.dir}/conf" />
diff --git a/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactory.java b/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactory.java
new file mode 100644
index 0000000..fb11c91
--- /dev/null
+++ b/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactory.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import java.util.Map;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom {@link ISslContextFactory} implementation based on Kubernetes Secrets. It allows the keystore and
+ * truststore paths to be configured from the K8 secrets via volumeMount and passwords via K8 secrets environment
+ * variables. The official Kubernetes Secret Spec can be found <a href="https://kubernetes.io/docs/concepts/configuration/secret/ ">here</a>.
+ * <p>
+ * When keystore or truststore is updated, this implementation can detect that based on updated K8 secrets
+ * at the mounted paths ({@code KEYSTORE_UPDATED_TIMESTAMP_PATH} for the keystore and {@code
+ * TRUSTSTORE_UPDATED_TIMESTAMP_PATH} for the truststore. The values in those paths are expected to be numeric values.
+ * The most obvious choice might be to just use the time in nano/milli-seconds precision but any other strategy would work
+ * as well, as far as the comparison of those values can be done in a consistent/predictable manner. Again, those
+ * values do not have to necessarily reflect actual file's update timestamps, using the actual file's timestamps is
+ * just one of the valid options to signal updates.
+ * <p>
+ * Defaults:
+ * <pre>
+ *     private key password = cassandra
+ *     keystore updated timestamp path = /etc/my-ssl-store/keystore-last-updatedtime
+ *     truststore updated timestamp path = /etc/my-ssl-store/truststore-last-updatedtime
+ * </pre>
+ * <p>
+ * Customization: In order to customize the K8s secret configuration, override appropriate values in the below Cassandra
+ * configuration. The similar configuration can be applied to {@code client_encryption_options}.
+ * <pre>
+ *     server_encryption_options:
+ *       internode_encryption: none
+ *       ssl_context_factory:
+ *         class_name: org.apache.cassandra.security.KubernetesSecretsPEMSslContextFactory
+ *         parameters:
+ *           PRIVATE_KEY_ENV_VAR: PRIVATE_KEY
+ *           PRIVATE_KEY_PASSWORD_ENV_VAR: PRIVATE_KEY_PASSWORD
+ *           KEYSTORE_UPDATED_TIMESTAMP_PATH: /etc/my-ssl-store/keystore-last-updatedtime
+ *           TRUSTED_CERTIFICATES_ENV_VAR: TRUSTED_CERTIFICATES
+ *           TRUSTSTORE_UPDATED_TIMESTAMP_PATH: /etc/my-ssl-store/truststore-last-updatedtime
+ * </pre>
+ * <p>
+ * Below is the corresponding sample YAML configuration for K8 env.
+ * <pre>
+ * apiVersion: v1
+ * kind: Pod
+ * metadata:
+ *   name: my-pod
+ *   labels:
+ *     app: my-app
+ * spec:
+ *   containers:
+ *   - name: my-app
+ *     image: my-app:latest
+ *     imagePullPolicy: Always
+ *     env:
+ *       - name: PRIVATE_KEY
+ *         valueFrom:
+ *           secretKeyRef:
+ *             name: my-ssl-store
+ *             key: private-key
+ *       - name: PRIVATE_KEY_PASSWORD
+ *         valueFrom:
+ *           secretKeyRef:
+ *             name: my-ssl-store
+ *             key: private-key-password
+ *       - name: TRUSTED_CERTIFICATES
+ *         valueFrom:
+ *           secretKeyRef:
+ *             name: my-ssl-store
+ *             key: trusted-certificates
+ *     volumeMounts:
+ *     - name: my-ssl-store
+ *       mountPath: "/etc/my-ssl-store"
+ *       readOnly: true
+ *   volumes:
+ *   - name: my-ssl-store
+ *     secret:
+ *       secretName: my-ssl-store
+ *       items:
+ *         - key: keystore-last-updatedtime
+ *           path: keystore-last-updatedtime
+ *         - key: truststore-last-updatedtime
+ *           path: truststore-last-updatedtime
+ * </pre>
+ */
+public class KubernetesSecretsPEMSslContextFactory extends KubernetesSecretsSslContextFactory
+{
+    public static final String DEFAULT_PRIVATE_KEY = "";
+    public static final String DEFAULT_PRIVATE_KEY_PASSWORD = "";
+    public static final String DEFAULT_TRUSTED_CERTIFICATES = "";
+
+    @VisibleForTesting
+    static final String DEFAULT_PRIVATE_KEY_ENV_VAR_NAME = "PRIVATE_KEY";
+    @VisibleForTesting
+    static final String DEFAULT_PRIVATE_KEY_PASSWORD_ENV_VAR_NAME = "PRIVATE_KEY_PASSWORD";
+    @VisibleForTesting
+    static final String DEFAULT_TRUSTED_CERTIFICATES_ENV_VAR_NAME = "TRUSTED_CERTIFICATES";
+
+    private static final Logger logger = LoggerFactory.getLogger(KubernetesSecretsPEMSslContextFactory.class);
+    private String pemEncodedKey;
+    private String keyPassword;
+    private String pemEncodedCertificates;
+    private PEMBasedSslContextFactory pemBasedSslContextFactory;
+
+    public KubernetesSecretsPEMSslContextFactory()
+    {
+        pemBasedSslContextFactory = new PEMBasedSslContextFactory();
+    }
+
+    public KubernetesSecretsPEMSslContextFactory(Map<String, Object> parameters)
+    {
+        super(parameters);
+
+        pemEncodedKey = getValueFromEnv(getString(PEMConfigKey.PRIVATE_KEY_ENV_VAR, DEFAULT_PRIVATE_KEY_ENV_VAR_NAME),
+                                        DEFAULT_PRIVATE_KEY);
+        keyPassword = getValueFromEnv(getString(PEMConfigKey.PRIVATE_KEY_PASSWORD_ENV_VAR,
+                                                DEFAULT_PRIVATE_KEY_PASSWORD_ENV_VAR_NAME),
+                                      DEFAULT_PRIVATE_KEY_PASSWORD);
+        pemEncodedCertificates = getValueFromEnv(getString(PEMConfigKey.TRUSTED_CERTIFICATE_ENV_VAR, DEFAULT_TRUSTED_CERTIFICATES_ENV_VAR_NAME),
+                                                 DEFAULT_TRUSTED_CERTIFICATES);
+
+        parameters.put(PEMBasedSslContextFactory.ConfigKey.ENCODED_KEY.getKeyName(), pemEncodedKey);
+        parameters.put(PEMBasedSslContextFactory.ConfigKey.KEY_PASSWORD.getKeyName(), keyPassword);
+        parameters.put(PEMBasedSslContextFactory.ConfigKey.ENCODED_CERTIFICATES.getKeyName(), pemEncodedCertificates);
+
+        pemBasedSslContextFactory = new PEMBasedSslContextFactory(parameters);
+    }
+
+    @Override
+    public synchronized void initHotReloading()
+    {
+        // No-op
+    }
+
+    @Override
+    public boolean hasKeystore()
+    {
+        return pemBasedSslContextFactory.hasKeystore();
+    }
+
+    @Override
+    protected KeyManagerFactory buildKeyManagerFactory() throws SSLException
+    {
+        KeyManagerFactory kmf = pemBasedSslContextFactory.buildKeyManagerFactory();
+        checkedExpiry = pemBasedSslContextFactory.checkedExpiry;
+        return kmf;
+    }
+
+    @Override
+    protected TrustManagerFactory buildTrustManagerFactory() throws SSLException
+    {
+        return pemBasedSslContextFactory.buildTrustManagerFactory();
+    }
+
+    public interface PEMConfigKey
+    {
+        String PRIVATE_KEY_ENV_VAR = "PRIVATE_KEY_ENV_VAR";
+        String PRIVATE_KEY_PASSWORD_ENV_VAR = "PRIVATE_KEY_PASSWORD_ENV_VAR";
+        String TRUSTED_CERTIFICATE_ENV_VAR = "TRUSTED_CERTIFICATE_ENV_VAR";
+    }
+}
diff --git a/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsSslContextFactory.java b/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsSslContextFactory.java
index 699efbc..c83fb03 100644
--- a/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsSslContextFactory.java
+++ b/examples/ssl-factory/src/org/apache/cassandra/security/KubernetesSecretsSslContextFactory.java
@@ -186,8 +186,8 @@ public class KubernetesSecretsSslContextFactory extends FileBasedSslContextFacto
     }
 
     /**
-     * Checks environment variables for {@code K8_SECRET_KEYSTORE_UPDATED_TIMESTAMP_ENV_VAR} and {@code K8_SECRET_TRUSTSTORE_UPDATED_TIMESTAMP_ENV_VAR}
-     * and compares the values for those variables with the current timestamps. In case the environment variables are
+     * Checks mounted paths for {@code KEYSTORE_UPDATED_TIMESTAMP_PATH} and {@code TRUSTSTORE_UPDATED_TIMESTAMP_PATH}
+     * and compares the values for those variables with the current timestamps. In case the mounted paths are
      * not valid (either they are not initialized yet, got removed or got corrupted in-flight), this method considers
      * that nothing has changed.
      * @return {@code true} if either of the timestamps (keystore or truststore) got updated;{@code false} otherwise
diff --git a/examples/ssl-factory/test/conf/cassandra_encrypted_private_key.pem b/examples/ssl-factory/test/conf/cassandra_encrypted_private_key.pem
new file mode 100644
index 0000000..ed981cc
--- /dev/null
+++ b/examples/ssl-factory/test/conf/cassandra_encrypted_private_key.pem
@@ -0,0 +1,51 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
+g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
+xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
+L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
+sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
+f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
+RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
+0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
+evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
+tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
+wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
+K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
+zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
+mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
+WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
+jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
+eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
+DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
+AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
+Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
+Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
+ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
+bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
+hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
+RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
+pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
+FujZhqbKUDbYAcqTkoQ=
+-----END ENCRYPTED PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+n3MVF9w=
+-----END CERTIFICATE-----
diff --git a/examples/ssl-factory/test/conf/cassandra_encrypted_private_key_multiplecerts.pem b/examples/ssl-factory/test/conf/cassandra_encrypted_private_key_multiplecerts.pem
new file mode 100644
index 0000000..fed57d4
--- /dev/null
+++ b/examples/ssl-factory/test/conf/cassandra_encrypted_private_key_multiplecerts.pem
@@ -0,0 +1,71 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIE6TAbBgkqhkiG9w0BBQMwDgQI4QuRiKYzf88CAggABIIEyPRVmPp38SIFr8H3
+wi+oc6b+HJH7SPflXO6XZe4Tignw/aSyBTsLm2dWrzojRAYMIRd1xC7yQ2ffYrvx
+uoYbtOQeAminNqvwXdRTnwu6oC0rxdBT8RQ9NK7xL2tQyD/shmOeTJG/glXxaeqS
+rT0CZ5P5GJh6xdIWLEu3lEa3NSWVFE2YacUphmxBoaWjBjsJfWTgkF665SgP+2lh
+8R2WTcHrHjD8jR4jHB03wlup0LOmOwzplUmqHB9TyuA4wF6tlJajwBcPa0PNI6ny
+e9YcdcRr7Y0IxnPQr7PhQNV5AQb9TivwX4WaZxR+BXtwMglp+mz0ohjwLS3z6pqr
+tLrFhv2qcacSl+CKukFb9umV/QBkUk/iu+jwLcNJKPC965GWdieNbO0akBQpQsUN
+mqaF9DYHogW5lRnybl8WWPIR8tXmSCbSUIgzw4lRK+o15I4vaMI0NfkwFD/2y1sn
+t3m9LnVBukkpx3g/CPKd9PbZZeWpOTrnRJQfOu9Fj2lmkpGp0peCBqLJpO0pieVl
+87EQ0ZCErtAGLGIAhWnDUqRK0MaWZ+DMQNKYn5klF4YTVBkfRc9tQbIgBaa77wvz
+gvVWBuJtTFpCt9c8jByTH1gLbchC4bhLsy1nO7moevypMmNW4rqw9x5f0EIR3zCU
+L5/buoIh91TG5JB7BaIbVHtbB/Y2siARRXJibuw3ChBjqPOfzQ66j//NCMqhfTwT
+x2wn7L1BB4xyLJgVW9803FUTzaaL3EvJjzpdvrGC7vzcB6Jd0La9cjHhWSAPOKro
+nD9XPCbgLs10vW9g1Tc8drnZklhw6f7xrIQhWFg6VlwmVpvCQhEpX48rCCo2PH9X
+uzeJA+oqFEH3zfDp0r+q6jbAl+5TkkbBBgC2GCoI1vTYSKaWjeKKHkgzGG0QQLAz
+YXWMXvWKOME4wVPkeVxJv80RqDv0JsoOrnVoaFAzAHJMWa7UZQnjkrbVz/y8ZbV4
+oLJjQjvuOnU2PoXXs6SXbzOs1xx5zbX1UUH3Wc7/CCaUec2hemQJ5m6b1XJciyrY
+GHpMKWtXky9Mo1ruTP7ZH1nk03b4PTObKSx2gQD5AZ/ASuTeahMqMb/2PJkDkpHA
+sy8b1zOn2YTbf4K6NWVNIOkiaApmKhhX0Af6Lg8Wr2ymRTXdp/Um8f+SQLADpB/F
+xOydEN622wmihKDge9DrUFqPG/bdIiRGLXLg8efNboC6/cn/i/sheO7/YlrvcUNo
+qxDa/Bb1N/DgmtgAQ1ZP+AKjk6FKkwZRF1X/VZkZ6auscDlaPetF7razPeIJUrKN
+z/x4AD2txGYKmeYztYR577hPXBw+PPKdggRhIugb6z5Tk89C2pDEwfnByA/wcGJr
+w5avxrubosPrp0QtJpZMzouOvcD52VUiZzDfu9dqI/hpinyt5rETj5E19qxBjIZt
+X3Nq5lY2ktbyqVIo8Z8w4EUU+3XHZKqDwjyYvjxCxv5lVVnqvQrH9h3kENBMrPYQ
+4XonQHpUGG7g7pA3ylmHi+nEedr0H5qKHzyFZlRdI7CLVNoAtBSdwvmtGd2cVVXU
+EaToKNbHPXXYYPX/oVAWZYwD7PHXIRJkiEZnrFARNhLypicU7yjvejUPXcVy5HMh
+XqEbrODPp4VXfbYhVg==
+-----END ENCRYPTED PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDXjCCAkYCAhI0MA0GCSqGSIb3DQEBBAUAMHcxCzAJBgNVBAYTAlVTMRMwEQYD
+VQQIDApDYWxpZm9ybmlhMREwDwYDVQQHDAhTYW4gSm9zZTEXMBUGA1UECgwOUGVy
+c29uYWwsIEluYy4xEDAOBgNVBAsMB1Jvb3QgQ0ExFTATBgNVBAMMDG15ZG9tYWlu
+LmNvbTAeFw0yMTExMjIyMjQ5MzlaFw0yMjExMjIyMjQ5MzlaMHIxCzAJBgNVBAYT
+AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQHDAhTYW4gSm9zZTEXMBUG
+A1UECgwOUGVyc29uYWwsIEluYy4xCzAJBgNVBAsMAklUMRUwEwYDVQQDDAxteWRv
+bWFpbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5fdA7wwD9
+9e5RcdLscvGB+hqJUEHuNC53SYKg5X4Sf0H4ExQUbsy8UaoWzWHhgGbCtTvUVavl
+72xsO74ei0EblopW7QknF0kaTO8Vi3mxhUAdtZFLG/o0NS9J16HdGDGojJwuqU9+
+sMQt1w0HCTMlriELnxaUFKP7M9b0uK5VODEKJ38QKNGXUDt66D7BVYeT/6hz2cXK
+QWDoHk/JadALSzW5ES8KIHfxCLnl2TcKxQhJ4CnL8qeGvc8N3VyTh2AXajaJW5RB
+8Oy4CVoYxcdmP1IapxCD+yNcmNt9XpUTD+6eM5gnvtbye+MSfwPz2MW+fWEDZXOv
+3VxhJyTRFNVTAgMBAAEwDQYJKoZIhvcNAQEEBQADggEBADYK/pn6QG7bvUL0Xnnw
+1KPf1nx36gfJE2V0bNk4uyNNeYufMKS8gPLzC+a3RigCEDc+hIZFE5BJexHd7DXA
+CWgHZJtdjM/Xlgoxbf1yfGV3DWeIZlNFSFZujBIpbm1Ug2BAeV31YRWODPZlUSEZ
+0jv8NEs8/oEz9bM4jwRdn09lo4D9hE6o8qDnrzmN2LBZP5dDIJ6g/M+mq/SJFnho
+qBrfUABZhbgk2+tkZ89OI2xpASCLo6X/vqua2iho6km3x+cz6EI9BbvVr6xOOdVK
+m6gs/Bi4MGTh35jdmvyXoyBUOd1w3yBBj86qbEt2ZHYqreRTxntQYx06598Q9Dsi
+xdg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDajCCAlICCQD/7mxPcMTPIDANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJV
+UzETMBEGA1UECAwKQ2FsaWZvcm5pYTERMA8GA1UEBwwIU2FuIEpvc2UxFzAVBgNV
+BAoMDlBlcnNvbmFsLCBJbmMuMRAwDgYDVQQLDAdSb290IENBMRUwEwYDVQQDDAxt
+eWRvbWFpbi5jb20wHhcNMjExMTIyMjExODAwWhcNNDkwNDA5MjExODAwWjB3MQsw
+CQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTERMA8GA1UEBwwIU2FuIEpv
+c2UxFzAVBgNVBAoMDlBlcnNvbmFsLCBJbmMuMRAwDgYDVQQLDAdSb290IENBMRUw
+EwYDVQQDDAxteWRvbWFpbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCkIwuNGv3ckew/o2UwaDlYgXH9bh1jap4ZCb6qpjvR3tq9nCerY6XMli0Z
+Xxg0wMHDNUr/jmVYIdQjbz0DVNz/l6ZBJHzHCEgqR40pNM3NgC5sDyuNhF3WLNvj
+WgHEwYosfb/9kFRjKUPqqtJ0ccj87OP3XrE/4epCTdJdmugroAQSpXt1ZZfwwPO4
+K27DzMD9W01EmeLcUhMfrpUnKGCfL22c0sZZm/6Khk4BExC3pSILP/NREKeUEAHw
++rxhNqbUyD/e4/DutdtJ5zONA+GVVGYCpu1Iy0W78Jve4MD2/TFPcEzf5omiWpPz
+WjpOWayD43ur0SZnYJ5haUlZ+bSLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABqN
+/eb+mKEw2zklPZuzy5uhOc7ImG2LP/ha3oc2ntwkqZQ2LmVA/2ZNVfcNrRRngIfn
+Ir9Kual7djmKmIQvanpnSyXOKOlIiuN0VOrewgzlBZZwlFJL/AH1l7K9uZfBbV5h
+oFfaR9wc+vRGsg3nqO9+hEuk6xbp0jk8QCt26EVhEPlijxzbxTQYiNPNSLuf/kPW
+C9xtIKSWIDT4N6DtH7BtHGQKQdRJ2b4SSUF4joEmBe6jcrLBeDybmuFtKqlVJKUk
+tzBd9CPseqMML1c518KzxlSkXNxTCa7PWEnuN5atLZ+pGGjxtGcDKkrZ9Cgi09G8
+MzB8b4C/goypyhBNlyI=
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/examples/ssl-factory/test/conf/cassandra_trusted_certificates.pem b/examples/ssl-factory/test/conf/cassandra_trusted_certificates.pem
new file mode 100644
index 0000000..8806ce8
--- /dev/null
+++ b/examples/ssl-factory/test/conf/cassandra_trusted_certificates.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+n3MVF9w=
+-----END CERTIFICATE-----
diff --git a/examples/ssl-factory/test/conf/cassandra_unencrypted_private_key.pem b/examples/ssl-factory/test/conf/cassandra_unencrypted_private_key.pem
new file mode 100644
index 0000000..ce3d8e7
--- /dev/null
+++ b/examples/ssl-factory/test/conf/cassandra_unencrypted_private_key.pem
@@ -0,0 +1,50 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOSZVf8dLj1xLw
+efqjbogbAwhwRXd3tmEfQY1zHyudJF3XR1T0Xp26BKNvAYUxxZRDNg16M3prPRZv
+wkhDJdE9NaN+BpZPJUavRsZOfGDq/CHN8j/2PPKrn3G/b065JjV3GZjfV/Sln047
+DeZptaSyOg7ZN7F8qjGqxEcnw+szV/wTzhnjGjZMlcbOm4jFGQAn6xUOooQCGsoB
+9FgxKB0nvNG3xVe/2eCNfbS4DabT3Y1wfQqZ62hOa5ZS0rwT+pw3tQs3zFFY1Sfi
+G7qYbqZrKTheWIZGojVVm6mqH4yA2ofOe5N3RsBitCwU/D0dpnaG9Wcl98nmXueM
+B6Rk04v7AgMBAAECggEAYnxIKjrFz/JkJ5MmiszM5HV698r9YB0aqHnFIHPoykIL
+uiCjiumantDrFsCkosixULwvI/BRwbxstTpyrheU9psT6P1CONICVPvV8ylgJAYU
+l+ofn56cEXKxVuICSWFLDH7pM1479g+IJJQAchbKQpqxAGTuMu3SpvJolfuj5srt
+bM7/RYhJFLwDuvHNA3ivlogMneItP03+C25aaxstM+lBuBf68+n78zMgSvt6J/6Y
+G2TOMKnxveMlG2qu9l2lAw/2i8daG/qre08nTH7wpRx0gZLZqNpe45exkrzticzF
+FgWYjG2K2brX21jqHroFgMhdXF7zhhRgLoIeC0BrIQKBgQDCfGfWrJESKBbVai5u
+7wqD9nlzjv6N6FXfTDOPXO1vz5frdvtLVWbs0SMPy+NglkaZK0iqHvb9mf2of8eC
+0D5cmewjn7WCDBQMypIMYgT912ak/BBVuGXcxb6UgD+xARfSARo2C8NG1hfprw1W
+ad14CjS5xhFMs44HpVYhI7iPYwKBgQC7SqVG/b37vZ7CINemdvoMujLvvYXDJJM8
+N21LqNJfVXdukdH3T0xuLnh9Z/wPHjJDMF/9+1foxSEPHijtyz5P19EilNEC/3qw
+fI19+VZoY0mdhPtXSGzy+rbTE2v71QgwFLizSos14Gr+eNiIjF7FYccK05++K/zk
+cd8ZA3bwiQKBgQCl+HTFBs9mpz+VMOAfW2+l3hkXPNiPUc62mNkHZ05ZNNd44jjh
+uSf0wSUiveR08MmevQlt5K7zDQ8jVKh2QjB15gVXAVxsdtJFeDnax2trFP9LnLBz
+9sE2/qn9INU5wK0LUlWD+dXUBbCyg+jl7cJKRqtoPldVFYYHkFlIPqup8QKBgHXv
+hyuw1FUVDkdHzwOvn70r8q8sNHKxMVWVwWkHIZGOi+pAQGrusD4hXRX6yKnsZdIR
+QCD6iFy25R5T64nxlYdJaxPPid3NakB/7ckJnPOWseBSwMIxhQlr/nvjmve1Kba9
+FaEwq4B9lGIxToiNe4/nBiM3JzvlDxX67nUdzWOhAoGAdFvriyvjshSJ4JHgIY9K
+37BVB0VKMcFV2P8fLVWO5oyRtE1bJhU4QVpQmauABU4RGSojJ3NPIVH1wxmJeYtj
+Q3b7EZaqI6ovna2eK2qtUx4WwxhRaXTT8xueBI2lgL6sBSTGG+K69ZOzGQzG/Mfr
+RXKInnLInFD9JD94VqmMozo=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+n3MVF9w=
+-----END CERTIFICATE-----
diff --git a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
similarity index 52%
copy from examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
copy to examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
index fec48fd..2d127f3 100644
--- a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
+++ b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
@@ -27,6 +27,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.TrustManagerFactory;
 
 import org.apache.commons.lang3.StringUtils;
@@ -37,63 +38,39 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.cassandra.config.EncryptionOptions;
-
-import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_PASSWORD_ENV_VAR;
+import static org.apache.cassandra.security.KubernetesSecretsPEMSslContextFactory.DEFAULT_PRIVATE_KEY_ENV_VAR_NAME;
+import static org.apache.cassandra.security.KubernetesSecretsPEMSslContextFactory.DEFAULT_PRIVATE_KEY_PASSWORD_ENV_VAR_NAME;
+import static org.apache.cassandra.security.KubernetesSecretsPEMSslContextFactory.PEMConfigKey.PRIVATE_KEY_PASSWORD_ENV_VAR;
+import static org.apache.cassandra.security.KubernetesSecretsPEMSslContextFactory.PEMConfigKey.TRUSTED_CERTIFICATE_ENV_VAR;
 import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_UPDATED_TIMESTAMP_PATH;
-import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.TRUSTSTORE_PASSWORD_ENV_VAR;
 import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.TRUSTSTORE_UPDATED_TIMESTAMP_PATH;
 
-public class KubernetesSecretsSslContextFactoryTest
+public class KubernetesSecretsPEMSslContextFactoryTest
 {
-    private static final Logger logger = LoggerFactory.getLogger(KubernetesSecretsSslContextFactoryTest.class);
-    private static final String TRUSTSTORE_PATH = EncryptionOptions.ConfigKey.TRUSTSTORE.toString();
-    private static final String KEYSTORE_PATH = EncryptionOptions.ConfigKey.KEYSTORE.toString();
-
-    private Map<String, Object> commonConfig = new HashMap<>();
-    private final static String truststoreUpdatedTimestampFilepath = "build/test/conf/cassandra_truststore_last_updatedtime";
-    private final static String keystoreUpdatedTimestampFilepath = "build/test/conf/cassandra_keystore_last_updatedtime";
+    private static final Logger logger = LoggerFactory.getLogger(KubernetesSecretsPEMSslContextFactoryTest.class);
 
-    private static class KubernetesSecretsSslContextFactoryForTestOnly extends KubernetesSecretsSslContextFactory
-    {
+    private static final String ENCRYPTED_PRIVATE_KEY_FILEPATH = "build/test/conf/cassandra_encrypted_private_key.pem";
+    private static final String ENCRYPTED_PRIVATE_KEY_WITH_MULTIPLE_CERTS_IN_CERTCHAIN_FILEPATH = "build/test/conf" +
+                                                                                                  "/cassandra_encrypted_private_key_multiplecerts.pem";
+    private static final String UNENCRYPTED_PRIVATE_KEY_FILEPATH = "build/test/conf/cassandra_unencrypted_private_key.pem";
+    private static final String TRUSTED_CERTIFICATES_FILEPATH = "build/test/conf/cassandra_trusted_certificates.pem";
+    private final static String TRUSTSTORE_UPDATED_TIMESTAMP_FILEPATH = "build/test/conf/cassandra_truststore_last_updatedtime";
+    private final static String KEYSTORE_UPDATED_TIMESTAMP_FILEPATH = "build/test/conf/cassandra_keystore_last_updatedtime";
 
-        public KubernetesSecretsSslContextFactoryForTestOnly()
-        {
-        }
+    private static String private_key;
+    private static String unencrypted_private_key;
+    private static String trusted_certificates;
 
-        public KubernetesSecretsSslContextFactoryForTestOnly(Map<String, Object> config)
-        {
-            super(config);
-        }
-
-        /*
-         * This is overriden to first give priority to the input map configuration since we should not be setting env
-         * variables from the unit tests. However, if the input map configuration doesn't have the value for the
-         * given key then fallback to loading from the real environment variables.
-         */
-        @Override
-        String getValueFromEnv(String envVarName, String defaultValue)
-        {
-            String envVarValue = parameters.get(envVarName) != null ? parameters.get(envVarName).toString() : null;
-            if (StringUtils.isEmpty(envVarValue))
-            {
-                logger.info("Configuration doesn't have env variable {}. Will use parent's implementation", envVarName);
-                return super.getValueFromEnv(envVarName, defaultValue);
-            }
-            else
-            {
-                logger.info("Configuration has env variable {} with value {}. Will use that.",
-                            envVarName, envVarValue);
-                return envVarValue;
-            }
-        }
-    }
+    private final Map<String, Object> commonConfig = new HashMap<>();
 
     @BeforeClass
     public static void prepare()
     {
-        deleteFileIfExists(truststoreUpdatedTimestampFilepath);
-        deleteFileIfExists(keystoreUpdatedTimestampFilepath);
+        deleteFileIfExists(TRUSTSTORE_UPDATED_TIMESTAMP_FILEPATH);
+        deleteFileIfExists(KEYSTORE_UPDATED_TIMESTAMP_FILEPATH);
+        private_key = readFile(ENCRYPTED_PRIVATE_KEY_FILEPATH);
+        unencrypted_private_key = readFile(UNENCRYPTED_PRIVATE_KEY_FILEPATH);
+        trusted_certificates = readFile(TRUSTED_CERTIFICATES_FILEPATH);
     }
 
     private static void deleteFileIfExists(String filePath)
@@ -113,68 +90,63 @@ public class KubernetesSecretsSslContextFactoryTest
         }
     }
 
+    private static String readFile(String file)
+    {
+        try
+        {
+            return new String(Files.readAllBytes(Paths.get(file)));
+        }
+        catch (Exception e)
+        {
+            logger.error("Unable to read the file {}. Without this tests in this file would fail.", file);
+        }
+        return null;
+    }
+
     @Before
     public void setup()
     {
-        commonConfig.put(TRUSTSTORE_PATH, "build/test/conf/cassandra_ssl_test.truststore");
-        commonConfig.put(TRUSTSTORE_PASSWORD_ENV_VAR, "MY_TRUSTSTORE_PASSWORD");
-        commonConfig.put(TRUSTSTORE_UPDATED_TIMESTAMP_PATH, truststoreUpdatedTimestampFilepath);
+        commonConfig.put(TRUSTED_CERTIFICATE_ENV_VAR,
+                         "MY_TRUSTED_CERTIFICATES");
+        commonConfig.put("MY_TRUSTED_CERTIFICATES", trusted_certificates);
+        commonConfig.put(TRUSTSTORE_UPDATED_TIMESTAMP_PATH, TRUSTSTORE_UPDATED_TIMESTAMP_FILEPATH);
         /*
          * In order to test with real 'env' variables comment out this line and set appropriate env variable. This is
          * done to avoid having a dependency on env in the unit test.
          */
-        commonConfig.put("MY_TRUSTSTORE_PASSWORD", "cassandra");
         commonConfig.put("require_client_auth", Boolean.FALSE);
         commonConfig.put("cipher_suites", Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
     }
 
     private void addKeystoreOptions(Map<String, Object> config)
     {
-        config.put(KEYSTORE_PATH, "build/test/conf/cassandra_ssl_test.keystore");
-        config.put(KEYSTORE_PASSWORD_ENV_VAR, "MY_KEYSTORE_PASSWORD");
-        config.put(KEYSTORE_UPDATED_TIMESTAMP_PATH, keystoreUpdatedTimestampFilepath);
+        config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, private_key);
+        config.put(PRIVATE_KEY_PASSWORD_ENV_VAR, "MY_KEY_PASSWORD");
+        config.put(KEYSTORE_UPDATED_TIMESTAMP_PATH, KEYSTORE_UPDATED_TIMESTAMP_FILEPATH);
         /*
          * In order to test with real 'env' variables comment out this line and set appropriate env variable. This is
          *  done to avoid having a dependency on env in the unit test.
          */
-        config.put("MY_KEYSTORE_PASSWORD", "cassandra");
+        config.put("MY_KEY_PASSWORD", "cassandra");
     }
 
-    @Test(expected = IOException.class)
-    public void buildTrustManagerFactoryWithInvalidTruststoreFile() throws IOException
+    private void addUnencryptedKeystoreOptions(Map<String, Object> config)
     {
-        Map<String, Object> config = new HashMap<>();
-        config.putAll(commonConfig);
-        config.put(TRUSTSTORE_PATH, "/this/is/probably/not/a/file/on/your/test/machine");
-
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
-        kubernetesSecretsSslContextFactory.checkedExpiry = false;
-        kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
+        config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, unencrypted_private_key);
+        config.put(KEYSTORE_UPDATED_TIMESTAMP_PATH, KEYSTORE_UPDATED_TIMESTAMP_FILEPATH);
+        config.remove(DEFAULT_PRIVATE_KEY_PASSWORD_ENV_VAR_NAME);
+        config.remove(PRIVATE_KEY_PASSWORD_ENV_VAR);
     }
 
-    @Test(expected = IOException.class)
-    public void buildTrustManagerFactoryWithBadPassword() throws IOException
-    {
-        Map<String, Object> config = new HashMap<>();
-        config.putAll(commonConfig);
-        config.remove(TRUSTSTORE_PASSWORD_ENV_VAR);
-        config.put(KubernetesSecretsSslContextFactory.DEFAULT_TRUSTSTORE_PASSWORD_ENV_VAR_NAME, "HomeOfBadPasswords");
-
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
-        kubernetesSecretsSslContextFactory.checkedExpiry = false;
-        kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
-    }
-
-    @Test
-    public void buildTrustManagerFactoryWithEmptyPassword() throws IOException
+    @Test(expected = SSLException.class)
+    public void buildTrustManagerFactoryWithInvalidTrustedCertificates() throws IOException
     {
         Map<String, Object> config = new HashMap<>();
         config.putAll(commonConfig);
-        config.put(TRUSTSTORE_PATH, "build/test/conf/cassandra_ssl_test.truststore-without-password");
-        config.remove(TRUSTSTORE_PASSWORD_ENV_VAR);
-        config.put(KubernetesSecretsSslContextFactory.DEFAULT_TRUSTSTORE_PASSWORD_ENV_VAR_NAME, "");
+        config.put("MY_TRUSTED_CERTIFICATES", trusted_certificates.replaceAll("\\s", String.valueOf(System.nanoTime())));
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory =
+        new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         kubernetesSecretsSslContextFactory.checkedExpiry = false;
         kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
     }
@@ -185,20 +157,21 @@ public class KubernetesSecretsSslContextFactoryTest
         Map<String, Object> config = new HashMap<>();
         config.putAll(commonConfig);
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         kubernetesSecretsSslContextFactory.checkedExpiry = false;
         TrustManagerFactory trustManagerFactory = kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
         Assert.assertNotNull(trustManagerFactory);
     }
 
-    @Test(expected = IOException.class)
-    public void buildKeyManagerFactoryWithInvalidKeystoreFile() throws IOException
+    @Test(expected = SSLException.class)
+    public void buildKeyManagerFactoryWithInvalidPrivateKey() throws IOException
     {
         Map<String, Object> config = new HashMap<>();
         config.putAll(commonConfig);
-        config.put(KEYSTORE_PATH, "/this/is/probably/not/a/file/on/your/test/machine");
+        config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, private_key.replaceAll("\\s", String.valueOf(System.nanoTime())));
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory =
+        new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         kubernetesSecretsSslContextFactory.checkedExpiry = false;
         kubernetesSecretsSslContextFactory.buildKeyManagerFactory();
     }
@@ -208,10 +181,11 @@ public class KubernetesSecretsSslContextFactoryTest
     {
         Map<String, Object> config = new HashMap<>();
         config.putAll(commonConfig);
-        config.put(KEYSTORE_PATH, "build/test/conf/cassandra_ssl_test.keystore");
-        config.put(KubernetesSecretsSslContextFactory.DEFAULT_KEYSTORE_PASSWORD_ENV_VAR_NAME, "HomeOfBadPasswords");
+        config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, private_key);
+        config.put(DEFAULT_PRIVATE_KEY_PASSWORD_ENV_VAR_NAME, "HomeOfBadPasswords");
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory =
+        new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         kubernetesSecretsSslContextFactory.buildKeyManagerFactory();
     }
 
@@ -221,19 +195,60 @@ public class KubernetesSecretsSslContextFactoryTest
         Map<String, Object> config = new HashMap<>();
         config.putAll(commonConfig);
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory1 = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory1 = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         // Make sure the exiry check didn't happen so far for the private key
         Assert.assertFalse(kubernetesSecretsSslContextFactory1.checkedExpiry);
 
         addKeystoreOptions(config);
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory2 = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory2 = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
+        // Trigger the private key loading. That will also check for expired private key
+        kubernetesSecretsSslContextFactory2.buildKeyManagerFactory();
+        // Now we should have checked the private key's expiry
+        Assert.assertTrue(kubernetesSecretsSslContextFactory2.checkedExpiry);
+
+        // Make sure that new factory object preforms the fresh private key expiry check
+
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory3 =
+        new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
+        Assert.assertFalse(kubernetesSecretsSslContextFactory3.checkedExpiry);
+        kubernetesSecretsSslContextFactory3.buildKeyManagerFactory();
+        Assert.assertTrue(kubernetesSecretsSslContextFactory3.checkedExpiry);
+    }
+
+    @Test
+    public void buildKeyManagerFactoryWithMultipleCertsInCertChain() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addKeystoreOptions(config);
+        config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, readFile(ENCRYPTED_PRIVATE_KEY_WITH_MULTIPLE_CERTS_IN_CERTCHAIN_FILEPATH));
+
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory2 = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
+        // Trigger the private key loading. That will also check for expired private key
+        kubernetesSecretsSslContextFactory2.buildKeyManagerFactory();
+    }
+
+    @Test
+    public void buildKeyManagerFactoryHappyPathForUnencryptedKey() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory1 = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
+        // Make sure the exiry check didn't happen so far for the private key
+        Assert.assertFalse(kubernetesSecretsSslContextFactory1.checkedExpiry);
+
+        addUnencryptedKeystoreOptions(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory2 = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         // Trigger the private key loading. That will also check for expired private key
         kubernetesSecretsSslContextFactory2.buildKeyManagerFactory();
         // Now we should have checked the private key's expiry
         Assert.assertTrue(kubernetesSecretsSslContextFactory2.checkedExpiry);
 
         // Make sure that new factory object preforms the fresh private key expiry check
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory3 = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory3 =
+        new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         Assert.assertFalse(kubernetesSecretsSslContextFactory3.checkedExpiry);
         kubernetesSecretsSslContextFactory3.buildKeyManagerFactory();
         Assert.assertTrue(kubernetesSecretsSslContextFactory3.checkedExpiry);
@@ -246,7 +261,7 @@ public class KubernetesSecretsSslContextFactoryTest
         config.putAll(commonConfig);
         addKeystoreOptions(config);
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         kubernetesSecretsSslContextFactory.checkedExpiry = false;
         TrustManagerFactory trustManagerFactory = kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
         Assert.assertNotNull(trustManagerFactory);
@@ -266,7 +281,7 @@ public class KubernetesSecretsSslContextFactoryTest
         config.putAll(commonConfig);
         addKeystoreOptions(config);
 
-        KubernetesSecretsSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsSslContextFactoryForTestOnly(config);
+        KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
         kubernetesSecretsSslContextFactory.checkedExpiry = false;
         KeyManagerFactory keyManagerFactory = kubernetesSecretsSslContextFactory.buildKeyManagerFactory();
         Assert.assertNotNull(keyManagerFactory);
@@ -293,4 +308,39 @@ public class KubernetesSecretsSslContextFactoryTest
             logger.warn("Failed to write to filePath {} from the mounted volume", filePath, e);
         }
     }
+
+    private static class KubernetesSecretsPEMSslContextFactoryForTestOnly extends KubernetesSecretsPEMSslContextFactory
+    {
+
+        public KubernetesSecretsPEMSslContextFactoryForTestOnly()
+        {
+        }
+
+        public KubernetesSecretsPEMSslContextFactoryForTestOnly(Map<String, Object> config)
+        {
+            super(config);
+        }
+
+        /*
+         * This is overriden to first give priority to the input map configuration since we should not be setting env
+         * variables from the unit tests. However, if the input map configuration doesn't have the value for the
+         * given key then fallback to loading from the real environment variables.
+         */
+        @Override
+        String getValueFromEnv(String envVarName, String defaultValue)
+        {
+            String envVarValue = parameters.get(envVarName) != null ? parameters.get(envVarName).toString() : null;
+            if (StringUtils.isEmpty(envVarValue))
+            {
+                logger.info("Configuration doesn't have env variable {}. Will use parent's implementation", envVarName);
+                return super.getValueFromEnv(envVarName, defaultValue);
+            }
+            else
+            {
+                logger.info("Configuration has environment variable {} with value {}. Will use that.",
+                            envVarName, envVarValue);
+                return envVarValue;
+            }
+        }
+    }
 }
diff --git a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
index fec48fd..8b22fff 100644
--- a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
+++ b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
@@ -18,10 +18,10 @@
 
 package org.apache.cassandra.security;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -38,6 +38,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.cassandra.config.EncryptionOptions;
+import org.apache.cassandra.io.util.PathUtils;
 
 import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_PASSWORD_ENV_VAR;
 import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_UPDATED_TIMESTAMP_PATH;
@@ -49,45 +50,9 @@ public class KubernetesSecretsSslContextFactoryTest
     private static final Logger logger = LoggerFactory.getLogger(KubernetesSecretsSslContextFactoryTest.class);
     private static final String TRUSTSTORE_PATH = EncryptionOptions.ConfigKey.TRUSTSTORE.toString();
     private static final String KEYSTORE_PATH = EncryptionOptions.ConfigKey.KEYSTORE.toString();
-
-    private Map<String, Object> commonConfig = new HashMap<>();
     private final static String truststoreUpdatedTimestampFilepath = "build/test/conf/cassandra_truststore_last_updatedtime";
     private final static String keystoreUpdatedTimestampFilepath = "build/test/conf/cassandra_keystore_last_updatedtime";
-
-    private static class KubernetesSecretsSslContextFactoryForTestOnly extends KubernetesSecretsSslContextFactory
-    {
-
-        public KubernetesSecretsSslContextFactoryForTestOnly()
-        {
-        }
-
-        public KubernetesSecretsSslContextFactoryForTestOnly(Map<String, Object> config)
-        {
-            super(config);
-        }
-
-        /*
-         * This is overriden to first give priority to the input map configuration since we should not be setting env
-         * variables from the unit tests. However, if the input map configuration doesn't have the value for the
-         * given key then fallback to loading from the real environment variables.
-         */
-        @Override
-        String getValueFromEnv(String envVarName, String defaultValue)
-        {
-            String envVarValue = parameters.get(envVarName) != null ? parameters.get(envVarName).toString() : null;
-            if (StringUtils.isEmpty(envVarValue))
-            {
-                logger.info("Configuration doesn't have env variable {}. Will use parent's implementation", envVarName);
-                return super.getValueFromEnv(envVarName, defaultValue);
-            }
-            else
-            {
-                logger.info("Configuration has env variable {} with value {}. Will use that.",
-                            envVarName, envVarValue);
-                return envVarValue;
-            }
-        }
-    }
+    private final Map<String, Object> commonConfig = new HashMap<>();
 
     @BeforeClass
     public static void prepare()
@@ -96,20 +61,13 @@ public class KubernetesSecretsSslContextFactoryTest
         deleteFileIfExists(keystoreUpdatedTimestampFilepath);
     }
 
-    private static void deleteFileIfExists(String filePath)
+    private static void deleteFileIfExists(String file)
     {
-        try
+        Path filePath = Paths.get(file);
+        boolean deleted = PathUtils.tryDelete(filePath);
+        if (!deleted)
         {
-            logger.info("Deleting the file {} to prepare for the tests", new File(filePath).getAbsolutePath());
-            File file = new File(filePath);
-            if (file.exists())
-            {
-                file.delete();
-            }
-        }
-        catch (Exception e)
-        {
-            logger.warn("File {} could not be deleted.", filePath, e);
+            logger.warn("File {} could not be deleted.", filePath);
         }
     }
 
@@ -293,4 +251,39 @@ public class KubernetesSecretsSslContextFactoryTest
             logger.warn("Failed to write to filePath {} from the mounted volume", filePath, e);
         }
     }
+
+    private static class KubernetesSecretsSslContextFactoryForTestOnly extends KubernetesSecretsSslContextFactory
+    {
+
+        public KubernetesSecretsSslContextFactoryForTestOnly()
+        {
+        }
+
+        public KubernetesSecretsSslContextFactoryForTestOnly(Map<String, Object> config)
+        {
+            super(config);
+        }
+
+        /*
+         * This is overriden to first give priority to the input map configuration since we should not be setting env
+         * variables from the unit tests. However, if the input map configuration doesn't have the value for the
+         * given key then fallback to loading from the real environment variables.
+         */
+        @Override
+        String getValueFromEnv(String envVarName, String defaultValue)
+        {
+            String envVarValue = parameters.get(envVarName) != null ? parameters.get(envVarName).toString() : null;
+            if (StringUtils.isEmpty(envVarValue))
+            {
+                logger.info("Configuration doesn't have env variable {}. Will use parent's implementation", envVarName);
+                return super.getValueFromEnv(envVarName, defaultValue);
+            }
+            else
+            {
+                logger.info("Configuration has env variable {} with value {}. Will use that.",
+                            envVarName, envVarValue);
+                return envVarValue;
+            }
+        }
+    }
 }
diff --git a/src/java/org/apache/cassandra/security/FileBasedSslContextFactory.java b/src/java/org/apache/cassandra/security/FileBasedSslContextFactory.java
index 4912364..3d47509 100644
--- a/src/java/org/apache/cassandra/security/FileBasedSslContextFactory.java
+++ b/src/java/org/apache/cassandra/security/FileBasedSslContextFactory.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.security.KeyStore;
+import java.security.KeyStoreException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Date;
@@ -37,6 +38,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.cassandra.io.util.File;
+import org.apache.cassandra.utils.Clock;
 
 /**
  * Abstract implementation for {@link ISslContextFactory} using file based, standard keystore format with the ability
@@ -124,26 +126,19 @@ abstract public class FileBasedSslContextFactory extends AbstractSslContextFacto
      * @return KeyManagerFactory built from the file based keystore.
      * @throws SSLException if any issues encountered during the build process
      */
+    @Override
     protected KeyManagerFactory buildKeyManagerFactory() throws SSLException
     {
+
         try (InputStream ksf = Files.newInputStream(Paths.get(keystore)))
         {
-            KeyManagerFactory kmf = KeyManagerFactory.getInstance(
-            algorithm == null ? KeyManagerFactory.getDefaultAlgorithm() : algorithm);
+            final String algorithm = this.algorithm == null ? KeyManagerFactory.getDefaultAlgorithm() : this.algorithm;
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
             KeyStore ks = KeyStore.getInstance(store_type);
             ks.load(ksf, keystore_password.toCharArray());
             if (!checkedExpiry)
             {
-                for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements(); )
-                {
-                    String alias = aliases.nextElement();
-                    if (ks.getCertificate(alias).getType().equals("X.509"))
-                    {
-                        Date expires = ((X509Certificate) ks.getCertificate(alias)).getNotAfter();
-                        if (expires.before(new Date()))
-                            logger.warn("Certificate for {} expired on {}", alias, expires);
-                    }
-                }
+                checkExpiredCerts(ks);
                 checkedExpiry = true;
             }
             kmf.init(ks, keystore_password.toCharArray());
@@ -161,12 +156,13 @@ abstract public class FileBasedSslContextFactory extends AbstractSslContextFacto
      * @return TrustManagerFactory from the file based truststore
      * @throws SSLException if any issues encountered during the build process
      */
+    @Override
     protected TrustManagerFactory buildTrustManagerFactory() throws SSLException
     {
         try (InputStream tsf = Files.newInputStream(Paths.get(truststore)))
         {
-            TrustManagerFactory tmf = TrustManagerFactory.getInstance(
-            algorithm == null ? TrustManagerFactory.getDefaultAlgorithm() : algorithm);
+            final String algorithm = this.algorithm == null ? TrustManagerFactory.getDefaultAlgorithm() : this.algorithm;
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
             KeyStore ts = KeyStore.getInstance(store_type);
             ts.load(tsf, truststore_password.toCharArray());
             tmf.init(ts);
@@ -178,10 +174,30 @@ abstract public class FileBasedSslContextFactory extends AbstractSslContextFacto
         }
     }
 
+    protected boolean checkExpiredCerts(KeyStore ks) throws KeyStoreException
+    {
+        boolean hasExpiredCerts = false;
+        final Date now = new Date(Clock.Global.currentTimeMillis());
+        for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements(); )
+        {
+            String alias = aliases.nextElement();
+            if (ks.getCertificate(alias).getType().equals("X.509"))
+            {
+                Date expires = ((X509Certificate) ks.getCertificate(alias)).getNotAfter();
+                if (expires.before(now))
+                {
+                    hasExpiredCerts = true;
+                    logger.warn("Certificate for {} expired on {}", alias, expires);
+                }
+            }
+        }
+        return hasExpiredCerts;
+    }
+
     /**
      * Helper class for hot reloading SSL Contexts
      */
-    private static class HotReloadableFile
+    protected static class HotReloadableFile
     {
         private final File file;
         private volatile long lastModTime;
diff --git a/src/java/org/apache/cassandra/security/PEMBasedSslContextFactory.java b/src/java/org/apache/cassandra/security/PEMBasedSslContextFactory.java
new file mode 100644
index 0000000..8ecbec5
--- /dev/null
+++ b/src/java/org/apache/cassandra/security/PEMBasedSslContextFactory.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.io.util.File;
+
+
+/**
+ * SslContextFactory for the <a href="">PEM standard</a> encoded PKCS#8 private keys and X509 certificates/public-keys.
+ * It parses the key material based on the standard defined in the <a href="https://datatracker.ietf.org/doc/html/rfc7468">RFC 7468</a>.
+ * It creates <a href="https://datatracker.ietf.org/doc/html/rfc5208">PKCS# 8</a> based private key and X509 certificate(s)
+ * for the public key to build the required keystore and the truststore managers that are used for the SSL context creation.
+ * Internally it builds Java {@link KeyStore} with <a href="https://datatracker.ietf.org/doc/html/rfc7292">PKCS# 12</a> <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#keystore-types">store type</a>
+ * to be used for keystore and the truststore managers.
+ * <p>
+ * This factory also supports 'hot reloading' of the key material, the same way as defined by {@link FileBasedSslContextFactory},
+ * <b>if it is file based</b>. This factory ignores the existing 'store_type' configuration used for other file based store
+ * types like JKS.
+ * <p>
+ * You can configure this factory with either inline PEM data or with the files having the required PEM data as shown
+ * below,
+ *
+ * <b>Configuration: PEM keys/certs defined inline (mind the spaces in the YAML!)</b>
+ * <pre>
+ *     client/server_encryption_options:
+ *      ssl_context_factory:
+ *         class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
+ *         parameters:
+ *             private_key: |
+ *              -----BEGIN ENCRYPTED PRIVATE KEY----- OR -----BEGIN PRIVATE KEY-----
+ *              <your base64 encoded private key>
+ *              -----END ENCRYPTED PRIVATE KEY----- OR -----END PRIVATE KEY-----
+ *              -----BEGIN CERTIFICATE-----
+ *              <your base64 encoded certificate chain>
+ *              -----END CERTIFICATE-----
+ *
+ *             private_key_password: "<your password if the private key is encrypted with a password>"
+ *
+ *             trusted_certificates: |
+ *               -----BEGIN CERTIFICATE-----
+ *               <your base64 encoded certificate>
+ *               -----END CERTIFICATE-----
+ * </pre>
+ *
+ * <b>Configuration: PEM keys/certs defined in files</b>
+ * <pre>
+ *     client/server_encryption_options:
+ *      ssl_context_factory:
+ *         class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
+ *      keystore: <file path to the keystore file in the PEM format with the private key and the certificate chain>
+ *      keystore_password: "<your password if the private key is encrypted with a password>"
+ *      truststore: <file path to the truststore file in the PEM format>
+ * </pre>
+ */
+public final class PEMBasedSslContextFactory extends FileBasedSslContextFactory
+{
+    public static final String DEFAULT_TARGET_STORETYPE = "PKCS12";
+    private static final Logger logger = LoggerFactory.getLogger(PEMBasedSslContextFactory.class);
+    private String pemEncodedKey;
+    private String keyPassword;
+    private String pemEncodedCertificates;
+    private boolean maybeFileBasedPrivateKey;
+    private boolean maybeFileBasedTrustedCertificates;
+
+    public PEMBasedSslContextFactory()
+    {
+    }
+
+    public PEMBasedSslContextFactory(Map<String, Object> parameters)
+    {
+        super(parameters);
+        pemEncodedKey = getString(ConfigKey.ENCODED_KEY.getKeyName());
+        keyPassword = getString(ConfigKey.KEY_PASSWORD.getKeyName());
+        if (StringUtils.isEmpty(keyPassword))
+        {
+            keyPassword = keystore_password;
+        }
+        else if (!StringUtils.isEmpty(keystore_password) && !keyPassword.equals(keystore_password))
+        {
+            throw new IllegalArgumentException("'keystore_password' and 'key_password' both configurations are given and the " +
+                                               "values do not match");
+        }
+        else
+        {
+            logger.warn("'keystore_password' and 'key_password' both are configured but since the values match it's " +
+                        "okay. Ideally you should only specify one of them.");
+        }
+
+        if (!StringUtils.isEmpty(truststore_password))
+        {
+            logger.warn("PEM based truststore should not be using password. Ignoring the given value in " +
+                        "'truststore_password' configuration.");
+        }
+
+        pemEncodedCertificates = getString(ConfigKey.ENCODED_CERTIFICATES.getKeyName());
+
+        maybeFileBasedPrivateKey = StringUtils.isEmpty(pemEncodedKey);
+        maybeFileBasedTrustedCertificates = StringUtils.isEmpty(pemEncodedCertificates);
+
+        enforceSinglePrivateKeySource();
+        enforceSingleTurstedCertificatesSource();
+    }
+
+    /**
+     * Decides if this factory has a keystore defined - key material specified in files or inline to the configuration.
+     *
+     * @return {@code true} if there is a keystore defined; {@code false} otherwise
+     */
+    @Override
+    public boolean hasKeystore()
+    {
+        return maybeFileBasedPrivateKey ? keystoreFileExists() :
+               !StringUtils.isEmpty(pemEncodedKey);
+    }
+
+    /**
+     * Checks if the keystore file exists.
+     *
+     * @return {@code true} if keystore file exists; {@code false} otherwise
+     */
+    private boolean keystoreFileExists()
+    {
+        return keystore != null && new File(keystore).exists();
+    }
+
+    /**
+     * Decides if this factory has a truststore defined - key material specified in files or inline to the
+     * configuration.
+     *
+     * @return {@code true} if there is a truststore defined; {@code false} otherwise
+     */
+    private boolean hasTruststore()
+    {
+        return maybeFileBasedTrustedCertificates ? truststoreFileExists() :
+               !StringUtils.isEmpty(pemEncodedCertificates);
+    }
+
+    /**
+     * Checks if the truststore file exists.
+     *
+     * @return {@code true} if truststore file exists; {@code false} otherwise
+     */
+    private boolean truststoreFileExists()
+    {
+        return truststore != null && new File(truststore).exists();
+    }
+
+    /**
+     * This enables 'hot' reloading of the key/trust stores based on the last updated timestamps if they are file based.
+     */
+    @Override
+    public synchronized void initHotReloading()
+    {
+        List<HotReloadableFile> fileList = new ArrayList<>();
+        if (maybeFileBasedPrivateKey && hasKeystore())
+        {
+            fileList.add(new HotReloadableFile(keystore));
+        }
+        if (maybeFileBasedTrustedCertificates && hasTruststore())
+        {
+            fileList.add(new HotReloadableFile(truststore));
+        }
+        if (!fileList.isEmpty())
+        {
+            hotReloadableFiles = fileList;
+        }
+    }
+
+    /**
+     * Builds required KeyManagerFactory from the PEM based keystore. It also checks for the PrivateKey's certificate's
+     * expiry and logs {@code warning} for each expired PrivateKey's certitificate.
+     *
+     * @return KeyManagerFactory built from the PEM based keystore.
+     * @throws SSLException if any issues encountered during the build process
+     */
+    @Override
+    protected KeyManagerFactory buildKeyManagerFactory() throws SSLException
+    {
+        try
+        {
+            if (hasKeystore())
+            {
+                if (maybeFileBasedPrivateKey)
+                {
+                    pemEncodedKey = readPEMFile(keystore); // read PEM from the file
+                }
+
+                KeyManagerFactory kmf = KeyManagerFactory.getInstance(
+                algorithm == null ? KeyManagerFactory.getDefaultAlgorithm() : algorithm);
+                KeyStore ks = buildKeyStore();
+                if (!checkedExpiry)
+                {
+                    checkExpiredCerts(ks);
+                    checkedExpiry = true;
+                }
+                kmf.init(ks, keyPassword != null ? keyPassword.toCharArray() : null);
+                return kmf;
+            }
+            else
+            {
+                throw new SSLException("Must provide keystore or private_key in configuration for PEMBasedSSlContextFactory");
+            }
+        }
+        catch (Exception e)
+        {
+            throw new SSLException("Failed to build key manager store for secure connections", e);
+        }
+    }
+
+    /**
+     * Builds TrustManagerFactory from the PEM based truststore.
+     *
+     * @return TrustManagerFactory from the PEM based truststore
+     * @throws SSLException if any issues encountered during the build process
+     */
+    @Override
+    protected TrustManagerFactory buildTrustManagerFactory() throws SSLException
+    {
+        try
+        {
+            if (hasTruststore())
+            {
+                if (maybeFileBasedTrustedCertificates)
+                {
+                    pemEncodedCertificates = readPEMFile(truststore); // read PEM from the file
+                }
+
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance(
+                algorithm == null ? TrustManagerFactory.getDefaultAlgorithm() : algorithm);
+                KeyStore ts = buildTrustStore();
+                tmf.init(ts);
+                return tmf;
+            }
+            else
+            {
+                throw new SSLException("Must provide truststore or trusted_certificates in configuration for " +
+                                       "PEMBasedSSlContextFactory");
+            }
+        }
+        catch (Exception e)
+        {
+            throw new SSLException("Failed to build trust manager store for secure connections", e);
+        }
+    }
+
+    private String readPEMFile(String file) throws IOException
+    {
+        return new String(Files.readAllBytes(Paths.get(file)));
+    }
+
+    /**
+     * Builds KeyStore object given the {@link #DEFAULT_TARGET_STORETYPE} out of the PEM formatted private key material.
+     * It uses {@code cassandra-ssl-keystore} as the alias for the created key-entry.
+     */
+    private KeyStore buildKeyStore() throws GeneralSecurityException, IOException
+    {
+        char[] keyPasswordArray = keyPassword != null ? keyPassword.toCharArray() : null;
+        PrivateKey privateKey = PEMReader.extractPrivateKey(pemEncodedKey, keyPassword);
+        Certificate[] certChainArray = PEMReader.extractCertificates(pemEncodedKey);
+        if (certChainArray == null || certChainArray.length == 0)
+        {
+            throw new SSLException("Could not read any certificates for the certChain for the private key");
+        }
+
+        KeyStore keyStore = KeyStore.getInstance(DEFAULT_TARGET_STORETYPE);
+        keyStore.load(null, null);
+        keyStore.setKeyEntry("cassandra-ssl-keystore", privateKey, keyPasswordArray, certChainArray);
+        return keyStore;
+    }
+
+    /**
+     * Builds KeyStore object given the {@link #DEFAULT_TARGET_STORETYPE} out of the PEM formatted certificates/public-key
+     * material.
+     * <p>
+     * It uses {@code cassandra-ssl-trusted-cert-<numeric-id>} as the alias for the created certificate-entry.
+     */
+    private KeyStore buildTrustStore() throws GeneralSecurityException, IOException
+    {
+        Certificate[] certChainArray = PEMReader.extractCertificates(pemEncodedCertificates);
+        if (certChainArray == null || certChainArray.length == 0)
+        {
+            throw new SSLException("Could not read any certificates from the given PEM");
+        }
+
+        KeyStore keyStore = KeyStore.getInstance(DEFAULT_TARGET_STORETYPE);
+        keyStore.load(null, null);
+        for (int i = 0; i < certChainArray.length; i++)
+        {
+            keyStore.setCertificateEntry("cassandra-ssl-trusted-cert-" + (i + 1), certChainArray[i]);
+        }
+        return keyStore;
+    }
+
+    /**
+     * Enforces that the configuration specified a sole source of loading private keys - either {@code keystore} (the
+     * actual file must exist) or {@code private_key}, not both.
+     */
+    private void enforceSinglePrivateKeySource()
+    {
+        if (keystoreFileExists() && !StringUtils.isEmpty(pemEncodedKey))
+        {
+            throw new IllegalArgumentException("Configuration must specify value for either keystore or private_key, " +
+                                               "not both for PEMBasedSSlContextFactory");
+        }
+    }
+
+    /**
+     * Enforces that the configuration specified a sole source of loading trusted certificates - either {@code
+     * truststore} (actual file must exist) or {@code trusted_certificates}, not both.
+     */
+    private void enforceSingleTurstedCertificatesSource()
+    {
+        if (truststoreFileExists() && !StringUtils.isEmpty(pemEncodedCertificates))
+        {
+            throw new IllegalArgumentException("Configuration must specify value for either truststore or " +
+                                               "trusted_certificates, not both for PEMBasedSSlContextFactory");
+        }
+    }
+
+    public enum ConfigKey
+    {
+        ENCODED_KEY("private_key"),
+        KEY_PASSWORD("private_key_password"),
+        ENCODED_CERTIFICATES("trusted_certificates");
+
+        final String keyName;
+
+        ConfigKey(String keyName)
+        {
+            this.keyName = keyName;
+        }
+
+        String getKeyName()
+        {
+            return keyName;
+        }
+    }
+}
diff --git a/src/java/org/apache/cassandra/security/PEMReader.java b/src/java/org/apache/cassandra/security/PEMReader.java
new file mode 100644
index 0000000..f539509
--- /dev/null
+++ b/src/java/org/apache/cassandra/security/PEMReader.java
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.EncryptedPrivateKeyInfo;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+import com.google.common.collect.ImmutableSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+
+/**
+ * This is a helper class to read private keys and X509 certifificates encoded based on <a href="https://datatracker.ietf.org/doc/html/rfc1421">PEM (RFC 1421)</a>
+ * format. It can read Password Based Encrypted (PBE henceforth) private keys as well as non-encrypted private keys
+ * along with the X509 certificates/cert-chain based on the textual encoding defined in the <a href="https://datatracker.ietf.org/doc/html/rfc7468">RFC 7468</a>
+ * <p>
+ * The input private key must be in PKCS#8 format.
+ * <p>
+ * It returns PKCS#8 formatted private key and X509 certificates.
+ */
+public final class PEMReader
+{
+    /**
+     * The private key can be with any of these algorithms in order for this read to successfully parse it.
+     * Currently, supported algorithms are,
+     * <pre>
+     *     RSA, DSA or EC
+     * </pre>
+     * The first one to be evaluated is RSA, being the most common for private keys.
+     */
+    public static final Set<String> SUPPORTED_PRIVATE_KEY_ALGORITHMS = ImmutableSet.of("RSA", "DSA", "EC");
+    private static final Logger logger = LoggerFactory.getLogger(PEMReader.class);
+    private static final Pattern CERT_PATTERN = Pattern.compile("-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+([a-z0-9+/=\\r\\n]+)-+END\\s+.*CERTIFICATE[^-]*-+", CASE_INSENSITIVE);
+    private static final Pattern KEY_PATTERN = Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+([a-z0-9+/=\\r\\n]+)-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", CASE_INSENSITIVE);
+
+    /**
+     * Extracts private key from the PEM content for the private key, assuming its not PBE.
+     *
+     * @param unencryptedPEMKey private key stored as PEM content
+     * @return {@link PrivateKey} upon successful reading of the private key
+     * @throws IOException              in case PEM reading fails
+     * @throws GeneralSecurityException in case any issue encountered while reading the private key
+     */
+    public static PrivateKey extractPrivateKey(String unencryptedPEMKey) throws IOException, GeneralSecurityException
+    {
+        return extractPrivateKey(unencryptedPEMKey, null);
+    }
+
+    /**
+     * Extracts private key from the Password Based Encrypted PEM content for the private key.
+     *
+     * @param pemKey      PBE private key stored as PEM content
+     * @param keyPassword password to be used for the private key decryption
+     * @return {@link PrivateKey} upon successful reading of the private key
+     * @throws IOException              in case PEM reading fails
+     * @throws GeneralSecurityException in case any issue encountered while reading the private key
+     */
+    public static PrivateKey extractPrivateKey(String pemKey, String keyPassword) throws IOException,
+                                                                                         GeneralSecurityException
+    {
+        PKCS8EncodedKeySpec keySpec;
+        String base64EncodedKey = extractBase64EncodedKey(pemKey);
+        byte[] derKeyBytes = decodeBase64(base64EncodedKey);
+
+        if (keyPassword != null)
+        {
+            logger.debug("Encrypted key's length: {}, key's password length: {}",
+                         derKeyBytes.length, keyPassword.length());
+
+            EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(derKeyBytes);
+            logger.debug("Encrypted private key info's algorithm name: {}", epki.getAlgName());
+
+            AlgorithmParameters params = epki.getAlgParameters();
+            PBEKeySpec pbeKeySpec = new PBEKeySpec(keyPassword.toCharArray());
+            Key encryptionKey = SecretKeyFactory.getInstance(epki.getAlgName()).generateSecret(pbeKeySpec);
+            pbeKeySpec.clearPassword();
+            logger.debug("Key algorithm: {}, key format: {}", encryptionKey.getAlgorithm(), encryptionKey.getFormat());
+
+            Cipher cipher = Cipher.getInstance(epki.getAlgName());
+            cipher.init(Cipher.DECRYPT_MODE, encryptionKey, params);
+            byte[] rawKeyBytes;
+            try
+            {
+                rawKeyBytes = cipher.doFinal(epki.getEncryptedData());
+            }
+            catch (BadPaddingException e)
+            {
+                throw new GeneralSecurityException("Failed to decrypt the private key data. Either the password " +
+                                                   "provided for the key is wrong or the private key data is " +
+                                                   "corrupted. msg=" + e.getMessage(), e);
+            }
+            logger.debug("Decrypted private key's length: {}", rawKeyBytes.length);
+
+            keySpec = new PKCS8EncodedKeySpec(rawKeyBytes);
+        }
+        else
+        {
+            logger.debug("Key length: {}", derKeyBytes.length);
+            keySpec = new PKCS8EncodedKeySpec(derKeyBytes);
+        }
+
+        PrivateKey privateKey = null;
+
+        /*
+         * Ideally we can inspect the OID (Object Identifier) from the private key with ASN.1 parser and identify the
+         * actual algorithm of the private key. For doing that, we have to use some special library like BouncyCastle.
+         * However in the absence of that, below brute-force approach can work- that is to try out all the supported
+         * private key algorithms given that there are only three major algorithms to verify against.
+         */
+        for (String privateKeyAlgorithm : SUPPORTED_PRIVATE_KEY_ALGORITHMS)
+        {
+            try
+            {
+                privateKey = KeyFactory.getInstance(privateKeyAlgorithm).generatePrivate(keySpec);
+                logger.info("Parsing for the private key finished with {} algorithm.", privateKeyAlgorithm);
+                return privateKey;
+            }
+            catch (Exception e)
+            {
+                logger.debug("Failed to parse the private key with {} algorithm. Will try the other supported " +
+                             "algorithms.", privateKeyAlgorithm);
+            }
+        }
+        throw new GeneralSecurityException("The given private key could not be parsed with any of the supported " +
+                                           "algorithms. Please see PEMReader#SUPPORTED_PRIVATE_KEY_ALGORITHMS.");
+    }
+
+    /**
+     * Extracts the certificates/cert-chain from the PEM content.
+     *
+     * @param pemCerts certificates/cert-chain stored as PEM content
+     * @return X509 certiificate list
+     * @throws GeneralSecurityException in case any issue encountered while reading the certificates
+     */
+    public static Certificate[] extractCertificates(String pemCerts) throws GeneralSecurityException
+    {
+        List<Certificate> certificateList = new ArrayList<>();
+        List<String> base64EncodedCerts = extractBase64EncodedCerts(pemCerts);
+        for (String base64EncodedCertificate : base64EncodedCerts)
+        {
+            certificateList.add(generateCertificate(base64EncodedCertificate));
+        }
+        Certificate[] certificates = certificateList.toArray(new Certificate[0]);
+        return certificates;
+    }
+
+    /**
+     * Generates the X509 certificate object given the base64 encoded PEM content.
+     *
+     * @param base64Certificate base64 encoded PEM content for the certificate
+     * @return X509 certificate
+     * @throws GeneralSecurityException in case any issue encountered while reading the certificate
+     */
+    private static Certificate generateCertificate(String base64Certificate) throws GeneralSecurityException
+    {
+        byte[] decodedCertificateBytes = decodeBase64(base64Certificate);
+        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate certificate =
+        (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(decodedCertificateBytes));
+        logCertificateDetails(certificate);
+        return certificate;
+    }
+
+    /**
+     * Logs X509 certificate details for the debugging purpose with {@code INFO} level log.
+     * Namely, it prints - Subject DN, Issuer DN, Certificate serial number and the certificate expiry date which
+     * could be very valuable for debugging any certificate related issues.
+     *
+     * @param certificate certificate to log
+     */
+    private static void logCertificateDetails(X509Certificate certificate)
+    {
+        assert certificate != null;
+        logger.info("*********** Certificate Details *****************");
+        logger.info("Subject DN: {}", certificate.getSubjectDN());
+        logger.info("Issuer DN: {}", certificate.getIssuerDN());
+        logger.info("Serial Number: {}", certificate.getSerialNumber());
+        logger.info("Expiry: {}", certificate.getNotAfter());
+    }
+
+    /**
+     * Parses the PEM formatted private key based on the standard pattern specified by the <a href="https://datatracker.ietf.org/doc/html/rfc7468#section-11">RFC 7468</a>.
+     *
+     * @param pemKey private key stored as PEM content
+     * @return base64 string contained within the defined encapsulation boundaries by the above RFC
+     * @throws GeneralSecurityException in case any issue encountered while parsing the key
+     */
+    private static String extractBase64EncodedKey(String pemKey) throws GeneralSecurityException
+    {
+        Matcher matcher = KEY_PATTERN.matcher(pemKey);
+        if (matcher.find())
+        {
+            return matcher.group(1).replaceAll("\\s", "");
+        }
+        else
+        {
+            throw new GeneralSecurityException("Invalid private key format");
+        }
+    }
+
+    /**
+     * Parses the PEM formatted certificate/public-key based on the standard pattern specified by the
+     * <a href="https://datatracker.ietf.org/doc/html/rfc7468#section-13">RFC 7468</a>.
+     *
+     * @param pemCerts certificate/public-key stored as PEM content
+     * @return list of base64 encoded certificates within the defined encapsulation boundaries by the above RFC
+     * @throws GeneralSecurityException in case any issue encountered parsing the certificate
+     */
+    private static List<String> extractBase64EncodedCerts(String pemCerts) throws GeneralSecurityException
+    {
+        List<String> certificateList = new ArrayList<>();
+        Matcher matcher = CERT_PATTERN.matcher(pemCerts);
+        if (!matcher.find())
+        {
+            throw new GeneralSecurityException("Invalid certificate format");
+        }
+
+        for (int start = 0; matcher.find(start); start = matcher.end())
+        {
+            String certificate = matcher.group(1).replaceAll("\\s", "");
+            certificateList.add(certificate);
+        }
+        return certificateList;
+    }
+
+    /**
+     * Decodes given input in Base64 format.
+     *
+     * @param base64Input input to be decoded
+     * @return byte[] containing decoded bytes
+     * @throws GeneralSecurityException in case it fails to decode the given base64 input
+     */
+    private static byte[] decodeBase64(String base64Input) throws GeneralSecurityException
+    {
+        try
+        {
+            return Base64.getDecoder().decode(base64Input);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new GeneralSecurityException("Failed to decode given base64 input. msg=" + e.getMessage(), e);
+        }
+    }
+}
diff --git a/test/conf/cassandra-pem-jks-sslcontextfactory.yaml b/test/conf/cassandra-pem-jks-sslcontextfactory.yaml
new file mode 100644
index 0000000..1f10e6c
--- /dev/null
+++ b/test/conf/cassandra-pem-jks-sslcontextfactory.yaml
@@ -0,0 +1,150 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Testing for pluggable ssl_context_factory option for client and server encryption options with a valid and a missing
+# implementation classes.
+#
+cluster_name: Test Cluster
+# memtable_allocation_type: heap_buffers
+memtable_allocation_type: offheap_objects
+commitlog_sync: batch
+commitlog_segment_size: 5MiB
+commitlog_directory: build/test/cassandra/commitlog
+# commitlog_compression:
+# - class_name: LZ4Compressor
+cdc_raw_directory: build/test/cassandra/cdc_raw
+cdc_enabled: false
+hints_directory: build/test/cassandra/hints
+partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner
+listen_address: 127.0.0.1
+storage_port: 7012
+ssl_storage_port: 17012
+start_native_transport: true
+native_transport_port: 9042
+column_index_size: 4KiB
+saved_caches_directory: build/test/cassandra/saved_caches
+data_file_directories:
+    - build/test/cassandra/data
+disk_access_mode: mmap
+seed_provider:
+    - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+      parameters:
+          - seeds: "127.0.0.1:7012"
+endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch
+dynamic_snitch: true
+client_encryption_options:
+    ssl_context_factory:
+        class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
+        parameters:
+          private_key: |
+            -----BEGIN ENCRYPTED PRIVATE KEY-----
+            MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
+            g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
+            xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
+            L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
+            sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
+            f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
+            RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
+            0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
+            evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
+            tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
+            wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
+            K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
+            zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
+            mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
+            WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
+            jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
+            eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
+            DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
+            AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
+            Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
+            Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
+            ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
+            bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
+            hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
+            RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
+            pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
+            FujZhqbKUDbYAcqTkoQ=
+            -----END ENCRYPTED PRIVATE KEY-----
+            -----BEGIN CERTIFICATE-----
+            MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+            bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+            VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+            Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+            EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+            a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+            FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+            MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+            ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+            q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+            TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+            TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+            YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+            N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+            iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+            IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+            6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+            qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+            HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+            n3MVF9w=
+            -----END CERTIFICATE-----
+          private_key_password: "cassandra"
+          trusted_certificates: |
+            -----BEGIN CERTIFICATE-----
+            MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+            bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+            VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+            Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+            EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+            a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+            FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+            MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+            ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+            q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+            TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+            TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+            YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+            N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+            iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+            IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+            6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+            qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+            HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+            n3MVF9w=
+            -----END CERTIFICATE-----
+server_encryption_options:
+    internode_encryption: none
+    keystore: test/conf/cassandra_ssl_test.keystore
+    keystore_password: cassandra
+    truststore: test/conf/cassandra_ssl_test.truststore
+    truststore_password: cassandra
+incremental_backups: true
+concurrent_compactors: 4
+compaction_throughput: 0MiB/s
+row_cache_class_name: org.apache.cassandra.cache.OHCProvider
+row_cache_size: 16MiB
+user_defined_functions_enabled: true
+scripted_user_defined_functions_enabled: true
+prepared_statements_cache_size: 1MiB
+corrupted_tombstone_strategy: exception
+stream_entire_sstables: true
+stream_throughput_outbound: 24MiB/s
+sasi_indexes_enabled: true
+materialized_views_enabled: true
+file_cache_enabled: true
diff --git a/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml b/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml
new file mode 100644
index 0000000..8c7d910
--- /dev/null
+++ b/test/conf/cassandra-pem-sslcontextfactory-invalidconfiguration.yaml
@@ -0,0 +1,147 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Testing for pluggable ssl_context_factory option for client and server encryption options with a valid and a missing
+# implementation classes.
+#
+cluster_name: Test Cluster
+# memtable_allocation_type: heap_buffers
+memtable_allocation_type: offheap_objects
+commitlog_sync: batch
+commitlog_segment_size: 5MiB
+commitlog_directory: build/test/cassandra/commitlog
+# commitlog_compression:
+# - class_name: LZ4Compressor
+cdc_raw_directory: build/test/cassandra/cdc_raw
+cdc_enabled: false
+hints_directory: build/test/cassandra/hints
+partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner
+listen_address: 127.0.0.1
+storage_port: 7012
+ssl_storage_port: 17012
+start_native_transport: true
+native_transport_port: 9042
+column_index_size: 4KiB
+saved_caches_directory: build/test/cassandra/saved_caches
+data_file_directories:
+    - build/test/cassandra/data
+disk_access_mode: mmap
+seed_provider:
+    - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+      parameters:
+          - seeds: "127.0.0.1:7012"
+endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch
+dynamic_snitch: true
+client_encryption_options:
+    ssl_context_factory:
+        class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
+        parameters:
+            private_key: |
+             -----BEGIN ENCRYPTED PRIVATE KEY-----
+             MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
+             g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
+             xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
+             L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
+             sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
+             f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
+             RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
+             0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
+             evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
+             tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
+             wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
+             K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
+             zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
+             mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
+             WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
+             jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
+             eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
+             DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
+             AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
+             Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
+             Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
+             ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
+             bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
+             hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
+             RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
+             pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
+             FujZhqbKUDbYAcqTkoQ=
+             -----END ENCRYPTED PRIVATE KEY-----
+             -----BEGIN CERTIFICATE-----
+             MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+             bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+             VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+             Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+             EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+             a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+             FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+             MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+             ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+             q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+             TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+             TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+             YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+             N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+             iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+             IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+             6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+             qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+             HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+             n3MVF9w=
+             -----END CERTIFICATE-----
+            private_key_password: "cassandra"
+            trusted_certificates: |
+              -----BEGIN CERTIFICATE-----
+              MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+              bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+              VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+              Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+              EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+              a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+              FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+              MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+              ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+              q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+              TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+              TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+              YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+              N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+              iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+              IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+              6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+              qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+              HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+              n3MVF9w=
+              -----END CERTIFICATE-----
+    keystore: test/conf/cassandra_ssl_test.keystore.pem
+    keystore_password: cassandra
+    truststore: test/conf/cassandra_ssl_test.truststore.pem
+incremental_backups: true
+concurrent_compactors: 4
+compaction_throughput: 0MiB/s
+row_cache_class_name: org.apache.cassandra.cache.OHCProvider
+row_cache_size: 16MiB
+user_defined_functions_enabled: true
+scripted_user_defined_functions_enabled: true
+prepared_statements_cache_size: 1MiB
+corrupted_tombstone_strategy: exception
+stream_entire_sstables: true
+stream_throughput_outbound: 24MiB/s
+sasi_indexes_enabled: true
+materialized_views_enabled: true
+file_cache_enabled: true
diff --git a/test/conf/cassandra-pem-sslcontextfactory.yaml b/test/conf/cassandra-pem-sslcontextfactory.yaml
new file mode 100644
index 0000000..26d0f1f
--- /dev/null
+++ b/test/conf/cassandra-pem-sslcontextfactory.yaml
@@ -0,0 +1,151 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Testing for pluggable ssl_context_factory option for client and server encryption options with a valid and a missing
+# implementation classes.
+#
+cluster_name: Test Cluster
+# memtable_allocation_type: heap_buffers
+memtable_allocation_type: offheap_objects
+commitlog_sync: batch
+commitlog_segment_size: 5MiB
+commitlog_directory: build/test/cassandra/commitlog
+# commitlog_compression:
+# - class_name: LZ4Compressor
+cdc_raw_directory: build/test/cassandra/cdc_raw
+cdc_enabled: false
+hints_directory: build/test/cassandra/hints
+partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner
+listen_address: 127.0.0.1
+storage_port: 7012
+ssl_storage_port: 17012
+start_native_transport: true
+native_transport_port: 9042
+column_index_size: 4KiB
+saved_caches_directory: build/test/cassandra/saved_caches
+data_file_directories:
+    - build/test/cassandra/data
+disk_access_mode: mmap
+seed_provider:
+    - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+      parameters:
+          - seeds: "127.0.0.1:7012"
+endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch
+dynamic_snitch: true
+client_encryption_options:
+    ssl_context_factory:
+        class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
+        parameters:
+            private_key: |
+             -----BEGIN ENCRYPTED PRIVATE KEY-----
+             MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
+             g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
+             xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
+             L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
+             sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
+             f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
+             RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
+             0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
+             evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
+             tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
+             wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
+             K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
+             zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
+             mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
+             WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
+             jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
+             eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
+             DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
+             AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
+             Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
+             Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
+             ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
+             bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
+             hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
+             RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
+             pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
+             FujZhqbKUDbYAcqTkoQ=
+             -----END ENCRYPTED PRIVATE KEY-----
+             -----BEGIN CERTIFICATE-----
+             MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+             bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+             VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+             Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+             EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+             a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+             FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+             MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+             ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+             q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+             TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+             TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+             YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+             N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+             iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+             IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+             6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+             qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+             HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+             n3MVF9w=
+             -----END CERTIFICATE-----
+            private_key_password: "cassandra"
+            trusted_certificates: |
+              -----BEGIN CERTIFICATE-----
+              MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+              bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+              VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+              Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+              EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+              a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+              FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+              MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+              ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+              q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+              TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+              TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+              YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+              N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+              iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+              IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+              6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+              qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+              HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+              n3MVF9w=
+              -----END CERTIFICATE-----
+server_encryption_options:
+    ssl_context_factory:
+        class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
+    internode_encryption: none
+    keystore: test/conf/cassandra_ssl_test.keystore.pem
+    keystore_password: cassandra
+    truststore: test/conf/cassandra_ssl_test.truststore.pem
+incremental_backups: true
+concurrent_compactors: 4
+compaction_throughput: 0MiB/s
+row_cache_class_name: org.apache.cassandra.cache.OHCProvider
+row_cache_size: 16MiB
+user_defined_functions_enabled: true
+scripted_user_defined_functions_enabled: true
+prepared_statements_cache_size: 1MiB
+corrupted_tombstone_strategy: exception
+stream_entire_sstables: true
+stream_throughput_outbound: 24MiB/s
+sasi_indexes_enabled: true
+materialized_views_enabled: true
+file_cache_enabled: true
diff --git a/test/conf/cassandra_ssl_test.keystore.pem b/test/conf/cassandra_ssl_test.keystore.pem
new file mode 100644
index 0000000..ed981cc
--- /dev/null
+++ b/test/conf/cassandra_ssl_test.keystore.pem
@@ -0,0 +1,51 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
+g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
+xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
+L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
+sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
+f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
+RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
+0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
+evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
+tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
+wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
+K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
+zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
+mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
+WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
+jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
+eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
+DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
+AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
+Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
+Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
+ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
+bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
+hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
+RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
+pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
+FujZhqbKUDbYAcqTkoQ=
+-----END ENCRYPTED PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+n3MVF9w=
+-----END CERTIFICATE-----
diff --git a/test/conf/cassandra_ssl_test.truststore.pem b/test/conf/cassandra_ssl_test.truststore.pem
new file mode 100644
index 0000000..8806ce8
--- /dev/null
+++ b/test/conf/cassandra_ssl_test.truststore.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+n3MVF9w=
+-----END CERTIFICATE-----
diff --git a/test/conf/cassandra_ssl_test.unencrypted_keystore.pem b/test/conf/cassandra_ssl_test.unencrypted_keystore.pem
new file mode 100644
index 0000000..ce3d8e7
--- /dev/null
+++ b/test/conf/cassandra_ssl_test.unencrypted_keystore.pem
@@ -0,0 +1,50 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOSZVf8dLj1xLw
+efqjbogbAwhwRXd3tmEfQY1zHyudJF3XR1T0Xp26BKNvAYUxxZRDNg16M3prPRZv
+wkhDJdE9NaN+BpZPJUavRsZOfGDq/CHN8j/2PPKrn3G/b065JjV3GZjfV/Sln047
+DeZptaSyOg7ZN7F8qjGqxEcnw+szV/wTzhnjGjZMlcbOm4jFGQAn6xUOooQCGsoB
+9FgxKB0nvNG3xVe/2eCNfbS4DabT3Y1wfQqZ62hOa5ZS0rwT+pw3tQs3zFFY1Sfi
+G7qYbqZrKTheWIZGojVVm6mqH4yA2ofOe5N3RsBitCwU/D0dpnaG9Wcl98nmXueM
+B6Rk04v7AgMBAAECggEAYnxIKjrFz/JkJ5MmiszM5HV698r9YB0aqHnFIHPoykIL
+uiCjiumantDrFsCkosixULwvI/BRwbxstTpyrheU9psT6P1CONICVPvV8ylgJAYU
+l+ofn56cEXKxVuICSWFLDH7pM1479g+IJJQAchbKQpqxAGTuMu3SpvJolfuj5srt
+bM7/RYhJFLwDuvHNA3ivlogMneItP03+C25aaxstM+lBuBf68+n78zMgSvt6J/6Y
+G2TOMKnxveMlG2qu9l2lAw/2i8daG/qre08nTH7wpRx0gZLZqNpe45exkrzticzF
+FgWYjG2K2brX21jqHroFgMhdXF7zhhRgLoIeC0BrIQKBgQDCfGfWrJESKBbVai5u
+7wqD9nlzjv6N6FXfTDOPXO1vz5frdvtLVWbs0SMPy+NglkaZK0iqHvb9mf2of8eC
+0D5cmewjn7WCDBQMypIMYgT912ak/BBVuGXcxb6UgD+xARfSARo2C8NG1hfprw1W
+ad14CjS5xhFMs44HpVYhI7iPYwKBgQC7SqVG/b37vZ7CINemdvoMujLvvYXDJJM8
+N21LqNJfVXdukdH3T0xuLnh9Z/wPHjJDMF/9+1foxSEPHijtyz5P19EilNEC/3qw
+fI19+VZoY0mdhPtXSGzy+rbTE2v71QgwFLizSos14Gr+eNiIjF7FYccK05++K/zk
+cd8ZA3bwiQKBgQCl+HTFBs9mpz+VMOAfW2+l3hkXPNiPUc62mNkHZ05ZNNd44jjh
+uSf0wSUiveR08MmevQlt5K7zDQ8jVKh2QjB15gVXAVxsdtJFeDnax2trFP9LnLBz
+9sE2/qn9INU5wK0LUlWD+dXUBbCyg+jl7cJKRqtoPldVFYYHkFlIPqup8QKBgHXv
+hyuw1FUVDkdHzwOvn70r8q8sNHKxMVWVwWkHIZGOi+pAQGrusD4hXRX6yKnsZdIR
+QCD6iFy25R5T64nxlYdJaxPPid3NakB/7ckJnPOWseBSwMIxhQlr/nvjmve1Kba9
+FaEwq4B9lGIxToiNe4/nBiM3JzvlDxX67nUdzWOhAoGAdFvriyvjshSJ4JHgIY9K
+37BVB0VKMcFV2P8fLVWO5oyRtE1bJhU4QVpQmauABU4RGSojJ3NPIVH1wxmJeYtj
+Q3b7EZaqI6ovna2eK2qtUx4WwxhRaXTT8xueBI2lgL6sBSTGG+K69ZOzGQzG/Mfr
+RXKInnLInFD9JD94VqmMozo=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
+bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
+VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
+Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
+EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
+a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
+FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
+ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
+q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
+TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
+TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
+YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
+N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
+iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
+IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
+6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
+qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
+HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
+n3MVF9w=
+-----END CERTIFICATE-----
diff --git a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryConfigTest.java b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryConfigTest.java
new file mode 100644
index 0000000..49a3653
--- /dev/null
+++ b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryConfigTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import javax.net.ssl.SSLException;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.exceptions.ConfigurationException;
+
+public class PEMBasedSslContextFactoryConfigTest
+{
+    @BeforeClass
+    public static void setupDatabaseDescriptor()
+    {
+        System.setProperty("cassandra.config", "cassandra-pem-sslcontextfactory.yaml");
+    }
+
+    @AfterClass
+    public static void tearDownDatabaseDescriptor()
+    {
+        System.clearProperty("cassandra.config");
+    }
+
+    @Test
+    public void testHappyPathInlinePEM() throws SSLException
+    {
+
+        Config config = DatabaseDescriptor.loadConfig();
+        config.client_encryption_options.applyConfig();
+
+        Assert.assertEquals("org.apache.cassandra.security.PEMBasedSslContextFactory",
+                            config.client_encryption_options.ssl_context_factory.class_name);
+        Assert.assertEquals(config.client_encryption_options.ssl_context_factory.class_name,
+                            config.client_encryption_options.sslContextFactoryInstance.getClass().getName());
+        PEMBasedSslContextFactory sslContextFactory =
+        (PEMBasedSslContextFactory) config.client_encryption_options.sslContextFactoryInstance;
+        sslContextFactory.buildKeyManagerFactory();
+        sslContextFactory.buildTrustManagerFactory();
+    }
+
+    @Test
+    public void testHappyPathFileBasedPEM() throws SSLException
+    {
+
+        Config config = DatabaseDescriptor.loadConfig();
+        config.server_encryption_options.applyConfig();
+
+        Assert.assertEquals("org.apache.cassandra.security.PEMBasedSslContextFactory",
+                            config.server_encryption_options.ssl_context_factory.class_name);
+        Assert.assertEquals(config.server_encryption_options.ssl_context_factory.class_name,
+                            config.server_encryption_options.sslContextFactoryInstance.getClass().getName());
+        PEMBasedSslContextFactory sslContextFactory =
+        (PEMBasedSslContextFactory) config.server_encryption_options.sslContextFactoryInstance;
+        sslContextFactory.buildKeyManagerFactory();
+        sslContextFactory.buildTrustManagerFactory();
+    }
+}
diff --git a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryInvalidConfigTest.java b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryInvalidConfigTest.java
new file mode 100644
index 0000000..2281137
--- /dev/null
+++ b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryInvalidConfigTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import javax.net.ssl.SSLException;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.exceptions.ConfigurationException;
+
+public class PEMBasedSslContextFactoryInvalidConfigTest
+{
+    @BeforeClass
+    public static void setupDatabaseDescriptor()
+    {
+        System.setProperty("cassandra.config", "cassandra-pem-sslcontextfactory-invalidconfiguration.yaml");
+    }
+
+    @AfterClass
+    public static void tearDownDatabaseDescriptor()
+    {
+        System.clearProperty("cassandra.config");
+    }
+
+    @Test(expected = ConfigurationException.class)
+    public void testFileBasedAndInlinePEMConfiguration() throws SSLException
+    {
+
+        Config config = DatabaseDescriptor.loadConfig();
+        config.client_encryption_options.applyConfig();
+
+        Assert.assertEquals("org.apache.cassandra.security.PEMBasedSslContextFactory",
+                            config.client_encryption_options.ssl_context_factory.class_name);
+        Assert.assertEquals(config.client_encryption_options.ssl_context_factory.class_name,
+                            config.client_encryption_options.sslContextFactoryInstance.getClass().getName());
+        PEMBasedSslContextFactory sslContextFactory =
+        (PEMBasedSslContextFactory) config.client_encryption_options.sslContextFactoryInstance;
+        sslContextFactory.buildKeyManagerFactory();
+        sslContextFactory.buildTrustManagerFactory();
+    }
+}
diff --git a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
new file mode 100644
index 0000000..243d300
--- /dev/null
+++ b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
@@ -0,0 +1,449 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.OpenSslContext;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslProvider;
+import org.apache.cassandra.config.EncryptionOptions;
+import org.apache.cassandra.config.ParameterizedClass;
+
+import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.ENCODED_CERTIFICATES;
+import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.ENCODED_KEY;
+import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.KEY_PASSWORD;
+
+public class PEMBasedSslContextFactoryTest
+{
+    private static final String private_key =
+    "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+    "MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/\n" +
+    "g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl\n" +
+    "xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29\n" +
+    "L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V\n" +
+    "sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/\n" +
+    "f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8\n" +
+    "RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR\n" +
+    "0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs\n" +
+    "evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU\n" +
+    "tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6\n" +
+    "wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN\n" +
+    "K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv\n" +
+    "zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5\n" +
+    "mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo\n" +
+    "WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ\n" +
+    "jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6\n" +
+    "eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny\n" +
+    "DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn\n" +
+    "AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY\n" +
+    "Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow\n" +
+    "Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut\n" +
+    "ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr\n" +
+    "bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH\n" +
+    "hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI\n" +
+    "RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw\n" +
+    "pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP\n" +
+    "FujZhqbKUDbYAcqTkoQ=\n" +
+    "-----END ENCRYPTED PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9w=\n" +
+    "-----END CERTIFICATE-----";
+    private static final String unencrypted_private_key =
+    "-----BEGIN PRIVATE KEY-----\n" +
+    "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOSZVf8dLj1xLw\n" +
+    "efqjbogbAwhwRXd3tmEfQY1zHyudJF3XR1T0Xp26BKNvAYUxxZRDNg16M3prPRZv\n" +
+    "wkhDJdE9NaN+BpZPJUavRsZOfGDq/CHN8j/2PPKrn3G/b065JjV3GZjfV/Sln047\n" +
+    "DeZptaSyOg7ZN7F8qjGqxEcnw+szV/wTzhnjGjZMlcbOm4jFGQAn6xUOooQCGsoB\n" +
+    "9FgxKB0nvNG3xVe/2eCNfbS4DabT3Y1wfQqZ62hOa5ZS0rwT+pw3tQs3zFFY1Sfi\n" +
+    "G7qYbqZrKTheWIZGojVVm6mqH4yA2ofOe5N3RsBitCwU/D0dpnaG9Wcl98nmXueM\n" +
+    "B6Rk04v7AgMBAAECggEAYnxIKjrFz/JkJ5MmiszM5HV698r9YB0aqHnFIHPoykIL\n" +
+    "uiCjiumantDrFsCkosixULwvI/BRwbxstTpyrheU9psT6P1CONICVPvV8ylgJAYU\n" +
+    "l+ofn56cEXKxVuICSWFLDH7pM1479g+IJJQAchbKQpqxAGTuMu3SpvJolfuj5srt\n" +
+    "bM7/RYhJFLwDuvHNA3ivlogMneItP03+C25aaxstM+lBuBf68+n78zMgSvt6J/6Y\n" +
+    "G2TOMKnxveMlG2qu9l2lAw/2i8daG/qre08nTH7wpRx0gZLZqNpe45exkrzticzF\n" +
+    "FgWYjG2K2brX21jqHroFgMhdXF7zhhRgLoIeC0BrIQKBgQDCfGfWrJESKBbVai5u\n" +
+    "7wqD9nlzjv6N6FXfTDOPXO1vz5frdvtLVWbs0SMPy+NglkaZK0iqHvb9mf2of8eC\n" +
+    "0D5cmewjn7WCDBQMypIMYgT912ak/BBVuGXcxb6UgD+xARfSARo2C8NG1hfprw1W\n" +
+    "ad14CjS5xhFMs44HpVYhI7iPYwKBgQC7SqVG/b37vZ7CINemdvoMujLvvYXDJJM8\n" +
+    "N21LqNJfVXdukdH3T0xuLnh9Z/wPHjJDMF/9+1foxSEPHijtyz5P19EilNEC/3qw\n" +
+    "fI19+VZoY0mdhPtXSGzy+rbTE2v71QgwFLizSos14Gr+eNiIjF7FYccK05++K/zk\n" +
+    "cd8ZA3bwiQKBgQCl+HTFBs9mpz+VMOAfW2+l3hkXPNiPUc62mNkHZ05ZNNd44jjh\n" +
+    "uSf0wSUiveR08MmevQlt5K7zDQ8jVKh2QjB15gVXAVxsdtJFeDnax2trFP9LnLBz\n" +
+    "9sE2/qn9INU5wK0LUlWD+dXUBbCyg+jl7cJKRqtoPldVFYYHkFlIPqup8QKBgHXv\n" +
+    "hyuw1FUVDkdHzwOvn70r8q8sNHKxMVWVwWkHIZGOi+pAQGrusD4hXRX6yKnsZdIR\n" +
+    "QCD6iFy25R5T64nxlYdJaxPPid3NakB/7ckJnPOWseBSwMIxhQlr/nvjmve1Kba9\n" +
+    "FaEwq4B9lGIxToiNe4/nBiM3JzvlDxX67nUdzWOhAoGAdFvriyvjshSJ4JHgIY9K\n" +
+    "37BVB0VKMcFV2P8fLVWO5oyRtE1bJhU4QVpQmauABU4RGSojJ3NPIVH1wxmJeYtj\n" +
+    "Q3b7EZaqI6ovna2eK2qtUx4WwxhRaXTT8xueBI2lgL6sBSTGG+K69ZOzGQzG/Mfr\n" +
+    "RXKInnLInFD9JD94VqmMozo=\n" +
+    "-----END PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9w=\n" +
+    "-----END CERTIFICATE-----";
+    private static final String trusted_certificates =
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9w=\n" +
+    "-----END CERTIFICATE-----";
+    private final Map<String, Object> commonConfig = new HashMap<>();
+
+    @Before
+    public void setup()
+    {
+        commonConfig.put(ENCODED_CERTIFICATES.getKeyName(), trusted_certificates);
+        commonConfig.put("require_client_auth", Boolean.FALSE);
+        commonConfig.put("cipher_suites", Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
+    }
+
+    private void addKeyStoreOptions(Map<String, Object> config)
+    {
+        config.put(ENCODED_KEY.getKeyName(), private_key);
+        config.put(KEY_PASSWORD.getKeyName(), "cassandra");
+    }
+
+    private void addUnencryptedKeyStoreOptions(Map<String, Object> config)
+    {
+        config.put(ENCODED_KEY.getKeyName(), unencrypted_private_key);
+    }
+
+    private void addFileBaseTrustStoreOptions(Map<String, Object> config)
+    {
+        config.put("truststore", "test/conf/cassandra_ssl_test.truststore.pem");
+    }
+
+    private void addFileBaseKeyStoreOptions(Map<String, Object> config)
+    {
+        config.put("keystore", "test/conf/cassandra_ssl_test.keystore.pem");
+        config.put("keystore_password", "cassandra");
+    }
+
+    private void addFileBaseUnencryptedKeyStoreOptions(Map<String, Object> config)
+    {
+        config.put("keystore", "test/conf/cassandra_ssl_test.unencrypted_keystore.pem");
+    }
+
+    @Test
+    public void getSslContextOpenSSL() throws IOException
+    {
+        ParameterizedClass sslContextFactory = new ParameterizedClass(PEMBasedSslContextFactory.class.getSimpleName()
+        , new HashMap<>());
+        EncryptionOptions options = new EncryptionOptions().withTrustStore("test/conf/cassandra_ssl_test.truststore.pem")
+                                                           .withKeyStore("test/conf/cassandra_ssl_test.keystore.pem")
+                                                           .withKeyStorePassword("cassandra")
+                                                           .withRequireClientAuth(false)
+                                                           .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
+                                                           .withSslContextFactory(sslContextFactory);
+        SslContext sslContext = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT);
+        Assert.assertNotNull(sslContext);
+        if (OpenSsl.isAvailable())
+            Assert.assertTrue(sslContext instanceof OpenSslContext);
+        else
+            Assert.assertTrue(sslContext instanceof SslContext);
+    }
+
+    @Test(expected = IOException.class)
+    public void buildTrustManagerFactoryWithInvalidTruststoreFile() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        config.remove("encoded_certificates");
+        config.put("truststore", "/this/is/probably/not/a/file/on/your/test/machine");
+
+        DefaultSslContextFactory defaultSslContextFactoryImpl = new DefaultSslContextFactory(config);
+        defaultSslContextFactoryImpl.checkedExpiry = false;
+        defaultSslContextFactoryImpl.buildTrustManagerFactory();
+    }
+
+    @Test
+    public void buildTrustManagerFactoryHappyPath() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.checkedExpiry = false;
+        TrustManagerFactory trustManagerFactory = sslContextFactory.buildTrustManagerFactory();
+        Assert.assertNotNull(trustManagerFactory);
+    }
+
+    @Test
+    public void buildFileBasedTrustManagerFactoryHappyPath() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        config.remove(ENCODED_CERTIFICATES.getKeyName());
+        addFileBaseTrustStoreOptions(config);
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.checkedExpiry = false;
+        TrustManagerFactory trustManagerFactory = sslContextFactory.buildTrustManagerFactory();
+        Assert.assertNotNull(trustManagerFactory);
+    }
+
+    @Test(expected = IOException.class)
+    public void buildKeyManagerFactoryWithInvalidKeystoreFile() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        config.put("keystore", "/this/is/probably/not/a/file/on/your/test/machine");
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.checkedExpiry = false;
+        sslContextFactory.buildKeyManagerFactory();
+    }
+
+    @Test(expected = IOException.class)
+    public void buildKeyManagerFactoryWithBadPassword() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addKeyStoreOptions(config);
+        config.put("keystore_password", "HomeOfBadPasswords");
+
+        DefaultSslContextFactory defaultSslContextFactoryImpl = new DefaultSslContextFactory(config);
+        defaultSslContextFactoryImpl.buildKeyManagerFactory();
+    }
+
+    @Test
+    public void buildKeyManagerFactoryHappyPath() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        PEMBasedSslContextFactory sslContextFactory1 = new PEMBasedSslContextFactory(config);
+        // Make sure the exiry check didn't happen so far for the private key
+        Assert.assertFalse(sslContextFactory1.checkedExpiry);
+
+        addKeyStoreOptions(config);
+        PEMBasedSslContextFactory sslContextFactory2 = new PEMBasedSslContextFactory(config);
+        // Trigger the private key loading. That will also check for expired private key
+        sslContextFactory2.buildKeyManagerFactory();
+        // Now we should have checked the private key's expiry
+        Assert.assertTrue(sslContextFactory2.checkedExpiry);
+
+        // Make sure that new factory object preforms the fresh private key expiry check
+        PEMBasedSslContextFactory sslContextFactory3 = new PEMBasedSslContextFactory(config);
+        Assert.assertFalse(sslContextFactory3.checkedExpiry);
+        sslContextFactory3.buildKeyManagerFactory();
+        Assert.assertTrue(sslContextFactory3.checkedExpiry);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void buildKeyManagerFactoryWithConflictingPasswordConfigs() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addKeyStoreOptions(config);
+        config.put("keystore_password", config.get("keyPassword") + "-conflict");
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.buildKeyManagerFactory();
+    }
+
+    @Test
+    public void buildKeyManagerFactoryWithMatchingPasswordConfigs() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addKeyStoreOptions(config);
+        config.put("keystore_password", config.get("keyPassword"));
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.buildKeyManagerFactory();
+    }
+
+    @Test
+    public void buildFileBasedKeyManagerFactoryHappyPath() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        PEMBasedSslContextFactory sslContextFactory1 = new PEMBasedSslContextFactory(config);
+        // Make sure the expiry check didn't happen so far for the private key
+        Assert.assertFalse(sslContextFactory1.checkedExpiry);
+
+        addFileBaseKeyStoreOptions(config);
+        PEMBasedSslContextFactory sslContextFactory2 = new PEMBasedSslContextFactory(config);
+        // Trigger the private key loading. That will also check for expired private key
+        sslContextFactory2.buildKeyManagerFactory();
+        // Now we should have checked the private key's expiry
+        Assert.assertTrue(sslContextFactory2.checkedExpiry);
+
+        // Make sure that new factory object preforms the fresh private key expiry check
+        PEMBasedSslContextFactory sslContextFactory3 = new PEMBasedSslContextFactory(config);
+        Assert.assertFalse(sslContextFactory3.checkedExpiry);
+        sslContextFactory3.buildKeyManagerFactory();
+        Assert.assertTrue(sslContextFactory3.checkedExpiry);
+    }
+
+    @Test
+    public void buildKeyManagerFactoryWithUnencryptedKey() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addUnencryptedKeyStoreOptions(config);
+
+        Assert.assertTrue("Unencrypted Key test must not specify a key password",
+                          StringUtils.isEmpty((String) config.get(KEY_PASSWORD.getKeyName())));
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.buildKeyManagerFactory();
+    }
+
+    @Test
+    public void buildKeyManagerFactoryWithFileBasedUnencryptedKey() throws IOException
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addFileBaseUnencryptedKeyStoreOptions(config);
+
+        Assert.assertTrue("Unencrypted Key test must not specify a key password",
+                          StringUtils.isEmpty((String) config.get(KEY_PASSWORD.getKeyName())));
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        sslContextFactory.buildKeyManagerFactory();
+    }
+
+    @Test
+    public void testDisableOpenSslForInJvmDtests()
+    {
+        // The configuration name below is hard-coded intentionally to make sure we don't break the contract without
+        // changing the documentation appropriately
+        System.setProperty("cassandra.disable_tcactive_openssl", "true");
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        PEMBasedSslContextFactory sslContextFactory = new PEMBasedSslContextFactory(config);
+        Assert.assertEquals(SslProvider.JDK, sslContextFactory.getSslProvider());
+        System.clearProperty("cassandra.disable_tcactive_openssl");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMultiplePrivateKeySources()
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addUnencryptedKeyStoreOptions(config);
+
+        // Check with a valid file path for the keystore
+        addFileBaseUnencryptedKeyStoreOptions(config);
+        new PEMBasedSslContextFactory(config);
+    }
+
+    @Test
+    public void testMultiplePrivateKeySourcesWithInvalidKeystorePath()
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+        addUnencryptedKeyStoreOptions(config);
+
+        // Check with an invalid file path for the keystore
+        config.put("keystore", "/path/to/nowhere");
+        new PEMBasedSslContextFactory(config);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMultipleTrustedCertificatesSources()
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        // Check with a valid file path for the truststore
+        addFileBaseTrustStoreOptions(config);
+        new PEMBasedSslContextFactory(config);
+    }
+
+    @Test
+    public void testMultipleTrustedCertificatesSourcesWithInvalidTruststorePath()
+    {
+        Map<String, Object> config = new HashMap<>();
+        config.putAll(commonConfig);
+
+        // Check with an invalid file path for the truststore
+        config.put("truststore", "/path/to/nowhere");
+        new PEMBasedSslContextFactory(config);
+    }
+}
diff --git a/test/unit/org/apache/cassandra/security/PEMJKSSslContextFactoryConfigTest.java b/test/unit/org/apache/cassandra/security/PEMJKSSslContextFactoryConfigTest.java
new file mode 100644
index 0000000..8efd3e4
--- /dev/null
+++ b/test/unit/org/apache/cassandra/security/PEMJKSSslContextFactoryConfigTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import javax.net.ssl.SSLException;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.DatabaseDescriptor;
+
+public class PEMJKSSslContextFactoryConfigTest
+{
+    @BeforeClass
+    public static void setupDatabaseDescriptor()
+    {
+        System.setProperty("cassandra.config", "cassandra-pem-jks-sslcontextfactory.yaml");
+    }
+
+    @AfterClass
+    public static void tearDownDatabaseDescriptor() {
+        System.clearProperty("cassandra.config");
+    }
+
+    @Test
+    public void testPEMAndJKSCombination() throws SSLException
+    {
+
+        Config config = DatabaseDescriptor.loadConfig();
+        config.client_encryption_options.applyConfig();
+
+        Assert.assertEquals("org.apache.cassandra.security.PEMBasedSslContextFactory",
+                            config.client_encryption_options.ssl_context_factory.class_name);
+        Assert.assertEquals(config.client_encryption_options.ssl_context_factory.class_name,
+                            config.client_encryption_options.sslContextFactoryInstance.getClass().getName());
+        PEMBasedSslContextFactory clientSslContextFactory =
+        (PEMBasedSslContextFactory)config.client_encryption_options.sslContextFactoryInstance;
+        clientSslContextFactory.buildKeyManagerFactory();
+        clientSslContextFactory.buildTrustManagerFactory();
+
+        config.server_encryption_options.applyConfig();
+
+        Assert.assertEquals("org.apache.cassandra.security.DefaultSslContextFactory",
+                            config.server_encryption_options.ssl_context_factory.class_name);
+        Assert.assertEquals(config.server_encryption_options.ssl_context_factory.class_name,
+                            config.server_encryption_options.sslContextFactoryInstance.getClass().getName());
+        DefaultSslContextFactory serverSslContextFactory =
+        (DefaultSslContextFactory)config.server_encryption_options.sslContextFactoryInstance;
+        serverSslContextFactory.buildKeyManagerFactory();
+        serverSslContextFactory.buildTrustManagerFactory();
+    }
+}
diff --git a/test/unit/org/apache/cassandra/security/PEMReaderTest.java b/test/unit/org/apache/cassandra/security/PEMReaderTest.java
new file mode 100644
index 0000000..5f4a867
--- /dev/null
+++ b/test/unit/org/apache/cassandra/security/PEMReaderTest.java
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.security;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PEMReaderTest
+{
+    private static final String encoded_encrypted_key =
+    "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+    "MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/\n" +
+    "g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl\n" +
+    "xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29\n" +
+    "L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V\n" +
+    "sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/\n" +
+    "f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8\n" +
+    "RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR\n" +
+    "0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs\n" +
+    "evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU\n" +
+    "tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6\n" +
+    "wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN\n" +
+    "K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv\n" +
+    "zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5\n" +
+    "mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo\n" +
+    "WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ\n" +
+    "jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6\n" +
+    "eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny\n" +
+    "DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn\n" +
+    "AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY\n" +
+    "Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow\n" +
+    "Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut\n" +
+    "ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr\n" +
+    "bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH\n" +
+    "hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI\n" +
+    "RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw\n" +
+    "pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP\n" +
+    "FujZhqbKUDbYAcqTkoQ=\n" +
+    "-----END ENCRYPTED PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9w=\n" +
+    "-----END CERTIFICATE-----";
+
+    private static final String encoded_certificates =
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9w=\n" +
+    "-----END CERTIFICATE-----";
+
+    private static final String encoded_key =
+    "-----BEGIN PRIVATE KEY-----\n" +
+    "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOSZVf8dLj1xLw\n" +
+    "efqjbogbAwhwRXd3tmEfQY1zHyudJF3XR1T0Xp26BKNvAYUxxZRDNg16M3prPRZv\n" +
+    "wkhDJdE9NaN+BpZPJUavRsZOfGDq/CHN8j/2PPKrn3G/b065JjV3GZjfV/Sln047\n" +
+    "DeZptaSyOg7ZN7F8qjGqxEcnw+szV/wTzhnjGjZMlcbOm4jFGQAn6xUOooQCGsoB\n" +
+    "9FgxKB0nvNG3xVe/2eCNfbS4DabT3Y1wfQqZ62hOa5ZS0rwT+pw3tQs3zFFY1Sfi\n" +
+    "G7qYbqZrKTheWIZGojVVm6mqH4yA2ofOe5N3RsBitCwU/D0dpnaG9Wcl98nmXueM\n" +
+    "B6Rk04v7AgMBAAECggEAYnxIKjrFz/JkJ5MmiszM5HV698r9YB0aqHnFIHPoykIL\n" +
+    "uiCjiumantDrFsCkosixULwvI/BRwbxstTpyrheU9psT6P1CONICVPvV8ylgJAYU\n" +
+    "l+ofn56cEXKxVuICSWFLDH7pM1479g+IJJQAchbKQpqxAGTuMu3SpvJolfuj5srt\n" +
+    "bM7/RYhJFLwDuvHNA3ivlogMneItP03+C25aaxstM+lBuBf68+n78zMgSvt6J/6Y\n" +
+    "G2TOMKnxveMlG2qu9l2lAw/2i8daG/qre08nTH7wpRx0gZLZqNpe45exkrzticzF\n" +
+    "FgWYjG2K2brX21jqHroFgMhdXF7zhhRgLoIeC0BrIQKBgQDCfGfWrJESKBbVai5u\n" +
+    "7wqD9nlzjv6N6FXfTDOPXO1vz5frdvtLVWbs0SMPy+NglkaZK0iqHvb9mf2of8eC\n" +
+    "0D5cmewjn7WCDBQMypIMYgT912ak/BBVuGXcxb6UgD+xARfSARo2C8NG1hfprw1W\n" +
+    "ad14CjS5xhFMs44HpVYhI7iPYwKBgQC7SqVG/b37vZ7CINemdvoMujLvvYXDJJM8\n" +
+    "N21LqNJfVXdukdH3T0xuLnh9Z/wPHjJDMF/9+1foxSEPHijtyz5P19EilNEC/3qw\n" +
+    "fI19+VZoY0mdhPtXSGzy+rbTE2v71QgwFLizSos14Gr+eNiIjF7FYccK05++K/zk\n" +
+    "cd8ZA3bwiQKBgQCl+HTFBs9mpz+VMOAfW2+l3hkXPNiPUc62mNkHZ05ZNNd44jjh\n" +
+    "uSf0wSUiveR08MmevQlt5K7zDQ8jVKh2QjB15gVXAVxsdtJFeDnax2trFP9LnLBz\n" +
+    "9sE2/qn9INU5wK0LUlWD+dXUBbCyg+jl7cJKRqtoPldVFYYHkFlIPqup8QKBgHXv\n" +
+    "hyuw1FUVDkdHzwOvn70r8q8sNHKxMVWVwWkHIZGOi+pAQGrusD4hXRX6yKnsZdIR\n" +
+    "QCD6iFy25R5T64nxlYdJaxPPid3NakB/7ckJnPOWseBSwMIxhQlr/nvjmve1Kba9\n" +
+    "FaEwq4B9lGIxToiNe4/nBiM3JzvlDxX67nUdzWOhAoGAdFvriyvjshSJ4JHgIY9K\n" +
+    "37BVB0VKMcFV2P8fLVWO5oyRtE1bJhU4QVpQmauABU4RGSojJ3NPIVH1wxmJeYtj\n" +
+    "Q3b7EZaqI6ovna2eK2qtUx4WwxhRaXTT8xueBI2lgL6sBSTGG+K69ZOzGQzG/Mfr\n" +
+    "RXKInnLInFD9JD94VqmMozo=\n" +
+    "-----END PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9w=\n" +
+    "-----END CERTIFICATE-----";
+    private static final String encoded_unencrypted_ec_private_key =
+    "-----BEGIN PRIVATE KEY-----\n" +
+    "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgMLP6H2Wdl28J5PHU\n" +
+    "gMLApCsjONhbyMd5br0byJaQpXShRANCAASmX26IPehdE1wdLW2fVndT9QbjURro\n" +
+    "h74aMnzlmq8GIBWnRzpd+JVJlHgeWLZIDwapthGCYUGivtH27wiO3g7d\n" +
+    "-----END PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIBizCCATACCQCtgEKhNta70DAKBggqhkjOPQQDAjBNMQswCQYDVQQGEwJVUzEL\n" +
+    "MAkGA1UECAwCQ0ExETAPBgNVBAcMCFNhbiBKb3NlMREwDwYDVQQKDAhQZXJzb25h\n" +
+    "bDELMAkGA1UECwwCSVQwHhcNMjExMDE5MDAzMDU4WhcNMjIxMDE0MDAzMDU4WjBN\n" +
+    "MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExETAPBgNVBAcMCFNhbiBKb3NlMREw\n" +
+    "DwYDVQQKDAhQZXJzb25hbDELMAkGA1UECwwCSVQwWTATBgcqhkjOPQIBBggqhkjO\n" +
+    "PQMBBwNCAASmX26IPehdE1wdLW2fVndT9QbjURroh74aMnzlmq8GIBWnRzpd+JVJ\n" +
+    "lHgeWLZIDwapthGCYUGivtH27wiO3g7dMAoGCCqGSM49BAMCA0kAMEYCIQDsNMGL\n" +
+    "b4+BEhgNXaXyHWkezUO/3hCmLDw2gUdwMXG+JQIhAIAm8wALKbb9jJDgFQTHyqGJ\n" +
+    "AVAkzYOwmRMYC9BHKjNs\n" +
+    "-----END CERTIFICATE-----";
+    private static final String encoded_encrypted_key_with_multiplecerts_in_chain =
+    "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+    "MIIE6TAbBgkqhkiG9w0BBQMwDgQI4QuRiKYzf88CAggABIIEyPRVmPp38SIFr8H3\n" +
+    "wi+oc6b+HJH7SPflXO6XZe4Tignw/aSyBTsLm2dWrzojRAYMIRd1xC7yQ2ffYrvx\n" +
+    "uoYbtOQeAminNqvwXdRTnwu6oC0rxdBT8RQ9NK7xL2tQyD/shmOeTJG/glXxaeqS\n" +
+    "rT0CZ5P5GJh6xdIWLEu3lEa3NSWVFE2YacUphmxBoaWjBjsJfWTgkF665SgP+2lh\n" +
+    "8R2WTcHrHjD8jR4jHB03wlup0LOmOwzplUmqHB9TyuA4wF6tlJajwBcPa0PNI6ny\n" +
+    "e9YcdcRr7Y0IxnPQr7PhQNV5AQb9TivwX4WaZxR+BXtwMglp+mz0ohjwLS3z6pqr\n" +
+    "tLrFhv2qcacSl+CKukFb9umV/QBkUk/iu+jwLcNJKPC965GWdieNbO0akBQpQsUN\n" +
+    "mqaF9DYHogW5lRnybl8WWPIR8tXmSCbSUIgzw4lRK+o15I4vaMI0NfkwFD/2y1sn\n" +
+    "t3m9LnVBukkpx3g/CPKd9PbZZeWpOTrnRJQfOu9Fj2lmkpGp0peCBqLJpO0pieVl\n" +
+    "87EQ0ZCErtAGLGIAhWnDUqRK0MaWZ+DMQNKYn5klF4YTVBkfRc9tQbIgBaa77wvz\n" +
+    "gvVWBuJtTFpCt9c8jByTH1gLbchC4bhLsy1nO7moevypMmNW4rqw9x5f0EIR3zCU\n" +
+    "L5/buoIh91TG5JB7BaIbVHtbB/Y2siARRXJibuw3ChBjqPOfzQ66j//NCMqhfTwT\n" +
+    "x2wn7L1BB4xyLJgVW9803FUTzaaL3EvJjzpdvrGC7vzcB6Jd0La9cjHhWSAPOKro\n" +
+    "nD9XPCbgLs10vW9g1Tc8drnZklhw6f7xrIQhWFg6VlwmVpvCQhEpX48rCCo2PH9X\n" +
+    "uzeJA+oqFEH3zfDp0r+q6jbAl+5TkkbBBgC2GCoI1vTYSKaWjeKKHkgzGG0QQLAz\n" +
+    "YXWMXvWKOME4wVPkeVxJv80RqDv0JsoOrnVoaFAzAHJMWa7UZQnjkrbVz/y8ZbV4\n" +
+    "oLJjQjvuOnU2PoXXs6SXbzOs1xx5zbX1UUH3Wc7/CCaUec2hemQJ5m6b1XJciyrY\n" +
+    "GHpMKWtXky9Mo1ruTP7ZH1nk03b4PTObKSx2gQD5AZ/ASuTeahMqMb/2PJkDkpHA\n" +
+    "sy8b1zOn2YTbf4K6NWVNIOkiaApmKhhX0Af6Lg8Wr2ymRTXdp/Um8f+SQLADpB/F\n" +
+    "xOydEN622wmihKDge9DrUFqPG/bdIiRGLXLg8efNboC6/cn/i/sheO7/YlrvcUNo\n" +
+    "qxDa/Bb1N/DgmtgAQ1ZP+AKjk6FKkwZRF1X/VZkZ6auscDlaPetF7razPeIJUrKN\n" +
+    "z/x4AD2txGYKmeYztYR577hPXBw+PPKdggRhIugb6z5Tk89C2pDEwfnByA/wcGJr\n" +
+    "w5avxrubosPrp0QtJpZMzouOvcD52VUiZzDfu9dqI/hpinyt5rETj5E19qxBjIZt\n" +
+    "X3Nq5lY2ktbyqVIo8Z8w4EUU+3XHZKqDwjyYvjxCxv5lVVnqvQrH9h3kENBMrPYQ\n" +
+    "4XonQHpUGG7g7pA3ylmHi+nEedr0H5qKHzyFZlRdI7CLVNoAtBSdwvmtGd2cVVXU\n" +
+    "EaToKNbHPXXYYPX/oVAWZYwD7PHXIRJkiEZnrFARNhLypicU7yjvejUPXcVy5HMh\n" +
+    "XqEbrODPp4VXfbYhVg==\n" +
+    "-----END ENCRYPTED PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDXjCCAkYCAhI0MA0GCSqGSIb3DQEBBAUAMHcxCzAJBgNVBAYTAlVTMRMwEQYD\n" +
+    "VQQIDApDYWxpZm9ybmlhMREwDwYDVQQHDAhTYW4gSm9zZTEXMBUGA1UECgwOUGVy\n" +
+    "c29uYWwsIEluYy4xEDAOBgNVBAsMB1Jvb3QgQ0ExFTATBgNVBAMMDG15ZG9tYWlu\n" +
+    "LmNvbTAeFw0yMTExMjIyMjQ5MzlaFw0yMjExMjIyMjQ5MzlaMHIxCzAJBgNVBAYT\n" +
+    "AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQHDAhTYW4gSm9zZTEXMBUG\n" +
+    "A1UECgwOUGVyc29uYWwsIEluYy4xCzAJBgNVBAsMAklUMRUwEwYDVQQDDAxteWRv\n" +
+    "bWFpbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5fdA7wwD9\n" +
+    "9e5RcdLscvGB+hqJUEHuNC53SYKg5X4Sf0H4ExQUbsy8UaoWzWHhgGbCtTvUVavl\n" +
+    "72xsO74ei0EblopW7QknF0kaTO8Vi3mxhUAdtZFLG/o0NS9J16HdGDGojJwuqU9+\n" +
+    "sMQt1w0HCTMlriELnxaUFKP7M9b0uK5VODEKJ38QKNGXUDt66D7BVYeT/6hz2cXK\n" +
+    "QWDoHk/JadALSzW5ES8KIHfxCLnl2TcKxQhJ4CnL8qeGvc8N3VyTh2AXajaJW5RB\n" +
+    "8Oy4CVoYxcdmP1IapxCD+yNcmNt9XpUTD+6eM5gnvtbye+MSfwPz2MW+fWEDZXOv\n" +
+    "3VxhJyTRFNVTAgMBAAEwDQYJKoZIhvcNAQEEBQADggEBADYK/pn6QG7bvUL0Xnnw\n" +
+    "1KPf1nx36gfJE2V0bNk4uyNNeYufMKS8gPLzC+a3RigCEDc+hIZFE5BJexHd7DXA\n" +
+    "CWgHZJtdjM/Xlgoxbf1yfGV3DWeIZlNFSFZujBIpbm1Ug2BAeV31YRWODPZlUSEZ\n" +
+    "0jv8NEs8/oEz9bM4jwRdn09lo4D9hE6o8qDnrzmN2LBZP5dDIJ6g/M+mq/SJFnho\n" +
+    "qBrfUABZhbgk2+tkZ89OI2xpASCLo6X/vqua2iho6km3x+cz6EI9BbvVr6xOOdVK\n" +
+    "m6gs/Bi4MGTh35jdmvyXoyBUOd1w3yBBj86qbEt2ZHYqreRTxntQYx06598Q9Dsi\n" +
+    "xdg=\n" +
+    "-----END CERTIFICATE-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDajCCAlICCQD/7mxPcMTPIDANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJV\n" +
+    "UzETMBEGA1UECAwKQ2FsaWZvcm5pYTERMA8GA1UEBwwIU2FuIEpvc2UxFzAVBgNV\n" +
+    "BAoMDlBlcnNvbmFsLCBJbmMuMRAwDgYDVQQLDAdSb290IENBMRUwEwYDVQQDDAxt\n" +
+    "eWRvbWFpbi5jb20wHhcNMjExMTIyMjExODAwWhcNNDkwNDA5MjExODAwWjB3MQsw\n" +
+    "CQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTERMA8GA1UEBwwIU2FuIEpv\n" +
+    "c2UxFzAVBgNVBAoMDlBlcnNvbmFsLCBJbmMuMRAwDgYDVQQLDAdSb290IENBMRUw\n" +
+    "EwYDVQQDDAxteWRvbWFpbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" +
+    "AoIBAQCkIwuNGv3ckew/o2UwaDlYgXH9bh1jap4ZCb6qpjvR3tq9nCerY6XMli0Z\n" +
+    "Xxg0wMHDNUr/jmVYIdQjbz0DVNz/l6ZBJHzHCEgqR40pNM3NgC5sDyuNhF3WLNvj\n" +
+    "WgHEwYosfb/9kFRjKUPqqtJ0ccj87OP3XrE/4epCTdJdmugroAQSpXt1ZZfwwPO4\n" +
+    "K27DzMD9W01EmeLcUhMfrpUnKGCfL22c0sZZm/6Khk4BExC3pSILP/NREKeUEAHw\n" +
+    "+rxhNqbUyD/e4/DutdtJ5zONA+GVVGYCpu1Iy0W78Jve4MD2/TFPcEzf5omiWpPz\n" +
+    "WjpOWayD43ur0SZnYJ5haUlZ+bSLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABqN\n" +
+    "/eb+mKEw2zklPZuzy5uhOc7ImG2LP/ha3oc2ntwkqZQ2LmVA/2ZNVfcNrRRngIfn\n" +
+    "Ir9Kual7djmKmIQvanpnSyXOKOlIiuN0VOrewgzlBZZwlFJL/AH1l7K9uZfBbV5h\n" +
+    "oFfaR9wc+vRGsg3nqO9+hEuk6xbp0jk8QCt26EVhEPlijxzbxTQYiNPNSLuf/kPW\n" +
+    "C9xtIKSWIDT4N6DtH7BtHGQKQdRJ2b4SSUF4joEmBe6jcrLBeDybmuFtKqlVJKUk\n" +
+    "tzBd9CPseqMML1c518KzxlSkXNxTCa7PWEnuN5atLZ+pGGjxtGcDKkrZ9Cgi09G8\n" +
+    "MzB8b4C/goypyhBNlyI=\n" +
+    "-----END CERTIFICATE-----";
+    private static final String encoded_encrypted_dsa_key =
+    "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
+    "MIICkTAbBgkqhkiG9w0BBQMwDgQIL1GiUDca2x0CAggABIICcCrSiNBy5kZNC7RK\n" +
+    "fVF3IZ9Ecl00OYIjvBhlWGkaiNt9ZAeWPpYx57HSQAygzJ8ba+3jtz1dV8Bhz5D5\n" +
+    "4kggzolC820I/QLPxClH5R6ZPzDHGu/JFuNNZWASq2JHZfolEP+itdnz0F6VvQx9\n" +
+    "imngOGIJMkRWIzvwuCnq8xpNSEJHJcs8kyLBP/3qT+kUiEwJ5KrmJ8DQsHluwpyo\n" +
+    "GoiGEtiA+MNUSzc/DkuLxhC45k7K4afe4QzpWl9eR/z91F9Q06jdc5mnUWke8qCg\n" +
+    "ZopBVWbSI6fMZVlqLxKeSFljDZ+moW+2/Lh8cjEvNMccVTwWQE7JH9RLnVogqixV\n" +
+    "HIpN58wrlHd4uMVH47wD18A11AGEbghO1MEShX4SCEJIdZsr67bNx8mIkhSWjqAx\n" +
+    "BT9OmITzdbnR9sHi4CyEWhGMAMDp7YBySwpt+U7Y6DvRwWJbXUaF8zYJUlrp2IbH\n" +
+    "qdSE+oJPKxGB1s2B5KGrJUA0JkElkBUYm6ghXZlTI32w8HoV9fDOhjx1ATu7SdZY\n" +
+    "8DX0mwVVdc88Msr5RPxdeB3V4yN2iFJs4i3usicPkB0N+29LJ/lKQlm/pia0yl9j\n" +
+    "yDNN3R0RiCJdYHme4t1PqRqeTfjMauz05ObennQmkzMxD8mlBZ0zhaKL5I4TuQod\n" +
+    "PITFgYihTR9OYfa8lryvHQNCIi5iZ6M9myLUxbjPoeVBdp8pSlMAjmekEHo47vn3\n" +
+    "7IGF7AfnKVNymc/1Kim3WQx8D7nryCb8EUyb7BuNA7izGKq+NW51l39J8RGVcFSx\n" +
+    "sVkpFUbqGXusUgLWWwi21EJHCJceFnOWJRgsoeqNeCt5VCUSag==\n" +
+    "-----END ENCRYPTED PRIVATE KEY-----\n" +
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIEeDCCBB4CCQD9UojL1A7cmzAJBgcqhkjOOAQDME0xCzAJBgNVBAYTAlVTMQsw\n" +
+    "CQYDVQQIDAJDQTERMA8GA1UEBwwIU2FuIEpvc2UxETAPBgNVBAoMCFBlcnNvbmFs\n" +
+    "MQswCQYDVQQLDAJJVDAeFw0yMTEwMTgyMzU0MjRaFw0yMjEwMTgyMzU0MjRaME0x\n" +
+    "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTERMA8GA1UEBwwIU2FuIEpvc2UxETAP\n" +
+    "BgNVBAoMCFBlcnNvbmFsMQswCQYDVQQLDAJJVDCCA0YwggI5BgcqhkjOOAQBMIIC\n" +
+    "LAKCAQEAzxMsktQYQ06q71pwqWAa9YvDIkF34RJgQT5tqCZdzwtP7HXyrwaZBgLK\n" +
+    "oC4grIPKyUlPmIbm+oucucbTzDCYCx2d09VMVR471vqJi7vRCYnBIqMTTOSvbTGw\n" +
+    "8VuwA2prSt4TiqFYeqYM0Afv/KrI2MLNTP+z8RFfiLkijZIxZ9CzyHmTOEqHxOft\n" +
+    "Ln145NNcuPy3U7Bmh5k7i+RR+5jYweuLzOfTK4bTUnG111mjAhAeo+ST14ydfiuQ\n" +
+    "2e85UwLucVheh4REqm+n7g0k0B/+nG+9Os1nxfQVRVKHP4/iTrNLpf35EoPeh9XT\n" +
+    "D0BtV9lqQuttCB1aPLH66TKl9v4FowIhAMXhJ3LooBT7ypUWMtN07FvpPpoLERBr\n" +
+    "Pk9MeGwQnBPFAoIBAAcVYJ+RH1i8JFyD8MUwwOKgkmVzbRvc28B5F17oek76R6fi\n" +
+    "yyqyhHrkxmjwxktXuwRlWQBJqCTqpmMstLplqsVjcETHo7KRaHgDpT9tHf14PZvP\n" +
+    "qpoxYNpa5z4wPpOFhZ3K6VVaFUSlqSSHhaS1HqVzZC0FefFFd3TDKu0EWHDDkLM8\n" +
+    "luOxDUMydKpRtrVba+iF4kx9NcCyXjSMhFAOw3cWhGvoq6R3h4UkRJO67pzYtqK4\n" +
+    "yAETPsQKT0c8NUM+VUiPAHL/+f1EqaVtzatk9G0OHWZXgDxuhx2CAM6QfELew+ys\n" +
+    "D/QUUT0tXHQRxRCKmYE+uacwyQx5G0DeCHN0c9gDggEFAAKCAQBFGXF7flzfRH/8\n" +
+    "r/qDuNC+9y3fRtDWXaados+XQkzujizBBK9dkkAd/j/pgP5p7/bRvoeR507Oyrge\n" +
+    "6xBbfE7SlRu0cx08Ihlw7a6mCowMnP0psFBwW7npKJRYPXxRDpu9oUuuJHi1WYp7\n" +
+    "8/Ekg6hG21zAYO0JLGyU6aH5Rvuls754R+rxqveVqLiig5NTnfw7ymLb2kF1z8g7\n" +
+    "fDvX6OS242ceQIQHvq97cGnysEVSlavuHfbh3PKXklh91ip/BYuzanQjiEb97YIU\n" +
+    "DhMQyPGvk+iDdhD0PwgwOZB0P0mL7Xszw6p+lfzUc+g8jETjrnzksnVEg8On6q3Q\n" +
+    "RDJG9VnuMAkGByqGSM44BAMDSQAwRgIhAIay5hyZrwHslGXmTAx498903l7CQsdi\n" +
+    "fUrmGkxbPiZXAiEAitS8+gyG64c0jV7V/u8Z1NEL8MI47K2P9Jbn3cqzz3k=\n" +
+    "-----END CERTIFICATE-----";
+    private static final String invalid_encoded_private_key =
+    "-----BEGIN PRIVATE KEY-----\n" +
+    "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCOSZVf8dLj1xLw\n" +
+    "efqjbogbAwhwRXd3tmEfQY1zHyudJF3XR1T0Xp26BKNvAYUxxZRDNg16M3prPRZv\n" +
+    "wkhDJdE9NaN+BpZPJUavRsZOfGDq/CHN8j/2PPKrn3G/b065JjV3GZjfV/Sln047\n" +
+    "DeZptaSyOg7ZN7F8qjGqxEcnw+szV/wTzhnjGjZMlcbOm4jFGQAn6xUOooQCGsoB\n" +
+    "9FgxKB0nvNG3xVe/2eCNfbS4DabT3Y1wfQqZ62hOa5ZS0rwT+pw3tQs3zFFY1Sfi\n" +
+    "G7qYbqZrKTheWIZGojVVm6mqH4yA2ofOe5N3RsBitCwU/D0dpnaG9Wcl98nmXueM\n" +
+    "B6Rk04v7AgMBAAECggEAYnxIKjrFz/JkJ5MmiszM5HV698r9YB0aqHnFIHPoykIL\n" +
+    "uiCjiumantDrFsCkosixULwvI/BRwbxstTpyrheU9psT6P1CONICVPvV8ylgJAYU\n" +
+    "l+ofn56cEXKxVuICSWFLDH7pM1479g+IJJQAchbKQpqxAGTuMu3SpvJolfuj5srt\n" +
+    "bM7/RYhJFLwDuvHNA3ivlogMneItP03+C25aaxstM+lBuBf68+n78zMgSvt6J/6Y\n" +
+    "G2TOMKnxveMlG2qu9l2lAw/2i8daG/qre08nTH7wpRx0gZLZqNpe45exkrzticzF\n" +
+    "FgWYjG2K2brX21jqHroFgMhdXF7zhhRgLoIeC0BrIQKBgQDCfGfWrJESKBbVai5u\n" +
+    "7wqD9nlzjv6N6FXfTDOPXO1vz5frdvtLVWbs0SMPy+NglkaZK0iqHvb9mf2of8eC\n" +
+    "0D5cmewjn7WCDBQMypIMYgT912ak/BBVuGXcxb6UgD+xARfSARo2C8NG1hfprw1W\n" +
+    "ad14CjS5xhFMs44HpVYhI7iPYwKBgQC7SqVG/b37vZ7CINemdvoMujLvvYXDJJM8\n" +
+    "N21LqNJfVXdukdH3T0xuLnh9Z/wPHjJDMF/9+1foxSEPHijtyz5P19EilNEC/3qw\n" +
+    "fI19+VZoY0mdhPtXSGzy+rbTE2v71QgwFLizSos14Gr+eNiIjF7FYccK05++K/zk\n" +
+    "cd8ZA3bwiQKBgQCl+HTFBs9mpz+VMOAfW2+l3hkXPNiPUc62mNkHZ05ZNNd44jjh\n" +
+    "uSf0wSUiveR08MmevQlt5K7zDQ8jVKh2QjB15gVXAVxsdtJFeDnax2trFP9LnLBz\n" +
+    "9sE2/qn9INU5wK0LUlWD+dXUBbCyg+jl7cJKRqtoPldVFYYHkFlIPqup8QKBgHXv\n" +
+    "hyuw1FUVDkdHzwOvn70r8q8sNHKxMVWVwWkHIZGOi+pAQGrusD4hXRX6yKnsZdIR\n" +
+    "QCD6iFy25R5T64nxlYdJaxPPid3NakB/7ckJnPOWseBSwMIxhQlr/nvjmve1Kba9\n" +
+    "FaEwq4B9lGIxToiNe4/nBiM3JzvlDxX67nUdzWOhAoGAdFvriyvjshSJ4JHgIY9K\n" +
+    "37BVB0VKMcFV2P8fLVWO5oyRtE1bJhU4QVpQmauABU4RGSojJ3NPIVH1wxmJeYtj\n" +
+    "Q3b7EZaqI6ovna2eK2qtUx4WwxhRaXTT8xueBI2lgL6sBSTGG+K69ZOzGQzG/Mfr\n" +
+    "RXKInnLInFD9JD94VqmMozoInvalidData=\n" +
+    "-----END PRIVATE KEY-----\n";
+    private static final String invalid_encoded_certificate =
+    "-----BEGIN CERTIFICATE-----\n" +
+    "MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV\n" +
+    "bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD\n" +
+    "VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh\n" +
+    "Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx\n" +
+    "EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu\n" +
+    "a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw\n" +
+    "FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
+    "MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d\n" +
+    "ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy\n" +
+    "q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2\n" +
+    "TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto\n" +
+    "TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA\n" +
+    "YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD\n" +
+    "N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v\n" +
+    "iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh\n" +
+    "IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv\n" +
+    "6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG\n" +
+    "qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa\n" +
+    "HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru\n" +
+    "n3MVF9wInvalidData=\n" +
+    "-----END CERTIFICATE-----";
+
+    @Test
+    public void readEncryptedKey() throws IOException, GeneralSecurityException
+    {
+        PrivateKey privateKey = PEMReader.extractPrivateKey(encoded_encrypted_key, "cassandra");
+        Assert.assertNotNull(privateKey);
+    }
+
+    @Test
+    public void readEncryptedDSAKey() throws IOException, GeneralSecurityException
+    {
+        PrivateKey privateKey = PEMReader.extractPrivateKey(encoded_encrypted_dsa_key, "mytest");
+        Assert.assertNotNull(privateKey);
+    }
+
+    @Test(expected = GeneralSecurityException.class)
+    public void readEncryptedDSAKeyWithBadPassword() throws IOException, GeneralSecurityException
+    {
+        try
+        {
+            PEMReader.extractPrivateKey(encoded_encrypted_dsa_key, "bad-password");
+        } catch(GeneralSecurityException e) {
+            Assert.assertTrue(e.getMessage().startsWith("Failed to decrypt the private key data. Either the password " +
+                                                        "provided for the key is wrong or the private key data is " +
+                                                        "corrupted. msg="));
+            throw e;
+        }
+    }
+
+    @Test(expected = GeneralSecurityException.class)
+    public void readInvalidEncryptedKey() throws IOException, GeneralSecurityException
+    {
+        // Test by injecting junk data in the given key and making it invalid
+        PrivateKey privateKey = PEMReader.extractPrivateKey(encoded_encrypted_key.replaceAll("\\s",
+                                                                                             String.valueOf(System.nanoTime())),
+                                                            "cassandra");
+        Assert.assertNotNull(privateKey);
+    }
+
+    @Test(expected = GeneralSecurityException.class)
+    public void readInvalidBase64PrivateKey() throws IOException, GeneralSecurityException
+    {
+        PEMReader.extractPrivateKey(invalid_encoded_private_key);
+    }
+
+    @Test
+    public void readUnencryptedKey() throws IOException, GeneralSecurityException
+    {
+        PrivateKey privateKey = PEMReader.extractPrivateKey(encoded_key);
+        Assert.assertNotNull(privateKey);
+    }
+
+    @Test
+    public void readUnencryptedECKey() throws IOException, GeneralSecurityException
+    {
+        PrivateKey privateKey = PEMReader.extractPrivateKey(encoded_unencrypted_ec_private_key);
+        Assert.assertNotNull(privateKey);
+    }
+
+    @Test
+    public void readCertChain() throws GeneralSecurityException
+    {
+        Certificate[] certificates = PEMReader.extractCertificates(encoded_encrypted_key);
+        Assert.assertNotNull("CertChain must not be null", certificates);
+        Assert.assertTrue("CertChain must have only one certificate", certificates.length == 1);
+    }
+
+    @Test
+    public void readCertChainWithMoreThanOneCerts() throws GeneralSecurityException
+    {
+        Certificate[] certificates = PEMReader.extractCertificates(encoded_encrypted_key_with_multiplecerts_in_chain);
+        Assert.assertNotNull("CertChain must not be null", certificates);
+    }
+
+    @Test(expected = GeneralSecurityException.class)
+    public void readInvalidCertificate() throws GeneralSecurityException
+    {
+        // Test by injecting junk data in the given key and making it invalid
+        Certificate[] certificates = PEMReader.extractCertificates(encoded_encrypted_key.replaceAll("\\s",
+                                                                                                    String.valueOf(System.nanoTime())));
+        Assert.assertNotNull("CertChain must not be null", certificates);
+        Assert.assertTrue("CertChain must have only one certificate", certificates.length == 1);
+    }
+
+    @Test(expected = GeneralSecurityException.class)
+    public void readInvalidBase64Certificate() throws GeneralSecurityException
+    {
+        PEMReader.extractCertificates(invalid_encoded_certificate);
+    }
+
+    @Test
+    public void readTrustedCertificates() throws GeneralSecurityException
+    {
+        Certificate[] certificates = PEMReader.extractCertificates(encoded_certificates);
+        Assert.assertNotNull("Trusted certificate list must not be null", certificates);
+        Assert.assertTrue("Trusted certificate list must have only one certificate", certificates.length == 1);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void tamperSupportedAlgorithms() throws GeneralSecurityException
+    {
+        Set<String> original = PEMReader.SUPPORTED_PRIVATE_KEY_ALGORITHMS;
+        for (int i = 0; i < original.size(); i++)
+        {
+            original.remove("RSA");
+            original.remove("DSA");
+            original.remove("EC");
+            original.add("TAMPERED");
+        }
+    }
+}
diff --git a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
index 049d887..e5aa4b1 100644
--- a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
@@ -74,6 +74,16 @@ public class SSLFactoryTest
                       .withKeyStorePassword("cassandra");
     }
 
+    private ServerEncryptionOptions addPEMKeystoreOptions(ServerEncryptionOptions options)
+    {
+        ParameterizedClass sslContextFactoryClass = new ParameterizedClass("org.apache.cassandra.security.PEMBasedSslContextFactory",
+                                                                           new HashMap<>());
+        return options.withSslContextFactory(sslContextFactoryClass)
+                      .withKeyStore("test/conf/cassandra_ssl_test.keystore.pem")
+                      .withKeyStorePassword("cassandra")
+                      .withTrustStore("test/conf/cassandra_ssl_test.truststore.pem");
+    }
+
     @Test
     public void testSslContextReload_HappyPath() throws IOException, InterruptedException
     {
@@ -106,6 +116,38 @@ public class SSLFactoryTest
         }
     }
 
+    @Test
+    public void testPEMSslContextReload_HappyPath() throws IOException, InterruptedException
+    {
+        try
+        {
+            ServerEncryptionOptions options = addPEMKeystoreOptions(encryptionOptions)
+                                              .withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
+
+            SSLFactory.initHotReloading(options, options, true);
+
+            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT);
+            File keystoreFile = new File(options.keystore);
+
+            SSLFactory.checkCertFilesForHotReloading(options, options);
+
+            keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000);
+
+            SSLFactory.checkCertFilesForHotReloading(options, options);
+            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT);
+
+            Assert.assertNotSame(oldCtx, newCtx);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        finally
+        {
+            DatabaseDescriptor.loadConfig();
+        }
+    }
+
     @Test(expected = IOException.class)
     public void testSslFactorySslInit_BadPassword_ThrowsException() throws IOException
     {

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org