// Path: src/main/java/com/verifalia/api/rest/RestClient.java
// public class RestClient {
//     private final List<URI> baseURIs;
//     private final String apiVersion;
//     private final String userAgent;
//     private final AuthenticationProvider defaultAuthenticationProvider;
//     private int currentBaseURIIndex;
// 
//     public RestClient(@NonNull final AuthenticationProvider defaultAuthenticationProvider, @NonNull final List<URI> baseURIs, @NonNull final String apiVersion) {
//         this.baseURIs = baseURIs;
//         this.apiVersion = apiVersion;
//         this.userAgent = getUserAgent();
//         this.defaultAuthenticationProvider = defaultAuthenticationProvider;
//     }
// 
//     public RestResponse execute(@NonNull final RestRequest request) throws VerifaliaException {
//         return execute(request, this.defaultAuthenticationProvider);
//     }
// 
//     public RestResponse execute(@NonNull final RestRequest request, @NonNull final AuthenticationProvider authenticationProvider)
//             throws VerifaliaException {
//         @Getter
//         @Setter
//         class EndpointServerError {
//             URI baseUri;
//             Exception exception;
// 
//             EndpointServerError(@NonNull final URI baseUri, @NonNull final Exception exception) {
//                 this.setBaseUri(baseUri);
//                 this.setException(exception);
//             }
//         }
// 
//         ArrayList<EndpointServerError> errors = new ArrayList<>();
// 
//         if (nonNull(this.baseURIs)) {
//             for (int idxAttempt = 0; idxAttempt < this.baseURIs.size(); idxAttempt++) {
//                 CloseableHttpResponse response;
//                 URI baseURI = this.baseURIs.get(currentBaseURIIndex++ % this.baseURIs.size());
// 
//                 try {
//                     response = sendRequest(baseURI, request, authenticationProvider);
//                 } catch (IOException e) {
//                     // Continue with the next attempt on IO exceptions, if needed
//                     errors.add(new EndpointServerError(baseURI, e));
//                     continue;
//                 }
// 
//                 if (nonNull(response)) {
//                     int statusCode = response.getStatusLine().getStatusCode();
// 
//                     // Automatically retry with another host on HTTP 5xx status codes
// 
//                     if (statusCode >= 500 && statusCode <= 599) {
//                         errors.add(new EndpointServerError(baseURI, new EndpointServerErrorException(String.format("The API endpoint %s returned a server error HTTP status code %d.", baseURI, statusCode))));
//                         continue;
//                     }
// 
//                     return new RestResponse(statusCode, response.getEntity());
//                 }
//             }
//         }
// 
//         // Aggregate exception
// 
//         StringBuilder sbAggregateError = new StringBuilder("All the base URIs are unreachable: ");
//         errors.forEach(e -> sbAggregateError.append(e.baseUri).append(" => ").append(e.exception.getMessage()).append(" "));
// 
//         throw new ServiceUnreachableException(sbAggregateError.toString());
//     }
// 
//     private CloseableHttpResponse sendRequest(@NonNull final URI baseURI, @NonNull final RestRequest restRequest, @NonNull final AuthenticationProvider authenticationProviderOverride)
//             throws VerifaliaException, IOException {
// 
//         // Determine the intermediate URI, including the API version, for this invocation
// 
//         StringBuilder sbApiVersionURI = new StringBuilder();
//         sbApiVersionURI.append(baseURI.toString()).append("/").append(apiVersion).append("/");
// 
//         URI apiVersionURI;
// 
//         try {
//             apiVersionURI = new URI(sbApiVersionURI.toString());
//         } catch (URISyntaxException e) {
//             throw new IOException("Invalid URI " + sbApiVersionURI);
//         }
// 
//         // Build the HTTP client and the HTTP request out of the provided RestRequest
// 
//         CloseableHttpClient client = authenticationProviderOverride.buildClient(this);
//         HttpRequestBase request = restRequest.buildHttpRequest(apiVersionURI);
// 
//         // Common headers and authentication handling
// 
//         request.setHeader(HttpHeaders.USER_AGENT, this.userAgent);
//         authenticationProviderOverride.decorateRequest(this, request);
// 
//         return client.execute(request);
//     }
// 
//     private String getUserAgent() {
//         StringBuilder sbUserAgent = new StringBuilder("verifalia-rest-client/java");
// 
//         // Java version
// 
//         sbUserAgent.append(System.getProperty("java.version"));
// 
//         // Package version
// 
//         String packageVersion = getClass().getPackage().getImplementationVersion();
// 
//         if (packageVersion != null) {
//             sbUserAgent.append("/");
//             sbUserAgent.append(packageVersion);
//         }
// 
//         return sbUserAgent.toString();
//     }
// }


