From 75e45f73e3a2bc9f7559fd7275469d12e886ff27 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Thu, 29 Oct 2026 17:26:55 +0700 Subject: [PATCH 1/3] feat: implement tls/mtls --- pom.xml | 8 +- .../v3/client/config/ClientConfig.java | 111 +++++++- .../v3/client/internal/FlightSqlClient.java | 28 +- .../v3/client/internal/RestClient.java | 30 +- .../influxdb/v3/client/internal/Utils.java | 231 ++++++++++++++++ .../v3/client/InfluxDBClientTest.java | 257 +++++++++++++++++- .../com/influxdb/v3/client/TestUtils.java | 28 +- .../v3/client/config/ClientConfigTest.java | 35 +++ .../v3/client/internal/UtilsTest.java | 106 ++++++++ .../client/testdata/certificates/client.crt | 20 ++ .../client/testdata/certificates/client.key | 28 ++ .../client/testdata/certificates/client.p12 | Bin 0 -> 2682 bytes .../testdata/certificates/client_pkcs8.key | 30 ++ .../certificates/generate-self-signed-cert.sh | 52 ++++ .../client/testdata/certificates/influxdb.crt | 21 ++ .../client/testdata/certificates/influxdb.key | 28 ++ .../client/testdata/certificates/influxdb.p12 | Bin 0 -> 2714 bytes .../testdata/certificates/other-server.crt | 21 ++ .../testdata/certificates/other-server.key | 28 ++ .../testdata/certificates/other-server.p12 | Bin 0 -> 2714 bytes 20 files changed, 1045 insertions(+), 17 deletions(-) create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/client.crt create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/client.key create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/client.p12 create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/client_pkcs8.key create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/generate-self-signed-cert.sh create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.crt create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.key create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.p12 create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.crt create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.key create mode 100644 src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.p12 diff --git a/pom.xml b/pom.xml index 725eacd9..959ac077 100644 --- a/pom.xml +++ b/pom.xml @@ -461,7 +461,7 @@ **/target/**, **/*.jar, **/.git/**, **/.*, **/*.png, **/*.iml, **/*.bolt, .idea/**, **/*nightly*/**, **/.m2/**, LICENSE, **/*.md, **/.github/**, license_header.txt, release.properties/, **/pom.xml.releaseBackup, **/pom.xml.tag, **/semantic.yml, - .circleci/config.yml, **/*.pem + .circleci/config.yml, **/*.pem, **/*.p12, **/*.crt, **/*.key @@ -540,6 +540,9 @@ org.apache.maven.plugins maven-surefire-plugin ${plugin.surefire.version} + + @{argLine} --add-opens=java.base/java.nio=ALL-UNNAMED + org.junit.vintage @@ -553,6 +556,9 @@ org.apache.maven.plugins maven-failsafe-plugin ${plugin.surefire.version} + + @{argLine} --add-opens=java.base/java.nio=ALL-UNNAMED + diff --git a/src/main/java/com/influxdb/v3/client/config/ClientConfig.java b/src/main/java/com/influxdb/v3/client/config/ClientConfig.java index a87bfab8..60b0ddbc 100644 --- a/src/main/java/com/influxdb/v3/client/config/ClientConfig.java +++ b/src/main/java/com/influxdb/v3/client/config/ClientConfig.java @@ -129,7 +129,15 @@ public final class ClientConfig { private final String proxyUrl; private final Authenticator authenticator; private final Map headers; + + private final String certificatePath; + private final String certificateKeyPath; + private final String certificateP12FilePath; + private final char[] keyPassword; + private final String sslRootsFilePath; + private final char[] sslRootFilePassword; + private final boolean disableGRPCCompression; private final List interceptors; @@ -337,6 +345,31 @@ public String sslRootsFilePath() { return sslRootsFilePath; } + @Nullable + public char[] getSslRootFilePassword() { + return sslRootFilePassword; + } + + @Nullable + public String getCertificatePath() { + return certificatePath; + } + + @Nullable + public String getCertificateKeyPath() { + return certificateKeyPath; + } + + @Nullable + public String getCertificateP12FilePath() { + return certificateP12FilePath; + } + + @Nullable + public char[] getKeyPassword() { + return keyPassword; + } + /** * Gets the (proxy) authenticator. * @@ -389,6 +422,18 @@ public void validate() { if (uri.getHost() == null) { throw new URISyntaxException(host, "Invalid URL."); } + + if ("https".equals(uri.getScheme())) { + if (certificatePath != null && certificateP12FilePath != null) { + throw new IllegalArgumentException("Cannot set both certificateFilePath " + + "and certificateP12FilePath"); + } + + if (certificatePath != null && certificateKeyPath == null) { + throw new IllegalArgumentException("Cannot set certificatePath without certificateKeyPath"); + } + } + } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URL."); } @@ -424,6 +469,11 @@ public boolean equals(final Object o) { && Objects.equals(authenticator, that.authenticator) && Objects.equals(headers, that.headers) && Objects.equals(sslRootsFilePath, that.sslRootsFilePath) + && Arrays.equals(sslRootFilePassword, that.sslRootFilePassword) + && Objects.equals(certificatePath, that.certificatePath) + && Objects.equals(certificateKeyPath, that.certificateKeyPath) + && Objects.equals(certificateP12FilePath, that.certificateP12FilePath) + && Arrays.equals(keyPassword, that.keyPassword) && disableGRPCCompression == that.disableGRPCCompression && Objects.equals(interceptors, that.interceptors); } @@ -434,7 +484,9 @@ public int hashCode() { database, writePrecision, gzipThreshold, writeNoSync, writeAcceptPartial, writeUseV2Api, timeout, writeTimeout, queryTimeout, allowHttpRedirects, disableServerCertificateValidation, proxy, proxyUrl, authenticator, headers, - defaultTags, sslRootsFilePath, disableGRPCCompression, interceptors); + defaultTags, sslRootsFilePath, Arrays.hashCode(sslRootFilePassword), certificatePath, + certificateKeyPath, certificateP12FilePath, Arrays.hashCode(keyPassword), + disableGRPCCompression, interceptors); } @Override @@ -459,6 +511,11 @@ public String toString() { .add("headers=" + headers) .add("defaultTags=" + defaultTags) .add("sslRootsFilePath=" + sslRootsFilePath) + .add("sslRootFilePassword=" + Arrays.toString(sslRootFilePassword)) + .add("certificatePath=" + certificatePath) + .add("certificateKeyPath=" + certificateKeyPath) + .add("certificateP12FilePath=" + certificateP12FilePath) + .add("keyPassword=" + Arrays.toString(keyPassword)) .add("disableGRPCCompression=" + disableGRPCCompression) .toString(); } @@ -490,7 +547,15 @@ public static final class Builder { private String proxyUrl; private Authenticator authenticator; private Map headers; + + private String certificatePath; + private String certificateKeyPath; + private String certificateP12FilePath; + private char[] keyPassword; + private String sslRootsFilePath; + private char[] sslRootFilePassword; + private boolean disableGRPCCompression; private List interceptors; @@ -805,6 +870,45 @@ public Builder sslRootsFilePath(@Nullable final String sslRootsFilePath) { return this; } + @Nonnull + public Builder sslRootFilePassword(@Nullable final char[] sslRootFilePassword) { + + this.sslRootFilePassword = sslRootFilePassword; + 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 ClientConfig.Builder} instance. + */ + @Nonnull + public Builder certificateFilePath(@Nonnull final String certificatePath, + @Nonnull final String certificateKeyPath) { + this.certificatePath = certificatePath; + this.certificateKeyPath = certificateKeyPath; + + return this; + } + + @Nonnull + public Builder certificateP12FilePath(@Nonnull final String certificateP12FilePath, + @Nullable final char[] password) { + this.certificateP12FilePath = certificateP12FilePath; + this.keyPassword = password; + + return this; + } + + @Nonnull + public Builder keyPassword(@Nullable final char[] keyPassword) { + + this.keyPassword = keyPassword; + return this; + } + /** * Sets whether to disable gRPC compression. Default is 'false'. * @@ -1023,6 +1127,11 @@ private ClientConfig(@Nonnull final Builder builder) { authenticator = builder.authenticator; headers = builder.headers; sslRootsFilePath = builder.sslRootsFilePath; + sslRootFilePassword = builder.sslRootFilePassword; + certificatePath = builder.certificatePath; + certificateKeyPath = builder.certificateKeyPath; + certificateP12FilePath = builder.certificateP12FilePath; + keyPassword = builder.keyPassword; disableGRPCCompression = builder.disableGRPCCompression; interceptors = builder.interceptors; } diff --git a/src/main/java/com/influxdb/v3/client/internal/FlightSqlClient.java b/src/main/java/com/influxdb/v3/client/internal/FlightSqlClient.java index 9880e7f2..8d8b0811 100644 --- a/src/main/java/com/influxdb/v3/client/internal/FlightSqlClient.java +++ b/src/main/java/com/influxdb/v3/client/internal/FlightSqlClient.java @@ -21,7 +21,6 @@ */ package com.influxdb.v3.client.internal; -import java.io.FileInputStream; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; @@ -38,6 +37,8 @@ import java.util.stream.StreamSupport; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -190,9 +191,28 @@ SslContext createNettySslContext(@Nonnull final ClientConfig config) { sslContextBuilder = GrpcSslContexts.forClient(); if (config.getDisableServerCertificateValidation()) { sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); - } else if (config.sslRootsFilePath() != null) { - try (FileInputStream fileInputStream = new FileInputStream(config.sslRootsFilePath())) { - sslContextBuilder.trustManager(fileInputStream); + } else { + TrustManagerFactory tmf = null; + KeyManagerFactory kmf = null; + if (config.sslRootsFilePath() != null) { + tmf = Utils.createTmf( + config.sslRootsFilePath(), + config.getSslRootFilePassword()); + } + + if (config.getCertificatePath() != null && config.getCertificateKeyPath() != null) { + kmf = Utils.createKmf(config.getCertificatePath(), config.getCertificateKeyPath()); + } + + if (config.getCertificateP12FilePath() != null) { + kmf = Utils.createKmfP12(config.getCertificateP12FilePath(), config.getKeyPassword()); + } + + if (tmf != null) { + sslContextBuilder.trustManager(tmf); + } + if (kmf != null) { + sslContextBuilder.keyManager(kmf); } } return sslContextBuilder.build(); diff --git a/src/main/java/com/influxdb/v3/client/internal/RestClient.java b/src/main/java/com/influxdb/v3/client/internal/RestClient.java index 72de031e..210c55a1 100644 --- a/src/main/java/com/influxdb/v3/client/internal/RestClient.java +++ b/src/main/java/com/influxdb/v3/client/internal/RestClient.java @@ -42,6 +42,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -127,16 +128,32 @@ public void checkServerTrusted( if (baseUrl.startsWith("https")) { try { - SSLContext sslContext = SSLContext.getInstance("TLS"); if (config.getDisableServerCertificateValidation()) { + SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, TRUST_ALL_CERTS, new SecureRandom()); - } else if (config.sslRootsFilePath() != null) { - X509TrustManager x509TrustManager = getX509TrustManagerFromFile(config.sslRootsFilePath()); - sslContext.init(null, new X509TrustManager[]{x509TrustManager}, new SecureRandom()); + builder.sslContext(sslContext); } else { - sslContext.init(null, null, new SecureRandom()); + TrustManagerFactory tmf = null; + KeyManagerFactory kmf = null; + if (config.sslRootsFilePath() != null) { + tmf = Utils.createTmf( + config.sslRootsFilePath(), + config.getSslRootFilePassword()); + } + + if (config.getCertificatePath() != null && config.getCertificateKeyPath() != null) { + kmf = Utils.createKmf(config.getCertificatePath(), config.getCertificateKeyPath()); + } + + if (config.getCertificateP12FilePath() != null) { + kmf = Utils.createKmfP12(config.getCertificateP12FilePath(), config.getKeyPassword()); + } + + SSLContext sslContext = Utils.buildSslContext(kmf, tmf); + if (sslContext != null) { + builder.sslContext(sslContext); + } } - builder.sslContext(sslContext); } catch (Exception e) { throw new RuntimeException(e); } @@ -487,6 +504,7 @@ private String errNonEmptyField(@Nullable final JsonNode object, @Nonnull final return errNonEmptyText(object.get(fieldName)); } + //todo remove? private X509TrustManager getX509TrustManagerFromFile(@Nonnull final String filePath) { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); diff --git a/src/main/java/com/influxdb/v3/client/internal/Utils.java b/src/main/java/com/influxdb/v3/client/internal/Utils.java index 70cc0eb1..c9d8f8f4 100644 --- a/src/main/java/com/influxdb/v3/client/internal/Utils.java +++ b/src/main/java/com/influxdb/v3/client/internal/Utils.java @@ -21,8 +21,33 @@ */ package com.influxdb.v3.client.internal; +import java.io.FileInputStream; +import java.io.IOException; +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 Utils { + 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 Utils() { } @@ -43,4 +68,210 @@ public static boolean isInteger(final String str) { return false; } } + + @Nullable + public static SSLContext buildSslContext(@Nullable final KeyManagerFactory kmf, + @Nullable final TrustManagerFactory tmf) throws GeneralSecurityException { + 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; + } + + + /** + * 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 IOException if an error occurs while reading the file. + * @throws GeneralSecurityException if an error occurs while loading the keystore + * or initializing the {@link KeyManagerFactory}. + */ + public static KeyManagerFactory createKmfP12(@Nonnull final String path, + @Nullable final char[] password) + throws IOException, GeneralSecurityException { + 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 IOException if an error occurs while reading the files. + * @throws GeneralSecurityException if an error occurs while loading the credentials or initializing + * the {@link KeyManagerFactory}. + */ + public static KeyManagerFactory createKmf(@Nonnull final String certPath, + @Nonnull final String keyPath) + throws IOException, GeneralSecurityException { + 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", + 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 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 IOException if an error occurs while reading the file. + * @throws GeneralSecurityException if an error occurs while 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 IOException, GeneralSecurityException { + 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; + } + + /** + * 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 IOException if an error occurs while reading the file. + * @throws GeneralSecurityException if an error occurs while loading the keystore + * or initializing the TrustManagerFactory. + */ + public static TrustManagerFactory createTmfP12(@Nonnull final String path, + @Nullable final char[] password) + throws IOException, GeneralSecurityException { + 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; + } + + /** + * 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 GeneralSecurityException if an error occurs during the initialization or retrieval + * of the {@link X509TrustManager}. + */ + @Nonnull + public static X509TrustManager getX509TrustManager(@Nullable final TrustManagerFactory tmf) + throws GeneralSecurityException { + 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 IOException if an error occurs during file reading. + * @throws GeneralSecurityException if an error occurs during Base64 decoding or key generation. + */ + public static PrivateKey loadPrivateKey(@Nonnull final String path) throws IOException, GeneralSecurityException { + 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."); + } + + if (!keyPem.contains("-----BEGIN PRIVATE KEY-----") || !keyPem.contains("-----END PRIVATE KEY-----")) { + throw new IllegalArgumentException("Invalid private key format. Private key must be in PKCS#8 format."); + } + + 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); + } } diff --git a/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java b/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java index de6f52a1..bc2c97b1 100644 --- a/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java +++ b/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java @@ -21,16 +21,32 @@ */ package com.influxdb.v3.client; +import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import mockwebserver3.MockResponse; +import mockwebserver3.MockWebServer; +import okhttp3.Headers; +import org.apache.arrow.flight.FlightServer; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.VectorSchemaRoot; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import com.influxdb.v3.client.config.ClientConfig; +import com.influxdb.v3.client.internal.Utils; + +import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable; public class InfluxDBClientTest { @@ -144,10 +160,10 @@ void withDefaultTags() throws Exception { Map defaultTags = Map.of("unit", "U2", "model", "M1"); try (InfluxDBClient client = InfluxDBClient.getInstance( - "http://localhost:8086", - "MY-TOKEN".toCharArray(), - "MY-DATABASE", - defaultTags)) { + "http://localhost:8086", + "MY-TOKEN".toCharArray(), + "MY-DATABASE", + defaultTags)) { Assertions.assertThat(client).isNotNull(); } } @@ -166,4 +182,237 @@ public void unsupportedQueryParams() throws Exception { + "class com.influxdb.v3.client.internal.InfluxDBClientImpl"); } } + + String tlsDir = "src/test/java/com/influxdb/v3/client/testdata/certificates/"; + + 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( + @Nonnull String name, + @Nullable String clientCertFilePath, + @Nullable String clientKeyFilePath, + @Nonnull String sslRootFilePath, + boolean expectSuccess + ) { + } + + private List tlsCases() { + return List.of( + new TlsTest( + "Tls success with correct server certificate", + null, + null, + influxdbCertPath, + true + ), + new TlsTest( + "Tls success with correct .p12 server certificate", + null, + null, + influxdbP12, + true + ), + new TlsTest( + "Tls fail with incorrect server certificate", + null, + null, + otherCertPath, + false + ), + new TlsTest( + "mtls success with correct client certificate .key and .crt file", + clientCertPath, + clientKeyPath, + influxdbCertPath, + true + ), + new TlsTest( + "mtls success with correct client certificate .p12 file", + clientP12, + null, + influxdbCertPath, + true + ) + ); + } + + @Test + public void testWriteSecureServer() { + for (TlsTest tlsCase : tlsCases()) { + Throwable thrown = catchThrowable(() -> + testWriteSecureMockServer( + tlsCase.clientCertFilePath, + tlsCase.clientKeyFilePath, + defaultPassword, + tlsCase.sslRootFilePath, + defaultPassword + )); + if (!tlsCase.expectSuccess) { + Assertions.assertThat(thrown).isNotNull(); + Assertions.assertThat(thrown).isInstanceOf(InfluxDBApiException.class); + Assertions.assertThat(thrown).hasMessageContaining("ssl"); + } else { + Assertions.assertThat(thrown).isNull(); + } + } + } + + @Test + public void testQuerySecureServer() throws Exception { + for (TlsTest tlsCase : tlsCases()) { + try (var flightServer = createQuerySecureServer( + influxdbCertPath, + influxdbKeyPath, + tlsCase.clientCertFilePath != null ? clientCertPath : null)) { + flightServer.start(); + URI uri = flightServer.getLocation().getUri(); + + ClientConfig clientConfig = getSecureClientConfigBuilder( + String.format("https://%s:%d", uri.getHost(), uri.getPort()), + tlsCase.sslRootFilePath, + defaultPassword, + tlsCase.clientCertFilePath, + tlsCase.clientKeyFilePath, + defaultPassword + ).build(); + try (InfluxDBClient influxDBClient = InfluxDBClient.getInstance(clientConfig)) { + if (!tlsCase.expectSuccess) { + Throwable thrown = catchThrowable(() -> { + Stream stream = influxDBClient.query("Select * from \"nothing\""); + stream.forEach(objects -> { + Assertions.assertThat(objects[0]).isEqualTo("Value"); + }); + }); + System.out.println(thrown); + } else { + Stream stream = influxDBClient.query("Select * from \"nothing\""); + stream.forEach(objects -> { + Assertions.assertThat(objects[0]).isEqualTo("Value"); + }); + } + } + } + } + } + + @Nonnull + public FlightServer createQuerySecureServer(@Nonnull final String serverCertFilePath, + @Nonnull final String serverKeyFilePath, + @Nullable final String clientCertPath) + throws Exception { + URI uri = URI.create("https://127.0.0.1:" + TestUtils.findFreePort()); + VectorSchemaRoot vectorSchemaRoot = TestUtils.generateVectorSchemaRootWithNull(); + return TestUtils.secureFlightServer( + uri, + new RootAllocator(Long.MAX_VALUE), + TestUtils.simpleProducer(vectorSchemaRoot), + serverCertFilePath, + serverKeyFilePath, + clientCertPath + ); + } + + public ClientConfig.Builder getSecureClientConfigBuilder( + @Nonnull String host, + @Nonnull final String trustFilePath, + @Nullable final char[] trustFilePathPassword, + @Nullable final String clientCertFilePath, + @Nullable final String clientKeyFilePath, + @Nullable final char[] clientCertFilePassword + ) { + ClientConfig.Builder configBuilder = new ClientConfig.Builder() + .token("my-token".toCharArray()) + .database("mydb") + .sslRootsFilePath(trustFilePath); + + if (trustFilePathPassword != null) { + configBuilder.sslRootFilePassword(trustFilePathPassword); + } + + if (clientCertFilePath != null) { + if (clientCertFilePath.endsWith(".p12")) { + configBuilder.certificateP12FilePath(clientCertFilePath, clientCertFilePassword); + } else { + configBuilder.certificateFilePath(clientCertFilePath, clientKeyFilePath); + } + } + + configBuilder.host(host); + return configBuilder; + } + + public void testWriteSecureMockServer(@Nullable final String clientCertFilePath, + @Nullable final String clientKeyFilePath, + @Nullable final char[] clientCertFilePassword, + @Nonnull final String trustFilePath, + @Nullable final char[] trustFilePathPassword + ) + throws Exception { + MockWebServer mockServer = null; + InfluxDBClient client = null; + try { + mockServer = createWriteSecureServer( + influxdbCertPath, + influxdbKeyPath, + clientCertFilePath != null ? clientCertPath : null); + final ClientConfig clientConfig = getSecureClientConfigBuilder( + mockServer.url("/").url().toString(), + trustFilePath, + trustFilePathPassword, + clientCertFilePath, + clientKeyFilePath, + clientCertFilePassword + ).build(); + client = InfluxDBClient.getInstance(clientConfig); + client.writeRecord("mem,tag=one value=1.0"); + } catch (Exception e) { + throw e; + } finally { + if (client != null) { + client.close(); + } + + mockServer.close(); + } + } + + private MockWebServer createWriteSecureServer( + @Nonnull final String serverCertFilePath, + @Nonnull final String serverKeyFilePath, + @Nullable final String clientCertFilePath + ) throws Exception { + KeyManagerFactory kmf = Utils.createKmf(serverCertFilePath, serverKeyFilePath); + var mockServer = new MockWebServer(); + TrustManagerFactory tmf = null; + if (clientCertFilePath != null) { + tmf = Utils.createTmf(clientCertFilePath, null); + mockServer.requireClientAuth(); + } + SSLContext sslContext = Utils.buildSslContext(kmf, tmf); + mockServer.useHttps(sslContext.getSocketFactory()); + mockServer.start(); + mockServer.enqueue(createResponse()); + + return mockServer; + } + + @Nonnull + protected MockResponse createResponse() { + return new MockResponse(200, + Headers.of("something", "something"), + "ok"); + } + } diff --git a/src/test/java/com/influxdb/v3/client/TestUtils.java b/src/test/java/com/influxdb/v3/client/TestUtils.java index 28103513..027df552 100644 --- a/src/test/java/com/influxdb/v3/client/TestUtils.java +++ b/src/test/java/com/influxdb/v3/client/TestUtils.java @@ -21,6 +21,7 @@ */ package com.influxdb.v3.client; +import java.io.File; import java.io.IOException; import java.net.ServerSocket; import java.net.URI; @@ -30,6 +31,7 @@ import java.util.List; import java.util.Map; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.apache.arrow.flight.FlightServer; import org.apache.arrow.flight.Location; @@ -51,13 +53,37 @@ private TestUtils() { throw new IllegalStateException("Utility class"); } + @Nonnull public static FlightServer simpleFlightServer(@Nonnull final URI uri, @Nonnull final BufferAllocator allocator, - @Nonnull final NoOpFlightProducer producer) throws Exception { + @Nonnull final NoOpFlightProducer producer) { Location location = Location.forGrpcInsecure(uri.getHost(), uri.getPort()); return FlightServer.builder(allocator, location, producer).build(); } + @Nonnull + public static FlightServer secureFlightServer(@Nonnull final URI uri, + @Nonnull final BufferAllocator allocator, + @Nonnull final NoOpFlightProducer producer, + @Nonnull final String serverCertPath, + @Nonnull final String serverKeyPath, + @Nullable final String clientCertPath) throws IOException { + Location location = Location.forGrpcTls(uri.getHost(), uri.getPort()); + + var serverBuilder = FlightServer.builder(allocator, location, producer) + .useTls(new File(serverCertPath), new File(serverKeyPath)); + + if (clientCertPath != null) { + if (clientCertPath.endsWith(".p12")) { + throw new IllegalArgumentException("Client certificate path must be in PEM " + + "format likes .pem, .crt, .cert"); + } + serverBuilder.useMTlsClientVerification(new File(clientCertPath)); + } + + return serverBuilder.build(); + } + public static NoOpFlightProducer simpleProducer(@Nonnull final VectorSchemaRoot vectorSchemaRoot) { return new NoOpFlightProducer() { @Override diff --git a/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java b/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java index 573cc288..fb668b66 100644 --- a/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java +++ b/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java @@ -30,9 +30,12 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import com.influxdb.v3.client.InfluxDBClient; import com.influxdb.v3.client.write.WriteOptions; import com.influxdb.v3.client.write.WritePrecision; +import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable; + class ClientConfigTest { private final ClientConfig.Builder configBuilder = new ClientConfig.Builder() @@ -448,4 +451,36 @@ void standardTimeoutUsedWhenWriteTimeoutUndefinedTest() { Assertions.assertThat(config.getWriteTimeout()).isEqualTo(Duration.ofSeconds(testTimeout)); } + @Test + void setBothCertFilePathAndP12FilePathTest() { + ClientConfig config = new ClientConfig.Builder() + .host("https://localhost:8086") + .token("my-token".toCharArray()) + .certificateFilePath("path/to/client.crt", "path/to/client.key") + .certificateP12FilePath("path/to/client.p12", "changeit".toCharArray()) + .build(); + + Throwable thrown = catchThrowable(() -> InfluxDBClient.getInstance(config)); + Assertions.assertThat(thrown).isNotNull(); + Assertions.assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + Assertions.assertThat(thrown.getMessage()).isEqualTo("Cannot set both certificateFilePath and certificateP12FilePath"); + + } + + @Test + void setCertFileWithoutKeyfile() { + ClientConfig config = new ClientConfig.Builder() + .host("https://localhost:8086") + .token("my-token".toCharArray()) + .certificateFilePath("path/to/client.crt", null) + .build(); + + Throwable thrown = catchThrowable(() -> InfluxDBClient.getInstance(config)); + Assertions.assertThat(thrown).isNotNull(); + Assertions.assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + Assertions.assertThat(thrown.getMessage()) + .isEqualTo("Cannot set certificatePath without certificateKeyPath"); + + } + } diff --git a/src/test/java/com/influxdb/v3/client/internal/UtilsTest.java b/src/test/java/com/influxdb/v3/client/internal/UtilsTest.java index a4bd023c..038ca03b 100644 --- a/src/test/java/com/influxdb/v3/client/internal/UtilsTest.java +++ b/src/test/java/com/influxdb/v3/client/internal/UtilsTest.java @@ -21,14 +21,33 @@ */ package com.influxdb.v3.client.internal; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.util.Base64; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; class UtilsTest { + + private final Path tempDir; + + UtilsTest(@TempDir final Path tempDir) { + this.tempDir = tempDir; + } + @ParameterizedTest @ValueSource(strings = { "0", @@ -70,4 +89,91 @@ void isIntegerInvalid(final String value) { void testEmpty() { Assertions.assertThat(Utils.isInteger("")).isFalse(); } + + @Test + void loadPrivateKeyMissingBeginHeader() throws IOException { + Path keyFile = tempDir.resolve("missing_begin.key"); + Files.writeString(keyFile, + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3\n-----END PRIVATE KEY-----"); + + Assertions.assertThatThrownBy(() -> Utils.loadPrivateKey(keyFile.toString())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid private key format. Private key must be in PKCS#8 format."); + } + + @Test + void loadPrivateKeyMissingEndHeader() throws IOException { + Path keyFile = tempDir.resolve("missing_end.key"); + Files.writeString(keyFile, + "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3"); + + Assertions.assertThatThrownBy(() -> Utils.loadPrivateKey(keyFile.toString())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid private key format. Private key must be in PKCS#8 format."); + } + + @Test + void loadPrivateKeyMissingBothHeaders() throws IOException { + Path keyFile = tempDir.resolve("missing_both.key"); + Files.writeString(keyFile, "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3"); + + Assertions.assertThatThrownBy(() -> Utils.loadPrivateKey(keyFile.toString())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid private key format. Private key must be in PKCS#8 format."); + } + + @Test + void loadPrivateKeyValid(final @TempDir Path tempDir) throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); + keyPairGen.initialize(2048); + KeyPair keyPair = keyPairGen.generateKeyPair(); + String encodedKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); + String pem = "-----BEGIN PRIVATE KEY-----\n" + encodedKey + "\n-----END PRIVATE KEY-----\n"; + Path keyFile = tempDir.resolve("valid.key"); + Files.writeString(keyFile, pem); + + PrivateKey privateKey = Utils.loadPrivateKey(keyFile.toString()); + Assertions.assertThat(privateKey).isNotNull(); + } + + @Test + void loadPrivateKeyEmptyFile() throws IOException { + Path keyFile = tempDir.resolve("empty.key"); + Files.writeString(keyFile, ""); + + Assertions.assertThatThrownBy(() -> Utils.loadPrivateKey(keyFile.toString())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid private key format. Private key must be in PKCS#8 format."); + } + + @Test + void loadPrivateKeyEncryptedPkcs8Rejected() throws IOException { + Path keyFile = tempDir.resolve("encrypted_pkcs8.key"); + Files.writeString(keyFile, + "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIE...\n-----END ENCRYPTED PRIVATE KEY-----"); + + Assertions.assertThatThrownBy(() -> Utils.loadPrivateKey(keyFile.toString())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Encrypted PKCS#8 private keys are not supported. " + + "Use an unencrypted PKCS#8 key or a PKCS#12 file."); + } + + @Test + void getX509TrustManagerDefault() throws Exception { + X509TrustManager trustManager = Utils.getX509TrustManager(null); + Assertions.assertThat(trustManager).isNotNull(); + } + + @Test + void getX509TrustManagerFromFactory() throws Exception { + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore) null); + X509TrustManager trustManager = Utils.getX509TrustManager(tmf); + Assertions.assertThat(trustManager).isNotNull(); + } + + @Test + void buildSslContextNull() throws Exception { + Assertions.assertThat(Utils.buildSslContext(null, null)).isNull(); + } } diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/client.crt b/src/test/java/com/influxdb/v3/client/testdata/certificates/client.crt new file mode 100644 index 00000000..12a7c7a1 --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIUc3JF73VbBZjwUWvj9IE/Za7RU2QwDQYJKoZIhvcNAQEL +BQAwOTEUMBIGA1UEAwwLdGVzdC1jbGllbnQxFDASBgNVBAoMC0RldmVsb3BtZW50 +MQswCQYDVQQGEwJVUzAeFw0yNjEwMjkxMDM3MDlaFw0zNjEwMjYxMDM3MDlaMDkx +FDASBgNVBAMMC3Rlc3QtY2xpZW50MRQwEgYDVQQKDAtEZXZlbG9wbWVudDELMAkG +A1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNRUUv0653 +3LU9IwVIPc8ir8wDiwUAumLHRPplwon/3Z7i3vfDb/7r6EPR0Bdp+WtKBj7M0PY0 +X8/xWhD2eu/QX/W6Hzd9OKXP3RfM7vC0vNGdnqEjnvA/GEOB/vDoBCjsqm2H12FA +Ib+mwQ7mKjItVy6BkSNUIEJnYtKaGzXBF7mrJS5WIYxoN0ndHuyBfEmwwp0Aq8/p +Q4KNHu4dD0WOgBf8O14n1keVvHcLH5d2cCM5H+FeOtaJNaVb6qtKUstvYrl5/0I8 +14r6j0cQi9lIjR/U8XRA+KGBGtRuWHETcKpmdCQm0qOz1BrMKWzWqsYytedsASVT +NmPa6FnOiZSFAgMBAAGjUzBRMB0GA1UdDgQWBBSwxa3/EgP5Aq9QKjN///waCk2A +0jAfBgNVHSMEGDAWgBSwxa3/EgP5Aq9QKjN///waCk2A0jAPBgNVHRMBAf8EBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAUU5iIfhi9B1Lpl5l8nSUy2RKwjBVVymfq +IYTqkPPFbhSOWAYEpdgi0J0NO/ieqU2/USK3gotEO/r1pmqekdekUyEglExON4j4 +RxpJdbz/l4MLXg2X48m2wDMFkaGwU8RuYUQkFRjht09Yz6lc2Z3CPm6R1xEt5/32 +yUfZZsJ/eMGq4bvLRFa9hfnZpGI8gRWC9bMoWNJCieejYEkDTUCxk5IGm2ZCthKD +IqyPj5uKaz3qzO5R9XdlHGWSmMVxQws+gc+5fTi+PoMgSSv/hnDghpj9YiSEA2Rq +YsMZ3CEhi4UES4wsPQFgmsXJidByQa4Rs3lU9djJ731Q9RhE7sCP +-----END CERTIFICATE----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/client.key b/src/test/java/com/influxdb/v3/client/testdata/certificates/client.key new file mode 100644 index 00000000..64c74bbb --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNRUUv06533LU9 +IwVIPc8ir8wDiwUAumLHRPplwon/3Z7i3vfDb/7r6EPR0Bdp+WtKBj7M0PY0X8/x +WhD2eu/QX/W6Hzd9OKXP3RfM7vC0vNGdnqEjnvA/GEOB/vDoBCjsqm2H12FAIb+m +wQ7mKjItVy6BkSNUIEJnYtKaGzXBF7mrJS5WIYxoN0ndHuyBfEmwwp0Aq8/pQ4KN +Hu4dD0WOgBf8O14n1keVvHcLH5d2cCM5H+FeOtaJNaVb6qtKUstvYrl5/0I814r6 +j0cQi9lIjR/U8XRA+KGBGtRuWHETcKpmdCQm0qOz1BrMKWzWqsYytedsASVTNmPa +6FnOiZSFAgMBAAECggEAOziGZu930M3knfBKGFb4uQWl4snLVjPvxpTAdvZCsQZj +zglMyt43WiEm29PgxWq3iOI3YQjSVzbjO7rj1PCP2nqs3pcd/6sFr2wWb0HqQQKO +qR44WBoFLvbVlx4Rq6Zzlh/vPLcnMATskXP2U/INiuYxnSDd4+nwrnokz2AVSJEG +dGuVchOXSy3KfJ68ZU0wyCL/OvFzXg0bVO/o82YcXj2cU0VzdY8Rm/mlLVi8dDhw +UqudffLk7V6p3VXYtJjXcxTmVOrTjE2nXs2fBe806h+pN1VnrtiZsbkaNuhr/gKF +dPPkF+y/sfs6XZy9nOkLjdar4+uPrCn7LBX8SdvviwKBgQD57AZOfJK4hOq8l9Ax +vjZT+kd98h/q/DN95mlaRUBqtiKgudA9KpP0RaNSF4OZtFq6ShG2PHPkn0mIJ6yw +3oMFLvc/X/0NSx8ZURRVaHC07DCW79mM1sIsv75jzoYojCgho5X7A0C6V+d2l6+R +Fp2gi3jx7idT3MPOUIBeB55lHwKBgQDSQ0DHhhX2IDng5HubOMaJmfgzsuGDtHQv +i0gT15qmObxE7XXjxRU5BnB5AydrAMREOD6XNgAEHTq6hU0DbP0qUS1YS7JicvXD +MrYDSNMiOwhf8AuW56/n3xOVja0vqDmZugdM1q5DCRcTnGlL4hS8jAOB4C7P0kOq +NTuyecmN2wKBgBVufAv2+s3/9/E8Q4pAeYAyEjdmr/4Cqpe9ToODNMbx335Q8N6N +7CoiXqMHiS2BHdajy2kJPhb44eFfJZv1ON8U6/bvUE/3hS8ymL/KOQAK0Q/geZNu +RzB3MOh2KWMIwdhAovfni4FvUFYHI/MZMWM3EqQMfCrg35mtpZRqqtaZAoGAPmA5 +LXe3uCZjRTZC/Q9gxp1opl2gzSORfDFDlq9Vb46dLY2MdZ+X5izk3OF0pie6Ecrl +tpIMeyv4qjC1YhjJGe6vFwyNxOr5wYifumW2TOdaWquppIDjDFR/SNSY1hR/0f0A +AWmGlNhzghvamOpe4Hhg6fyJppln/WyBYzcB9hUCgYEAoA6h5VW7Y6qndjLixH/G +zGJkIDDRp97jEcIlZt2gpK6M4eHOaXE4LXJslUyKAXaREX+fCP8wSgEuLvEHYXHO +hO7TK3ysPI1UsLduPA1xM171MP25oZjs+cRPvoXzdz6k1m4sn9ftmV9XVSE7CNVV +GeKnEV8BAYwRAKeoavZM0bI= +-----END PRIVATE KEY----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/client.p12 b/src/test/java/com/influxdb/v3/client/testdata/certificates/client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..83c7293bf2980b0f9f9b62592cd73f9da9417d3d GIT binary patch literal 2682 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 O88}$lA?V&OBL5F}qtE*Q literal 0 HcmV?d00001 diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/client_pkcs8.key b/src/test/java/com/influxdb/v3/client/testdata/certificates/client_pkcs8.key new file mode 100644 index 00000000..48ae830a --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/client_pkcs8.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFNjBgBgkqhkiG9w0BBQ0wUzAyBgkqhkiG9w0BBQwwJQQQ5aaRvLf8W1TGutZi +OMCyPgIDD0JAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDRRiANcE00LJIu +GMt9sio/BIIE0G3qanDXKcg6OTWjAWDN1E4NFv+D/jl/UVhEJF+7Cc9ru9EfpLlw +wX24MZt1dDlahH6FHFgSrIdTjGtWQgNVFR3QvjcopbcCQiXzIZIVBEKKn7KISzxj +dIvGSvw8BPyKHscDXlSCbiIEfxzXNi9BtOQCeetFF+FhmbEPrfRLfo+c/R+BmPPD +h7an/zR1DT1Ri+dsQBIPvZOUGBoSabbZiCDdNxzoK8snElO9T+w8r1jZH07K/Udn +aKqu1rRSpEYrOrG4pZE/qdkcyNU5jEX42JRbfz1Sp+KhNs/rxQwYWXlh6roa84Es +e+aOwteUB+AR6W0zXq0qkemYbWtCAcVVW071R7YFN81Z+pyjubrO/i0Xd6uoIYOg +D1ZfYHFT/7Jh4Zti4rqIR+8L8JljPyU39fXsbmJ6Ge+UEX+ZGGOYI3/lM+DvCn5H +67l5b36Adoqz4hfA0OXf1hpyH9cVjutFDcV8k7PLHywgIiBdhtlKVSnf57DoSuGz +2ca9nx3jiEBk2gcFvg3SPeyxsP0tcJp755pGK3D/NGzuWOIRjmSSfqDgucmNbFwW +ybzMzpvkwxPfmrMa+Wawrr2/AqDEmTigpVUzBogfh0rNEe0erULrQnuHpj1TP4Mk +OOKg8Sj1lI6+Q+Ank2SklObm4tFkO7gnsDQpGF4bHunsH9skdGD2DmSMqvgZsgmp +wt0x+30FeO1b/WcaSTb2qFst/Yf3bbR282gftoqN88l1P2wY6FKRWPysppHwmZhg +1YrFUaO4KXYyNQC//Wv2B77DWDGKl0r+6cKht96KSPUnGMq2/hmt4wVwzQl/kYDR +qPtr482CHgIuJfgWwFpsyyxFfVRKgOkHbPoX5KgJ/uCyZt8sGZB/kw+M3wvDKbrm +hE4rWDHicr0iSm8wXRh3MrYqkSdFfxS3+2euo0Yl5Uj9eS/2XPp3lVaqBnIydDxy +xlG1NqBW/pK77JVyG190B/WfcXUvaewxK9/IQLC0Sh/jKm7hrLey2BS7RojmoP6p +zrAwOKVR/ipGMrluuojh6Oq1HYF+Kwc/7ULWY96sSPGKVF05kPVszvC2b9oiqbBs +kjO6lARtC4MXwJqbCScm+5miZLTVO5sp8pHZeVg+4N9RnYVCWRkPSRhN2kSzGVh7 +ISgwjpP+WN0RbNuU4bQFKGRP5i6mq0sPkL7Zg6KO4L3eSWYpaHicw5ZTV/WzDX5D +kFhxI5ZCnc/gffNLmJal1VL/sRsnmMATE4lQEEOjTW12x/Gifq1f7Fp39IEt2mTu ++D5jrkdlcSnAlqo9exPl0TVDfL4Z3ZGFlf/o7jZaGqIzKjBYGLsZdjgkRQ++mTik +27DZ5oYknFtzSlddb26Irg8DE3u0C/5k1PnLw25F+SAxMxcWIsaMiP35O09g+/3X +OVOmXmxwaV2dLdvQlSj2KNx7NSWkAzVCYSgeFlfxCgaQVrYh1dB1x2zYelKVp+ao +yr6VfRCiX+mVgcSFHPRUHxndwnco8OIWhS5LOb7tttI1LU62otliQ9x0JECqYGdI +WHXFC9zWrdDaKpBzwedYpDZM1CalXKL9ruVo2Gw6ni8pf5beCeFN+V6kDuFDqrAZ +6OjRoLqpkbbB+dJbPL8PfCh2f0XCzlYjemnzmPh22DA14t1IfnBRNDD9 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/generate-self-signed-cert.sh b/src/test/java/com/influxdb/v3/client/testdata/certificates/generate-self-signed-cert.sh new file mode 100644 index 00000000..4ccdde58 --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/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/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.crt b/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.crt new file mode 100644 index 00000000..fc6c1ffa --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUCuI5OLr6MTBM04hUhk/cD7FaaFgwDQYJKoZIhvcNAQEL +BQAwNzESMBAGA1UEAwwJbG9jYWxob3N0MRQwEgYDVQQKDAtEZXZlbG9wbWVudDEL +MAkGA1UEBhMCVVMwHhcNMjYxMDI5MTAzNzA5WhcNMzYxMDI2MTAzNzA5WjA3MRIw +EAYDVQQDDAlsb2NhbGhvc3QxFDASBgNVBAoMC0RldmVsb3BtZW50MQswCQYDVQQG +EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKeu7sXw4Hmim6m +tUQCbHLbuovpxtrJPtJq7tvwR38zXeD1v1ht1pzknByT/GoUBAasUUdFuNnLdk8b +xNhcSxShSbrtvypu00Ar+vJWavXOfJ6Jbqd3N8CwhIxhivE3qy3vyHXRm/6CrBe/ +nq2H/6LN7tC04n83FM6guEFJPJ8RtJCMTV4rngsJWha5+8JJjEAOwjeKmq+kOD8v +g0B8FBUPgB7f5ewJIhny7X5/WxjJ10/mm5BTPWSKPz5BDE5U+9h4ajeAV02SJD2b +ZnxOkQlusn6l22hVGwgGXmfu15kw9CHEHdy+skYyrciCUXhHcbb3P/kNvi+6bdwa +g2jHcTUCAwEAAaNvMG0wHQYDVR0OBBYEFNhopvlaL0fs7z6yHuEBVn4reBGmMB8G +A1UdIwQYMBaAFNhopvlaL0fs7z6yHuEBVn4reBGmMA8GA1UdEwEB/wQFMAMBAf8w +GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQAF ++y0SHe3W4hbhxnBvty33LlVrveFVHEAw4+BEi2r9PONNtxnz1dWExNhH5ZgGCIVz +sQQ8IDosbMdR1X8UwQpdlQtJyzq6olFao8gWGAngD+jaXEc8WbZUW5ilmUDrKf10 +lfiYdOU+Idsrrill0KCLgdijtCWOftCNV2SPPGn7ReGDUHu1I8MiTeX1GKkMd5b3 +kPhtJLjxgXUxVrXOML88qqaBh5G8sx1oJivkbsyPpBn6coxQ5tMB5LqSVfJOu9yc +Ajm39LcONlegWGd+qhvNJ50KjIeAtkM4wqls7ErKFSuUxeO5qNsCCLprfx2AISFK +OrUX7hMEWppILwdNh20u +-----END CERTIFICATE----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.key b/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.key new file mode 100644 index 00000000..9587d7c9 --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCSnru7F8OB5opu +prVEAmxy27qL6cbayT7Sau7b8Ed/M13g9b9Ybdac5Jwck/xqFAQGrFFHRbjZy3ZP +G8TYXEsUoUm67b8qbtNAK/ryVmr1znyeiW6ndzfAsISMYYrxN6st78h10Zv+gqwX +v56th/+ize7QtOJ/NxTOoLhBSTyfEbSQjE1eK54LCVoWufvCSYxADsI3ipqvpDg/ +L4NAfBQVD4Ae3+XsCSIZ8u1+f1sYyddP5puQUz1kij8+QQxOVPvYeGo3gFdNkiQ9 +m2Z8TpEJbrJ+pdtoVRsIBl5n7teZMPQhxB3cvrJGMq3IglF4R3G29z/5Db4vum3c +GoNox3E1AgMBAAECggEAChhs47J8rzaRBJJs+7xQ/E2d2CFmJE1kCnf7b2Bpg9wl +FCbHoI6bjtz81MjpweIT7uOxiv9FfZWRVVwbnywnrUl3uqvBFGpiGFKO6l3B6p/n +m8DpwhUm3RkqX0IeH4LOQhD3FAm1PH6B2/bBNcCd/PdCANPbnpfL0nhlh3VD+83R +FI0xjAtQ1IQa6kfNhRQTfzYPhTzamor1W/Uq/vhvCJp5DF9z5bJfoDMsEnNSeKlI +xuc+1b+lyKja+Qivnfv0mRwUxEygNLAOmtyBjvsvzekSMVEQm6QGT+n1AldeGkmP +AlkSa7J4r7z8cJLCrX+d3Y+iJ/o/uwiA4vD0LHlzwQKBgQDKoscnT19EOQnpgFI8 +ARXmrNdmjgKc54nyfJ9xlViogcxfre0S93gwQYjG+wHwyD1Im/FrKiCWp0P52r38 +DDdl+WDYITlJ5QAg36LADlUeRJSElfPoftyidRdGJkISVvLfuWgUBAa2GDM3TucE +Himcdzm0UDi0qPb6KZJU8r1qYQKBgQC5O4E8Vvfe8ykyq/qqg/u0l7HbLWHDcHpE +db8+ZK6cOGDYZrVeZHQM2GXSzLUogdzHbHJaaLkwEYM4qKiN6I+jmMNx9J0VyXPY +B2vgPVA7I24vebbNvcerqfre8OWBSS7Unlz1lAXww8mAUIWWk9LtUkNkBsGYTZfq +TBzW3z1/VQKBgQC1exnQTBbvXAJTnTiJQxOhV/VGhTMz4W67vP9z06TmrfEjU/r6 +vKZo2ZitVQsE4eF5ESkjqEQtiTqpIE582VgYPUON7huH4c++oWv6hBuZkyRMppwf +AteJrEKe8g7ErBizZDMhkgWMupnMFB90oar01hbZNevDaXxgyjCjYg9goQKBgAbr +QhiO5xNc4d0Sr3pPE8fbIrmdKFm0idykyU/7UspjELsEmyG7fto7+xG0s8+fii+o +zVyzLLeu78feBO3IEhV6T1KiYHu/2qPnIxBtOeH7oNIFKd7lh9/RqZ1KQkhiA23N +vIsvzEkCBzTTzSmiu+5BdGL4HaMiwtntneYo0JNBAoGAes4n3XYApugRulCUtR3a +59aoNFSWEA5G2M6iOUOWLdYg4ARVIeIz1tSPkZnNBdANXkY7qXpTi+QKyblc4ac9 +6TWH7FFE+ipPZ8Xjm1txvKNdWnsXAdMMdacbtPYWm9b/93jixq1pOfHQFuPJpqNH +mZoE6SUmooHxrHu5yctgGVA= +-----END PRIVATE KEY----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.p12 b/src/test/java/com/influxdb/v3/client/testdata/certificates/influxdb.p12 new file mode 100644 index 0000000000000000000000000000000000000000..47d8f665ff8d01e4c50386ea9760b3c6dc0eb7d3 GIT binary patch literal 2714 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 literal 0 HcmV?d00001 diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.crt b/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.crt new file mode 100644 index 00000000..d4b8eda3 --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUGU4zBhzMFJ1hIMTiqPg7hHnlTd0wDQYJKoZIhvcNAQEL +BQAwNzESMBAGA1UEAwwJbG9jYWxob3N0MRQwEgYDVQQKDAtEZXZlbG9wbWVudDEL +MAkGA1UEBhMCVVMwHhcNMjYxMDI5MTAzNzA5WhcNMzYxMDI2MTAzNzA5WjA3MRIw +EAYDVQQDDAlsb2NhbGhvc3QxFDASBgNVBAoMC0RldmVsb3BtZW50MQswCQYDVQQG +EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALV80uIg8NVtlw8O +2CQjLS8qOOUBWqMF0AaXPyElrPbGjE6vKz8ScbTePGj5YIl+KMclAuQAGMBxrZ+W +Bv2xOKua927mlOUFaExTcTT3Ya0oGJaUzOPGiV/Zoiz3+veEb9m26H7ngvdDmHjH +3P3qpsWbqoo8JWF3FT4JZ9RendMNuPblnxpgGgirLPccj61aDO/2d/xX9PirlIfS +Hv3W8bJO3bvCVqO4QQ41z9DtR5NFwnSe1QqoMw9kCpBTqZUiTLUjzWpyE5HTmvpm +ImK8uBsw2TLO2PE+yRR4oxS4CTJ/e4vcM9JJInqX2xl0fgsFxIgf2WBku0sffvOm +s8zfBvcCAwEAAaNvMG0wHQYDVR0OBBYEFBkdD0m4zH0bYVFxW7arArtaYyboMB8G +A1UdIwQYMBaAFBkdD0m4zH0bYVFxW7arArtaYyboMA8GA1UdEwEB/wQFMAMBAf8w +GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQA2 +HXDimpOM75yYQ40Nhw/WFGZPshckouAYzDgwgOp9arrGs8x1X2swiHLbO6QD0oXu +UJsI/iEccha101glK+2d16U8hX0jmj4oLEBRt5URy0rpTXtl5QGmkn8/tf5Mxb7N +w8DZeo1xacXRboTQgPSw9dKRy6Tw/HqJSY5cVfNU80gPPUoQoqgifJxqxYDumeMa +vtH8Z7yqk1AXl3WUlG8hKk5KvsRN1TzvMWjqDzWcSElGNAz9ctAHTYjGyFWFIcaS +Vueom0wSYkkogKDOqo+evx1DPAriksmzoJQ3doXIIR65HvyvpGI9+0YCNUd87PjD +Dn+I1GLBJE1JF5X67ymO +-----END CERTIFICATE----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.key b/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.key new file mode 100644 index 00000000..42189430 --- /dev/null +++ b/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC1fNLiIPDVbZcP +DtgkIy0vKjjlAVqjBdAGlz8hJaz2xoxOrys/EnG03jxo+WCJfijHJQLkABjAca2f +lgb9sTirmvdu5pTlBWhMU3E092GtKBiWlMzjxolf2aIs9/r3hG/Ztuh+54L3Q5h4 +x9z96qbFm6qKPCVhdxU+CWfUXp3TDbj25Z8aYBoIqyz3HI+tWgzv9nf8V/T4q5SH +0h791vGyTt27wlajuEEONc/Q7UeTRcJ0ntUKqDMPZAqQU6mVIky1I81qchOR05r6 +ZiJivLgbMNkyztjxPskUeKMUuAkyf3uL3DPSSSJ6l9sZdH4LBcSIH9lgZLtLH37z +prPM3wb3AgMBAAECggEAAYJXLeqIAeIRCwD6kCMqWHR9yxdOwhI2zXWgte1rPgQW +csuWCJNmQR95Ku0ZRnC/5B/Gw75lqX5ogG4wHFxo79yjmAYwXaQaK476NGGmGupo +Erx7BsAflup8vmG3x5FUU9eBKX5Seeh+WrfT0AxRo5Fbk8iglkf+liAtB6/1EDcs +16gHSVnog0EC9+UKJ3RZfrao29T3SRQ9OIQvmF7qSLd/q/iQinMHNKOV6lWz5Hpg +wzaR7YnFh8v2jkArb0UrqfQbbMRrwZ6StFRz76e4YdfIelzd4c48Y5lHypAZ9JBH +I9mmtNALq2xTyYGvF14L+gT+ED08og0zzm0Eb+YVpQKBgQDgMvUFrYTlq9yFAf2Z +jR755Z4fkGn1G8LRxBuuo41kC7ocILgB4RiGDJAF8/xLKMYGKO0EnqQynNmFF+oU +pRYbensQIcslX8NqIpo3hixX2UQyt5q7yDuJjdyo8KaYa0XOz+a5ZPTJzOsP/yzJ +6wm6RDbwiDO7cJzVcnHKM989LQKBgQDPOvEsl8dNz0JUir/UPmchMoPDf4G+k83U +DoKKldTfQ2CScOLpynO8WlNWP0SW3UYL4EcpK5DROIOPYC1JuiIVHGOqGTuAnUtO +T46JXkiMfon2ATAFi2ro2w3RmLIadDhLi2/+pfkY5oxEFF/xo3dgbxQ6lRhFqhPt +/0cD8XmTMwKBgHIffjn1qcB/SzEpYj3XZ/0stOC8UjNCfljoym6LMl3K2mGXBhNu +0Vzq6CUfnT1k8+gOzRec6VgaH19wXOdFYYt7PUlo6k+L/5z3IClDUy8kmWJ3s3wC +4MuUOs6EMUlE0bQEgB2/FhvjA4SRD+t4iOsX7h9iNXMkT7EXIHUdEnXBAoGAW96n +mLtY8hfWMiCo1nEryz3XyE24uUJRV5dvK/0suCXopENDdZ35HSF2HKBDyhYivZUa +NxhHRVmmiWN78FmCTpWdYYV6KZSSFA+VAzU3OIHIq7TvEyYxqTWjtK3bF3s6kdMp +PV1bRzy+JlhaCIgNnkTgVPBQKGI0H6iyVP8yhJ0CgYBHHGIdVxXHdlzpZbovZTh3 +wbsAWum6OiQZgAHlROE5EPQ9TN5Ee/+5MqGDhgOLuhQoD6PENwbExY9v/Pr13TbA +SHasvCnarv42WZGIDOKqtYWw4HRQ5MS4J01SSwVo59HGL8ZlmqxSHOm83KFknQ82 +L56ZvWE6jVRSXuRXrJHBVg== +-----END PRIVATE KEY----- diff --git a/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.p12 b/src/test/java/com/influxdb/v3/client/testdata/certificates/other-server.p12 new file mode 100644 index 0000000000000000000000000000000000000000..4b4985a2da71fc84b53803fe3821d5582af10be3 GIT binary patch literal 2714 zcmai$X*d*$7shAC%rIkj4N=CBjJ;9GG8p?_$&w}eR-#bGzE0N45;vKwg9sD4*S=+m zNwOx(y%`iUh)iJ&ioc%!eQqD`hjY&JzVCl1b7IH;^4z zOn|F^32@~jTMGn zHDjY>7a78wRq9}cl)cyH0oG2!`*&D4dXV$exBUX`^7nHtB3imu0tRY6A!pL?Ymv|5 zEQ%-c|FrZ`b&9D#){S)YhU|f-YP9rv?!C5L>EU~mOX+G$?`L~)(NzZV6e5!a59#jc zRhkDS(Dm{1pJGi?7cwXNlprP@lLZ_HuWutOLS#=BaUdd>-%vH;GfG4&^aKV|Qq(Fh zMc}fj)=^F6@-9GMWl_3}59?A$cZ|=D676wgX0(Hdp1N$vn^rbfBHri+z3-Ot@|Ucv z21SPJmW>T@!IEp8sA#Tl_elL^s0pg0IBI;L_c*d)M&Y{1P*6B_dCm~N1~;cOdkvS= z1Z_V8Po9guF|b0!*8FLI;FqBhYNddq49W0qKk83NtsqHsNs)rZQhL;v8 zfR{vEy@*6{5kv@aSQraQi`Kzv@3}Z}#YlWJvqU3F23uDKmSrr&w@?d z=3DH+ct@EShDHmv7M|D6!T**zgbeHl!-XSq73};q?>FfF{n)_j>$8@oOSw~89x!2W zOaZ)6mGNwe`q4Hs+_y4}T-Ga$7-=9Oae1o&ng=OoQU>9YUd)Z@V#(I8%ClAuhO0I_ zX;d*gf3$<6uH5gCX^oWYI>w)czGS)ire!q-zZIxAxEpW$LFOvX=ONF9TV+CYjp@Fd z$(10TdYz5woVB|Wa-J)p@3K=Vx>NqZTqbc`3H-aoaAb!aD}fY0v(=ZZ;^P0qyKPx zY7*gHYGl(m?=Fdhc{Z;+pf$TNoHe2Cbfk88;HEUa-2D?Srn1q+pe$6&{x3S5NgA+C zsT_1NlYG{`-wB42ns`E5oEX(&5*Wv>$LsOR4>_tU2c`)J=c;iVY1>p6IFIwW0S7qu z4979<4(+k(Qi_Y#o9kuX)_t)agZ)1+1W5J29Qg?*NIsYV$vv`vKblM^=ReHif&)QE zq56@j@?RiI5hbo)&=>1Lc{ra_M@`!QCy3b0V!fvYug|G9W<(_@M@E7P;L!lP0xdDU zt)dXC{J)Sw_n`ZMCwfY!lJw6%Sf03|q$Dk2-jZ>;*# zO3!s%?PL|{)FQ`G4?!^Vvs1StJ*e?o@!_S<*^AIax-IcZ00*xsPnd!$gti zq1PILSHPa-J3AAy;{9q~%h|WIEKhK%q78SIl^%jfj%bfKzmn~QgMP%rI^5+F&-RJ! z^yF7fr^ihabDeFiS=WR}uzXXS?W?73l#o^X`zbDm*FpHR4SFtx=DWYCT>Y_Ux|H$k z>C`wuZ8>doG#|7O_)gVw_LSy}1gU77w39}0B_)6Vp; z(0Sj@Pg_R>Q7CjkDD~s&Tgirx0{K?W8`loaO7aY&a_*4(9sSAHX2FZK&>sWBbmlPRz9Ga#Z9kJN6$ND2`;6}!X6UHy(yz}U#%=j(eAi*SPw+~dzP3po z$!mt$(|b=;1oB2S4v~S2D5w$lq0t%DrD{&x@w=U};<8;SqiE;5A}iKd8Tr7kWF@W6 z#vqB5Yqj}?#<}-W;?lNuTRq?MKH9tS^&(oQd#?CqZRWtr>ldUOYrK!^78_>bwrSwa zJ=f;(y3tlKYW*K=g3)|w*tr1Ig9DP(X$k|9e-`W0>};Q-9l0dMqVQWQFj&E>I^`!< zB=pPK`JEF-OOp#*D?Nb1>CT1DBK~gaRDENCWNKEh3)fyOuU@KL30=FZe+UO8nkjFJZ(wH%})=<2ct9gKEbXUIjm< z58n+cx)mVZ;;(RuIFwlIo)a{;=-1RDr}DbEAlLM?+g%)@r()dX*|GMF{u9Zg;}YuY zot&vlo142!T*-{tjoE;j7lh`$4}tcfYV^d77HcdXWb%E^hVT>t88N>Z=ivs~605B?rfA5FtA3VnC wr`}t@U6j~Mm@)?yf8jfoXIf_l`RN!iU!Ro*u}Z{hj4G Date: Fri, 30 Oct 2026 23:38:39 +0700 Subject: [PATCH 2/3] feat: implement tls/mtls --- src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java | 2 +- .../java/com/influxdb/v3/client/config/ClientConfigTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java b/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java index bc2c97b1..d239abd3 100644 --- a/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java +++ b/src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java @@ -325,7 +325,7 @@ public FlightServer createQuerySecureServer(@Nonnull final String serverCertFile } public ClientConfig.Builder getSecureClientConfigBuilder( - @Nonnull String host, + @Nonnull final String host, @Nonnull final String trustFilePath, @Nullable final char[] trustFilePathPassword, @Nullable final String clientCertFilePath, diff --git a/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java b/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java index fb668b66..b320cf00 100644 --- a/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java +++ b/src/test/java/com/influxdb/v3/client/config/ClientConfigTest.java @@ -463,7 +463,8 @@ void setBothCertFilePathAndP12FilePathTest() { Throwable thrown = catchThrowable(() -> InfluxDBClient.getInstance(config)); Assertions.assertThat(thrown).isNotNull(); Assertions.assertThat(thrown).isInstanceOf(IllegalArgumentException.class); - Assertions.assertThat(thrown.getMessage()).isEqualTo("Cannot set both certificateFilePath and certificateP12FilePath"); + Assertions.assertThat(thrown.getMessage()).isEqualTo("Cannot set both certificateFilePath " + + "and certificateP12FilePath"); } From 3919048eaad36ca8c0d577525c6a7378f394f010 Mon Sep 17 00:00:00 2001 From: NguyenHoangSon96 Date: Fri, 30 Oct 2026 23:41:28 +0700 Subject: [PATCH 3/3] feat: implement tls/mtls --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 959ac077..c1b11aba 100644 --- a/pom.xml +++ b/pom.xml @@ -540,9 +540,6 @@ org.apache.maven.plugins maven-surefire-plugin ${plugin.surefire.version} - - @{argLine} --add-opens=java.base/java.nio=ALL-UNNAMED - org.junit.vintage @@ -556,9 +553,6 @@ org.apache.maven.plugins maven-failsafe-plugin ${plugin.surefire.version} - - @{argLine} --add-opens=java.base/java.nio=ALL-UNNAMED -