Skip to content

Commit 12dd264

Browse files
authored
Merge pull request #97 from ammajumd/future
Introduced flag certificateCacheEnabled(default true) to cache merchant certs. now it also check for if p12 file is replaced or not. If replaced then it would load the certs again and store into cache.
2 parents 1eb4459 + c16b21e commit 12dd264

File tree

24 files changed

+172
-51
lines changed

24 files changed

+172
-51
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ To install the `cybersource-sdk-java` from central repository, add dependency to
1010
<dependency>
1111
<groupId>com.cybersource</groupId>
1212
<artifactId>cybersource-sdk-java</artifactId>
13-
<version>6.2.5</version>
13+
<version>6.2.6</version>
1414
</dependency>
1515
```
1616
Run `mvn install` to install dependency
@@ -19,7 +19,7 @@ Run `mvn install` to install dependency
1919
Add the dependency to your build.gradle
2020
```java
2121
dependencies {
22-
compile 'com.cybersource:cybersource-sdk-java:6.2.5'
22+
compile 'com.cybersource:cybersource-sdk-java:6.2.6'
2323
}
2424
```
2525
## Requirements
@@ -61,6 +61,7 @@ You do not need to download and build the source to use the SDK but if you want
6161
- if `enablejdkcert` parameter is set to true, certificates will be read from the JKS file specified at keysDirectory location. The JKS file should be of the same name as specified in keyFilename.
6262
- To know how to convert p12 to JKS refer the JKS creation section of this document.
6363
- `enableCacerts` property is considered only if `enablejdkcert` is set to true. If `enableCacerts` is set to true, certificates will be read from the cacerts folder under the JDK.
64+
- if `certificateCacheEnabled` parameter is set to false (default is true), the p12 certificate of a merchant will be reloaded from filesystem every time a transaction is made
6465
- `allowRetry` config parameter will only work for HttpClient. Set `allowRetry` config parameter to "true" to enable retry mechanism and set merchant specific values for the retry.
6566
- Set integer values for config parameter `numberOfRetries` *and* `retryInterval`. Retry Interval is time delay for next retry in seconds.
6667
- Number of retry parameter should be set between 1 to 5. Any other value will throw an Error Message.
@@ -181,6 +182,12 @@ Retry Pattern allows to retry sending a failed request and it will only work wit
181182

182183
## Changes
183184

185+
Version Cybersource-sdk-java 6.2.6 (JAN,2018)
186+
_______________________________
187+
1) Added certificateCacheEnabled optional feature. certificateCacheEnabled parameter is set to false (default is true), the p12 certificate of a merchant will be reloaded from filesystem every time a transaction is made.If the certificateCacheEnabled is true then only at the first time certificate of a merchant will loaded from filesystem.
188+
2) Intreduced a new feature to check merchant .p12 certificate file validity at run time.If it is not valid and replaced at runtime then SDK will be able to reload the new certificate data into cache.
189+
3) Changed clientLibrary version to 6.2.6;
190+
184191
Version Cybersource-sdk-java 6.2.5 (OCT,2017)
185192
_______________________________
186193
1) Merchant cert to be read from JAVA key store. Flag is added to enable reading cert from Java keystore.

java/src/main/java/com/cybersource/ws/client/Client.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,13 @@ private static void setVersionInformation(Map<String, String> request) {
212212
* @throws SignException if signing fails.
213213
* @throws SAXException
214214
* @throws SignEncryptException
215+
* @throws ConfigException
215216
*/
216217
private static Document soapWrapAndSign(
217218
Map request, MerchantConfig mc, DocumentBuilder builder,
218219
LoggerWrapper logger)
219220
throws
220-
IOException, SignException, SAXException, SignEncryptException {
221+
IOException, SignException, SAXException, SignEncryptException, ConfigException {
221222
boolean logSignedData = mc.getLogSignedData();
222223
if (!logSignedData) {
223224
logger.log(

java/src/main/java/com/cybersource/ws/client/Identity.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
package com.cybersource.ws.client;
1111

12+
import java.io.File;
1213
import java.security.PrivateKey;
1314
import java.security.cert.CertificateEncodingException;
1415
import java.security.cert.X509Certificate;
@@ -35,9 +36,13 @@ public class Identity {
3536
private PrivateKey privateKey;
3637

3738
private MerchantConfig merchantConfig;
39+
40+
private long lastModifiedDate;
3841

3942
private static final String SERVER_ALIAS = "CyberSource_SJC_US";
4043

44+
private Logger logger = null;
45+
4146
/**
4247
* Creates an Identity instance.this type of the instance can
4348
* only be used to store server certificate identity.
@@ -46,9 +51,12 @@ public class Identity {
4651
* @param x509Certificate
4752
* @throws SignException
4853
*/
49-
public Identity(MerchantConfig merchantConfig,X509Certificate x509Certificate) throws SignException {
54+
public Identity(MerchantConfig merchantConfig,X509Certificate x509Certificate,Logger logger) throws SignException {
5055
this.merchantConfig = merchantConfig;
5156
this.x509Cert=x509Certificate;
57+
if(this.logger == null){
58+
this.logger=logger;
59+
}
5260
if(merchantConfig.isJdkCertEnabled()){
5361
setupJdkServerCerts();
5462
}
@@ -89,13 +97,42 @@ else if (subjectDNrray.length == 2 && subjectDNrray[1].contains(SERVER_ALIAS)) {
8997
* @param privateKey
9098
* @throws SignException
9199
*/
92-
public Identity(MerchantConfig merchantConfig,X509Certificate x509Certificate, PrivateKey privateKey) throws SignException {
100+
public Identity(MerchantConfig merchantConfig,X509Certificate x509Certificate, PrivateKey privateKey,Logger logger) throws SignException {
93101
this.merchantConfig = merchantConfig;
94102
this.x509Cert = x509Certificate;
95103
this.privateKey = privateKey;
104+
if(this.logger == null){
105+
this.logger=logger;
106+
}
107+
try {
108+
this.lastModifiedDate=merchantConfig.getKeyFile().lastModified();
109+
} catch (ConfigException e) {
110+
111+
logger.log(Logger.LT_EXCEPTION,
112+
"Identity object ,cannot instantiate with key file lastModifiedDate. "
113+
+ e.getMessage());
114+
throw new SignException("Exception While initializing the merchant identity constructor with keyfile last modified date"+e.getMessage());
115+
}
96116
setUpMerchant();
97117
}
98118

119+
/**
120+
* Replace of merchant certificate not happened at runtime then isValid method will return true and certificate reload will not happen.
121+
* But replace of merchant certificate happened at at runtime then isValid method will return false and certificate reload will happen.
122+
*/
123+
124+
public boolean isValid(File keyFile) {
125+
126+
boolean changeKeyFileStatus=(this.lastModifiedDate == keyFile.lastModified());
127+
128+
if (!changeKeyFileStatus) {
129+
130+
logger.log(Logger.LT_INFO, "Key file changed");
131+
logger.log(Logger.LT_INFO, "Timestamp of current key file:"+this.lastModifiedDate);
132+
}
133+
return changeKeyFileStatus;
134+
}
135+
99136
private void setUpMerchant() throws SignException {
100137
if (serialNumber == null && x509Cert != null) {
101138
String subjectDN = x509Cert.getSubjectDN().getName();
@@ -234,5 +271,4 @@ public String toString() {
234271
+ serialNumber + ",expiration=" + expireStr+ " }";
235272
}
236273

237-
238274
}

java/src/main/java/com/cybersource/ws/client/MerchantConfig.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public class MerchantConfig {
6666
private String cacertPassword;
6767
private String customHttpClass;
6868
private boolean customHttpClassEnabled;
69+
private boolean certificateCacheEnabled;
6970

7071
public String getcustomHttpClass() {
7172
return customHttpClass;
@@ -83,8 +84,7 @@ public boolean isCustomHttpClassEnabled() {
8384
private int numberOfRetries = 0;
8485
private long retryInterval = 0;
8586
private boolean allowRetry=true;
86-
87-
87+
8888
// getter methods
8989
public boolean getUseSignAndEncrypted() { return useSignAndEncrypted; }
9090

@@ -183,6 +183,10 @@ public String getProxyPassword() {
183183
return proxyPassword != null ? proxyPassword : "";
184184
}
185185

186+
public boolean isCertificateCacheEnabled() {
187+
return certificateCacheEnabled;
188+
}
189+
186190
/**
187191
* Returns the effective server URL to which the request will be sent.
188192
* If a serverURL is specified, then that is what is returned.
@@ -271,6 +275,7 @@ public MerchantConfig(Properties _props, String _merchantID)
271275
enableCacert=getBooleanProperty(merchantID, "enableCacert", false);
272276
cacertPassword=getProperty(merchantID,"cacertPassword","changeit");
273277
customHttpClassEnabled=getBooleanProperty(merchantID,"customHttpClassEnabled",false);
278+
certificateCacheEnabled=getBooleanProperty(merchantID,"certificateCacheEnabled",true);
274279
// compute and store effective namespace URI
275280

276281
if (namespaceURI == null && targetAPIVersion == null) {
@@ -495,6 +500,7 @@ public String getLogString() {
495500
}
496501
}
497502
appendPair(sb, "useSignAndEncrypted", useSignAndEncrypted);
503+
appendPair(sb, "certificateCacheEnabled", certificateCacheEnabled);
498504
return (sb.toString());
499505
}
500506

@@ -580,4 +586,5 @@ public boolean isJdkCertEnabled() {
580586
public String getCacertPassword(){
581587
return cacertPassword;
582588
}
589+
583590
}

java/src/main/java/com/cybersource/ws/client/SecurityUtil.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.security.UnrecoverableKeyException;
2424
import java.security.cert.CertificateException;
2525
import java.security.cert.X509Certificate;
26-
import java.util.ArrayList;
2726
import java.util.Collections;
2827
import java.util.Enumeration;
2928
import java.util.concurrent.ConcurrentHashMap;
@@ -48,7 +47,6 @@ public class SecurityUtil {
4847

4948
private static BouncyCastleProvider bcProvider = new BouncyCastleProvider();
5049

51-
5250
// This is loaded by WSS4J but since we use it lets make sure its here
5351
static {
5452
Security.addProvider(bcProvider);
@@ -69,6 +67,13 @@ private static void initKeystore() throws KeyStoreException, CredentialException
6967
/**
7068
* Method loads the Merchant P12 key.
7169
* IMPORTANT :This change is made based on the assumptions that at point of time , a merchant will have only one P12 Key
70+
*
71+
*CertificateCacheEnabled : If it is true then only first time merchant p12 file will be loaded.
72+
* If it is false then every time merchant p12 file will be loaded.
73+
*
74+
*isValid() method checks : If this method returns true that means existing certificate is valid and reload of merchant p12 file will not happen.
75+
* : If method returns false that means existing certificate is not valid and reload of new merchant p12 file will happen.
76+
*
7277
* @param merchantConfig - Merchant Config
7378
* @param logger - logger instance
7479
* @throws SignException - Signature exception
@@ -77,12 +82,10 @@ private static void initKeystore() throws KeyStoreException, CredentialException
7782
* @throws IOException
7883
* @throws CredentialException
7984
*/
80-
public static void loadMerchantP12File(MerchantConfig merchantConfig, Logger logger) throws SignException, SignEncryptException {
81-
82-
// Load the KeyStore and get the signing key and certificate do this once only
83-
// This change is made based on the assumptions that at point of time , a merchant will have only one P12 Key
84-
85-
if(identities.get(merchantConfig.getMerchantID()) == null){
85+
public static void loadMerchantP12File(MerchantConfig merchantConfig, Logger logger) throws SignException, SignEncryptException, ConfigException {
86+
87+
Identity identity=identities.get(merchantConfig.getMerchantID());
88+
if(!merchantConfig.isCertificateCacheEnabled() || identity == null || !(identity.isValid(merchantConfig.getKeyFile()))){
8689
try {
8790
if (localKeyStoreHandler == null)
8891
initKeystore();
@@ -159,12 +162,12 @@ private static void readAndStoreCertificateAndPrivateKey(MerchantConfig merchant
159162
throw new SignException(e);
160163
}
161164

162-
Identity identity = new Identity(merchantConfig,(X509Certificate) keyEntry.getCertificate(),keyEntry.getPrivateKey());
165+
Identity identity = new Identity(merchantConfig,(X509Certificate) keyEntry.getCertificate(),keyEntry.getPrivateKey(),logger);
163166
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
164167
identities.put(identity.getName(), identity);
165168
continue;
166169
}
167-
Identity identity = new Identity(merchantConfig, (X509Certificate) merchantKeyStore.getCertificate(merchantKeyAlias));
170+
Identity identity = new Identity(merchantConfig, (X509Certificate) merchantKeyStore.getCertificate(merchantKeyAlias),logger);
168171
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
169172
identities.put(identity.getName(), identity);
170173
}
@@ -307,12 +310,12 @@ public static void readJdkCert(MerchantConfig merchantConfig, Logger logger) thr
307310
throw new SignException(e);
308311
}
309312

310-
Identity identity = new Identity(merchantConfig,(X509Certificate) keyEntry.getCertificate(),keyEntry.getPrivateKey());
313+
Identity identity = new Identity(merchantConfig,(X509Certificate) keyEntry.getCertificate(),keyEntry.getPrivateKey(),logger);
311314
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
312315
identities.put(identity.getName(), identity);
313316
continue;
314317
}
315-
Identity identity = new Identity(merchantConfig, (X509Certificate) keystore.getCertificate(merchantKeyAlias));
318+
Identity identity = new Identity(merchantConfig, (X509Certificate) keystore.getCertificate(merchantKeyAlias),logger);
316319
localKeyStoreHandler.addIdentityToKeyStore(identity, logger);
317320
identities.put(identity.getName(), identity);
318321
}
@@ -353,13 +356,13 @@ private static void loadJavaKeystore(String keystore_location, MerchantConfig me
353356
if (merchantConfig.getKeyAlias().equals(
354357
keystore.getCertificateAlias(cert[i]))) {
355358
identity = new Identity(merchantConfig,
356-
(X509Certificate) cert[i], key);
359+
(X509Certificate) cert[i], key,logger);
357360
localKeyStoreHandler
358361
.addIdentityToKeyStore(identity, logger);
359362
identities.put(identity.getName(), identity);
360363
} else {
361364
identity = new Identity(merchantConfig,
362-
(X509Certificate) cert[i]);
365+
(X509Certificate) cert[i],logger);
363366
localKeyStoreHandler
364367
.addIdentityToKeyStore(identity, logger);
365368
identities.put(identity.getName(), identity);
@@ -370,7 +373,7 @@ private static void loadJavaKeystore(String keystore_location, MerchantConfig me
370373
throw new SignException("Missing Server Certificate ");
371374
}
372375
identity = new Identity(merchantConfig,
373-
(X509Certificate) serverCert);
376+
(X509Certificate) serverCert,logger);
374377
localKeyStoreHandler
375378
.addIdentityToKeyStore(identity, logger);
376379
identities.put(identity.getName(), identity);

java/src/main/java/com/cybersource/ws/client/Utility.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private Utility() {
4848
/**
4949
* Version number of this release.
5050
*/
51-
public static final String VERSION = "6.2.5";
51+
public static final String VERSION = "6.2.6";
5252

5353
/**
5454
* If in the Request map, a key called "_has_escapes" is present and is set

java/src/main/java/com/cybersource/ws/client/XMLClient.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,11 +355,12 @@ private static void setVersionInformation(Document request, String nsURI) {
355355
* @return signed document.
356356
* @throws SignException if signing fails.
357357
* @throws SignEncryptException
358+
* @throws ConfigException
358359
*/
359360
private static Document soapWrapAndSign(
360361
Document doc, MerchantConfig mc, DocumentBuilder builder,
361362
LoggerWrapper logger)
362-
throws SignException, SignEncryptException {
363+
throws SignException, SignEncryptException, ConfigException {
363364
boolean logSignedData = mc.getLogSignedData();
364365

365366
if (!logSignedData) {

java/src/main/resources/cybs.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ enableLog=true
5454
logDirectory=../../logs
5555
logMaximumSize=10
5656

57+
# If this property is set to false then the p12 certificate of a merchant will be reloaded
58+
# every time a transaction is made
59+
60+
certificateCacheEnabled=true
61+
5762
# Optional proxy server settings
5863
#proxyHost=<-- Set the Proxy Host-->
5964
#proxyPort=< -- Set the Proxy port-->

java/src/test/java/com/cybersource/ws/client/IdentityTest.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import static org.junit.Assert.*;
88

9+
import java.io.File;
910
import java.io.InputStream;
1011
import java.security.Principal;
1112
import java.security.PrivateKey;
@@ -29,15 +30,21 @@ public void setUp() throws Exception {
2930
}
3031

3132
@Test
32-
public void testSetUpMerchant() throws InstantiationException, IllegalAccessException, SignException{
33-
String keyAlias = "CN="+config.getMerchantID()+",SERIALNUMBER=400000009910179089277";
33+
public void testSetUpMerchant() throws InstantiationException, IllegalAccessException, SignException, ConfigException{
34+
File p12file = Mockito.mock(File.class);
35+
MerchantConfig mc = Mockito.mock(MerchantConfig.class);
36+
37+
String keyAlias = "CN="+mc.getMerchantID()+",SERIALNUMBER=400000009910179089277";
3438
X509Certificate x509Cert = Mockito.mock(X509Certificate.class);
3539
Principal principal = Mockito.mock(Principal.class);
3640
PrivateKey pkey = Mockito.mock(PrivateKey.class);
41+
Logger logger = Mockito.mock(Logger.class);
3742
Mockito.when(x509Cert.getSubjectDN()).thenReturn(principal);
3843
Mockito.when(principal.getName()).thenReturn(keyAlias);
39-
Identity identity = new Identity(config,x509Cert,pkey);
40-
assertEquals(identity.getName(), config.getMerchantID());
44+
45+
Mockito.when(mc.getKeyFile()).thenReturn(p12file);
46+
Identity identity = new Identity(mc,x509Cert,pkey,logger);
47+
assertEquals(identity.getName(), mc.getMerchantID());
4148
assertEquals(identity.getSerialNumber(), "400000009910179089277");
4249
assertNotNull(identity.getPrivateKey());
4350
}
@@ -47,9 +54,10 @@ public void testsetUpServer() throws InstantiationException, IllegalAccessExcept
4754
String keyAlias = "CN=CyberSource_SJC_US,SERIALNUMBER=400000009910179089277";
4855
X509Certificate x509Cert = Mockito.mock(X509Certificate.class);
4956
Principal principal = Mockito.mock(Principal.class);
57+
Logger logger = Mockito.mock(Logger.class);
5058
Mockito.when(x509Cert.getSubjectDN()).thenReturn(principal);
5159
Mockito.when(principal.getName()).thenReturn(keyAlias);
52-
Identity identity = new Identity(config,x509Cert);
60+
Identity identity = new Identity(config,x509Cert,logger);
5361
assertEquals(identity.getName(), "CyberSource_SJC_US");
5462
assertEquals(identity.getSerialNumber(), "400000009910179089277");
5563
assertNull(identity.getPrivateKey());

0 commit comments

Comments
 (0)