Add support for PEM based key material for SSL

patch by Maulin Vasavada; reviewed by Jon Meredith and Stefan Miklosovic for CASSANDRA-17031
This commit is contained in:
Maulin Vasavada 2021-10-08 23:51:47 -07:00 committed by Stefan Miklosovic
parent 72302adec8
commit 3655b26adf
26 changed files with 3191 additions and 72 deletions

2
.gitignore vendored
View File

@ -66,7 +66,7 @@ target/
.DS_Store
Thumbs.db
/.ant-targets-build.xml
**/.ant-targets-build.xml
.ant_targets
# Generated files from the documentation

View File

@ -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)

View File

@ -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" />

View File

@ -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";
}
}

View File

@ -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

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -0,0 +1,346 @@
/*
* 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.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
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;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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_UPDATED_TIMESTAMP_PATH;
public class KubernetesSecretsPEMSslContextFactoryTest
{
private static final Logger logger = LoggerFactory.getLogger(KubernetesSecretsPEMSslContextFactoryTest.class);
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";
private static String private_key;
private static String unencrypted_private_key;
private static String trusted_certificates;
private final Map<String, Object> commonConfig = new HashMap<>();
@BeforeClass
public static void prepare()
{
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)
{
try
{
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);
}
}
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(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("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(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_KEY_PASSWORD", "cassandra");
}
private void addUnencryptedKeystoreOptions(Map<String, Object> config)
{
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 = SSLException.class)
public void buildTrustManagerFactoryWithInvalidTrustedCertificates() throws IOException
{
Map<String, Object> config = new HashMap<>();
config.putAll(commonConfig);
config.put("MY_TRUSTED_CERTIFICATES", trusted_certificates.replaceAll("\\s", String.valueOf(System.nanoTime())));
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory =
new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
kubernetesSecretsSslContextFactory.checkedExpiry = false;
kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
}
@Test
public void buildTrustManagerFactoryHappyPath() throws IOException
{
Map<String, Object> config = new HashMap<>();
config.putAll(commonConfig);
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
kubernetesSecretsSslContextFactory.checkedExpiry = false;
TrustManagerFactory trustManagerFactory = kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
Assert.assertNotNull(trustManagerFactory);
}
@Test(expected = SSLException.class)
public void buildKeyManagerFactoryWithInvalidPrivateKey() throws IOException
{
Map<String, Object> config = new HashMap<>();
config.putAll(commonConfig);
config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, private_key.replaceAll("\\s", String.valueOf(System.nanoTime())));
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory =
new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
kubernetesSecretsSslContextFactory.checkedExpiry = false;
kubernetesSecretsSslContextFactory.buildKeyManagerFactory();
}
@Test(expected = IOException.class)
public void buildKeyManagerFactoryWithBadPassword() throws IOException
{
Map<String, Object> config = new HashMap<>();
config.putAll(commonConfig);
config.put(DEFAULT_PRIVATE_KEY_ENV_VAR_NAME, private_key);
config.put(DEFAULT_PRIVATE_KEY_PASSWORD_ENV_VAR_NAME, "HomeOfBadPasswords");
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory =
new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
kubernetesSecretsSslContextFactory.buildKeyManagerFactory();
}
@Test
public void buildKeyManagerFactoryHappyPath() 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);
addKeystoreOptions(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
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory3 =
new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
Assert.assertFalse(kubernetesSecretsSslContextFactory3.checkedExpiry);
kubernetesSecretsSslContextFactory3.buildKeyManagerFactory();
Assert.assertTrue(kubernetesSecretsSslContextFactory3.checkedExpiry);
}
@Test
public void checkTruststoreUpdateReloading() throws IOException
{
Map<String, Object> config = new HashMap<>();
config.putAll(commonConfig);
addKeystoreOptions(config);
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
kubernetesSecretsSslContextFactory.checkedExpiry = false;
TrustManagerFactory trustManagerFactory = kubernetesSecretsSslContextFactory.buildTrustManagerFactory();
Assert.assertNotNull(trustManagerFactory);
Assert.assertFalse(kubernetesSecretsSslContextFactory.shouldReload());
updateTimestampFile(config, TRUSTSTORE_UPDATED_TIMESTAMP_PATH);
Assert.assertTrue(kubernetesSecretsSslContextFactory.shouldReload());
config.remove(TRUSTSTORE_UPDATED_TIMESTAMP_PATH);
Assert.assertFalse(kubernetesSecretsSslContextFactory.shouldReload());
}
@Test
public void checkKeystoreUpdateReloading() throws IOException
{
Map<String, Object> config = new HashMap<>();
config.putAll(commonConfig);
addKeystoreOptions(config);
KubernetesSecretsPEMSslContextFactory kubernetesSecretsSslContextFactory = new KubernetesSecretsPEMSslContextFactoryForTestOnly(config);
kubernetesSecretsSslContextFactory.checkedExpiry = false;
KeyManagerFactory keyManagerFactory = kubernetesSecretsSslContextFactory.buildKeyManagerFactory();
Assert.assertNotNull(keyManagerFactory);
Assert.assertFalse(kubernetesSecretsSslContextFactory.shouldReload());
updateTimestampFile(config, KEYSTORE_UPDATED_TIMESTAMP_PATH);
Assert.assertTrue(kubernetesSecretsSslContextFactory.shouldReload());
config.remove(KEYSTORE_UPDATED_TIMESTAMP_PATH);
Assert.assertFalse(kubernetesSecretsSslContextFactory.shouldReload());
}
private void updateTimestampFile(Map<String, Object> config, String filePathKey)
{
String filePath = config.containsKey(filePathKey) ? config.get(filePathKey).toString() : null;
try (OutputStream os = Files.newOutputStream(Paths.get(filePath)))
{
String timestamp = String.valueOf(System.nanoTime());
os.write(timestamp.getBytes());
logger.info("Successfully wrote to file {}", filePath);
}
catch (IOException e)
{
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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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");
}
}
}

View File

@ -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
{