diff --git a/CHANGELOG.md b/CHANGELOG.md index efb046df..881436bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.8.0 + - Fixed SSL Java KeyStore support [#171](https://github.com/logstash-plugins/logstash-input-http/pull/171) + - Added `ssl_keystore_type` configuration + - Added SSL Java TrustStore configurations (`ssl_truststore_type`, `ssl_truststore_path` and `ssl_truststore_password`) + ## 3.7.3 - bump netty to 4.1.100 [#170](https://github.com/logstash-plugins/logstash-input-http/pull/170) diff --git a/VERSION b/VERSION index c1e43e6d..19811903 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.3 +3.8.0 diff --git a/build.gradle b/build.gradle index d28beb86..16d99ce0 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ dependencies { testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" testImplementation 'org.hamcrest:hamcrest-library:2.2' testImplementation "org.apache.logging.log4j:log4j-core:${log4jVersion}" + testImplementation 'org.elasticsearch:securemock:1.2' implementation "io.netty:netty-buffer:${nettyVersion}" implementation "io.netty:netty-codec:${nettyVersion}" diff --git a/docs/index.asciidoc b/docs/index.asciidoc index 2ad05927..5c345413 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -112,7 +112,11 @@ This plugin supports the following configuration options plus the <> |<>|No | <> |<>|No | <> |<>|No +| <> |<>|No | <> |<>|No +| <> |<>|No +| <> |<>|No +| <> |<>|No | <> |<>, one of `["none", "peer", "force_peer"]`|__Deprecated__ | <> |<>|No | <> |<>|__Deprecated__ @@ -405,7 +409,18 @@ SSL key passphrase to use. * Value type is <> * There is no default value for this setting. -The JKS keystore to validate the client's certificates +The path for the keystore file that contains a private key and certificate. +It must be either a Java keystore (jks) or a PKCS#12 file. + +NOTE: You cannot use this setting and <> at the same time. + +[id="plugins-{type}s-{plugin}-ssl_keystore_type"] +===== `ssl_keystore_type` + + * Value can be any of: `jks`, `pkcs12` + * If not provided, the value will be inferred from the keystore filename. + +The format of the keystore file. It must be either `jks` or `pkcs12`. [id="plugins-{type}s-{plugin}-ssl_keystore_password"] ===== `ssl_keystore_password` @@ -432,6 +447,32 @@ NOTE: If you configure the plugin to use `'TLSv1.1'` on any recent JVM, such as the protocol is disabled by default and needs to be enabled manually by changing `jdk.tls.disabledAlgorithms` in the *$JDK_HOME/conf/security/java.security* configuration file. That is, `TLSv1.1` needs to be removed from the list. +[id="plugins-{type}s-{plugin}-ssl_truststore_password"] +===== `ssl_truststore_password` + + * Value type is <> + * There is no default value for this setting. + +Set the truststore password + +[id="plugins-{type}s-{plugin}-ssl_truststore_path"] +===== `ssl_truststore_path` + + * Value type is <> + * There is no default value for this setting. + +The path for the keystore that contains the certificates to trust. It must be either a Java keystore (jks) or a PKCS#12 file. + +NOTE: You cannot use this setting and <> at the same time. + +[id="plugins-{type}s-{plugin}-ssl_truststore_type"] +===== `ssl_truststore_type` + +* Value can be any of: `jks`, `pkcs12` +* If not provided, the value will be inferred from the truststore filename. + +The format of the truststore file. It must be either `jks` or `pkcs12`. + [id="plugins-{type}s-{plugin}-ssl_verify_mode"] ===== `ssl_verify_mode` deprecated[3.7.0, Replaced by <>] diff --git a/lib/logstash/inputs/http.rb b/lib/logstash/inputs/http.rb index df1aeeb8..69c03416 100644 --- a/lib/logstash/inputs/http.rb +++ b/lib/logstash/inputs/http.rb @@ -76,12 +76,24 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base # The JKS keystore password config :ssl_keystore_password, :validate => :password - # The JKS keystore to validate the client's certificates + # The path for the keystore file that contains a private key and certificate config :ssl_keystore_path, :validate => :path + # The format of the keystore file. It must be either jks or pkcs12 + config :ssl_keystore_type, :validate => %w[pkcs12 jks] + # SSL key passphrase to use. config :ssl_key_passphrase, :validate => :password + # Set the truststore password + config :ssl_truststore_password, :validate => :password + + # The path for the keystore that contains the certificates to trust. It must be either a Java keystore (jks) or a PKCS#12 file + config :ssl_truststore_path, :validate => :path + + # The format of the truststore file. It must be either jks or pkcs12 + config :ssl_truststore_type, :validate => %w[pkcs12 jks] + # Validate client certificates against these authorities. # You can define multiple files or paths. All the certificates will # be read and added to the trust store. You need to configure the `ssl_client_authentication` @@ -301,18 +313,31 @@ def validate_ssl_settings! raise LogStash::ConfigurationError, 'An `ssl_certificate` is required when using an `ssl_key`' end - unless ssl_key_configured? || ssl_jks_configured? + unless ssl_certificate_configured? || ssl_keystore_configured? raise LogStash::ConfigurationError, "Either an `ssl_certificate` or `ssl_keystore_path` is required when SSL is enabled `#{ssl_config_name} => true`" end - if require_certificate_authorities? && !certificate_authorities_configured? - config_name, optional, required = provided_client_authentication_config([SSL_CLIENT_AUTH_OPTIONAL, SSL_CLIENT_AUTH_REQUIRED]) - raise LogStash::ConfigurationError, "Using `#{config_name}` set to `#{optional}` or `#{required}`, requires the configuration of `ssl_certificate_authorities`" + if ssl_certificate_configured? && ssl_keystore_configured? + raise LogStash::ConfigurationError, 'Use either an `ssl_certificate` or an `ssl_keystore_path`' end - if !require_certificate_authorities? && certificate_authorities_configured? - config_name, optional, required = provided_client_authentication_config([SSL_CLIENT_AUTH_OPTIONAL, SSL_CLIENT_AUTH_REQUIRED]) - raise LogStash::ConfigurationError, "The configuration of `ssl_certificate_authorities` requires setting `#{config_name}` to `#{optional}` or '#{required}'" + if ssl_certificate_authorities_configured? && ssl_truststore_configured? + raise LogStash::ConfigurationError, 'Use either an `ssl_certificate_authorities` or an `ssl_truststore_path`' + end + + cli_auth_config_name, cli_auth_optional_val, cli_auth_required_val = provided_ssl_client_authentication_config([SSL_CLIENT_AUTH_OPTIONAL, SSL_CLIENT_AUTH_REQUIRED]) + if ssl_client_authentication_enabled? + # Ensure any CA is configured. By default, the keystore can also be used as CA + unless ssl_certificate_authorities_configured? || ssl_truststore_configured? || ssl_keystore_configured? + raise LogStash::ConfigurationError, "Using `#{cli_auth_config_name}` set to `#{cli_auth_optional_val}` or `#{cli_auth_required_val}`, requires the configuration of `ssl_certificate_authorities` or `ssl_truststore_path`" + end + else + if ssl_truststore_configured? + raise LogStash::ConfigurationError, "The configuration of `ssl_truststore_path` requires setting `#{cli_auth_config_name}` to `#{cli_auth_optional_val}` or '#{cli_auth_required_val}'" + end + if ssl_certificate_authorities_configured? + raise LogStash::ConfigurationError, "The configuration of `ssl_certificate_authorities` requires setting `#{cli_auth_config_name}` to `#{cli_auth_optional_val}` or '#{cli_auth_required_val}'" + end end end @@ -372,73 +397,76 @@ def create_http_server(message_handler) def build_ssl_params return nil unless @ssl_enabled - if @ssl_keystore_path && @ssl_keystore_password - ssl_builder = org.logstash.plugins.inputs.http.util.JksSslBuilder.new(@ssl_keystore_path, @ssl_keystore_password.value) - else - ssl_builder = new_ssl_simple_builder - end - - new_ssl_handshake_provider(ssl_builder) + new_ssl_handshake_provider(new_ssl_simple_builder) end def new_ssl_simple_builder - passphrase = @ssl_key_passphrase.nil? ? nil : @ssl_key_passphrase.value begin - ssl_context_builder = SslSimpleBuilder.new(@ssl_certificate, @ssl_key, passphrase) - .setProtocols(@ssl_supported_protocols) - .setCipherSuites(normalized_cipher_suites) + if ssl_keystore_configured? + ssl_context_builder = SslSimpleBuilder.withKeyStore(@ssl_keystore_type, @ssl_keystore_path, @ssl_keystore_password&.value) + else + ssl_context_builder = SslSimpleBuilder.withPemCertificate(@ssl_certificate, @ssl_key, @ssl_key_passphrase&.value) + end - if client_authentication_enabled? - ssl_context_builder.setClientAuthentication(ssl_simple_builder_verify_mode, @ssl_certificate_authorities) + ssl_context_builder.setProtocols(@ssl_supported_protocols) + .setCipherSuites(normalized_cipher_suites) + .setClientAuthentication(ssl_simple_builder_verify_mode) + + if ssl_client_authentication_enabled? + if ssl_certificate_authorities_configured? + ssl_context_builder.setCertificateAuthorities(@ssl_certificate_authorities) + elsif ssl_truststore_configured? + ssl_context_builder.setTrustStore(@ssl_truststore_type, @ssl_truststore_path, @ssl_truststore_password&.value) + end end ssl_context_builder - rescue java.lang.IllegalArgumentException => e + rescue => e @logger.error("SSL configuration invalid", error_details(e)) raise LogStash::ConfigurationError, e end end def ssl_simple_builder_verify_mode - return SslSimpleBuilder::SslClientVerifyMode::OPTIONAL if client_authentication_optional? - return SslSimpleBuilder::SslClientVerifyMode::REQUIRED if client_authentication_required? - return SslSimpleBuilder::SslClientVerifyMode::NONE if client_authentication_none? + return SslSimpleBuilder::SslClientVerifyMode::OPTIONAL if ssl_client_authentication_optional? + return SslSimpleBuilder::SslClientVerifyMode::REQUIRED if ssl_client_authentication_required? + return SslSimpleBuilder::SslClientVerifyMode::NONE if ssl_client_authentication_none? raise LogStash::ConfigurationError, "Invalid `ssl_client_authentication` value #{@ssl_client_authentication}" end - def ssl_key_configured? - !!(@ssl_certificate && @ssl_key) + def ssl_certificate_configured? + !(@ssl_certificate.nil? || @ssl_certificate.empty?) end - def ssl_jks_configured? - !!(@ssl_keystore_path && @ssl_keystore_password) + def ssl_keystore_configured? + !(@ssl_keystore_path.nil? || @ssl_keystore_path.empty?) end - def client_authentication_enabled? - client_authentication_optional? || client_authentication_required? + def ssl_truststore_configured? + !(@ssl_truststore_path.nil? || @ssl_truststore_path.empty?) end - def require_certificate_authorities? - client_authentication_required? || client_authentication_optional? + def ssl_client_authentication_enabled? + ssl_client_authentication_optional? || ssl_client_authentication_required? end - def certificate_authorities_configured? + def ssl_certificate_authorities_configured? @ssl_certificate_authorities && @ssl_certificate_authorities.size > 0 end - def client_authentication_required? + def ssl_client_authentication_required? @ssl_client_authentication && @ssl_client_authentication.downcase == SSL_CLIENT_AUTH_REQUIRED end - def client_authentication_none? + def ssl_client_authentication_none? @ssl_client_authentication && @ssl_client_authentication.downcase == SSL_CLIENT_AUTH_NONE end - def client_authentication_optional? + def ssl_client_authentication_optional? @ssl_client_authentication && @ssl_client_authentication.downcase == SSL_CLIENT_AUTH_OPTIONAL end - def provided_client_authentication_config(values = [@ssl_client_authentication]) + def provided_ssl_client_authentication_config(values = [@ssl_client_authentication]) if original_params.include?('ssl_verify_mode') ['ssl_verify_mode', *values.map { |v| SSL_VERIFY_MODE_TO_CLIENT_AUTHENTICATION_MAP.key(v) }] elsif original_params.include?('verify_mode') diff --git a/spec/fixtures/certs/generate.sh b/spec/fixtures/certs/generate.sh index c1c71498..95549791 100755 --- a/spec/fixtures/certs/generate.sh +++ b/spec/fixtures/certs/generate.sh @@ -12,11 +12,16 @@ echo "DO NOT USE THESE CERTIFICATES IN PRODUCTION" >> ./README.txt # certificate authority openssl genrsa -out root.key 4096 openssl req -new -x509 -days 1826 -extensions ca -key root.key -out root.crt -subj "/C=LS/ST=NA/L=Http Input/O=Logstash/CN=root" -config ../openssl.cnf +# using keytool here as openssl < 3.2 won't add the "2.16.840.1.113894.746875.1.1" OID to make the cert "trusted" # See more: +# * https://github.com/openssl/openssl/pull/19025 +# * https://github.com/openssl/openssl/commit/e869c867c1c405de3b6538586f17b67937556a4b +keytool -import -file root.crt -alias rootCA -keystore truststore.jks -noprompt -storepass 12345678 # server certificate from root openssl genrsa -out server_from_root.key 4096 openssl req -new -key server_from_root.key -out server_from_root.csr -subj "/C=LS/ST=NA/L=Http Input/O=Logstash/CN=server" -config ../openssl.cnf openssl x509 -req -extensions server_cert -extfile ../openssl.cnf -days 1096 -in server_from_root.csr -CA root.crt -CAkey root.key -set_serial 03 -out server_from_root.crt +openssl pkcs12 -export -out server_from_root.p12 -inkey server_from_root.key -in server_from_root.crt -certfile root.crt -password pass:12345678 # client certificate from root openssl genrsa -out client_from_root.key 4096 diff --git a/spec/fixtures/certs/generated/server_from_root.p12 b/spec/fixtures/certs/generated/server_from_root.p12 new file mode 100644 index 00000000..aec9a662 Binary files /dev/null and b/spec/fixtures/certs/generated/server_from_root.p12 differ diff --git a/spec/fixtures/certs/generated/truststore.jks b/spec/fixtures/certs/generated/truststore.jks new file mode 100644 index 00000000..1258a580 Binary files /dev/null and b/spec/fixtures/certs/generated/truststore.jks differ diff --git a/spec/inputs/http_spec.rb b/spec/inputs/http_spec.rb index b594d0ca..ba539954 100644 --- a/spec/inputs/http_spec.rb +++ b/spec/inputs/http_spec.rb @@ -735,6 +735,16 @@ def setup_server_client(url = self.url) end end + context "and with :ssl_keystore_path" do + let(:config) do + super().merge('ssl_keystore_path' => certificate_path( 'server_from_root.p12'), 'ssl_enabled' => true ) + end + + it "should raise a configuration error" do + expect { subject.register }.to raise_error LogStash::ConfigurationError, /Use either an `ssl_certificate` or an `ssl_keystore_path`/i + end + end + context "with ssl_client_authentication" do context "normalized from ssl_verify_mode 'none'" do let(:config) { super().merge("ssl_verify_mode" => "none") } @@ -766,7 +776,7 @@ def setup_server_client(url = self.url) context "with no ssl_certificate_authorities set " do let(:config) { super().reject { |key| "ssl_certificate_authorities".eql?(key) } } it "raise a configuration error" do - expect {subject.register}.to raise_error(LogStash::ConfigurationError, "Using `ssl_verify_mode` set to `peer` or `force_peer`, requires the configuration of `ssl_certificate_authorities`") + expect {subject.register}.to raise_error(LogStash::ConfigurationError, "Using `ssl_verify_mode` set to `peer` or `force_peer`, requires the configuration of `ssl_certificate_authorities` or `ssl_truststore_path`") end end end @@ -786,13 +796,21 @@ def setup_server_client(url = self.url) expect {subject.register}.to raise_error(LogStash::ConfigurationError, "The configuration of `ssl_certificate_authorities` requires setting `ssl_client_authentication` to `optional` or 'required'") end end + + context "with ssl_truststore_path set" do + let(:config) { super().merge("ssl_truststore_path" => certificate_path('truststore.jks'), "ssl_truststore_password" => "12345678") } + + it "raise a configuration error" do + expect {subject.register}.to raise_error(LogStash::ConfigurationError, "The configuration of `ssl_truststore_path` requires setting `ssl_client_authentication` to `optional` or 'required'") + end + end end context "configured to 'required'" do let(:config) { super().merge("ssl_client_authentication" => "required") } it "raise a ConfigurationError when certificate_authorities is not set" do - expect {subject.register}.to raise_error(LogStash::ConfigurationError, "Using `ssl_client_authentication` set to `optional` or `required`, requires the configuration of `ssl_certificate_authorities`") + expect {subject.register}.to raise_error(LogStash::ConfigurationError, "Using `ssl_client_authentication` set to `optional` or `required`, requires the configuration of `ssl_certificate_authorities` or `ssl_truststore_path`") end context "with ssl_certificate_authorities set" do @@ -802,13 +820,30 @@ def setup_server_client(url = self.url) expect {subject.register}.not_to raise_error end end + + context "with ssl_truststore_path set to a valid truststore" do + let(:config) { super().merge("ssl_truststore_path" => certificate_path('truststore.jks'), "ssl_truststore_password" => "12345678") } + + it "doesn't raise a configuration error" do + expect {subject.register}.not_to raise_error + end + end + + context "with ssl_truststore_path set with no trusted certificate" do + let(:truststore_path) { certificate_path('server_from_root.p12') } + let(:config) { super().merge("ssl_truststore_path" => truststore_path, "ssl_truststore_password" => "12345678") } + + it "raise a configuration error" do + expect {subject.register}.to raise_error(LogStash::ConfigurationError, "The provided Trust Store file does not contains any trusted certificate entry: #{truststore_path}") + end + end end context "configured to 'optional'" do let(:config) { super().merge("ssl_client_authentication" => "optional") } it "raise a ConfigurationError when certificate_authorities is not set" do - expect {subject.register}.to raise_error(LogStash::ConfigurationError, "Using `ssl_client_authentication` set to `optional` or `required`, requires the configuration of `ssl_certificate_authorities`") + expect {subject.register}.to raise_error(LogStash::ConfigurationError, "Using `ssl_client_authentication` set to `optional` or `required`, requires the configuration of `ssl_certificate_authorities` or `ssl_truststore_path`") end context "with certificate_authorities set" do @@ -818,9 +853,71 @@ def setup_server_client(url = self.url) expect {subject.register}.not_to raise_error end end + + context "with ssl_truststore_path set" do + let(:config) { super().merge("ssl_truststore_path" => certificate_path('truststore.jks'), "ssl_truststore_password" => "12345678") } + + it "doesn't raise a configuration error" do + expect {subject.register}.not_to raise_error + end + end + + context "with ssl_truststore_path set with no trusted certificate" do + let(:config) { super().merge("ssl_truststore_path" => certificate_path('server_from_root.p12'), "ssl_truststore_password" => "12345678") } + + it "doesn't raise a configuration error" do + expect {subject.register}.not_to raise_error + end + end + end + end + end + context "with :ssl_keystore_path" do + let(:config) do + { + "port" => port, + "ssl_enabled" => true, + "ssl_keystore_path" => certificate_path( 'server_from_root.p12'), + "ssl_keystore_password" => "12345678" + } + end + + subject { LogStash::Inputs::Http.new(config) } + + it "should not raise exception" do + expect { subject.register }.to_not raise_exception + end + end + context "with :ssl_truststore_path" do + let(:config) do + { + "port" => port, + "ssl_enabled" => true, + "ssl_client_authentication" => "optional", + "ssl_keystore_path" => certificate_path( 'server_from_root.p12'), + "ssl_keystore_password" => "12345678", + "ssl_truststore_path" => certificate_path( 'truststore.jks'), + "ssl_truststore_password" => "12345678" + } + end + + subject { LogStash::Inputs::Http.new(config) } + + it "should not raise exception" do + expect { subject.register }.to_not raise_exception + end + + context "and with :ssl_certificate_authorities configured" do + let(:config) do + super().merge('ssl_certificate_authorities' => [certificate_path( 'root.crt')], 'ssl_enabled' => true ) + end + + it "should raise a configuration error" do + expect { subject.register }.to raise_error LogStash::ConfigurationError, /Use either an `ssl_certificate_authorities` or an `ssl_truststore_path`/i end end end + end end diff --git a/src/main/java/org/logstash/plugins/inputs/http/util/JksSslBuilder.java b/src/main/java/org/logstash/plugins/inputs/http/util/JksSslBuilder.java deleted file mode 100644 index c326278c..00000000 --- a/src/main/java/org/logstash/plugins/inputs/http/util/JksSslBuilder.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.logstash.plugins.inputs.http.util; - -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManagerFactory; -import java.io.FileInputStream; -import java.security.KeyStore; -import java.security.Security; - -public class JksSslBuilder implements SslBuilder { - private static final String ALGORITHM_SUN_X509 = "SunX509"; - private static final String ALGORITHM = "ssl.KeyManagerFactory.algorithm"; - private final String keyStorePath; - private final char[] keyStorePassword; - - public JksSslBuilder(String keyStorePath, String keyStorePassword) { - this.keyStorePath = keyStorePath; - this.keyStorePassword = keyStorePassword.toCharArray(); - } - - public SslContext build() throws Exception { - String algorithm = Security.getProperty(ALGORITHM); - if (algorithm == null) { - algorithm = ALGORITHM_SUN_X509; - } - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType()); - ks.load(new FileInputStream(keyStorePath), keyStorePassword); - ts.load(new FileInputStream(keyStorePath), keyStorePassword); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); - kmf.init(ks, keyStorePassword); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); - tmf.init(ts); - - SslContextBuilder builder = SslContextBuilder.forServer(kmf); - builder.trustManager(tmf); - - return SslSimpleBuilder.doBuild(builder); - } -} diff --git a/src/main/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilder.java b/src/main/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilder.java index 25f17fa5..5c8fc56e 100644 --- a/src/main/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilder.java +++ b/src/main/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilder.java @@ -10,18 +10,30 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Enumeration; import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Objects; import java.util.Set; import javax.crypto.Cipher; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.TrustManagerFactory; public class SslSimpleBuilder implements SslBuilder { @@ -86,26 +98,47 @@ public ClientAuth toClientAuth() { private String[] protocols = new String[] { "TLSv1.2", "TLSv1.3" }; private String[] ciphers = getDefaultCiphers(); - private final File sslKeyFile; - private final File sslCertificateFile; + private File sslKeyFile; + private File sslCertificateFile; private String[] certificateAuthorities; - private final String passphrase; + private KeyStore keyStore; + private char[] keyStorePassword; + private KeyStore trustStore; + private String passphrase; private SslClientVerifyMode verifyMode = SslClientVerifyMode.NONE; - public SslSimpleBuilder(String sslCertificateFilePath, String sslKeyFilePath, String pass) { - sslCertificateFile = new File(sslCertificateFilePath); - if (!sslCertificateFile.canRead()) { + public static SslSimpleBuilder withPemCertificate(String sslCertificateFilePath, String sslKeyFilePath, String pass) { + SslSimpleBuilder builder = new SslSimpleBuilder(); + + builder.sslCertificateFile = new File(sslCertificateFilePath); + if (!builder.sslCertificateFile.canRead()) { throw new IllegalArgumentException( String.format("Certificate file cannot be read. Please confirm the user running Logstash has permissions to read: %s", sslCertificateFilePath)); } - sslKeyFile = new File(sslKeyFilePath); - if (!sslKeyFile.canRead()) { + builder.sslKeyFile = new File(sslKeyFilePath); + if (!builder.sslKeyFile.canRead()) { throw new IllegalArgumentException( String.format("Private key file cannot be read. Please confirm the user running Logstash has permissions to read: %s", sslKeyFilePath)); } - passphrase = pass; + builder.passphrase = pass; + return builder; + } + + public static SslSimpleBuilder withKeyStore(String keyStoreType, String keyStoreFile, String keyStorePassword) throws Exception { + SslSimpleBuilder builder = new SslSimpleBuilder(); + final Path keyStorePath = Paths.get(Objects.requireNonNull(keyStoreFile, "Keystore path cannot be null")); + if (!Files.isReadable(keyStorePath)) { + throw new IllegalArgumentException(String.format("Keystore file cannot be read. Please confirm the user running Logstash has permissions to read: %s", keyStoreFile)); + } + + builder.keyStorePassword = formatJksPassword(keyStorePassword); + builder.keyStore = readKeyStore(keyStorePath, resolveKeyStoreType(keyStoreType, keyStorePath), builder.keyStorePassword); + return builder; + } + + private SslSimpleBuilder() { } public SslSimpleBuilder setProtocols(String[] protocols) { @@ -129,16 +162,39 @@ public SslSimpleBuilder setCipherSuites(String[] ciphersSuite) throws IllegalArg return this; } - public SslSimpleBuilder setClientAuthentication(SslClientVerifyMode verifyMode, String[] certificateAuthorities) { - if (isClientAuthenticationEnabled(verifyMode) && (certificateAuthorities == null || certificateAuthorities.length < 1)) { - throw new IllegalArgumentException("Certificate authorities are required to enable client authentication"); + public SslSimpleBuilder setClientAuthentication(SslClientVerifyMode verifyMode) { + this.verifyMode = verifyMode; + return this; + } + + public SslSimpleBuilder setCertificateAuthorities(String[] certificateAuthorities) { + if (certificateAuthorities == null || certificateAuthorities.length == 0){ + throw new IllegalArgumentException("SSL certificate authorities is required"); } - this.verifyMode = verifyMode; this.certificateAuthorities = certificateAuthorities; return this; } + public SslSimpleBuilder setTrustStore(String trustStoreType, String trustStoreFile, String trustStorePassword) throws Exception { + final Path trustStorePath = Paths.get(Objects.requireNonNull(trustStoreFile, "Trust Store path cannot be null")); + if (!Files.isReadable(trustStorePath)) { + throw new IllegalArgumentException(String.format("Trust Store file cannot be read. Please confirm the user running Logstash has permissions to read: %s", trustStoreFile)); + } + + this.trustStore = readKeyStore( + trustStorePath, + resolveKeyStoreType(trustStoreType, trustStorePath), + formatJksPassword(trustStorePassword) + ); + + if (!hasTrustStoreEntry(this.trustStore) && isClientAuthenticationRequired()) { + throw new IllegalArgumentException(String.format("The provided Trust Store file does not contains any trusted certificate entry: %s", trustStoreFile)); + } + + return this; + } + private boolean isClientAuthenticationEnabled(final SslClientVerifyMode mode) { return mode == SslClientVerifyMode.OPTIONAL || mode == SslClientVerifyMode.REQUIRED; } @@ -148,28 +204,80 @@ public boolean isClientAuthenticationRequired() { } public SslContext build() throws Exception { + if (this.trustStore != null && this.certificateAuthorities != null) { + throw new IllegalStateException("Use either a bundle of Certificate Authorities or a Trust Store to configure client authentication"); + } + if (logger.isDebugEnabled()) { logger.debug("Available ciphers: {}", SUPPORTED_CIPHERS); logger.debug("Ciphers: {}", Arrays.toString(ciphers)); } - SslContextBuilder builder = SslContextBuilder - .forServer(sslCertificateFile, sslKeyFile, passphrase) + SslContextBuilder builder = createSslContextBuilder() .ciphers(Arrays.asList(ciphers)) - .protocols(protocols); + .protocols(protocols) + .clientAuth(verifyMode.toClientAuth()); if (isClientAuthenticationEnabled(verifyMode)) { - if (logger.isDebugEnabled()) { - logger.debug("Certificate Authorities: {}", Arrays.toString(certificateAuthorities)); - } + if (certificateAuthorities != null) { + if (logger.isDebugEnabled()) { + logger.debug("Certificate Authorities: {}", Arrays.toString(certificateAuthorities)); + } - builder.clientAuth(verifyMode.toClientAuth()) - .trustManager(loadCertificateCollection(certificateAuthorities)); + builder.trustManager(loadCertificateCollection(certificateAuthorities)); + } else if (trustStore != null || keyStore != null) { + builder.trustManager(createTrustManagerFactory()); + } else { + throw new IllegalStateException("Either an SSL certificate or an SSL Trust Store is required when SSL is enabled"); + } } return doBuild(builder); } + private SslContextBuilder createSslContextBuilder() throws Exception { + if (sslCertificateFile != null) { + return SslContextBuilder.forServer(sslCertificateFile, sslKeyFile, passphrase); + } + + if (keyStore != null) { + return SslContextBuilder.forServer(createKeyManagerFactory()); + } + + throw new IllegalStateException("Either a KeyStore or an SSL certificate must be provided"); + } + + private KeyManagerFactory createKeyManagerFactory() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException { + final KeyManagerFactory kmf = getDefaultKeyManagerFactory(); + kmf.init(this.keyStore, this.keyStorePassword); + return kmf; + } + + KeyManagerFactory getDefaultKeyManagerFactory() throws NoSuchAlgorithmException { + return KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + } + + private TrustManagerFactory createTrustManagerFactory() throws Exception { + final TrustManagerFactory tmf = getDefaultTrustManagerFactory(); + if (this.trustStore == null) { + logger.info("SSL Trust Store not configured, using the provided Key Store instead."); + + if (logger.isDebugEnabled() && !hasTrustStoreEntry(this.keyStore)) { + logger.debug("The provided SSL Key Store, used as Trust Store, has no trusted certificate entry."); + } + + tmf.init(this.keyStore); + return tmf; + } + + tmf.init(this.trustStore); + return tmf; + } + + TrustManagerFactory getDefaultTrustManagerFactory() throws NoSuchAlgorithmException { + return TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + } + // NOTE: copy-pasta from input-beats static SslContext doBuild(final SslContextBuilder builder) throws Exception { try { @@ -240,4 +348,58 @@ String[] getCiphers() { SslClientVerifyMode getVerifyMode() { return verifyMode; } + + static String resolveKeyStoreType(String type, Path path) { + if (type == null || type.isEmpty()) { + return inferKeyStoreType(path); + } + return type; + } + + private static String inferKeyStoreType(Path path) { + String name = path == null ? "" : path.getFileName().toString().toLowerCase(Locale.ROOT); + if (name.endsWith(".p12") || name.endsWith(".pfx") || name.endsWith(".pkcs12")) { + return "PKCS12"; + } else { + return "jks"; + } + } + + private static char[] formatJksPassword(String password) { + if (password == null){ + return null; + } + + return password.toCharArray(); + } + + private static KeyStore readKeyStore(Path path, String ksType, char[] password) throws GeneralSecurityException, IOException { + final KeyStore keyStore = KeyStore.getInstance(ksType); + if (path != null) { + try (InputStream in = Files.newInputStream(path)) { + keyStore.load(in, password); + } + } + return keyStore; + } + + private boolean hasTrustStoreEntry(KeyStore store) throws GeneralSecurityException { + Enumeration aliases = store.aliases(); + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + if (store.isCertificateEntry(alias)) { + return true; + } + } + + return false; + } + + KeyStore getKeyStore() { + return keyStore; + } + + KeyStore getTrustStore() { + return trustStore; + } } diff --git a/src/test/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilderTest.java b/src/test/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilderTest.java index 38659ff6..53b4e386 100644 --- a/src/test/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilderTest.java +++ b/src/test/java/org/logstash/plugins/inputs/http/util/SslSimpleBuilderTest.java @@ -5,16 +5,24 @@ import io.netty.handler.ssl.SslContext; import org.junit.jupiter.api.Test; +import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.TrustManagerFactorySpi; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.Security; import java.util.Arrays; import java.util.List; +import static org.elasticsearch.mock.orig.Mockito.spy; +import static org.elasticsearch.mock.orig.Mockito.verify; +import static org.elasticsearch.mock.orig.Mockito.when; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.isIn; -import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Every.everyItem; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -26,23 +34,29 @@ import static org.logstash.plugins.inputs.http.util.SslSimpleBuilder.SUPPORTED_CIPHERS; import static org.logstash.plugins.inputs.http.util.SslSimpleBuilder.SslClientVerifyMode; import static org.logstash.plugins.inputs.http.util.SslSimpleBuilder.getDefaultCiphers; -import static org.logstash.plugins.inputs.http.util.TestUtils.resourcePath; +import static org.logstash.plugins.inputs.http.util.TestCertificates.CA; +import static org.logstash.plugins.inputs.http.util.TestCertificates.CERTIFICATE; +import static org.logstash.plugins.inputs.http.util.TestCertificates.KEY; +import static org.logstash.plugins.inputs.http.util.TestCertificates.KEYSTORE; +import static org.logstash.plugins.inputs.http.util.TestCertificates.KEYSTORE_PASSWORD; +import static org.logstash.plugins.inputs.http.util.TestCertificates.KEYSTORE_TYPE; +import static org.logstash.plugins.inputs.http.util.TestCertificates.KEY_ENCRYPTED; +import static org.logstash.plugins.inputs.http.util.TestCertificates.KEY_ENCRYPTED_PASS; +import static org.logstash.plugins.inputs.http.util.TestCertificates.TRUSTSTORE; +import static org.logstash.plugins.inputs.http.util.TestCertificates.TRUSTSTORE_PASSWORD; +import static org.logstash.plugins.inputs.http.util.TestCertificates.TRUSTSTORE_TYPE; +import static org.mockito.Matchers.eq; /** * Unit test for {@link SslSimpleBuilder} */ class SslSimpleBuilderTest { - private static final String CERTIFICATE = resourcePath("host.crt"); - private static final String KEY = resourcePath("host.key"); - private static final String KEY_ENCRYPTED = resourcePath("host.enc.key"); - private static final String KEY_ENCRYPTED_PASS = "1234"; - private static final String CA = resourcePath("root-ca.crt"); @Test - void testConstructorShouldFailWhenCertificatePathIsInvalid() { + void testWithPemCertificateShouldFailWhenCertificatePathIsInvalid() { final IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> new SslSimpleBuilder("foo-bar.crt", KEY_ENCRYPTED, KEY_ENCRYPTED_PASS) + () -> SslSimpleBuilder.withPemCertificate("foo-bar.crt", KEY_ENCRYPTED, KEY_ENCRYPTED_PASS) ); assertEquals( @@ -52,10 +66,10 @@ void testConstructorShouldFailWhenCertificatePathIsInvalid() { } @Test - void testConstructorShouldFailWhenKeyPathIsInvalid() { + void testWithPemCertificateShouldFailWhenKeyPathIsInvalid() { final IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> new SslSimpleBuilder(CERTIFICATE, "invalid.key", KEY_ENCRYPTED_PASS) + () -> SslSimpleBuilder.withPemCertificate(CERTIFICATE, "invalid.key", KEY_ENCRYPTED_PASS) ); assertEquals( @@ -64,15 +78,44 @@ void testConstructorShouldFailWhenKeyPathIsInvalid() { ); } + @Test + void testWithPemCertificateShouldNotFailWithValidConfig() { + assertDoesNotThrow(this::createPemSslSimpleBuilder); + } + + @Test + void testWithKeyStoreShouldFailWhenKeystorePathIsInvalid() { + final IllegalArgumentException thrown = assertThrows( + IllegalArgumentException.class, + () -> SslSimpleBuilder.withKeyStore(KEYSTORE_TYPE, "foo-bar.jks", KEYSTORE_PASSWORD) + ); + + assertEquals( + "Keystore file cannot be read. Please confirm the user running Logstash has permissions to read: foo-bar.jks", + thrown.getMessage() + ); + } + + @Test + void testWithKeyStoreShouldNotFailWithValidConfig() { + assertDoesNotThrow(this::createJksSslSimpleBuilder); + } + + @Test + void testWithKeyStoreShouldNotFailWithNullType() throws Exception { + final SslSimpleBuilder sslSimpleBuilder = SslSimpleBuilder.withKeyStore(null, KEYSTORE, KEYSTORE_PASSWORD); + assertEquals(KEYSTORE_TYPE, sslSimpleBuilder.getKeyStore().getType()); + } + @Test void testSetCipherSuitesShouldNotFailIfAllCiphersAreValid() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder(); assertDoesNotThrow(() -> sslSimpleBuilder.setCipherSuites(SUPPORTED_CIPHERS.toArray(new String[0]))); } @Test void testSetCipherSuitesShouldThrowIfAnyCiphersIsInValid() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder(); final String[] ciphers = SUPPORTED_CIPHERS .toArray(new String[SUPPORTED_CIPHERS.size() + 1]); @@ -88,7 +131,7 @@ void testSetCipherSuitesShouldThrowIfAnyCiphersIsInValid() { @Test void testSetProtocols() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder(); assertArrayEquals(new String[]{"TLSv1.2", "TLSv1.3"}, sslSimpleBuilder.getProtocols()); sslSimpleBuilder.setProtocols(new String[]{"TLSv1.1"}); @@ -110,64 +153,79 @@ void testGetDefaultCiphers() { } @Test - void testSetClientAuthentication() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); + void testSetCertificateAuthorities() { final String[] certificateAuthorities = {CA}; - sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.REQUIRED, certificateAuthorities); + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + .setCertificateAuthorities(certificateAuthorities); assertThat(sslSimpleBuilder.getVerifyMode(), is(SslClientVerifyMode.REQUIRED)); assertThat(Arrays.asList(sslSimpleBuilder.getCertificateAuthorities()), everyItem(isIn(certificateAuthorities))); } @Test - void testSetClientAuthenticationWithRequiredAndNoCertAuthorities() { - assertSetClientAuthenticationThrowsWhenCAIsNullOrEmpty(SslClientVerifyMode.REQUIRED); - } + void testSetCertificateAuthoritiesWithNoValue() { + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED); - @Test - void testSetClientAuthenticationWithOptionalAndNoCertAuthorities() { - assertSetClientAuthenticationThrowsWhenCAIsNullOrEmpty(SslClientVerifyMode.OPTIONAL); + final IllegalArgumentException emptyThrown = assertThrows( + IllegalArgumentException.class, + ()-> sslSimpleBuilder.setCertificateAuthorities(new String[0]) + ); + + final IllegalArgumentException nullThrown = assertThrows( + IllegalArgumentException.class, + ()-> sslSimpleBuilder.setCertificateAuthorities(null) + ); + + final String expectedMessage = "SSL certificate authorities is required"; + assertEquals(expectedMessage, emptyThrown.getMessage()); + assertEquals(expectedMessage, nullThrown.getMessage()); } @Test - void testSetClientAuthenticationWithNoneAndEmptyCA() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); - sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.NONE, new String[0]); - assertThat(sslSimpleBuilder.getVerifyMode(), is(SslClientVerifyMode.NONE)); - assertThat(sslSimpleBuilder.getCertificateAuthorities(), arrayWithSize(0)); + void testSetTrustStoreWithInvalidPath() { + final SslSimpleBuilder sslSimpleBuilder = createJksSslSimpleBuilder(); + + final IllegalArgumentException thrown = assertThrows( + IllegalArgumentException.class, + () -> sslSimpleBuilder.setTrustStore(TRUSTSTORE_TYPE, "trust-me.jks", TRUSTSTORE_PASSWORD) + ); + + assertEquals( + "Trust Store file cannot be read. Please confirm the user running Logstash has permissions to read: trust-me.jks", + thrown.getMessage() + ); } @Test - void testSetClientAuthenticationWithNoneAndNullCA() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); - sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.NONE, null); - assertThat(sslSimpleBuilder.getVerifyMode(), is(SslClientVerifyMode.NONE)); - assertThat(sslSimpleBuilder.getCertificateAuthorities(), nullValue()); + void testSetTrustStoreWithValidArguments() { + assertDoesNotThrow(() -> createPemSslSimpleBuilder().setTrustStore(TRUSTSTORE_TYPE, TRUSTSTORE, TRUSTSTORE_PASSWORD)); } @Test - void testDefaultVerifyMode() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); - assertThat(sslSimpleBuilder.getVerifyMode(), is(SslClientVerifyMode.NONE)); + void testSetTrustStoreWithNullTrustStoreType() throws Exception { + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder() + .setTrustStore(null, TRUSTSTORE, TRUSTSTORE_PASSWORD); + assertEquals(TRUSTSTORE_TYPE, sslSimpleBuilder.getTrustStore().getType()); } - private void assertSetClientAuthenticationThrowsWhenCAIsNullOrEmpty(SslClientVerifyMode mode) { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); - final String expectedMessage = "Certificate authorities are required to enable client authentication"; - - final IllegalArgumentException emptyThrown = assertThrows( + @Test + void testSetTrustStoreWithNoTrustedCertificate() { + assertThrows( IllegalArgumentException.class, - () -> sslSimpleBuilder.setClientAuthentication(mode, new String[0]) - ); - - final IllegalArgumentException nullThrown = assertThrows(IllegalArgumentException.class, - () -> sslSimpleBuilder.setClientAuthentication(mode, null), - expectedMessage + () -> createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + .setTrustStore(KEYSTORE_TYPE, KEYSTORE, KEYSTORE_PASSWORD), + String.format("The provided Trust Store file does not contains any trusted certificate entry: %s", KEYSTORE) ); + } - assertEquals(expectedMessage, emptyThrown.getMessage()); - assertEquals(expectedMessage, nullThrown.getMessage()); + @Test + void testDefaultVerifyModeIsNone() { + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder(); + assertThat(sslSimpleBuilder.getVerifyMode(), is(SslClientVerifyMode.NONE)); } @Test @@ -178,21 +236,28 @@ void testSslClientVerifyModeToClientAuth() { } @Test - void testBuildContextWithNonEncryptedKey() { - final SslSimpleBuilder sslSimpleBuilder = new SslSimpleBuilder(CERTIFICATE, KEY, null); + void testBuildContextWithNonEncryptedCertificateKey() { + final SslSimpleBuilder sslSimpleBuilder = SslSimpleBuilder.withPemCertificate(CERTIFICATE, KEY, null); + assertDoesNotThrow(sslSimpleBuilder::build); + } + + @Test + void testBuildContextWithEncryptedCertificateKey() { + final SslSimpleBuilder sslSimpleBuilder = SslSimpleBuilder.withPemCertificate(CERTIFICATE, KEY_ENCRYPTED, KEY_ENCRYPTED_PASS); assertDoesNotThrow(sslSimpleBuilder::build); } @Test - void testBuildContextWithEncryptedKey() { - final SslSimpleBuilder sslSimpleBuilder = new SslSimpleBuilder(CERTIFICATE, KEY_ENCRYPTED, "1234"); + void testBuildContextWithKeyStore() throws Exception { + final SslSimpleBuilder sslSimpleBuilder = SslSimpleBuilder.withKeyStore(KEYSTORE_TYPE, KEYSTORE, KEYSTORE_PASSWORD); assertDoesNotThrow(sslSimpleBuilder::build); } @Test - void testBuildContextWhenClientAuthenticationIsRequired() throws Exception { - final SSLEngine sslEngine = assertSSlEngineFromBuilder(createSslSimpleBuilder() - .setClientAuthentication(SslClientVerifyMode.REQUIRED, new String[]{CA}) + void testBuildContextWithClientAuthenticationRequiredAndCAs() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + .setCertificateAuthorities(new String[]{CA}) ); assertTrue(sslEngine.getNeedClientAuth()); @@ -200,9 +265,21 @@ void testBuildContextWhenClientAuthenticationIsRequired() throws Exception { } @Test - void testBuildContextWhenClientAuthenticationIsOptional() throws Exception { - final SSLEngine sslEngine = assertSSlEngineFromBuilder(createSslSimpleBuilder() - .setClientAuthentication(SslClientVerifyMode.OPTIONAL, new String[]{CA}) + void testBuildContextWithClientAuthenticationRequiredAndTrustStore() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + .setTrustStore(TRUSTSTORE_TYPE, TRUSTSTORE, TRUSTSTORE_PASSWORD) + ); + + assertTrue(sslEngine.getNeedClientAuth()); + assertFalse(sslEngine.getWantClientAuth()); + } + + @Test + void testBuildContextWithClientAuthenticationOptionalAndCAs() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.OPTIONAL) + .setCertificateAuthorities(new String[]{CA}) ); assertFalse(sslEngine.getNeedClientAuth()); @@ -210,29 +287,139 @@ void testBuildContextWhenClientAuthenticationIsOptional() throws Exception { } @Test - void testBuildContextWhenClientAuthenticationIsNone() throws Exception { - final SSLEngine sslEngine = assertSSlEngineFromBuilder(createSslSimpleBuilder() - .setClientAuthentication(SslClientVerifyMode.NONE, new String[]{CA})); + void testBuildContextWithClientAuthenticationOptionalAndTrustStore() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.OPTIONAL) + .setTrustStore(TRUSTSTORE_TYPE, TRUSTSTORE, TRUSTSTORE_PASSWORD) + ); + + assertFalse(sslEngine.getNeedClientAuth()); + assertTrue(sslEngine.getWantClientAuth()); + } + + @Test + void testBuildContextWithClientAuthenticationNone() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.NONE)); + + assertFalse(sslEngine.getNeedClientAuth()); + assertFalse(sslEngine.getWantClientAuth()); + } + + @Test + void testBuildContextWithClientAuthenticationNoneAndTrustStore() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.NONE) + .setTrustStore(TRUSTSTORE_TYPE, TRUSTSTORE, TRUSTSTORE_PASSWORD) + ); assertFalse(sslEngine.getNeedClientAuth()); assertFalse(sslEngine.getWantClientAuth()); } + @Test + void testBuildContextWithClientAuthenticationAndKeyStoreAsTrustStore() throws Exception { + final SSLEngine sslEngine = assertSSlEngineFromBuilder(createJksSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + ); + + assertTrue(sslEngine.getNeedClientAuth()); + assertFalse(sslEngine.getWantClientAuth()); + } + + @Test + void testBuildContextWithCAsAndTrustStore() throws Exception { + final SslSimpleBuilder sslSimpleBuilder = createJksSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + .setCertificateAuthorities(new String[]{CA}) + .setTrustStore(TRUSTSTORE_TYPE, TRUSTSTORE, TRUSTSTORE_PASSWORD); + + final IllegalStateException thrown = assertThrows( + IllegalStateException.class, + sslSimpleBuilder::build + ); + + assertEquals("Use either a bundle of Certificate Authorities or a Trust Store to configure client authentication", thrown.getMessage()); + } + + @Test + void testBuildContextWithPemCertificateAndNoCAsNeitherTrustStore() { + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder() + .setClientAuthentication(SslClientVerifyMode.REQUIRED); + + final IllegalStateException thrown = assertThrows( + IllegalStateException.class, + sslSimpleBuilder::build + ); + + assertEquals("Either an SSL certificate or an SSL Trust Store is required when SSL is enabled", thrown.getMessage()); + } + @Test void testIsClientAuthenticationRequired() { - final SslSimpleBuilder sslSimpleBuilder = createSslSimpleBuilder(); - final String[] certificateAuthorities = {CA}; + final SslSimpleBuilder sslSimpleBuilder = createPemSslSimpleBuilder(); - sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.NONE, certificateAuthorities); + sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.NONE); assertFalse(sslSimpleBuilder.isClientAuthenticationRequired()); - sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.OPTIONAL, certificateAuthorities); + sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.OPTIONAL); assertFalse(sslSimpleBuilder.isClientAuthenticationRequired()); - sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.REQUIRED, certificateAuthorities); + sslSimpleBuilder.setClientAuthentication(SslClientVerifyMode.REQUIRED); assertTrue(sslSimpleBuilder.isClientAuthenticationRequired()); } + @Test + void testClientAuthenticationWithTrustStoreUsesTruststore() throws Exception { + final SslSimpleBuilder jksSslBuilder = spy(createJksSslSimpleBuilder()) + .setClientAuthentication(SslClientVerifyMode.REQUIRED) + .setTrustStore(TRUSTSTORE_TYPE, TRUSTSTORE, TRUSTSTORE_PASSWORD); + + final DummyTrustManagerFactorySpi factorySpi = spy(new DummyTrustManagerFactorySpi()); + final TrustManagerFactory trustManagerFactory = new DummyTrustManagerFactory(factorySpi); + + when(jksSslBuilder.getDefaultTrustManagerFactory()) + .thenReturn(trustManagerFactory); + + jksSslBuilder.build(); + + verify(factorySpi).engineInit(eq(jksSslBuilder.getTrustStore())); + } + + @Test + void testClientAuthenticationUsesKeystoreWhenNoTrustStoreIsConfigured() throws Exception { + final SslSimpleBuilder jksSslBuilder = spy(createJksSslSimpleBuilder()); + jksSslBuilder.setClientAuthentication(SslClientVerifyMode.REQUIRED); + + final DummyTrustManagerFactorySpi factorySpi = spy(new DummyTrustManagerFactorySpi()); + final TrustManagerFactory trustManagerFactory = new DummyTrustManagerFactory(factorySpi); + + when(jksSslBuilder.getDefaultTrustManagerFactory()) + .thenReturn(trustManagerFactory); + + jksSslBuilder.build(); + + verify(factorySpi).engineInit(eq(jksSslBuilder.getKeyStore())); + } + + @Test + void testResolveKeyStoreType() { + assertEquals("PKCS12", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.p12"))); + assertEquals("PKCS12", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.P12"))); + + assertEquals("PKCS12", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.pfx"))); + assertEquals("PKCS12", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.PfX"))); + + assertEquals("PKCS12", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.pkcs12"))); + assertEquals("PKCS12", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.PKCS12"))); + + assertEquals("jks", SslSimpleBuilder.resolveKeyStoreType(null, Paths.get("dummy.anyOther"))); + + assertEquals("foo", SslSimpleBuilder.resolveKeyStoreType("foo", Paths.get("foo.p12"))); + assertEquals("bar", SslSimpleBuilder.resolveKeyStoreType("bar", Paths.get("bar.pfx"))); + assertEquals("any", SslSimpleBuilder.resolveKeyStoreType("any", Paths.get("any.pkcs12"))); + } + private SSLEngine assertSSlEngineFromBuilder(SslSimpleBuilder sslSimpleBuilder) throws Exception { final SslContext context = sslSimpleBuilder.build(); assertTrue(context.isServer()); @@ -244,7 +431,37 @@ private SSLEngine assertSSlEngineFromBuilder(SslSimpleBuilder sslSimpleBuilder) return sslEngine; } - private SslSimpleBuilder createSslSimpleBuilder() { - return new SslSimpleBuilder(CERTIFICATE, KEY_ENCRYPTED, KEY_ENCRYPTED_PASS); + private SslSimpleBuilder createPemSslSimpleBuilder() { + return SslSimpleBuilder.withPemCertificate(CERTIFICATE, KEY_ENCRYPTED, KEY_ENCRYPTED_PASS); + } + + private SslSimpleBuilder createJksSslSimpleBuilder() { + try { + return SslSimpleBuilder.withKeyStore(KEYSTORE_TYPE, KEYSTORE, KEYSTORE_PASSWORD); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static class DummyTrustManagerFactorySpi extends TrustManagerFactorySpi { + @Override + public void engineInit(KeyStore ks) { + } + + @Override + public void engineInit(ManagerFactoryParameters spec) { + + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return new TrustManager[0]; + } + } + + private static class DummyTrustManagerFactory extends TrustManagerFactory { + DummyTrustManagerFactory(TrustManagerFactorySpi spi) { + super(spi, Security.getProviders()[0], "SunX509"); + } } } \ No newline at end of file diff --git a/src/test/java/org/logstash/plugins/inputs/http/util/TestCertificates.java b/src/test/java/org/logstash/plugins/inputs/http/util/TestCertificates.java new file mode 100644 index 00000000..6f0fdb6d --- /dev/null +++ b/src/test/java/org/logstash/plugins/inputs/http/util/TestCertificates.java @@ -0,0 +1,21 @@ +package org.logstash.plugins.inputs.http.util; + +import static org.logstash.plugins.inputs.http.util.TestUtils.resourcePath; + +interface TestCertificates { + String CERTIFICATE = resourcePath("certificates/host.crt"); + String KEY = resourcePath("certificates/host.key"); + String KEY_ENCRYPTED = resourcePath("certificates/host.enc.key"); + String KEY_ENCRYPTED_PASS = "changeme"; + String CA = resourcePath("certificates/root-ca.crt"); + + + String KEYSTORE = resourcePath("certificates/host-keystore.p12"); + String KEYSTORE_TYPE = "PKCS12"; + String KEYSTORE_PASSWORD = "changeme"; + + + String TRUSTSTORE = resourcePath("certificates/truststore.jks"); + String TRUSTSTORE_TYPE = "jks"; + String TRUSTSTORE_PASSWORD = "changeme"; +} diff --git a/src/test/resources/certificates/generate.sh b/src/test/resources/certificates/generate.sh new file mode 100755 index 00000000..07d173c0 --- /dev/null +++ b/src/test/resources/certificates/generate.sh @@ -0,0 +1,15 @@ +# Warning: do not use the certificates produced by this tool in production. This is for testing purposes only +# CA +openssl genrsa 4096 | openssl pkcs8 -topk8 -nocrypt -out root-ca.key +openssl req -sha256 -x509 -newkey rsa:4096 -nodes -key root-ca.key -sha256 -days 999999 -out root-ca.crt -subj "/C=ES/ST=The Internet/L=The Internet/O=Logstash CA/OU=Logstash/CN=127.0.0.1" +keytool -import -file root-ca.crt -alias rootCA -keystore truststore.jks -noprompt -storepass changeme + +# Server +openssl genrsa 4096 | openssl pkcs8 -topk8 -nocrypt -out host.key +openssl req -sha256 -key host.key -newkey rsa:4096 -out host.csr -subj "/C=ES/ST=The Internet/L=The Internet/O=Logstash CA/OU=Logstash/CN=127.0.0.1" +openssl x509 -req -in host.csr -CA root-ca.crt -CAkey root-ca.key -CAcreateserial -out host.crt -sha256 -days 999999 +openssl pkcs12 -export -out host-keystore.p12 -inkey host.key -in host.crt -certfile root-ca.crt -password pass:changeme +openssl pkcs8 -topk8 -passout "pass:changeme" -in host.key -out host.enc.key + +rm -rf ./*.csr +rm -rf ./*.srl \ No newline at end of file diff --git a/src/test/resources/certificates/host-keystore.p12 b/src/test/resources/certificates/host-keystore.p12 new file mode 100644 index 00000000..044a0afb Binary files /dev/null and b/src/test/resources/certificates/host-keystore.p12 differ diff --git a/src/test/resources/certificates/host.crt b/src/test/resources/certificates/host.crt new file mode 100644 index 00000000..539826c4 --- /dev/null +++ b/src/test/resources/certificates/host.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFeTCCA2ECFBXbdq6k5O7NPXVDva8uMq7VU8ZlMA0GCSqGSIb3DQEBCwUAMHgx +CzAJBgNVBAYTAkVTMRUwEwYDVQQIDAxUaGUgSW50ZXJuZXQxFTATBgNVBAcMDFRo +ZSBJbnRlcm5ldDEUMBIGA1UECgwLTG9nc3Rhc2ggQ0ExETAPBgNVBAsMCExvZ3N0 +YXNoMRIwEAYDVQQDDAkxMjcuMC4wLjEwIBcNMjMxMTE1MTQxNTUxWhgPNDc2MTEw +MTExNDE1NTFaMHgxCzAJBgNVBAYTAkVTMRUwEwYDVQQIDAxUaGUgSW50ZXJuZXQx +FTATBgNVBAcMDFRoZSBJbnRlcm5ldDEUMBIGA1UECgwLTG9nc3Rhc2ggQ0ExETAP +BgNVBAsMCExvZ3N0YXNoMRIwEAYDVQQDDAkxMjcuMC4wLjEwggIiMA0GCSqGSIb3 +DQEBAQUAA4ICDwAwggIKAoICAQC0foYD+YvW8POFHaPllMD/KTzaIxo6yGpbhX5e +EzFOODjG3Z2WuVoojUli8LRKOBU2LRCQpNTfE+0YdGHPeaFoOgNT4QbamVXHtOkr ++GE5bVCXB48fqU8/xrP3YNmZfZE+wax+xWExn7+mrbWbiz6VpRVkZIPcWiKHXlPJ +MUteLRDVTPlnqB4kaktaQ/husqlcbvorNfTtsaJCK6ImyVEWi6zvIyhJtDF2C8C8 +0Gf/Ph/DCIMJeETgnF9iqIWo/Ixw/B5wW9Um4ew/hznnjzcAFaB7oLU4OLaEycvR +cu6J3DsoZy5cczzBcJkjicx7vbF2Bqf1uNj2JyXNqt+AdWQ5ZX2tAySxwuDLo0CK +OdJDl1vuII58iaESED2Za6XhEXeAPUAIzOsbJCXzzwZLtPp0dd8J8Fo1laPc+I0Q +v4pr+4m2xgLnMCUjZzZsYftlt3mvBtr0IZ5s9zUA4eujj8pKRUgnrJQozbR0prDf +OGrMhYunOBYSOq91uI61afmFujFWIyM9Ulr+aEwDRrfEciF47g3Ft3Y4xh23Ukna +oI+FAJZC1V1XaUutx1umeJUGY1qOKyraggMZULWCHk0PeArreMW338RgG+yYxpFC +oyf6JCx6IHiaEVNOISo3JRzNzKJvjIjAmh56phYli9bYQseUme9Xq99hAXtDxZXT +FeGt7wIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQADGYGKj9wWMf6Baj/Pr6R3uMXc +9yqmCf15mHsmmjiSB3zsSf8tfHc3sn0BnEX5U0vS7EIt0TW2LwMlEsU6K+qe5BAv +WxF4VqrqBWnHWkGi0mVPB/IkUlA6/NQbj1XQH7+Irop9ewMme9x/AGubRgqNyvtv +sVXvZy/XdQ3ZSpstmIl/OqzGDjX5CXbGeNPhAEtnWfMZXpBm7wDI/cye1bZI8+y/ +bH8iwHtpGHZXSiTR73cG3QSw1oSRJ2rX+By4L33THemD7AMymi5m5Dl82UKxVco4 +DotrIu3GKNrym8F7B5WmTNyUa/aa3HPRvrMXRM9XEwvof0fFiNvoviOodfsXrGjy +gr7m0NfkglL32aZj3ffcn2gBMEpYIMLuUE8OiG9YZB7dlRjihvUAWmZcUBaSGh1Y +rOmGm2EWd6TpSAIRG+2/PeGHoEu773n4Qe56VmHZ5z/cbqrUIvA3yd+INmudt+GT +u18rmt9gUVesRqrR8vhkI5oJBzz1k8Zen2s6MFpJ0tUIXKH3xt0953fFu0UqYf9J +xGxE9R4YBLc9fwsqDqmknGek4zxH4mMySiEiTBInIKXJu7t8jU7FFpfOpVm0tcRr +AGr+dv5AhwrEc2zUU87vWysfVZzVqu9N4ENTYUh96uM1amfJVt7tPDrpsDAB2Prk +idL9Q+QpOvWSDyFS2w== +-----END CERTIFICATE----- diff --git a/src/test/resources/certificates/host.enc.key b/src/test/resources/certificates/host.enc.key new file mode 100644 index 00000000..301a6a0e --- /dev/null +++ b/src/test/resources/certificates/host.enc.key @@ -0,0 +1,54 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/certificates/host.key b/src/test/resources/certificates/host.key new file mode 100644 index 00000000..c6a7ec55 --- /dev/null +++ b/src/test/resources/certificates/host.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +w5W8n4FakTGMeSV3N4sjO2njwP2lIg== +-----END PRIVATE KEY----- diff --git a/src/test/resources/certificates/root-ca.crt b/src/test/resources/certificates/root-ca.crt new file mode 100644 index 00000000..7aa5e05a --- /dev/null +++ b/src/test/resources/certificates/root-ca.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +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 +ZbxJygnaWA== +-----END CERTIFICATE----- diff --git a/src/test/resources/certificates/root-ca.key b/src/test/resources/certificates/root-ca.key new file mode 100644 index 00000000..ea6d4cb6 --- /dev/null +++ b/src/test/resources/certificates/root-ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +kc6q2HJgyEXdg0HTeTvM+RMmkPmDew== +-----END PRIVATE KEY----- diff --git a/src/test/resources/certificates/truststore.jks b/src/test/resources/certificates/truststore.jks new file mode 100644 index 00000000..b7ee170e Binary files /dev/null and b/src/test/resources/certificates/truststore.jks differ diff --git a/src/test/resources/host.crt b/src/test/resources/host.crt deleted file mode 100644 index 63c22df9..00000000 --- a/src/test/resources/host.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFezCCA2MCFAwv/gPfChqhnJU0fI/a3Ft3A8DTMA0GCSqGSIb3DQEBCwUAMHgx -CzAJBgNVBAYTAlVTMRUwEwYDVQQIDAxUaGUgSW50ZXJuZXQxFTATBgNVBAcMDFRo -ZSBJbnRlcm5ldDEUMBIGA1UECgwLTG9nc3Rhc2ggQ0ExETAPBgNVBAsMCExvZ3N0 -YXNoMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMjMwMzMwMTEzOTIzWhgPMjI5NzAx -MTExMTM5MjNaMHoxCzAJBgNVBAYTAlVTMRUwEwYDVQQIDAxUaGUgSW50ZXJuZXQx -FTATBgNVBAcMDFRoZSBJbnRlcm5ldDEWMBQGA1UECgwNTG9nc3Rhc2ggSG9zdDER -MA8GA1UECwwITG9nc3Rhc2gxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAOwLooFUlwzwae9GACzrlO/E+b0w1yAsRRYP -0yD3EfCogTjGibHEV/MM3y34ZSUUExZWERt1SXs3SPqfUTOkDhXjAzu8HbNjBJk3 -3R6Yzju+kJGxSxtheQLK+9tjP2O6/j6KXFhMwB7RqfN/aEB+BtmDBAdiTRuMMrWs -qViAcMt76of8lvnUXhvb7K1Um17eyc9wv7JLQN5TSG5EtB4x6zqvbfmTcCXLa7D8 -9PN3ayaBSLxAllv82RWts11i+PnVHYyU24qmdwmRIv6eKN9BRmgPqKw34yeCk4xT -T8pDXoiS9vGXxmrU1i1pZNUeVEAN+h4o8sWcseItUKsPSFFYga653XFtqzxyk4Cq -3gEPp05yH7s68T6T2H+CAyV0YuOgAL6gtt2Y97+oTwjJ1pxLjUaVz5nOjFZqxBQC -hjBItBtpO+wtngZbErfziWx2PTTSJ99lI1KSQbC8cBEUYXG3kdiiUHUm8meq7Mja -8OjUiSi2x2Qv1WBvHrbOpw5Vw1/v8QcW/OY1TwdByW4iJnBiqw+p1hA6bGANc+sP -Lcr5jz7VPHuAxRNecQmqvhjAHGIAkqCJecT9314lgehM3YpcsDVLXnGT96FF5Aq0 -LSEDKFTyC1OXDf2pfQOu9yVTz/SXSr91kj+3+ZeY9Ot9D8L1Xu6dwMsmf/CtV3+4 -lPRbQEEDAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADygVFQGSy4R2htt6WX4DCb7 -FiB/KSxmhh2K+mRC77sqGDP3xRfHsWh2s1eejDde0Vp1OBjDXjsWidFuGzxhV3WC -svt5K8y47+wFboFKuIJUlK17mM3NlsArfwI2/58zIdMokJttSwX37M/jZamTG5kK -xqr3gpGjUxr+qPEizlsFlx1T4d2XIRu8/RZMoqHxI1IZSxlU6GYsvUa2T3vzz9/C -WCxj+CJ1kUnYf3SxeGX+IPVid55LOyWicRO5hKVfKfL1c9AwGL6+0lFYFdz+q5Ku -AMZ0KHk11up4c5oB8xrSet/WHRqIgEy78JxfolkIT5wAlx3sTcmo9mZOjv2tB2AD -1H95Sod/dAD84EWM5Epbt2wlldAGpMbxWH7Yp5gpH5PP8lob9ImNcf9v+wkj9alK -n+C/yhA+B8+hxPClHHgM6M4bdaXREjZ5iYa68riZtBQ3SuIWHJgOl3ZPInRs01kA -IvVFteKu5S4pnEXLAP5EpcM6Mpoxvf+H5z5sdxq3R7uVrbpmzRm7FtW188x0e/dB -ydA2Oz3kSWzwDHRc/WWglbHingf1472aNM02lPuUeN4ALILtIu38e/9IvhhyOkm9 -o7w0jVXCsB/hCcnfchgwG33NvWB2BZ/Zc5f2nTufxUYNODE2MZDaZYZeKd8LzerW -buJc6LvltjYly0HXsNf0 ------END CERTIFICATE----- diff --git a/src/test/resources/host.enc.key b/src/test/resources/host.enc.key deleted file mode 100644 index a7d420ef..00000000 --- a/src/test/resources/host.enc.key +++ /dev/null @@ -1,53 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -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 -DR5g0lBs888AHpjmPQ== ------END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/host.key b/src/test/resources/host.key deleted file mode 100644 index 7c171495..00000000 --- a/src/test/resources/host.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -C8kebbjUgzIkPvLG87A6BCgFBj2p9UU= ------END PRIVATE KEY----- diff --git a/src/test/resources/root-ca.crt b/src/test/resources/root-ca.crt deleted file mode 100644 index f6c5a76c..00000000 --- a/src/test/resources/root-ca.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -jMMZNVWQzQ== ------END CERTIFICATE-----