diff --git a/CHANGELOG.md b/CHANGELOG.md index 186639517f1..72f2952807f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 8.1.0 [unreleased] +### Features + +- [#948](https://github.com/influxdata/influxdb-client-java/pull/948): Support TLS and mTLS configurations. + ### Dependencies Update dependencies: diff --git a/client-core/src/test/java/com/influxdb/internal/RestClientTest.java b/client-core/src/test/java/com/influxdb/internal/RestClientTest.java index 0da95f5be9d..579d43cc6cd 100644 --- a/client-core/src/test/java/com/influxdb/internal/RestClientTest.java +++ b/client-core/src/test/java/com/influxdb/internal/RestClientTest.java @@ -26,6 +26,25 @@ import java.util.concurrent.CountDownLatch; import javax.annotation.Nonnull; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.mockwebserver.MockResponse; +import okio.Buffer; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.http.GET; +import retrofit2.http.Headers; +import retrofit2.http.Path; + import com.influxdb.LogLevel; import com.influxdb.exceptions.BadGatewayException; import com.influxdb.exceptions.BadRequestException; @@ -46,25 +65,6 @@ import com.influxdb.exceptions.UnprocessableEntityException; import com.influxdb.test.AbstractMockServerTest; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.mockwebserver.MockResponse; -import okio.Buffer; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import retrofit2.Call; -import retrofit2.Response; -import retrofit2.Retrofit; -import retrofit2.http.GET; -import retrofit2.http.Headers; -import retrofit2.http.Path; - /** * @author Jakub Bednar (bednar@github) (04/10/2018 07:57) */ diff --git a/client-utils/src/main/java/com/influxdb/utils/TlsUtils.java b/client-utils/src/main/java/com/influxdb/utils/TlsUtils.java new file mode 100644 index 00000000000..8b574d8cd18 --- /dev/null +++ b/client-utils/src/main/java/com/influxdb/utils/TlsUtils.java @@ -0,0 +1,251 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.utils; + +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.Locale; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +public final class TlsUtils { + private static final String TLS = "TLS"; + private static final char[] DEFAULT_PASSWORD_CHAR_ARRAY = "".toCharArray(); + private static final String X509 = "X.509"; + private static final String PKCS12 = "PKCS12"; + + private TlsUtils() { + } + + /** + * Builds an {@link SSLContext} using the provided {@link KeyManagerFactory} and/or + * {@link TrustManagerFactory}. If both factories are null, this method returns null. + * + * @param kmf the {@link KeyManagerFactory} to use for key management, or null if no key management is required. + * @param tmf the {@link TrustManagerFactory} to use for trust management, or null if no trust management is + * required. + * @return an initialized {@link SSLContext}, or null if both input parameters are null. + * @throws Exception if an error occurs during the SSLContext initialization. + */ + @Nullable + public static SSLContext buildSslContext(@Nullable final KeyManagerFactory kmf, + @Nullable final TrustManagerFactory tmf) throws Exception { + if (kmf == null && tmf == null) { + return null; + } + + SSLContext sslContext = SSLContext.getInstance(TLS); + sslContext.init(kmf != null ? kmf.getKeyManagers() : null, tmf != null ? tmf.getTrustManagers() : null, null); + return sslContext; + } + + /** + * Retrieves an instance of {@link X509TrustManager} from the provided {@link TrustManagerFactory}. + * If the input TrustManagerFactory is null, a default TrustManagerFactory is created and initialized. + * + * @param tmf the {@link TrustManagerFactory} to retrieve the {@link X509TrustManager} from, + * or null to use a default {@link TrustManagerFactory}. + * @return an instance of {@link X509TrustManager} initialized from the given or + * default {@link TrustManagerFactory}. + * @throws Exception if an error occurs during the initialization or retrieval of the {@link X509TrustManager}. + */ + @Nonnull + public static X509TrustManager getX509TrustManager(@Nullable final TrustManagerFactory tmf) throws Exception { + TrustManagerFactory factory = tmf; + if (factory == null) { + factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init((KeyStore) null); + } + return (X509TrustManager) factory.getTrustManagers()[0]; + } + + /** + * Loads a private key from a file specified by the provided path. The key must be in PKCS#8 format and unencrypted. + * Encrypted private keys are not supported. + * + * @param path the file system path to the private key file; must not be null. + * @return the {@link PrivateKey} object loaded from the provided file path. + * @throws IllegalArgumentException if the private key is encrypted or in an unsupported format. + * @throws Exception if an error occurs during file reading, Base64 decoding, or key generation. + */ + public static PrivateKey loadPrivateKey(@Nonnull final String path) throws Exception { + String keyPem = Files.readString(Paths.get(path)); + if (keyPem.contains("-----BEGIN ENCRYPTED PRIVATE KEY-----")) { + throw new IllegalArgumentException("Encrypted PKCS#8 private keys are not supported. Use an unencrypted " + + "PKCS#8 key or a PKCS#12 file."); + } + + String privateKeyPEM = keyPem + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + + byte[] encoded = Base64.getDecoder().decode(privateKeyPEM); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + GeneralSecurityException lastException = null; + for (String algorithm : new String[]{"RSA", "EC", "DSA"}) { + try { + return KeyFactory.getInstance(algorithm).generatePrivate(keySpec); + } catch (GeneralSecurityException e) { + lastException = e; + } + } + + throw new GeneralSecurityException("Unsupported private key algorithm", lastException); + } + + /** + * Creates and initializes a {@link KeyManagerFactory} using a PKCS#12 keystore file + * located at the specified path. This method loads the keystore using the provided + * password (or a default password if none is provided) and initializes a + * {@link KeyManagerFactory} with it. + * + * @param path the file path to the PKCS#12 keystore; must not be null. + * @param password the password for the keystore, or null/empty if the default password + * should be used. + * @return a {@link KeyManagerFactory} instance initialized with the provided keystore. + * @throws Exception if an error occurs while reading the file, loading the keystore, + * or initializing the {@link KeyManagerFactory}. + */ + public static KeyManagerFactory createKmfP12(@Nonnull final String path, + @Nullable final char[] password) throws Exception { + char[] pass = password != null && password.length > 0 ? password : DEFAULT_PASSWORD_CHAR_ARRAY; + KeyStore keyStore = KeyStore.getInstance(PKCS12); + try (FileInputStream fis = new FileInputStream(path)) { + keyStore.load(fis, pass); + } + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, pass); + return kmf; + } + + /** + * Creates and initializes a {@link KeyManagerFactory} using the specified certificate and private key files. + * The method loads the certificate chain from the provided `certPath` and the private key from the provided + * `keyPath`. + * These are stored in a {@link KeyStore}, which is then used to initialize the {@link KeyManagerFactory}. + * + * @param certPath the file path to the certificate chain in X.509 format; must not be null. + * @param keyPath the file path to the private key in PKCS#8 format; must not be null. + * @return a {@link KeyManagerFactory} instance initialized with the provided certificate and private key. + * @throws Exception if an error occurs while reading the files, loading the credentials, or initializing + * the {@link KeyManagerFactory}. + */ + public static KeyManagerFactory createKmf(@Nonnull final String certPath, + @Nonnull final String keyPath) throws Exception { + java.util.Collection certificateChain; + try (FileInputStream fis = new FileInputStream(certPath)) { + certificateChain = CertificateFactory.getInstance(X509).generateCertificates(fis); + } + + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + keyStore.setKeyEntry("alias", + TlsUtils.loadPrivateKey(keyPath), + null, + certificateChain.toArray(new Certificate[0])); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, null); + + return kmf; + } + + /** + * Creates and initializes a TrustManagerFactory from a PKCS#12 keystore located at the specified path. + * This method loads the keystore using the provided password and initializes a TrustManagerFactory with it. + * + * @param path the file path to the PKCS#12 keystore; must not be null. + * @param password the password for the keystore, or null/empty if the default password should be used. + * @return a TrustManagerFactory instance initialized with the provided keystore. + * @throws Exception if an error occurs while reading the file, loading the keystore, + * or initializing the TrustManagerFactory. + */ + public static TrustManagerFactory createTmfP12(@Nonnull final String path, + @Nullable final char[] password) throws Exception { + char[] pass = password != null && password.length > 0 ? password : DEFAULT_PASSWORD_CHAR_ARRAY; + + KeyStore trustStore = KeyStore.getInstance("PKCS12"); + try (FileInputStream fis = new FileInputStream(path)) { + trustStore.load(fis, pass); + } + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + + return tmf; + } + + /** + * Creates and initializes a TrustManagerFactory from the provided certificate file. + * The file format is determined based on its extension. Supported formats include: + * - .p12 or .pfx: Loaded as a PKCS#12 keystore. + * - .crt, .cert, or .pem: Loaded as individual X.509 certificates. + * + * @param path the file path to the certificate or keystore; must not be null. + * @param password the password for the keystore, or null if not required. + * @return a TrustManagerFactory instance initialized with the provided certificates or keystore. + * @throws Exception if an error occurs while reading the file, processing the certificates, + * or initializing the TrustManagerFactory. + * @throws IllegalArgumentException if the provided file format is unsupported. + */ + public static TrustManagerFactory createTmf(@Nonnull final String path, + @Nullable final char[] password) throws Exception { + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + + String extension = path.toLowerCase(Locale.ROOT); + + if (extension.endsWith(".p12") || extension.endsWith(".pfx")) { + char[] pass = password != null && password.length > 0 ? password : DEFAULT_PASSWORD_CHAR_ARRAY; + tmf = createTmfP12(path, pass); + } else if (extension.endsWith(".crt") || extension.endsWith(".cert") || extension.endsWith(".pem")) { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); + + CertificateFactory certFactory = CertificateFactory.getInstance(X509); + try (FileInputStream fis = new FileInputStream(path)) { + int alias = 0; + for (Certificate certificate : certFactory.generateCertificates(fis)) { + trustStore.setCertificateEntry("alias-" + alias++, certificate); + } + } + tmf.init(trustStore); + } else { + throw new IllegalArgumentException("Unsupported certificate format"); + } + + return tmf; + } +} diff --git a/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java b/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java index 6ae4e0746a2..6c758a8e802 100644 --- a/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java +++ b/client/src/main/java/com/influxdb/client/InfluxDBClientOptions.java @@ -32,6 +32,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import com.influxdb.LogLevel; import com.influxdb.client.domain.WriteConsistency; @@ -40,6 +44,7 @@ import com.influxdb.client.write.WriteParameters; import com.influxdb.exceptions.InfluxException; import com.influxdb.utils.Arguments; +import com.influxdb.utils.TlsUtils; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -65,6 +70,15 @@ public final class InfluxDBClientOptions { private final String username; private final char[] password; + private final String certificatePath; + private final String certificateKeyPath; + private final String certificateP12FilePath; + private final char[] keyPassword; + + private final String trustFilePath; + private final char[] trustFilePassword; + + private final String org; private final String bucket; private final WritePrecision precision; @@ -89,6 +103,13 @@ private InfluxDBClientOptions(@Nonnull final InfluxDBClientOptions.Builder build this.precision = builder.precision != null ? builder.precision : WriteParameters.DEFAULT_WRITE_PRECISION; this.consistency = builder.consistency; this.pointSettings = builder.pointSettings; + + this.certificatePath = builder.certificatePath; + this.certificateKeyPath = builder.certificateKeyPath; + this.certificateP12FilePath = builder.certificateP12FilePath; + this.keyPassword = builder.keyPassword; + this.trustFilePath = builder.trustFilePath; + this.trustFilePassword = builder.trustFilePassword; } /** @@ -238,6 +259,66 @@ public PointSettings getPointSettings() { return pointSettings; } + /** + * Retrieves the file path of the certificate used for secure communication. + * + * @return the file path of the certificate, or null if no certificate path is specified + */ + @Nullable + public String getCertificatePath() { + return certificatePath; + } + + /** + * Retrieves the file path of the certificate key used for secure communication. + * + * @return the file path of the certificate key, or null if no certificate key path is specified + */ + @Nullable + public String getCertificateKeyPath() { + return certificateKeyPath; + } + + /** + * Retrieves the file path of the PKCS#12 (P12) certificate used for secure communication. + * + * @return the file path of the PKCS#12 certificate, or null if no certificate path is specified + */ + @Nullable + public String getCertificateP12FilePath() { + return certificateP12FilePath; + } + + /** + * Retrieves the password for the key used in secure communication. + * + * @return the key password as a character array, or null if no key password is specified + */ + @Nullable + public char[] getKeyPassword() { + return keyPassword; + } + + /** + * Retrieves the file path of the trust store used for secure communication. + * + * @return the file path of the trust store, or null if no trust store path is specified + */ + @Nullable + public String getTrustFilePath() { + return trustFilePath; + } + + /** + * Retrieves the password for the trust store used in secure communication. + * + * @return the trust store password as a character array, or null if no trust store password is specified + */ + @Nullable + public char[] getTrustFilePassword() { + return trustFilePassword; + } + /** * Creates a builder instance. * @@ -264,6 +345,14 @@ public static class Builder { private String username; private char[] password; + private String certificatePath; + private String certificateKeyPath; + private String certificateP12FilePath; + private char[] keyPassword; + + private String trustFilePath; + private char[] trustFilePassword; + private String org; private String bucket; private WritePrecision precision; @@ -448,6 +537,54 @@ public InfluxDBClientOptions.Builder consistency(@Nullable final WriteConsistenc return this; } + /** + * Sets the file paths for the certificate and its corresponding private key for secure connections. + * + * @param certificatePath the file path to the certificate file in PEM format, must not be null. + * @param certificateKeyPath the file path to the certificate's private key in PEM format, must not be null. + * @return the updated {@link InfluxDBClientOptions.Builder} instance. + */ + @Nonnull + public InfluxDBClientOptions.Builder certificateFilePath(@Nonnull final String certificatePath, + @Nonnull final String certificateKeyPath) { + this.certificatePath = certificatePath; + this.certificateKeyPath = certificateKeyPath; + + return this; + } + + /** + * Sets the file path to the certificate in P12 format and the optional password for the certificate key. + * + * @param p12FilePath the file path to the P12 certificate. Must not be null. + * @param password the optional password for the certificate key. Can be null if no password is required. + * @return the Builder instance for method chaining. + */ + @Nonnull + public InfluxDBClientOptions.Builder certificateP12FilePath(@Nonnull final String p12FilePath, + @Nullable final char[] password) { + this.certificateP12FilePath = p12FilePath; + this.keyPassword = password; + + return this; + } + + /** + * Sets the file path to the trusted certificate for SSL/TLS communication. + * + * @param trustFilePath the file path to the trusted certificate; must not be null. + * @param password the password for the trusted certificate file; can be null if not required. + * @return the updated {@link InfluxDBClientOptions.Builder} instance. + */ + @Nonnull + public InfluxDBClientOptions.Builder trustFilePath(@Nonnull final String trustFilePath, + @Nullable final char[] password) { + this.trustFilePath = trustFilePath; + this.trustFilePassword = password; + + return this; + } + /** * Add default tag that will be use for writes by Point and POJO. *