import com.verifalia.api.rest.RestClient;
import lombok.Getter;
import lombok.Setter;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import static java.util.Objects.nonNull;
        // Load identity key store
        KeyStore identityKeyStore = KeyStore.getInstance(TLS_AUTHENTICATION_JKS);
        FileInputStream identityKeyStoreFile = new FileInputStream(identityStoreJksFile);
        identityKeyStore.load(identityKeyStoreFile, certPassword.toCharArray());

        // Load trust key store
        KeyStore trustKeyStore = KeyStore.getInstance(TLS_AUTHENTICATION_JKS);
        FileInputStream trustKeyStoreFile = new FileInputStream(trustKeyStoreJksFile);
        trustKeyStore.load(trustKeyStoreFile, certPassword.toCharArray());

        // Load SSL context
        SSLContext sslContext = SSLContexts
                .custom()
                .loadKeyMaterial(identityKeyStore,
                        certPassword.toCharArray(),
                        (aliases, socket) -> certAlias)
                .loadTrustMaterial(trustKeyStore, null)
                .build();

        // Initialize socket factory
        return new SSLConnectionSocketFactory(sslContext,
                new String[]{
                        "TLSv1.1",
                        "TLSv1.2"
                },
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    }

    @Override
// Path: src/main/java/com/verifalia/api/rest/RestClient.java
// public class RestClient {
//     private final List<URI> baseURIs;
//     private final String apiVersion;
//     private final String userAgent;
//     private final AuthenticationProvider defaultAuthenticationProvider;
//     private int currentBaseURIIndex;
// 
//     public RestClient(@NonNull final AuthenticationProvider defaultAuthenticationProvider, @NonNull final List<URI> baseURIs, @NonNull final String apiVersion) {
//         this.baseURIs = baseURIs;
//         this.apiVersion = apiVersion;
//         this.userAgent = getUserAgent();
//         this.defaultAuthenticationProvider = defaultAuthenticationProvider;
//     }
// 
//     public RestResponse execute(@NonNull final RestRequest request) throws VerifaliaException {
//         return execute(request, this.defaultAuthenticationProvider);
//     }
// 
//     public RestResponse execute(@NonNull final RestRequest request, @NonNull final AuthenticationProvider authenticationProvider)
//             throws VerifaliaException {
//         @Getter
//         @Setter
//         class EndpointServerError {
//             URI baseUri;
//             Exception exception;
// 
//             EndpointServerError(@NonNull final URI baseUri, @NonNull final Exception exception) {
//                 this.setBaseUri(baseUri);
//                 this.setException(exception);
//             }
//         }
// 
//         ArrayList<EndpointServerError> errors = new ArrayList<>();
// 
//         if (nonNull(this.baseURIs)) {
//             for (int idxAttempt = 0; idxAttempt < this.baseURIs.size(); idxAttempt++) {
//                 CloseableHttpResponse response;
//                 URI baseURI = this.baseURIs.get(currentBaseURIIndex++ % this.baseURIs.size());
// 
//                 try {
//                     response = sendRequest(baseURI, request, authenticationProvider);
//                 } catch (IOException e) {
//                     // Continue with the next attempt on IO exceptions, if needed
//                     errors.add(new EndpointServerError(baseURI, e));
//                     continue;
//                 }
// 
//                 if (nonNull(response)) {
//                     int statusCode = response.getStatusLine().getStatusCode();
// 
//                     // Automatically retry with another host on HTTP 5xx status codes
// 
//                     if (statusCode >= 500 && statusCode <= 599) {
//                         errors.add(new EndpointServerError(baseURI, new EndpointServerErrorException(String.format("The API endpoint %s returned a server error HTTP status code %d.", baseURI, statusCode))));
//                         continue;
//                     }
// 
//                     return new RestResponse(statusCode, response.getEntity());
//                 }
//             }
//         }
// 
//         // Aggregate exception
// 
//         StringBuilder sbAggregateError = new StringBuilder("All the base URIs are unreachable: ");
//         errors.forEach(e -> sbAggregateError.append(e.baseUri).append(" => ").append(e.exception.getMessage()).append(" "));
// 
//         throw new ServiceUnreachableException(sbAggregateError.toString());
//     }
// 
//     private CloseableHttpResponse sendRequest(@NonNull final URI baseURI, @NonNull final RestRequest restRequest, @NonNull final AuthenticationProvider authenticationProviderOverride)
//             throws VerifaliaException, IOException {
// 
//         // Determine the intermediate URI, including the API version, for this invocation
// 
//         StringBuilder sbApiVersionURI = new StringBuilder();
//         sbApiVersionURI.append(baseURI.toString()).append("/").append(apiVersion).append("/");
// 
//         URI apiVersionURI;
// 
//         try {
//             apiVersionURI = new URI(sbApiVersionURI.toString());
//         } catch (URISyntaxException e) {
//             throw new IOException("Invalid URI " + sbApiVersionURI);
//         }
// 
//         // Build the HTTP client and the HTTP request out of the provided RestRequest
// 
//         CloseableHttpClient client = authenticationProviderOverride.buildClient(this);
//         HttpRequestBase request = restRequest.buildHttpRequest(apiVersionURI);
// 
//         // Common headers and authentication handling
// 
//         request.setHeader(HttpHeaders.USER_AGENT, this.userAgent);
//         authenticationProviderOverride.decorateRequest(this, request);
// 
//         return client.execute(request);
//     }
// 
//     private String getUserAgent() {
//         StringBuilder sbUserAgent = new StringBuilder("verifalia-rest-client/java");
// 
//         // Java version
// 
//         sbUserAgent.append(System.getProperty("java.version"));
// 
//         // Package version
// 
//         String packageVersion = getClass().getPackage().getImplementationVersion();
// 
//         if (packageVersion != null) {
//             sbUserAgent.append("/");
//             sbUserAgent.append(packageVersion);
//         }
// 
//         return sbUserAgent.toString();
//     }
// }


