From 4c4783fe46a1c76627897240066484104821a89c Mon Sep 17 00:00:00 2001 From: gaubansa Date: Tue, 17 Jun 2025 20:31:19 +0530 Subject: [PATCH 1/2] server cert made as optional and also server cert can be passed as cert chain --- src/main/java/Api/BatchUploadwithMTLSApi.java | 17 ++--- .../pgpBatchUpload/BatchUploadUtility.java | 39 +++++++---- .../MutualAuthUploadUtility.java | 65 ++++++++++++------- 3 files changed, 75 insertions(+), 46 deletions(-) diff --git a/src/main/java/Api/BatchUploadwithMTLSApi.java b/src/main/java/Api/BatchUploadwithMTLSApi.java index b5793379..9ccdf1cc 100644 --- a/src/main/java/Api/BatchUploadwithMTLSApi.java +++ b/src/main/java/Api/BatchUploadwithMTLSApi.java @@ -3,6 +3,7 @@ import java.io.File; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.Collection; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,9 +31,9 @@ public class BatchUploadwithMTLSApi { * @param inputFile The file to be uploaded. * @param environmentHostname The environment hostname (e.g., secure-batch-test.cybersource.com). * @param pgpEncryptionCertPath Path to the PGP encryption certificate. - * @param keystorePath Path to the JKS keystore file. + * @param keystorePath Path to the JKS keystore file containing client certificates. * @param keystorePassword Password for the keystore. - * @param truststorePath Path to the truststore file. + * @param truststorePath Path to the JKS truststore file containing trusted server certificates. Optional: Can be null if not required. * @param truststorePassword Password for the truststore. * @return ApiResponse containing the server response as a String. * @throws ApiException If an API error occurs. @@ -56,9 +57,9 @@ public ApiResponse uploadBatchAPI(File inputFile, String environmentHost * @param inputFile The file to be uploaded. * @param environmentHostname The environment hostname (e.g., api.cybersource.com). * @param pgpEncryptionCertPath Path to the PGP encryption certificate. - * @param clientCertP12FilePath Path to the PKCS#12 client certificate file. + * @param clientCertP12FilePath Path to the PKCS#12 client certificate file (.p12 or .pfx). * @param clientCertP12Password Password for the PKCS#12 client certificate. - * @param serverTrustCertPath Path to the server trust certificate. + * @param serverTrustCertPath Path to the server trust certificate(s) in PEM format. Optional: Can be null if not required. * @return ApiResponse containing the server response as a String. * @throws ApiException If an API error occurs. * @throws Exception If a general error occurs. @@ -83,20 +84,20 @@ public ApiResponse uploadBatchAPI(File inputFile, String environmentHost * @param pgpPublicKey The PGP public key for encryption. * @param clientPrivateKey The client's private key. * @param clientCert The client's X509 certificate. - * @param serverTrustCert The server's trust X509 certificate. + * @param serverTrustCerts A collection of server's trusted X509 certificates (can be a certificate chain). Optional: Can be null or empty if not required. * @return ApiResponse containing the server response as a String. * @throws ApiException If an API error occurs. * @throws Exception If a general error occurs. */ - public ApiResponse uploadBatchAPI(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , X509Certificate serverTrustCert) throws ApiException, Exception { + public ApiResponse uploadBatchAPI(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , Collection serverTrustCerts) throws ApiException, Exception { logger.info("Starting batch upload with client private key and certs for given file"); - BatchUploadUtility.validateBatchApiKeysInputs(inputFile, environmentHostname, pgpPublicKey, clientPrivateKey, clientCert, serverTrustCert); + BatchUploadUtility.validateBatchApiKeysInputs(inputFile, environmentHostname, pgpPublicKey, clientPrivateKey, clientCert, serverTrustCerts); String endpoint = "/pts/v1/transaction-batch-upload"; String endpointUrl = BatchUploadUtility.getEndpointUrl(environmentHostname, endpoint); byte[] encryptedPgpBytes = PgpEncryptionUtility.handlePGPEncrypt(inputFile, pgpPublicKey); return MutualAuthUploadUtility.handleUploadOperationUsingPrivateKeyAndCerts( encryptedPgpBytes, endpointUrl, inputFile.getName(), - clientPrivateKey, clientCert, serverTrustCert + clientPrivateKey, clientCert, serverTrustCerts ); } diff --git a/src/main/java/utilities/pgpBatchUpload/BatchUploadUtility.java b/src/main/java/utilities/pgpBatchUpload/BatchUploadUtility.java index 2c9af921..b72927e8 100644 --- a/src/main/java/utilities/pgpBatchUpload/BatchUploadUtility.java +++ b/src/main/java/utilities/pgpBatchUpload/BatchUploadUtility.java @@ -12,7 +12,10 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; +import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -33,21 +36,27 @@ public class BatchUploadUtility { private static final Logger logger = LogManager.getLogger(BatchUploadUtility.class); private static final long MAX_FILE_SIZE_BYTES = 75 * 1024 * 1024; - - /** - * Loads an X509 certificate from a PEM file. + + /** + * Loads X509 certificates from a PEM file. * * @param certFilePath The file path to the PEM certificate file. - * @return The loaded X509Certificate object. + * @return The loaded X509Certificate(s) as a Collection. * @throws CertificateException If the certificate cannot be parsed or is invalid. * @throws IOException If the file cannot be read or does not exist. */ - public static X509Certificate loadCertificateFromPemFile(String certFilePath) throws CertificateException, IOException { - try (FileInputStream inStream = new FileInputStream(certFilePath)) { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - return (X509Certificate) cf.generateCertificate(inStream); - } - } + public static Collection loadCertificatesFromPemFile(String certFilePath) throws CertificateException, IOException { + try (FileInputStream inStream = new FileInputStream(certFilePath)) { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Collection certs = cf.generateCertificates(inStream); + // Cast to X509Certificate + List x509Certs = new ArrayList<>(); + for (java.security.cert.Certificate cert : certs) { + x509Certs.add((X509Certificate) cert); + } + return x509Certs; + } + } /** * Reads a PGP public key from the specified file. @@ -114,7 +123,8 @@ public static void validateBatchApiJKSInputs(File inputFile, String environmentH } validatePathAndFile(pgpEncryptionCertPath, "PGP Encryption Cert Path"); validatePathAndFile(keystorePath, "Keystore Path"); - validatePathAndFile(truststorePath, "Truststore Path"); + if (!StringUtils.isEmpty(truststorePath)) + validatePathAndFile(truststorePath, "Truststore Path"); } /** @@ -135,7 +145,8 @@ public static void validateBatchApiP12Inputs(File inputFile, String environmentH } validatePathAndFile(pgpEncryptionCertPath, "PGP Encryption Cert Path"); validatePathAndFile(clientCertP12FilePath, "Client Cert P12 File Path"); - validatePathAndFile(serverTrustCertPath, "Server Trust Cert Path"); + if (!StringUtils.isEmpty(serverTrustCertPath)) + validatePathAndFile(serverTrustCertPath, "Server Trust Cert Path"); } /** @@ -149,7 +160,7 @@ public static void validateBatchApiP12Inputs(File inputFile, String environmentH * @param serverTrustCert The server trust X509 certificate. * @throws Exception If any validation fails. */ - public static void validateBatchApiKeysInputs(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , X509Certificate serverTrustCert) throws Exception{ + public static void validateBatchApiKeysInputs(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , Collection serverTrustCert) throws Exception{ validateInputFile(inputFile); if(StringUtils.isEmpty(environmentHostname)) { logger.error("Environment Host Name for Batch Upload API cannot be null or empty."); @@ -158,7 +169,7 @@ public static void validateBatchApiKeysInputs(File inputFile, String environment if (pgpPublicKey == null) throw new IllegalArgumentException("PGP Public Key is null"); if (clientPrivateKey == null) throw new IllegalArgumentException("Client Private Key is null"); if (clientCert == null) throw new IllegalArgumentException("Client Certificate is null"); - if (serverTrustCert == null) throw new IllegalArgumentException("Server Trust Certificate is null"); + //if (serverTrustCert == null) throw new IllegalArgumentException("Server Trust Certificate is null"); serverTrustCert is optional so can be null } /** diff --git a/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java b/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java index 2d08e817..7bbb63ef 100644 --- a/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java +++ b/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java @@ -12,6 +12,7 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Collection; import java.util.UUID; import javax.net.ssl.KeyManagerFactory; @@ -52,7 +53,7 @@ public class MutualAuthUploadUtility { * @param fileName The name of the file to be uploaded (will be suffixed with .pgp) * @param keystorePath The file path to the JKS keystore containing client certificates * @param keystorePassword The password for the JKS keystore - * @param truststorePath The file path to the JKS truststore containing trusted server certificates + * @param truststorePath (Optional) The file path to the JKS truststore containing trusted server certificates. Can be null if not required. * @param truststorePassword The password for the JKS truststore * @return ApiResponse containing the upload response details * @throws IOException If file operations or network communication fails @@ -67,7 +68,7 @@ public static ApiResponse handleUploadOperationWithJKS(byte[] encryptedP NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { KeyStore clientKeyStore = KeyStore.getInstance("JKS"); - KeyStore serverTrustStore = KeyStore.getInstance("JKS"); + KeyStore serverTrustStore = null; // Load the JKS keyStore using try-with-resources try (FileInputStream keyStoreFile = new FileInputStream(keystorePath)) { @@ -75,8 +76,11 @@ public static ApiResponse handleUploadOperationWithJKS(byte[] encryptedP } // Load the JKS trustStore using try-with-resources - try (FileInputStream trustStoreFile = new FileInputStream(truststorePath)) { - serverTrustStore.load(trustStoreFile, truststorePassword); + if(!StringUtils.isEmpty(truststorePath)) { + serverTrustStore = KeyStore.getInstance("JKS"); + try (FileInputStream trustStoreFile = new FileInputStream(truststorePath)) { + serverTrustStore.load(trustStoreFile, truststorePassword); + } } try { @@ -89,14 +93,14 @@ public static ApiResponse handleUploadOperationWithJKS(byte[] encryptedP } /** - * Handles file upload operation using P12/PFX keystore and PEM server certificate for mutual authentication. - * - * @param encryptedPgpBytes The encrypted PGP file content as byte array + * Handles file upload operation using P12/PFX keystore and PEM server certificate(s) for mutual authentication + * + * @param encryptedPgpBytes The encrypted PGP file content as a byte array * @param endpointUrl The target URL endpoint for file upload * @param fileName The name of the file to be uploaded (will be suffixed with .pgp) * @param p12FilePath The file path to the P12/PFX keystore containing client certificates * @param p12FilePassword The password for the P12/PFX keystore - * @param serverTrustCertPath The file path to the PEM file containing server trust certificate + * @param serverTrustCertPath (Optional) The file path to the PEM file containing one or more server trust certificates. Can be null if not required * @return ApiResponse containing the upload response details * @throws IOException If file operations or network communication fails * @throws KeyStoreException If keystore operations fail @@ -108,14 +112,24 @@ public static ApiResponse handleUploadOperationWithJKS(byte[] encryptedP public static ApiResponse handleUploadOperationUsingP12orPfx(byte[] encryptedPgpBytes, String endpointUrl, String fileName, String p12FilePath, char[] p12FilePassword, String serverTrustCertPath) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { - X509Certificate serverTrustCert; + Collection serverTrustCert; KeyStore clientKeyStore; + KeyStore serverTrustStore =null; - try { - serverTrustCert = BatchUploadUtility.loadCertificateFromPemFile(serverTrustCertPath); - } catch (CertificateException e) { - logger.error("Error loading certificate from PEM file", e); - throw new CertificateException("Failed to load certificate from PEM file: " + serverTrustCertPath, e); + if(!StringUtils.isEmpty(serverTrustCertPath)) { + try { + serverTrustCert = BatchUploadUtility.loadCertificatesFromPemFile(serverTrustCertPath); + } catch (CertificateException e) { + logger.error("Error loading certificate from PEM file", e); + throw new CertificateException("Failed to load certificate from PEM file: " + serverTrustCertPath, e); + } + serverTrustStore = KeyStore.getInstance("JKS"); + serverTrustStore.load(null, null); + int i = 0; + for (X509Certificate cert : serverTrustCert) { + serverTrustStore.setCertificateEntry("server-" + i, cert); + i++; + } } clientKeyStore = KeyStore.getInstance("PKCS12", new BouncyCastleProvider()); @@ -124,11 +138,7 @@ public static ApiResponse handleUploadOperationUsingP12orPfx(byte[] encr try (FileInputStream file = new FileInputStream(new File(p12FilePath))) { clientKeyStore.load(file, p12FilePassword); } - - KeyStore serverTrustStore = KeyStore.getInstance("JKS"); - serverTrustStore.load(null, null); - serverTrustStore.setCertificateEntry("server", serverTrustCert); - + try { OkHttpClient client = getOkHttpClientForMutualAuth(clientKeyStore, serverTrustStore, p12FilePassword); return uploadFile(encryptedPgpBytes, fileName, endpointUrl, client); @@ -146,7 +156,7 @@ public static ApiResponse handleUploadOperationUsingP12orPfx(byte[] encr * @param fileName The name of the file to be uploaded (will be suffixed with .pgp) * @param clientPrivateKey The client's private key for authentication * @param clientCert The client's X509 certificate - * @param serverTrustCert The server's trusted X509 certificate + * @param serverTrustCerts (Optional) A collection of server's trusted X509 certificates (can be a certificate chain). Can be null or empty if not required. * @return ApiResponse containing the upload response details * @throws KeyStoreException If keystore operations fail * @throws NoSuchAlgorithmException If required cryptographic algorithms are not available @@ -155,7 +165,7 @@ public static ApiResponse handleUploadOperationUsingP12orPfx(byte[] encr * @throws UnrecoverableKeyException If private key cannot be recovered * @throws KeyManagementException If SSL key management fails */ - public static ApiResponse handleUploadOperationUsingPrivateKeyAndCerts(byte[] encryptedPgpBytes, String endpointUrl, String fileName, PrivateKey clientPrivateKey,X509Certificate clientCert,X509Certificate serverTrustCert) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException + public static ApiResponse handleUploadOperationUsingPrivateKeyAndCerts(byte[] encryptedPgpBytes, String endpointUrl, String fileName, PrivateKey clientPrivateKey, X509Certificate clientCert, Collection serverTrustCerts) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException { // Create a KeyStore containing the client private key and certificate KeyStore clientKeyStore = KeyStore.getInstance("PKCS12"); @@ -164,9 +174,16 @@ public static ApiResponse handleUploadOperationUsingPrivateKeyAndCerts(b clientKeyStore.setKeyEntry("client", clientPrivateKey, new char[] {}, new java.security.cert.Certificate[]{clientCert}); // Create a KeyStore containing the server's trusted certificate - KeyStore serverTrustStore = KeyStore.getInstance("JKS"); - serverTrustStore.load(null, null); - serverTrustStore.setCertificateEntry("server", serverTrustCert); + KeyStore serverTrustStore =null; + if(serverTrustCerts!=null && !serverTrustCerts.isEmpty()) { + serverTrustStore = KeyStore.getInstance("JKS"); + serverTrustStore.load(null, null); + int i = 0; + for (X509Certificate cert : serverTrustCerts) { + serverTrustStore.setCertificateEntry("server-" + i, cert); + i++; + } + } try { OkHttpClient client = getOkHttpClientForMutualAuth(clientKeyStore, serverTrustStore, new char[] {}); From f2973aec029f3133ed95fe418da4ceb745c868e4 Mon Sep 17 00:00:00 2001 From: gaubansa Date: Tue, 17 Jun 2025 21:12:11 +0530 Subject: [PATCH 2/2] adding code for disable ssl verification --- .../MutualAuthUploadUtility.java | 72 ++++++++++++++----- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java b/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java index 7bbb63ef..e90bcb0b 100644 --- a/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java +++ b/src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java @@ -42,9 +42,12 @@ *

*/ public class MutualAuthUploadUtility { - + private static final Logger logger = LogManager.getLogger(MutualAuthUploadUtility.class); - + + // Global variable to control SSL verification + private static boolean disableSslVerification = false; + /** * Handles file upload operation using JKS keystore and truststore for mutual authentication. * @@ -193,6 +196,19 @@ public static ApiResponse handleUploadOperationUsingPrivateKeyAndCerts(b throw new IOException("Failed to perform upload operation with private/cert keys ", e); } } + + /** + * Sets whether SSL verification should be disabled. + * @param disable true to disable SSL verification, false to enable + * By default, SSL verification is enabled. + */ + public static void setDisableSslVerification(boolean disable) { + logger.warn("Setting disableSslVerification to: " + disable); + if (disable) { + logger.warn("SSL verification is DISABLED. This setting is NOT SAFE for production and should NOT be used in production environments!"); + } + disableSslVerification = disable; + } /** * Creates an OkHttpClient configured for mutual TLS authentication. @@ -209,22 +225,42 @@ public static ApiResponse handleUploadOperationUsingPrivateKeyAndCerts(b private static OkHttpClient getOkHttpClientForMutualAuth(KeyStore clientKeyStore, KeyStore serverTrustStore, char[] keyPassword) throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException - { - // Set up KeyManager and TrustManager - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(clientKeyStore, keyPassword); // Use the provided password - TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(serverTrustStore); - - // Create SSL context - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); - - // Build OkHttpClient with mutual TLS - return new OkHttpClient.Builder() - .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0]) - .build(); - } + { + if (disableSslVerification) { + logger.warn("SSL verification is DISABLED. This setting is NOT SAFE for production and should NOT be used in production environments!"); + // Trust all certificates + X509TrustManager trustAllManager = new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + public void checkServerTrusted(X509Certificate[] chain, String authType) {} + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + }; + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(clientKeyStore, keyPassword); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), new javax.net.ssl.TrustManager[]{trustAllManager}, new SecureRandom()); + + return new OkHttpClient.Builder() + .sslSocketFactory(sslContext.getSocketFactory(), trustAllManager) + .hostnameVerifier((hostname, session) -> true) + .build(); + } else { + // Set up KeyManager and TrustManager + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(clientKeyStore, keyPassword); // Use the provided password + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(serverTrustStore); + + // Create SSL context + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + + // Build OkHttpClient with mutual TLS + return new OkHttpClient.Builder() + .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0]) + .build(); + } + } /** * Uploads a file to the specified endpoint using the provided HTTP client.