@@ -584,6 +721,11 @@ public InfluxDBClientOptions build() { .protocols(Collections.singletonList(Protocol.HTTP_1_1)); } + HttpUrl parsedUrl = HttpUrl.parse(url); + if (parsedUrl != null && parsedUrl.isHttps()) { + configureTls(okHttpClient); + } + if (logLevel == null) { logLevel = LogLevel.NONE; } @@ -591,6 +733,50 @@ public InfluxDBClientOptions build() { return new InfluxDBClientOptions(this); } + /** + * Configures TLS for an OkHttpClient by setting up SSL context and trust managers based on + * provided certificate paths or trust file paths. + * + * @param okHttpClient an OkHttpClient.Builder instance on which TLS configuration will be applied. + * This is required to establish secure connections with the server. + * @throws IllegalArgumentException if both {@code certificatePath} and {@code certificateP12FilePath} + * are set, as only one can be specified at a time. + * @throws InfluxException if there is an error during the TLS configuration process, such as issues + * with loading the certificate, trust file, or setting up the SSL context. + */ + private void configureTls(@Nonnull final OkHttpClient.Builder okHttpClient) { + + if (certificatePath != null && certificateP12FilePath != null) { + throw new IllegalArgumentException("Cannot set both p12FilePath and certificatePath"); + } + + if (certificatePath == null && certificateP12FilePath == null && trustFilePath == null) { + return; + } + + try { + TrustManagerFactory tmf = null; + if (trustFilePath != null) { + tmf = TlsUtils.createTmf(trustFilePath, trustFilePassword); + } + + KeyManagerFactory kmf = null; + if (certificatePath != null) { + kmf = TlsUtils.createKmf(certificatePath, certificateKeyPath); + } else if (certificateP12FilePath != null) { + kmf = TlsUtils.createKmfP12(certificateP12FilePath, keyPassword); + } + + SSLContext sslContext = TlsUtils.buildSslContext(kmf, tmf); + if (sslContext != null) { + X509TrustManager trustManager = TlsUtils.getX509TrustManager(tmf); + okHttpClient.sslSocketFactory(sslContext.getSocketFactory(), trustManager); + } + } catch (Exception e) { + throw new InfluxException(e); + } + } + @Nonnull private InfluxDBClientOptions.Builder configure(@Nonnull final String url, @Nullable final String org, diff --git a/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java b/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java index c95ae134bd6..6a6cf6295ab 100644 --- a/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java +++ b/client/src/test/java/com/influxdb/client/InfluxDBClientOptionsTest.java @@ -27,8 +27,9 @@ import java.util.Map; import com.influxdb.client.domain.WritePrecision; - import com.influxdb.exceptions.InfluxException; +import com.influxdb.utils.TlsUtils; + import okhttp3.OkHttpClient; import okhttp3.Protocol; import org.assertj.core.api.Assertions; @@ -153,6 +154,35 @@ public void customClientTypeFromConnectionString() { Assertions.assertThat(options.getClientType()).isEqualTo("url-service"); } + @Test + public void tlsFilesConfig() { + String tlsDir = "src/test/java/com/influxdb/client/tls/"; + String influxdbCertPath = tlsDir + "influxdb.crt"; + String clientCertPath = tlsDir + "client.crt"; + String clientKeyPath = tlsDir + "client.key"; + String clientP12 = tlsDir + "client.p12"; + + InfluxDBClientOptions options = InfluxDBClientOptions.builder() + .url("http://localhost:8086") + .trustFilePath(influxdbCertPath, null) + .certificateFilePath(clientCertPath, clientKeyPath) + .build(); + + Assertions.assertThat(options.getTrustFilePath()).isEqualTo(influxdbCertPath); + Assertions.assertThat(options.getCertificatePath()).isEqualTo(clientCertPath); + Assertions.assertThat(options.getCertificateKeyPath()).isEqualTo(clientKeyPath); + + // For .p12 files + var password = "changeit".toCharArray(); + InfluxDBClientOptions options1 = InfluxDBClientOptions.builder() + .url("http://localhost:8086") + .certificateP12FilePath(clientP12, password) + .build(); + + Assertions.assertThat(options1.getCertificateP12FilePath()).isEqualTo(clientP12); + Assertions.assertThat(options1.getKeyPassword()).isEqualTo(password); + } + @Test public void customClientTypeFromProperties() { InfluxDBClientOptions options = InfluxDBClientOptions.builder().loadProperties().build(); @@ -223,7 +253,58 @@ public void ipv6Invalid(){ .build();}).isInstanceOf(InfluxException.class) .hasMessage(String.format("Unable to parse connection string http://%s:9999/api/v2/query?orgID=my-org", ipv6)); } + } + @Test + void tlsBothCertificateAndP12Configured() { + Assertions.assertThatThrownBy(() -> InfluxDBClientOptions.builder() + .url("https://localhost:9999") + .certificateFilePath("cert.pem", "key.pem") + .certificateP12FilePath("client.p12", null) + .build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Cannot set both p12FilePath and certificatePath"); } + @Test + void tlsOnlyClientCertificatesConfigured() { + String clientCertPath = "src/test/java/com/influxdb/client/tls/client.crt"; + String clientKeyPath = "src/test/java/com/influxdb/client/tls/client.key"; + + InfluxDBClientOptions options = InfluxDBClientOptions.builder() + .url("https://localhost:9999") + .certificateFilePath(clientCertPath, clientKeyPath) + .build(); + + Assertions.assertThat(options.getOkHttpClient()).isNotNull(); + } + + @Test + void tlsOnlyClientP12Configured() { + String clientP12 = "src/test/java/com/influxdb/client/tls/client.p12"; + + InfluxDBClientOptions options = InfluxDBClientOptions.builder() + .url("https://localhost:9999") + .certificateP12FilePath(clientP12, "changeit".toCharArray()) + .build(); + + Assertions.assertThat(options.getOkHttpClient()).isNotNull(); + } + + @Test + void tlsInvalidCertificatePath() { + Assertions.assertThatThrownBy(() -> InfluxDBClientOptions.builder() + .url("https://localhost:9999") + .certificateFilePath("non_existing_file.pem", "non_existing_file.key") + .build()) + .isInstanceOf(InfluxException.class); + } + + @Test + void encryptedPemKeyIsRejected() { + Assertions.assertThatThrownBy(() -> + TlsUtils.loadPrivateKey("src/test/java/com/influxdb/client/tls/client_pkcs8.key")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Encrypted PKCS#8 private keys are not supported"); + } } \ No newline at end of file diff --git a/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java b/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java index d706e4d9e3b..8e2d3fc75fd 100644 --- a/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java +++ b/client/src/test/java/com/influxdb/client/InfluxDBClientTest.java @@ -30,6 +30,10 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import javax.annotation.Nonnull; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import okhttp3.HttpUrl; import okhttp3.Interceptor; @@ -53,6 +57,7 @@ import com.influxdb.client.domain.WritePrecision; import com.influxdb.client.internal.AbstractInfluxDBClientTest; import com.influxdb.client.service.InfluxQLQueryService; +import com.influxdb.utils.TlsUtils; /** * @author Jakub Bednar (bednar@github) (05/09/2018 14:00) @@ -421,6 +426,130 @@ public Response intercept(@Nonnull final Chain chain) throws IOException { proxy.shutdown(); } + String tlsDir = "src/test/java/com/influxdb/client/tls/"; + + String influxdbCertPath = tlsDir + "influxdb.crt"; + String influxdbKeyPath = tlsDir + "influxdb.key"; + String influxdbP12 = tlsDir + "influxdb.p12"; + + String otherCertPath = tlsDir + "other-server.crt"; + String otherKeyCertPath = tlsDir + "other-server.key"; + String otherP12 = tlsDir + "other-server.p12"; + + String clientCertPath = tlsDir + "client.crt"; + String clientKeyPath = tlsDir + "client.key"; + String clientP12 = tlsDir + "client.p12"; + + char[] defaultPassword = "changeit".toCharArray(); + + record TlsTest(boolean isMutualTls, boolean isP12) { + } + + private static List tlsCases() { + return List.of( + new TlsTest(false, false), + new TlsTest(false, true), + new TlsTest(true, false), + new TlsTest(true, true) + ); + } + + @Test + public void testMutualTlsSuccess() throws Exception { + for (TlsTest tlsCase : tlsCases()) { + boolean isMutualTls = tlsCase.isMutualTls; + boolean isP12 = tlsCase.isP12; + + MockWebServer mockServer = getMutualTlsMockServer(isMutualTls, isP12); + try { + InfluxDBClientOptions.Builder options = InfluxDBClientOptions.builder() + .url(mockServer.url("/").url().toString()) + .authenticateToken("my-token".toCharArray()) + .trustFilePath(isP12 ? influxdbP12 : influxdbCertPath, defaultPassword); + + if (isMutualTls) { + if (isP12) { + options.certificateP12FilePath(clientP12, defaultPassword); + } else { + options.certificateFilePath(clientCertPath, clientKeyPath); + } + } + + try (InfluxDBClient client = InfluxDBClientFactory.create(options.build())) { + client.version(); + } + } finally { + mockServer.shutdown(); + } + } + } + + @Test + public void testMutualTlsFailClientUntrustedServer() throws Exception { + // Client trusts other cert, but server uses influxdb cert -> handshake should fail + for (boolean isP12 : List.of(false, true)) { + MockWebServer mockServer = getMutualTlsMockServer(false, isP12); + InfluxDBClientOptions.Builder options = InfluxDBClientOptions.builder() + .url(mockServer.url("/").url().toString()) + .authenticateToken("my-token".toCharArray()); + if (isP12) { + options.trustFilePath(otherP12, defaultPassword); + } else { + options.trustFilePath(otherCertPath, defaultPassword); + } + + try (InfluxDBClient client = InfluxDBClientFactory.create(options.build())) { + Assertions.assertThatThrownBy(client::version) + .isInstanceOf(com.influxdb.exceptions.InfluxException.class); + } finally { + mockServer.shutdown(); + } + } + } + + @Test + public void testMutualTlsFailServerUntrustedClient() throws Exception { + // Server requires client auth and trusts client cert, but client provides wrong (other) cert -> handshake should fail + for (boolean isP12 : List.of(false, true)) { + MockWebServer mockServer = getMutualTlsMockServer(true, isP12); + InfluxDBClientOptions.Builder options = InfluxDBClientOptions.builder() + .url(mockServer.url("/").url().toString()) + .authenticateToken("my-token".toCharArray()); + + if (isP12) { + options.certificateP12FilePath(otherP12, defaultPassword) + .trustFilePath(influxdbP12, defaultPassword); + } else { + options.certificateFilePath(otherCertPath, otherKeyCertPath) + .trustFilePath(influxdbCertPath, defaultPassword); + } + + try (InfluxDBClient client = InfluxDBClientFactory.create(options.build())) { + Assertions.assertThatThrownBy(client::version) + .isInstanceOf(com.influxdb.exceptions.InfluxException.class); + } finally { + mockServer.shutdown(); + } + } + } + + @Test + public void testMutualTlsSuccessDifferentFileTypes() throws Exception { + MockWebServer mockServer = getMutualTlsMockServer(true, true); + InfluxDBClientOptions options = InfluxDBClientOptions.builder() + .url(mockServer.url("/").url().toString()) + .certificateFilePath(clientCertPath, clientKeyPath) + .trustFilePath(influxdbP12, defaultPassword) + .authenticateToken("my-token".toCharArray()) + .build(); + + try (InfluxDBClient client = InfluxDBClientFactory.create(options)) { + client.version(); + } finally { + mockServer.shutdown(); + } + } + @Test public void connectionStringPrecision() { InfluxDBClientOptions options = InfluxDBClientOptions.builder() @@ -547,4 +676,31 @@ private void queryAndTest(final String expected) throws InterruptedException { Assertions.assertThat(request.getRequestUrl()).isNotNull(); Assertions.assertThat(request.getRequestUrl().toString()).isEqualTo(expected + "?org=my-org"); } + + private MockWebServer getMutualTlsMockServer(final boolean isMutualTls, final boolean isP12) throws Exception { + KeyManagerFactory kmf; + TrustManagerFactory tmf; + + + if (isP12) { + kmf = TlsUtils.createKmfP12(influxdbP12, defaultPassword); + tmf = isMutualTls ? TlsUtils.createTmfP12(clientP12, defaultPassword) : null; + } else { + kmf = TlsUtils.createKmf(influxdbCertPath, influxdbKeyPath); + tmf = isMutualTls ? TlsUtils.createTmf(clientCertPath, defaultPassword) : null; + } + + SSLContext sslContext = TlsUtils.buildSslContext(kmf, tmf); + + var mockServer = new MockWebServer(); + mockServer.useHttps(sslContext.getSocketFactory(), false); + + if (isMutualTls) { + mockServer.requireClientAuth(); + } + mockServer.start(); + mockServer.enqueue(createResponse("ok")); + + return mockServer; + } } \ No newline at end of file diff --git a/client/src/test/java/com/influxdb/client/tls/client.crt b/client/src/test/java/com/influxdb/client/tls/client.crt new file mode 100644 index 00000000000..b9c82617ca0 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIUOAIDGap+seWtPsjBPcEsPOAuPs8wDQYJKoZIhvcNAQEL +BQAwOTEUMBIGA1UEAwwLdGVzdC1jbGllbnQxFDASBgNVBAoMC0RldmVsb3BtZW50 +MQswCQYDVQQGEwJVUzAeFw0yNjA5MjkxMDI5NDNaFw0zNjA5MjYxMDI5NDNaMDkx +FDASBgNVBAMMC3Rlc3QtY2xpZW50MRQwEgYDVQQKDAtEZXZlbG9wbWVudDELMAkG +A1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+27lZ3TDT +EK8rvgmFA4VM+632NaDzzRPs+hNO62vLbjgirimRY1NImnR/Nd5Bjxz04nwKvnyg +gegOxdxeGY+1U+ErMexP7LZ5rpJyI6XF9AjLPN1G96seYq5/B5QghR7Lm4O6Veag +P3EJJ8m1Ul4PGjvPD+fPMXV0PEo38kSS0//DCFVy5vkvhoIba44HDSdaw0Pm3Fuc +5AncV73IX9H3qdvWdyXwQH+0OLqcB09If0hifHEnChuD1HAJYMRIk0ylGs7W3ftS +vN1emJj3SJLAKKwhxN5SuHGJBL5st1ZT6/cM/ksUlUWPxMhlGoFBfqte6Xb+D2/z +7XvAn2X60TcPAgMBAAGjUzBRMB0GA1UdDgQWBBTcwxlCIzoI5kmr53H14yhkmWqA +wjAfBgNVHSMEGDAWgBTcwxlCIzoI5kmr53H14yhkmWqAwjAPBgNVHRMBAf8EBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBN7VCH9faju4KlGdaVT5OhNqvCpA/mWoUX +clwbNXrZuVd5VoUCn/r2TjTWH3S4E1e0Rq2pgYFfZfl65tmcZAgL5v6H2c9aTdrP +VeD7FZE3+oU7Pv170u0u8+KmVFBradR651cmosXfs7mVqvVAUejXjrECn5JbO74s +ghWu+mZm5EiVCiImkKx9LpEOs6t+LZWYJmS8ZyyvKEck9m8nYn5NxiR1V9Lh6eR7 +FrDG8mpUK9ZMedmVcO0XKldSgyTOiCjX9cw+MvnZTkGcbGosGddBTV6s+JHxNwL4 +6Ex29IEPb/+Nx7pD19iJcjxInLRpKmjKndsYsf1U/kxA2W0Xbp5E +-----END CERTIFICATE----- diff --git a/client/src/test/java/com/influxdb/client/tls/client.key b/client/src/test/java/com/influxdb/client/tls/client.key new file mode 100644 index 00000000000..091953b0684 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+27lZ3TDTEK8r +vgmFA4VM+632NaDzzRPs+hNO62vLbjgirimRY1NImnR/Nd5Bjxz04nwKvnyggegO +xdxeGY+1U+ErMexP7LZ5rpJyI6XF9AjLPN1G96seYq5/B5QghR7Lm4O6VeagP3EJ +J8m1Ul4PGjvPD+fPMXV0PEo38kSS0//DCFVy5vkvhoIba44HDSdaw0Pm3Fuc5Anc +V73IX9H3qdvWdyXwQH+0OLqcB09If0hifHEnChuD1HAJYMRIk0ylGs7W3ftSvN1e +mJj3SJLAKKwhxN5SuHGJBL5st1ZT6/cM/ksUlUWPxMhlGoFBfqte6Xb+D2/z7XvA +n2X60TcPAgMBAAECggEAU7LfL4ohgcZE072Exjjbif2rdhhhq9lJPjkTI555iN6e +rpLLNu+kk+fsY+c3P2/oBnqWZE3SML+XiXb646d5Ds+opP1BQXGxOl606Wo5pjyV +aK+Z6KfveqVTGfE0ZCiM70SVea93Mtpvk1DIFAT5q8zY4r76bTIm7KEN+Uf9C5g6 +THosL87Oo6nNVzYKSu5bwIyzbgUbbchX14DS7SAdRN0jUo0v8YOtYLNPiwOHoxuW +aTSeP6BKgw8YOiEsl0VXMI6DESvt/SP2zLx5N4B89sxJpzn4ZRrO0r07tY56QZex +n6gmmuxD440kreKLzE2erU+DYWpWU8O3R77nZFGysQKBgQD9zdsKqqe4XJcSYhHY +X7e7mXTM3Wm1I2HKPfaZ1V6PHiX5PGlK5dqGZMhT6quwZpTqV1Zx2Vhthj6YXAwY +Ka2bEnZQhw4umZOKzmiMSkFxDZQ/wfTzBOuGIdSsFk2387U1Q7hCwxZ2U5C+x+4S ++gfeISKeojpCxjk/qbIhdppK3wKBgQDAgnOGT1AWpfDNvGSxNq+vjuevFqlXbYjz +PE3jUF3IN/7iYJ2I5C43FoGl37BCxV2ZzxGIfvEu1fuI0+ZzmZdhjz4cpWkrIGf/ +y36UwLcxhfX+hmScqAQi8IrK+EGir7P4I8zlPH9gdhZGt7zP15EkCNo1OOoJAnMp +gBkYQQ/J0QKBgGd8Yk3NG6n+htLGDgStsprZjVhjB1EGZj8yWLSM3Yt5fX5MdZpG +cn3N5ijhTLQWf621gtfVCUtPK4KwNXY8uD68nmVjTBMQJ6q3UsWxGVHheYstqJTW +cro0XST1yyawRji7rgv6w9PnauN/XcF7FW4rEJSiDSNg88LTjIA8fdj9AoGAFjBz +JG4L6zBhWzV13b9R7MhqCBJynnMvr+mpiKQAy005AdrlDZxPf2YGt5na9TdOnKXz +fWo0XpOnlJPoIjb37fTW0fJ29tObaS6JfpfgBcNNIXNYMX5kS6qyNMb8ucXK1rU5 +rsqUXAgAdVJEXyiXwDGNBUl0IGm54HS1b8hAC6ECgYEAz1+mbr7Q37mjE+Z4+7rZ +zYe7plbcWn0wwPuhHC+18plqmQlsY6mbCsNXAwMyeW9/yDIsIfx7Xr9y/dTF3pCA +KfRhXvhS5hbDJatqkcSkGePbtOZuSPE8sMcFEQUAOgWF9aEyQ8VstfYWcXvOn8J6 +p/NjOzj9eM5NX2tFsIvHlAQ= +-----END PRIVATE KEY----- diff --git a/client/src/test/java/com/influxdb/client/tls/client.p12 b/client/src/test/java/com/influxdb/client/tls/client.p12 new file mode 100644 index 00000000000..6322a62b798 Binary files /dev/null and b/client/src/test/java/com/influxdb/client/tls/client.p12 differ diff --git a/client/src/test/java/com/influxdb/client/tls/client_pkcs8.key b/client/src/test/java/com/influxdb/client/tls/client_pkcs8.key new file mode 100644 index 00000000000..23768a6d6d0 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/client_pkcs8.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFNjBgBgkqhkiG9w0BBQ0wUzAyBgkqhkiG9w0BBQwwJQQQOtcvRcyoide95s6N +BTv97QIDD0JAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDYyE/OTmY47dVw +slVc8u9bBIIE0IJu+R+i5nWle3CzxKgm9o3p69pS6vWJ+QsbZHbSf9zYP7rqQ6R5 +cese63LVgxy+3FmszDspZmo/LgOHeCJ75KQre6WkfxG6fGeZ0HHyxU1mZ7GS0M51 +Vk4/hDtmzroBFTX4E2s2ozfbR6urg5KuVf4WQ9PdiCt2OcJQkCADI4nrHgD5NfBG +8FrXpxK7c/XyrMC/v3yOPGib0OjNriHgdzXDaA7M5Jdf4RsQeaTb82xF12pucPO7 +sxdbk0gpL7aQvYHO79phWE0dbqffo1yB1LZkyVTieJzusxhFs7szy9TUGpxjUiXc +25DUCdcsRUi7UMRzmmnDZaJHSTgGxYI4ytIW0Av/yyZmo1oP/N/RF+fNZC3yTYlO +gvmQsQl/gHLM8BJNlts52w/O3YCuYA53WEMd88OUY6TYSACOzKZOOOfXUMCsDtwV +hnIy3A/tDrnQsWfJWY3Yn1AHPEhgIfqEGeyU45Ur+xrJaBc4LnoZ7ndtvxAGTjpk +LG/9L5nNh+5qNFt3r6T8ReCWYTD6yozFIAxeXWeiLDagD6b2cBL6BVyZQy/RzFC3 +QVaaMtc+nL7KpVWnrNtn9uLxZwfXFxmDHCss/hVLtwFp1tK/oZV+BJ2AAnkRUiFE +6CwjMqTHwMmERN5kWY5hmEG6YBGjnA32Qk4J/ewTF05/TLUX2KhVQ/UihEgeuCao +v8na8eZVYcbZdnIkgNxWRpmJJuAY5jSXFZV4H1hty66pLPd5XbtdhNW+DefzmuBC +tFpsCBqxYM4TcHUBvnOmdf8wv89GfqpwOJBrFRs6+KBrhzxSXlPKqfErL3Pdh5cx +HAtx4RwdwR17ThdEmpgp1b4NRRawozN2PgT80XQ1VNtXqdL5aVRie3/aVYbPOhOf +pTbxKdchYvLoMdn+QZnHmo2pOxCmcgVslENQbrxGqwAibG2Hjl08fklRPvAykNvc +3XFqvibBeisTW0Zm/fjPz9MIOZLkuRL+nwU9PKYhQobheKPU72kOmlERuiEhD4sj +NYU83RU0GQq1aVdvCp8/Xn6hMttomDcC3CwqEJuzxUP6y3QLGw7t4xW8gg5+zVFV +ZL/fa3n07iF1v203jjswxhaOqACPXX1eHsH0ahTj/Zoto0fjGVXsGsxuEU3929b5 +cCE2LN++gaEdnQCQFKysh+0ne5EKaLOH4MXMiOs3OK1MovibFJuV2pxGrH5jW0cJ +y9kIHygXk83oAtig9//ErsHhshm0VZbX6CPT4Xlvu1RA6RAoRSZl2UszmExUkJK7 +Zhj7FWfsDXh4UhFyRcCO8WKYgNQYqJvBLTjSiMHxHAUdjbKXBUFN59kAbs45WI+f +Y/TA74KqFmirXoWnFg41CrIJaiIAPL2PeuJdyBoyLaC08hose6ih+U2X7fkXHpys +6ZmO1svTX/T8Tif/gaweYmXQAyrx2ZydG5njKs0r4Cox1udqH8MdZw0pvorc/lTg +BnNENR9t4nLRG2c9lVyn1Lgn58oG04nkCBSJtQy+mn78T9MnjhcRkLWaS3piL9xE +4EOmSG9pDJIZkTN5P6e9DoFA0hUuFLJwApo6hzWkVI4iJQY7X4/JeXR4AQmzr7xv +xF+APefWoS8avFkgKXpl/+ZRSHHg9nkVdJeQdYbEcvZbGBVr/byvPSaf +-----END ENCRYPTED PRIVATE KEY----- diff --git a/client/src/test/java/com/influxdb/client/tls/generate-self-signed-cert.sh b/client/src/test/java/com/influxdb/client/tls/generate-self-signed-cert.sh new file mode 100644 index 00000000000..4ccdde58fec --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/generate-self-signed-cert.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# +# The MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +set -e + +PKCS12_PASSWORD="changeit" +INFLUXDB_SERVER_NAME="influxdb" +OTHER_SERVER_NAME="other-server" + +# Gen Server .crt and .key with SAN (Subject Alternative Names) then convert to .p12 file +for SERVER_NAME in "$INFLUXDB_SERVER_NAME" "$OTHER_SERVER_NAME"; do + echo "### Generate server key and certificate for $SERVER_NAME..." + openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 | openssl pkcs8 -topk8 -nocrypt -out "${SERVER_NAME}.key" + + openssl req -new -x509 -key "${SERVER_NAME}.key" -out "${SERVER_NAME}.crt" -days 3650 \ + -subj "/CN=localhost/O=Development/C=US" \ + -addext "subjectAltName = DNS:localhost,IP:127.0.0.1" + + openssl pkcs12 -export -in "${SERVER_NAME}.crt" -inkey "${SERVER_NAME}.key" \ + -out "${SERVER_NAME}.p12" -name "myalias" -password "pass:$PKCS12_PASSWORD" +done + +# Gen Client .crt and .key then convert to .p12 file +echo "### Generate client key..." +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 | openssl pkcs8 -topk8 -nocrypt -out client.key +openssl req -new -x509 -key client.key -out client.crt -days 3650 \ + -subj "/CN=test-client/O=Development/C=US" +openssl pkcs12 -export -in client.crt -inkey client.key \ + -out client.p12 -name "myalias" -password "pass:$PKCS12_PASSWORD" + +#Encrypt pkcs8 for client.key for testing +openssl pkcs8 -topk8 -v2 aes-256-cbc -iter 1000000 -in client.key -out client_pkcs8.key \ No newline at end of file diff --git a/client/src/test/java/com/influxdb/client/tls/influxdb.crt b/client/src/test/java/com/influxdb/client/tls/influxdb.crt new file mode 100644 index 00000000000..0c35189c4e1 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/influxdb.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUWWaZWiL+PUXwO22PUF5pduaF3tUwDQYJKoZIhvcNAQEL +BQAwNzESMBAGA1UEAwwJbG9jYWxob3N0MRQwEgYDVQQKDAtEZXZlbG9wbWVudDEL +MAkGA1UEBhMCVVMwHhcNMjYwOTI5MTAyOTQzWhcNMzYwOTI2MTAyOTQzWjA3MRIw +EAYDVQQDDAlsb2NhbGhvc3QxFDASBgNVBAoMC0RldmVsb3BtZW50MQswCQYDVQQG +EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALtYoIuLWvp3BrGU +4lsZO7bMTPVcdq9hFQE8ivmonzDYMiyc9oNdYF3gNh58gV2GNhGZvZqEos1S8EgL +2jVmegfRxBUW5bD7FjjcBwhpanHvxldkwfQJXSvS4dadTtqBlo+d+yY2WgWshnnT +aoMk3djiJxxHVHn7yvHDgC+tTxI4MBKC/5NRGCCbtJNaRIL0OE04DUzcGm5pVQN4 +AsEEXfqFOvz3QNtJbbWArlFuYkWwthWaRKTQmlxWmRpqH8RqDtcXQYgTtsJyFhH0 +CrF1lQ5KrncGW/W4GTM5ZBbCGG1x9l8t3GsEaJ2JeqKBGraWWAmmARG3yPyIQxHh +NixqrI8CAwEAAaNvMG0wHQYDVR0OBBYEFL5EDMd8Fe5/94JMeonCVd1Ha/g4MB8G +A1UdIwQYMBaAFL5EDMd8Fe5/94JMeonCVd1Ha/g4MA8GA1UdEwEB/wQFMAMBAf8w +GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQBx +oycxsXHIfzkuwt6aiemFialtrm+GqJLv+1lnTSlItnQGBm1vz4Tv9FdUdMD5YoyP +BTZduAk/aXMFWEQ8TsLlzdQjn5rIyv04rjApCPSFMz7XD4KPXzaqnqUpJkQyJ4/m +cGhu73GLAtgLamDITdaZL5k1I4vMRr0dOgSeLvl/vTC01LgzJPeDfAcFOhnTNOBW +5spL+9hmFRy1cQ3p8+/QurevmI8QBKo6/iUI9LESYBmTS6iXlJFAcerKE7RJWxJk +aDI2t58IbLd+5U8SAcHwhvP3iH4DLBVmIagaH2iAjRffSu/EJQJZ1iW7OkrHVN5p +v0BExOtzcZn2jpyfSiJE +-----END CERTIFICATE----- diff --git a/client/src/test/java/com/influxdb/client/tls/influxdb.key b/client/src/test/java/com/influxdb/client/tls/influxdb.key new file mode 100644 index 00000000000..b62fcc2231d --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/influxdb.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7WKCLi1r6dwax +lOJbGTu2zEz1XHavYRUBPIr5qJ8w2DIsnPaDXWBd4DYefIFdhjYRmb2ahKLNUvBI +C9o1ZnoH0cQVFuWw+xY43AcIaWpx78ZXZMH0CV0r0uHWnU7agZaPnfsmNloFrIZ5 +02qDJN3Y4iccR1R5+8rxw4AvrU8SODASgv+TURggm7STWkSC9DhNOA1M3BpuaVUD +eALBBF36hTr890DbSW21gK5RbmJFsLYVmkSk0JpcVpkaah/Eag7XF0GIE7bCchYR +9AqxdZUOSq53Blv1uBkzOWQWwhhtcfZfLdxrBGidiXqigRq2llgJpgERt8j8iEMR +4TYsaqyPAgMBAAECggEAUqRCUbwb2l+V+tDn1vK3FihiDFwI2M1U9DIuRfodmvDL +lUV6MrGGmHKWvPUEJerVwAbXDinTHQqohsd/rs2xJ/De88ShduQTxeo9SfciZvcz +wYpnSky1JmdEiyTiS14dueWhC5avOFCGE4hwtEAXgpwkT4Ohnpu1RteMHLvxJOIW +DsYSwE7qEN734TARVnPeg3XioKKurgRGrup2D49eIhqTgji5MOJXPP89KXzzxda+ +tw60TIkxd3zwokxln5wsXLBrXeIfE7n/nB7Ii4TXAmFYGM+tBPvdsvwe1MgOhwQc +qksJnanGD6sZdM4gXyP1Jq0gSJ53iypKEBkAIoVKUQKBgQDc0AmHPKfVZ05+Ljkk +/Rx6to2YS8iQ9aDtbxVYI6l0Yo/4r+6lRiaLf/KL5qptFFE+51+hKB6mXH7PliIz +DeqZ6Fil/UGCX+ENBEtcXqWaYG7rJWAcxnJS+kM3U5O69wuLTp0JobjAB4rim8Md +hZ8Ir/jfTx9Hh5Rc9jxaDleVEQKBgQDZM1b6kH9hiHXYzGP9Gjbs9yl+RoMEWpvr +K8J8DcFMm8XVd7WU/AZYupnF8cRHWDdXkba4IGEodwfUgQI4sxBYnEKW+GVNsK6k +7SOZS0lYHPCL3t/dY/9fqaQ3jIg4gArLy6lUvURGlfCgyPVivrjQ6y/v5zANcGKJ +1Ce86VennwKBgQCOY6TRTV7Y8T4fhgUZghKcWx3hqHTUbWBx26EC7jQ2tdwrPKAC +ecMfT8mDR6J8po5hpuf2zx08oAayLBkvnPi8eKS5nR9iSBuivjMuhGX7r9W20qow +xBMyyOkRQ1bPSMFr+qyvalbtla+Mw27FmcXc30T4E1iTUq1saOiwFfGKUQKBgQCc +WWCbLbkENH7geQDhCSXQnnq383oMgE0MlIuDUUN8KgXXDD0h6emHpYtb+hUYZmAw +ISwujuBhiI2SYut+dSenIZStqFEEA9Mz1aBykMdTLkHZ7a2oaHtEGZGkivzj7wfW +e9yAq12ikfyNQRui4TeY7DOttfXZNrU4Eggo7K+0cQKBgQC6AtruugX5PD4IQhFt +ejaIbKoZb6no9q/1gLpLQqAP3ayEibahFkK1OdN4d0ns1sT08jcHFhy3S8RfsTtS +t5TKIOooVTJk2CqQpKj/AJgBYshV71btAJrPuUc//41Uj4LhefdNALPs+0F8zEQm +jLdNMflXyDlEHW/t8rgy++p/Pw== +-----END PRIVATE KEY----- diff --git a/client/src/test/java/com/influxdb/client/tls/influxdb.p12 b/client/src/test/java/com/influxdb/client/tls/influxdb.p12 new file mode 100644 index 00000000000..6aad3565968 Binary files /dev/null and b/client/src/test/java/com/influxdb/client/tls/influxdb.p12 differ diff --git a/client/src/test/java/com/influxdb/client/tls/other-server.crt b/client/src/test/java/com/influxdb/client/tls/other-server.crt new file mode 100644 index 00000000000..5ba473688b1 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/other-server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUUXVpqx2KOSjn0ETaM4xfX0sUuhkwDQYJKoZIhvcNAQEL +BQAwNzESMBAGA1UEAwwJbG9jYWxob3N0MRQwEgYDVQQKDAtEZXZlbG9wbWVudDEL +MAkGA1UEBhMCVVMwHhcNMjYwOTI5MTAyOTQzWhcNMzYwOTI2MTAyOTQzWjA3MRIw +EAYDVQQDDAlsb2NhbGhvc3QxFDASBgNVBAoMC0RldmVsb3BtZW50MQswCQYDVQQG +EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOCAW1LKKaIJ9Xe4 +tI2DHZOt9wJSIAdZ0Ty+SuKMOiqaxD+XU4Q43cdFZKIl2s46Xm0oYYnkO0Fle0iO +0G7zd6MrN6K1Y/IuaH9yjMr7wzl5igNcnneDkxcvo6/jDjcd9o4S1yLAPKsdfZyD ++9+WunHVjHwAlsCfHyW3Z+iHnR00qRyLs98C7d1jDJdhbyj3IOV7Xr8cHqtRhftm +skErdf/TJ8OiFX6Wdi9mNoKLpa6LGlCragwNuul/Ixxxo6IiRNNYUv/jnIpPwUlB +++Skzu9XDZLnrMdjZfclcS6i2asPzQZ8qkU6XLJm+prb+RUzU2NTlaDOFV2Q3w+g +Py3mEoMCAwEAAaNvMG0wHQYDVR0OBBYEFHIlgQVGCWxnxHG4NU3kQd4bfjYXMB8G +A1UdIwQYMBaAFHIlgQVGCWxnxHG4NU3kQd4bfjYXMA8GA1UdEwEB/wQFMAMBAf8w +GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCy +Hem37l7DXxRdWyvqCkvunlGJViyplI3X7+0MmciHLraR2B79Qat88mrEc5eqiaWt +HD43za5FuFeU7n/AorP+c+xARwzimGM0BnrmBGehATktikm1Gw294nioUqk8rn55 +66GQzBCGaWTatCCLdR7GCnW4hldxOvFS/asEF+LCRgdSYeSNRLehZqcUlxkFArkK +UTvGw3b5aEt82fphX0AEGwa2tDIJvtYMU0Ts+gNRuekiLYledpuyIPRVcCdNC7Q2 +Bxv0TwkprGz36MLbgXmhIkZDKdywXjcksqnUX0ok4aXtIS0O6qK402xTDgOI/n+1 +vJRS3emYcH4bdYM6Vqwd +-----END CERTIFICATE----- diff --git a/client/src/test/java/com/influxdb/client/tls/other-server.key b/client/src/test/java/com/influxdb/client/tls/other-server.key new file mode 100644 index 00000000000..04345df22f9 --- /dev/null +++ b/client/src/test/java/com/influxdb/client/tls/other-server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDggFtSyimiCfV3 +uLSNgx2TrfcCUiAHWdE8vkrijDoqmsQ/l1OEON3HRWSiJdrOOl5tKGGJ5DtBZXtI +jtBu83ejKzeitWPyLmh/cozK+8M5eYoDXJ53g5MXL6Ov4w43HfaOEtciwDyrHX2c +g/vflrpx1Yx8AJbAnx8lt2foh50dNKkci7PfAu3dYwyXYW8o9yDle16/HB6rUYX7 +ZrJBK3X/0yfDohV+lnYvZjaCi6WuixpQq2oMDbrpfyMccaOiIkTTWFL/45yKT8FJ +QfvkpM7vVw2S56zHY2X3JXEuotmrD80GfKpFOlyyZvqa2/kVM1NjU5WgzhVdkN8P +oD8t5hKDAgMBAAECggEAJQ16RR1BptFO9q4A+rQvrv6Q3IyyZZQw4P4lPOPY+8wk +ZLx0l8eM7wUoeUyXIt9mU0zNYGVPVFlL8itEAnPjhIYu/gdDEicPry04SI4p5/QD +z887Bk2serCtqutmHrmuxj8cy8r/mFxbLXhpGzt01THiKFsuhmcczGxLVYkAaN/H +eD+wE9YfDlFxWk0jZfIaRFNr4VSkxSVxUH3QPOT4DLOh+HIoBr2MC+DfSuellkrD +0NsaGgfbakNNUbALCiXi1w6AuP+eQLwn49mTJS73N+7+Zdf+9NXv4OSDHhPRT9Pm +nP5OFQ2g9Kg56J6X+ak9y4GnqH3MaoXwGAaAqAzRbQKBgQD26LyI79XlerQcjGrw +XGxaSNPEneWioYArsfpTojn/alyA+GYmBoR0/1e+dV1QxPeawT4PXJ4QSH05zoi/ +OziRIbw9IWUeCJ8oHleNKJ39vweU+lkdgByvE6BQYfA0QCzP24kvijEi+DRIm2tG +MHUjKpo7/4egi10yMnxyv5I8JQKBgQDoxGoKV++C8wUYZuxfafFtxAJl0YkU9Za9 +riM1Mp+xc+wY//BRepDF5vwiZ2cxcrKVQrLtDdjVpFDXNfMZb7sWx4GDEjYi88H6 +qnwSJxuZfVHRcp7M45dPJWa3EA6k//mwAJOHhrnMCIiGK07XEz6ZAPbnzy3wXrIj +12j2cIp/hwKBgGCZHWnVqzFkmCKzLQNspVeNNoyt2bGOzb5Km00tItOyoQ40IIKv ++hJr88An2qJG7Tg2cjlAWvaz5YpAK2/lk3P0Ztjjv9PIO7xOYqJvSaCEpEq8xzCr +do1oeEf6iSdoFCAeG8XlouOzh4EAjS+gtbNUb5MZnc5Jt+RzrAyYtnWhAoGBANCo +ihp9m7J6ob2vPgOGWCu6wx99B3KneRrOSLd+ixMrRKFghZC026TcnHdG1IXKOPZ+ +lSDWAeP6aZGSxjquIzuXWq0hnTAV7XUEcTZgomgkpDyqkFpBRzlm4L4IAei9opm4 +2odqKZISsDf+CK3ajYnhCHTjD15jtTNV/by1RCJvAoGAYzAad9w3WyYcM40flIh/ +J3eLmt8jfU7NLPp0zwChLUph6WUgcG7mNHsTw5NzQrAUz6ioPDkU4SKXLhBZ/Tzv +vbCIPNK+3CBXzEzxXS7FSzodlXRbDOsfDz51lYABjshzJLhIchjkv0YW4NLwdCRu +K+AKfKAw5J9yWY1iR4FQwT0= +-----END PRIVATE KEY----- diff --git a/client/src/test/java/com/influxdb/client/tls/other-server.p12 b/client/src/test/java/com/influxdb/client/tls/other-server.p12 new file mode 100644 index 00000000000..bbdb865846e Binary files /dev/null and b/client/src/test/java/com/influxdb/client/tls/other-server.p12 differ diff --git a/pom.xml b/pom.xml index c270dab71b6..9aad10b608c 100644 --- a/pom.xml +++ b/pom.xml @@ -242,7 +242,7 @@ **/*nightly*/**, **/.m2/**, LICENSE, **/*.md, **/PLACEHOLDER, **/.influxdb/**, **/generated/**, **/openapi-generator/**, **/swagger.yml, **/*.json, **/spring.factories, **/PULL_REQUEST_TEMPLATE, release.properties/, **/pom.xml.releaseBackup, **/pom.xml.tag, **/semantic.yml, **/test.txt, **/*.csv, - codecov.yml, **/flowable/*.java, **/spring/*.imports + codecov.yml, **/flowable/*.java, **/spring/*.imports, **/*.p12, **/*.crt, **/*.key ${project.organization.name}