// Path: src/main/java/com/verifalia/api/rest/security/ClientCertificateAuthenticationProvider.java
import com.verifalia.api.rest.RestClient;
import lombok.Getter;
import lombok.Setter;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import static java.util.Objects.nonNull;

        // Load identity key store
        KeyStore identityKeyStore = KeyStore.getInstance(TLS_AUTHENTICATION_JKS);
        FileInputStream identityKeyStoreFile = new FileInputStream(identityStoreJksFile);
        identityKeyStore.load(identityKeyStoreFile, certPassword.toCharArray());

        // Load trust key store
        KeyStore trustKeyStore = KeyStore.getInstance(TLS_AUTHENTICATION_JKS);
        FileInputStream trustKeyStoreFile = new FileInputStream(trustKeyStoreJksFile);
        trustKeyStore.load(trustKeyStoreFile, certPassword.toCharArray());

        // Load SSL context
        SSLContext sslContext = SSLContexts
                .custom()
                .loadKeyMaterial(identityKeyStore,
                        certPassword.toCharArray(),
                        (aliases, socket) -> certAlias)
                .loadTrustMaterial(trustKeyStore, null)
                .build();

        // Initialize socket factory
        return new SSLConnectionSocketFactory(sslContext,
                new String[]{
                        "TLSv1.1",
                        "TLSv1.2"
                },
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    }

    @Override
    public CloseableHttpClient buildClient(RestClient client) throws IOException {