Skip to content

Commit 4c4783f

Browse files
committed
server cert made as optional and also server cert can be passed as cert chain
1 parent d542254 commit 4c4783f

File tree

3 files changed

+75
-46
lines changed

3 files changed

+75
-46
lines changed

src/main/java/Api/BatchUploadwithMTLSApi.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.File;
44
import java.security.PrivateKey;
55
import java.security.cert.X509Certificate;
6+
import java.util.Collection;
67

78
import org.apache.logging.log4j.LogManager;
89
import org.apache.logging.log4j.Logger;
@@ -30,9 +31,9 @@ public class BatchUploadwithMTLSApi {
3031
* @param inputFile The file to be uploaded.
3132
* @param environmentHostname The environment hostname (e.g., secure-batch-test.cybersource.com).
3233
* @param pgpEncryptionCertPath Path to the PGP encryption certificate.
33-
* @param keystorePath Path to the JKS keystore file.
34+
* @param keystorePath Path to the JKS keystore file containing client certificates.
3435
* @param keystorePassword Password for the keystore.
35-
* @param truststorePath Path to the truststore file.
36+
* @param truststorePath Path to the JKS truststore file containing trusted server certificates. <b>Optional</b>: Can be <code>null</code> if not required.
3637
* @param truststorePassword Password for the truststore.
3738
* @return ApiResponse containing the server response as a String.
3839
* @throws ApiException If an API error occurs.
@@ -56,9 +57,9 @@ public ApiResponse<String> uploadBatchAPI(File inputFile, String environmentHost
5657
* @param inputFile The file to be uploaded.
5758
* @param environmentHostname The environment hostname (e.g., api.cybersource.com).
5859
* @param pgpEncryptionCertPath Path to the PGP encryption certificate.
59-
* @param clientCertP12FilePath Path to the PKCS#12 client certificate file.
60+
* @param clientCertP12FilePath Path to the PKCS#12 client certificate file (.p12 or .pfx).
6061
* @param clientCertP12Password Password for the PKCS#12 client certificate.
61-
* @param serverTrustCertPath Path to the server trust certificate.
62+
* @param serverTrustCertPath Path to the server trust certificate(s) in PEM format. <b>Optional</b>: Can be <code>null</code> if not required.
6263
* @return ApiResponse containing the server response as a String.
6364
* @throws ApiException If an API error occurs.
6465
* @throws Exception If a general error occurs.
@@ -83,20 +84,20 @@ public ApiResponse<String> uploadBatchAPI(File inputFile, String environmentHost
8384
* @param pgpPublicKey The PGP public key for encryption.
8485
* @param clientPrivateKey The client's private key.
8586
* @param clientCert The client's X509 certificate.
86-
* @param serverTrustCert The server's trust X509 certificate.
87+
* @param serverTrustCerts A collection of server's trusted X509 certificates (can be a certificate chain). <b>Optional</b>: Can be <code>null</code> or empty if not required.
8788
* @return ApiResponse containing the server response as a String.
8889
* @throws ApiException If an API error occurs.
8990
* @throws Exception If a general error occurs.
9091
*/
91-
public ApiResponse<String> uploadBatchAPI(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , X509Certificate serverTrustCert) throws ApiException, Exception {
92+
public ApiResponse<String> uploadBatchAPI(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , Collection<X509Certificate> serverTrustCerts) throws ApiException, Exception {
9293
logger.info("Starting batch upload with client private key and certs for given file");
93-
BatchUploadUtility.validateBatchApiKeysInputs(inputFile, environmentHostname, pgpPublicKey, clientPrivateKey, clientCert, serverTrustCert);
94+
BatchUploadUtility.validateBatchApiKeysInputs(inputFile, environmentHostname, pgpPublicKey, clientPrivateKey, clientCert, serverTrustCerts);
9495
String endpoint = "/pts/v1/transaction-batch-upload";
9596
String endpointUrl = BatchUploadUtility.getEndpointUrl(environmentHostname, endpoint);
9697
byte[] encryptedPgpBytes = PgpEncryptionUtility.handlePGPEncrypt(inputFile, pgpPublicKey);
9798
return MutualAuthUploadUtility.handleUploadOperationUsingPrivateKeyAndCerts(
9899
encryptedPgpBytes, endpointUrl, inputFile.getName(),
99-
clientPrivateKey, clientCert, serverTrustCert
100+
clientPrivateKey, clientCert, serverTrustCerts
100101
);
101102
}
102103

src/main/java/utilities/pgpBatchUpload/BatchUploadUtility.java

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
import java.security.cert.CertificateException;
1313
import java.security.cert.CertificateFactory;
1414
import java.security.cert.X509Certificate;
15+
import java.util.ArrayList;
16+
import java.util.Collection;
1517
import java.util.Iterator;
18+
import java.util.List;
1619

1720
import org.apache.commons.lang3.StringUtils;
1821
import org.apache.logging.log4j.LogManager;
@@ -33,21 +36,27 @@ public class BatchUploadUtility {
3336

3437
private static final Logger logger = LogManager.getLogger(BatchUploadUtility.class);
3538
private static final long MAX_FILE_SIZE_BYTES = 75 * 1024 * 1024;
36-
37-
/**
38-
* Loads an X509 certificate from a PEM file.
39+
40+
/**
41+
* Loads X509 certificates from a PEM file.
3942
*
4043
* @param certFilePath The file path to the PEM certificate file.
41-
* @return The loaded X509Certificate object.
44+
* @return The loaded X509Certificate(s) as a Collection.
4245
* @throws CertificateException If the certificate cannot be parsed or is invalid.
4346
* @throws IOException If the file cannot be read or does not exist.
4447
*/
45-
public static X509Certificate loadCertificateFromPemFile(String certFilePath) throws CertificateException, IOException {
46-
try (FileInputStream inStream = new FileInputStream(certFilePath)) {
47-
CertificateFactory cf = CertificateFactory.getInstance("X.509");
48-
return (X509Certificate) cf.generateCertificate(inStream);
49-
}
50-
}
48+
public static Collection<X509Certificate> loadCertificatesFromPemFile(String certFilePath) throws CertificateException, IOException {
49+
try (FileInputStream inStream = new FileInputStream(certFilePath)) {
50+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
51+
Collection<? extends java.security.cert.Certificate> certs = cf.generateCertificates(inStream);
52+
// Cast to X509Certificate
53+
List<X509Certificate> x509Certs = new ArrayList<>();
54+
for (java.security.cert.Certificate cert : certs) {
55+
x509Certs.add((X509Certificate) cert);
56+
}
57+
return x509Certs;
58+
}
59+
}
5160

5261
/**
5362
* Reads a PGP public key from the specified file.
@@ -114,7 +123,8 @@ public static void validateBatchApiJKSInputs(File inputFile, String environmentH
114123
}
115124
validatePathAndFile(pgpEncryptionCertPath, "PGP Encryption Cert Path");
116125
validatePathAndFile(keystorePath, "Keystore Path");
117-
validatePathAndFile(truststorePath, "Truststore Path");
126+
if (!StringUtils.isEmpty(truststorePath))
127+
validatePathAndFile(truststorePath, "Truststore Path");
118128
}
119129

120130
/**
@@ -135,7 +145,8 @@ public static void validateBatchApiP12Inputs(File inputFile, String environmentH
135145
}
136146
validatePathAndFile(pgpEncryptionCertPath, "PGP Encryption Cert Path");
137147
validatePathAndFile(clientCertP12FilePath, "Client Cert P12 File Path");
138-
validatePathAndFile(serverTrustCertPath, "Server Trust Cert Path");
148+
if (!StringUtils.isEmpty(serverTrustCertPath))
149+
validatePathAndFile(serverTrustCertPath, "Server Trust Cert Path");
139150
}
140151

141152
/**
@@ -149,7 +160,7 @@ public static void validateBatchApiP12Inputs(File inputFile, String environmentH
149160
* @param serverTrustCert The server trust X509 certificate.
150161
* @throws Exception If any validation fails.
151162
*/
152-
public static void validateBatchApiKeysInputs(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , X509Certificate serverTrustCert) throws Exception{
163+
public static void validateBatchApiKeysInputs(File inputFile, String environmentHostname, PGPPublicKey pgpPublicKey, PrivateKey clientPrivateKey, X509Certificate clientCert , Collection<X509Certificate> serverTrustCert) throws Exception{
153164
validateInputFile(inputFile);
154165
if(StringUtils.isEmpty(environmentHostname)) {
155166
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
158169
if (pgpPublicKey == null) throw new IllegalArgumentException("PGP Public Key is null");
159170
if (clientPrivateKey == null) throw new IllegalArgumentException("Client Private Key is null");
160171
if (clientCert == null) throw new IllegalArgumentException("Client Certificate is null");
161-
if (serverTrustCert == null) throw new IllegalArgumentException("Server Trust Certificate is null");
172+
//if (serverTrustCert == null) throw new IllegalArgumentException("Server Trust Certificate is null"); serverTrustCert is optional so can be null
162173
}
163174

164175
/**

src/main/java/utilities/pgpBatchUpload/MutualAuthUploadUtility.java

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.security.UnrecoverableKeyException;
1313
import java.security.cert.CertificateException;
1414
import java.security.cert.X509Certificate;
15+
import java.util.Collection;
1516
import java.util.UUID;
1617

1718
import javax.net.ssl.KeyManagerFactory;
@@ -52,7 +53,7 @@ public class MutualAuthUploadUtility {
5253
* @param fileName The name of the file to be uploaded (will be suffixed with .pgp)
5354
* @param keystorePath The file path to the JKS keystore containing client certificates
5455
* @param keystorePassword The password for the JKS keystore
55-
* @param truststorePath The file path to the JKS truststore containing trusted server certificates
56+
* @param truststorePath (Optional) The file path to the JKS truststore containing trusted server certificates. Can be null if not required.
5657
* @param truststorePassword The password for the JKS truststore
5758
* @return ApiResponse containing the upload response details
5859
* @throws IOException If file operations or network communication fails
@@ -67,16 +68,19 @@ public static ApiResponse<String> handleUploadOperationWithJKS(byte[] encryptedP
6768
NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
6869

6970
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
70-
KeyStore serverTrustStore = KeyStore.getInstance("JKS");
71+
KeyStore serverTrustStore = null;
7172

7273
// Load the JKS keyStore using try-with-resources
7374
try (FileInputStream keyStoreFile = new FileInputStream(keystorePath)) {
7475
clientKeyStore.load(keyStoreFile, keystorePassword);
7576
}
7677

7778
// Load the JKS trustStore using try-with-resources
78-
try (FileInputStream trustStoreFile = new FileInputStream(truststorePath)) {
79-
serverTrustStore.load(trustStoreFile, truststorePassword);
79+
if(!StringUtils.isEmpty(truststorePath)) {
80+
serverTrustStore = KeyStore.getInstance("JKS");
81+
try (FileInputStream trustStoreFile = new FileInputStream(truststorePath)) {
82+
serverTrustStore.load(trustStoreFile, truststorePassword);
83+
}
8084
}
8185

8286
try {
@@ -89,14 +93,14 @@ public static ApiResponse<String> handleUploadOperationWithJKS(byte[] encryptedP
8993
}
9094

9195
/**
92-
* Handles file upload operation using P12/PFX keystore and PEM server certificate for mutual authentication.
93-
*
94-
* @param encryptedPgpBytes The encrypted PGP file content as byte array
96+
* Handles file upload operation using P12/PFX keystore and PEM server certificate(s) for mutual authentication
97+
*
98+
* @param encryptedPgpBytes The encrypted PGP file content as a byte array
9599
* @param endpointUrl The target URL endpoint for file upload
96100
* @param fileName The name of the file to be uploaded (will be suffixed with .pgp)
97101
* @param p12FilePath The file path to the P12/PFX keystore containing client certificates
98102
* @param p12FilePassword The password for the P12/PFX keystore
99-
* @param serverTrustCertPath The file path to the PEM file containing server trust certificate
103+
* @param serverTrustCertPath (Optional) The file path to the PEM file containing one or more server trust certificates. Can be null if not required
100104
* @return ApiResponse containing the upload response details
101105
* @throws IOException If file operations or network communication fails
102106
* @throws KeyStoreException If keystore operations fail
@@ -108,14 +112,24 @@ public static ApiResponse<String> handleUploadOperationWithJKS(byte[] encryptedP
108112
public static ApiResponse<String> handleUploadOperationUsingP12orPfx(byte[] encryptedPgpBytes, String endpointUrl, String fileName, String p12FilePath, char[] p12FilePassword, String serverTrustCertPath) throws IOException, KeyStoreException,
109113
NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
110114

111-
X509Certificate serverTrustCert;
115+
Collection<X509Certificate> serverTrustCert;
112116
KeyStore clientKeyStore;
117+
KeyStore serverTrustStore =null;
113118

114-
try {
115-
serverTrustCert = BatchUploadUtility.loadCertificateFromPemFile(serverTrustCertPath);
116-
} catch (CertificateException e) {
117-
logger.error("Error loading certificate from PEM file", e);
118-
throw new CertificateException("Failed to load certificate from PEM file: " + serverTrustCertPath, e);
119+
if(!StringUtils.isEmpty(serverTrustCertPath)) {
120+
try {
121+
serverTrustCert = BatchUploadUtility.loadCertificatesFromPemFile(serverTrustCertPath);
122+
} catch (CertificateException e) {
123+
logger.error("Error loading certificate from PEM file", e);
124+
throw new CertificateException("Failed to load certificate from PEM file: " + serverTrustCertPath, e);
125+
}
126+
serverTrustStore = KeyStore.getInstance("JKS");
127+
serverTrustStore.load(null, null);
128+
int i = 0;
129+
for (X509Certificate cert : serverTrustCert) {
130+
serverTrustStore.setCertificateEntry("server-" + i, cert);
131+
i++;
132+
}
119133
}
120134

121135
clientKeyStore = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
@@ -124,11 +138,7 @@ public static ApiResponse<String> handleUploadOperationUsingP12orPfx(byte[] encr
124138
try (FileInputStream file = new FileInputStream(new File(p12FilePath))) {
125139
clientKeyStore.load(file, p12FilePassword);
126140
}
127-
128-
KeyStore serverTrustStore = KeyStore.getInstance("JKS");
129-
serverTrustStore.load(null, null);
130-
serverTrustStore.setCertificateEntry("server", serverTrustCert);
131-
141+
132142
try {
133143
OkHttpClient client = getOkHttpClientForMutualAuth(clientKeyStore, serverTrustStore, p12FilePassword);
134144
return uploadFile(encryptedPgpBytes, fileName, endpointUrl, client);
@@ -146,7 +156,7 @@ public static ApiResponse<String> handleUploadOperationUsingP12orPfx(byte[] encr
146156
* @param fileName The name of the file to be uploaded (will be suffixed with .pgp)
147157
* @param clientPrivateKey The client's private key for authentication
148158
* @param clientCert The client's X509 certificate
149-
* @param serverTrustCert The server's trusted X509 certificate
159+
* @param serverTrustCerts (Optional) A collection of server's trusted X509 certificates (can be a certificate chain). Can be null or empty if not required.
150160
* @return ApiResponse containing the upload response details
151161
* @throws KeyStoreException If keystore operations fail
152162
* @throws NoSuchAlgorithmException If required cryptographic algorithms are not available
@@ -155,7 +165,7 @@ public static ApiResponse<String> handleUploadOperationUsingP12orPfx(byte[] encr
155165
* @throws UnrecoverableKeyException If private key cannot be recovered
156166
* @throws KeyManagementException If SSL key management fails
157167
*/
158-
public static ApiResponse<String> handleUploadOperationUsingPrivateKeyAndCerts(byte[] encryptedPgpBytes, String endpointUrl, String fileName, PrivateKey clientPrivateKey,X509Certificate clientCert,X509Certificate serverTrustCert) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException
168+
public static ApiResponse<String> handleUploadOperationUsingPrivateKeyAndCerts(byte[] encryptedPgpBytes, String endpointUrl, String fileName, PrivateKey clientPrivateKey, X509Certificate clientCert, Collection<X509Certificate> serverTrustCerts) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException
159169
{
160170
// Create a KeyStore containing the client private key and certificate
161171
KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
@@ -164,9 +174,16 @@ public static ApiResponse<String> handleUploadOperationUsingPrivateKeyAndCerts(b
164174
clientKeyStore.setKeyEntry("client", clientPrivateKey, new char[] {}, new java.security.cert.Certificate[]{clientCert});
165175

166176
// Create a KeyStore containing the server's trusted certificate
167-
KeyStore serverTrustStore = KeyStore.getInstance("JKS");
168-
serverTrustStore.load(null, null);
169-
serverTrustStore.setCertificateEntry("server", serverTrustCert);
177+
KeyStore serverTrustStore =null;
178+
if(serverTrustCerts!=null && !serverTrustCerts.isEmpty()) {
179+
serverTrustStore = KeyStore.getInstance("JKS");
180+
serverTrustStore.load(null, null);
181+
int i = 0;
182+
for (X509Certificate cert : serverTrustCerts) {
183+
serverTrustStore.setCertificateEntry("server-" + i, cert);
184+
i++;
185+
}
186+
}
170187

171188
try {
172189
OkHttpClient client = getOkHttpClientForMutualAuth(clientKeyStore, serverTrustStore, new char[] {});

0 commit comments

Comments
 (0)