SpringBoot_minio sdk uses self-signed https certificate error handling

minio sdk uses self-signed https certificate error handling

  • 1.Problem description
    • 1.1 Error log
    • 1.2 maven dependency configuration
    • 1.3 Current spring MinioClient configuration
  • 2.Problem analysis
  • 3.Problem solving
    • 3.1 Use trusted certificates
    • 3.2 Ignore certificate verification
      • 3.2.1 minio client
      • 3.2.2 minio sdk ignores certificate verification
        • 3.2.2.1 Extension: Supplement minioclient request log
  • 4. Problem summary
  • 5. Appendix

1.Problem description

minio 8.4.4 API connection using self-signed https will report a certificate error

1.1 Error log

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

1.2 maven dependency configuration

 <!--minio java sdk-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.4.4</version>
            <exclusions>
                <exclusion>
                    <groupId>com.squareup.okhttp3</groupId>
                    <artifactId>okhttp</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.10.0</version>
        </dependency>

1.3 Current spring MinioClient configuration

@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {<!-- -->
    @Bean
    public MinioClient minioClient(MinioProperties properties){<!-- -->
        properties.check();
        return MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();

    }
}

2. Problem analysis

Usually this is because MinIO verifies the server’s TLS certificate by default. In a production environment, using self-signed certificates is not recommended as they are not signed by a trusted Certificate Authority (CA) and may cause security issues.

3. Problem solving

3.1 Use trusted certificates

To ensure security in a production environment, it is recommended to obtain a trusted SSL certificate, which can be purchased from a certificate authority (CA) or obtained using a free certificate authority such as Let’s Encrypt.

3.2 Ignore certificate verification

3.2.1 minio client

In MinIO clients (such as the mc command line tool), the –insecure option can be used to ignore certificate verification. For example:

mc --insecure <command>

This tells the MinIO client not to verify the server’s TLS certificate. Please note that this approach reduces security and is not recommended for use in production environments.

3.2.2 minio sdk ignores certificate verification

When using the Java SDK to communicate with a server with a self-signed certificate, you can generally ignore certificate verification by customizing the SSLContext.

MinIO’s Java SDK (version 8.0.6 and above) allows customizing OkHttpClient. We can use the httpClient method to pass a custom OkHttpClient instance. for compatibility between HTTP, normal HTTPS and self-signed HTTPS

Here’s how to use a custom OkHttpClient to achieve compatibility with HTTP, normal HTTPS and self-signed HTTPS

@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {<!-- -->
    private static final Logger LOGGER = LoggerFactory.getLogger(MinioConfig.class);

    @Bean
    public MinioClient minioClient(MinioProperties properties){<!-- -->
        properties.check();

        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] {<!-- -->
                new X509TrustManager() {<!-- -->
                    public X509Certificate[] getAcceptedIssuers() {<!-- -->
                        return new X509Certificate[0];
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {<!-- -->
                        // Do nothing (trust any client certificate)
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {<!-- -->
                        // Do nothing (trust any server certificate)
                    }
                }
        };

        //Install the all-trusting trust manager
        SSLContext sslContext = null;
        try {<!-- -->
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        } catch (Exception e) {<!-- -->
            LOGGER.error("Install the all-trusting trust manager error:{}", e.getMessage());
        }


        // Create a custom OkHttpClient that trusts all certificates
        OkHttpClient customHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                .hostnameVerifier((hostname, session) -> true)
                .build();

        // Set the custom SSLContext for MinioClient
        return MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .httpClient(customHttpClient)
                .build();

    }


}

  1. In the above code, we create an SSLContext in which the X509TrustManager will trust all certificates regardless of whether they are signed by a trusted Certificate Authority (CA).
  2. Created a custom OkHttpClient which trusts all certificates. Then, we use the MinioClient.builder() method to pass the custom OkHttpClient to the httpClient() method

This method treats all certificates as trusted, so only use this method in non-production environments to ensure the security and integrity of communications. In a production environment, it is recommended to use a trusted SSL certificate.

3.2.2.1 Extension: Supplement minioclient request log

Previously, minioclient used the default httpclient client to interact with the server, and the request did not print detailed logs. Since you have customized your own httpclient above, you can add a custom interceptor to print the log.

public class CustomLoggingInterceptor implements Interceptor {<!-- -->
    @Override
    public Response intercept(Chain chain) throws IOException {<!-- -->
        Request request = chain.request();
        
        long startTime = System.nanoTime();
        System.out.println("Sending request " + request.url() + " on " + chain.connection() + "\\
" + request.headers());

        Response response = chain.proceed(request);
        
        long endTime = System.nanoTime();
        System.out.println("Received response for " + response.request().url() + " in " + ((endTime - startTime) / 1e6) + "ms\\
" + response .headers());

        MediaType contentType = response.body().contentType();
        String content = response.body().string();
        System.out.println("Response body:\\
" + content);

        ResponseBody wrappedBody = ResponseBody.create(contentType, content);
        return response.newBuilder().body(wrappedBody).build();
    }
}

Modify MinioConfig to add okhttp interceptor

 // Create a custom OkHttpClient that trusts all certificates
        OkHttpClient customHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                .hostnameVerifier((hostname, session) -> true)
                .addInterceptor(new CustomLoggingInterceptor()) // Add custom interceptor here
                .build();

Effect

4. Problem summary

The minio client essentially uses httpclient to interact with the server, so the certificate issue processing is actually just the compatibility processing of the certificate by httpclient. This processing method can be applied to other scenarios where httpclient is used.

5.Appendix

Code optimization

@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {<!-- -->
    private static final Logger LOGGER = LoggerFactory.getLogger(MinioConfig.class);

    @Bean
    public MinioClient minioClient(MinioProperties properties){<!-- -->
        properties.check();

        OkHttpClient customHttpClient = null;
        if (properties.getEndpoint().startsWith("https://")) {<!-- -->
            // If it is HTTPS, use custom OkHttpClient to handle self-signed HTTPS requests
            customHttpClient = createCustomOkHttpClient();
        }

        MinioClient minioClient;
        if (customHttpClient != null) {<!-- -->
            // If a custom OkHttpClient is used
            minioClient = MinioClient.builder()
                    .endpoint(properties.getEndpoint())
                    .credentials(properties.getAccessKey(), properties.getSecretKey())
                    .httpClient(customHttpClient)
                    .build();

        } else {<!-- -->
            // If it is normal HTTP, use the default OkHttpClient
            minioClient = MinioClient.builder()
                    .endpoint(properties.getEndpoint())
                    .credentials(properties.getAccessKey(), properties.getSecretKey())
                    .build();

        }
        return minioClient;
    }

    /**
     * Set the custom SSLContext for MinioClient
     * @return
     */
    private static OkHttpClient createCustomOkHttpClient() {<!-- -->
        //Create a custom OkHttpClient to handle self-signed HTTPS requests

        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] {<!-- -->
                new X509TrustManager() {<!-- -->
                    public X509Certificate[] getAcceptedIssuers() {<!-- -->
                        return new X509Certificate[0];
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {<!-- -->
                        // Do nothing (trust any client certificate)
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {<!-- -->
                        // Do nothing (trust any server certificate)
                    }
                }
        };

        //Install the all-trusting trust manager
        SSLContext sslContext = null;
        try {<!-- -->
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        } catch (Exception e) {<!-- -->
            LOGGER.error("Install the all-trusting trust manager error:{}", e.getMessage());
        }


        // Create a custom OkHttpClient that trusts all certificates
        OkHttpClient customHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                .hostnameVerifier((hostname, session) -> true)
                // Add minio http request log printing
                //.addInterceptor(new CustomLoggingInterceptor()) // Add custom interceptor here
                .build();
        return customHttpClient;
    }


}