Skip to content

Commit dcbe263

Browse files
committed
changes related certificate caching
1 parent 5f2148b commit dcbe263

File tree

11 files changed

+393
-105
lines changed

11 files changed

+393
-105
lines changed

CyberSource/Client/BaseClient.cs

+52-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using CyberSource.Base;
22
using System;
3+
using System.Collections;
34
using System.Net;
45
using System.ServiceModel;
56
using System.Xml.Serialization;
67
using System.ServiceModel.Channels;
78
using System.ServiceModel.Security.Tokens;
9+
using System.Security.Cryptography.X509Certificates;
810

911
namespace CyberSource.Clients
1012
{
@@ -17,6 +19,7 @@ public abstract class BaseClient
1719
/// Version of this client.
1820
/// </summary>
1921
public const string CLIENT_LIBRARY_VERSION = "1.4.3";
22+
public const string CYBS_SUBJECT_NAME = "CyberSource_SJC_US";
2023

2124
/// <summary>
2225
/// Proxy object that is initialized during start-up, if needed.
@@ -43,8 +46,9 @@ public abstract class BaseClient
4346

4447
public const string CYBERSOURCE_PUBLIC_KEY = "CyberSource_SJC_US";
4548
public const string X509_CLAIMTYPE = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/x500distinguishedname";
49+
protected static System.Collections.Hashtable merchantIdentities = new Hashtable();
4650

47-
static BaseClient()
51+
static BaseClient()
4852
{
4953
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | (SecurityProtocolType)768;
5054
SetupProxy();
@@ -222,6 +226,12 @@ int boolVal
222226
merchantID, Configuration.USE_SIGNED_AND_ENCRYPTED);
223227
if (boolVal != -1) config.UseSignedAndEncrypted = (boolVal == 1);
224228

229+
// certificate cache flag
230+
boolVal
231+
= AppSettings.GetBoolSetting(
232+
merchantID, Configuration.CERTIFICATE_CACHE_ENABLED);
233+
if (boolVal != -1) config.CertificateCacheEnabled = (boolVal == 1);
234+
225235
return (config);
226236
}
227237

@@ -360,5 +370,46 @@ protected static CustomBinding getWCFCustomBinding(Configuration config)
360370
currentBinding.Elements.Add(httpsTransport);
361371
return currentBinding;
362372
}
373+
374+
protected static X509Certificate2 GetOrFindValidMerchantCertFromStore(String merchantId, Hashtable merchantIdentities)
375+
{
376+
foreach (DictionaryEntry de in merchantIdentities)
377+
{
378+
if (de.Key.Equals(merchantId))
379+
{
380+
return ((CertificateEntry)de.Value).MerchantCert;
381+
}
382+
383+
}
384+
return null;
385+
}
386+
387+
protected static X509Certificate2 GetOrFindValidCybsCertFromStore(String merchantId, Hashtable merchantIdentities)
388+
{
389+
foreach (DictionaryEntry de in merchantIdentities)
390+
{
391+
if (de.Key.Equals(merchantId))
392+
{
393+
return ((CertificateEntry)de.Value).CybsCert;
394+
}
395+
396+
}
397+
return null;
398+
}
399+
400+
public static Boolean IsMerchantCertExpired(String merchantId, long lastModifiedTime, Hashtable merchantIdentities)
401+
{
402+
foreach (DictionaryEntry de in merchantIdentities)
403+
{
404+
if (de.Key.Equals(merchantId))
405+
{
406+
if(((CertificateEntry)de.Value).LastModifiedTime != lastModifiedTime){
407+
return true;
408+
}
409+
}
410+
411+
}
412+
return false;
413+
}
363414
}
364415
}
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Configuration;
3+
using System.Security.Cryptography.X509Certificates;
4+
5+
namespace CyberSource.Clients
6+
{
7+
/// <summary>
8+
/// Encapsulates retrieval of application settings. You can modify
9+
/// this code to suit your needs.
10+
/// </summary>
11+
public class CertificateEntry
12+
{
13+
private long lastModifiedTime;
14+
private X509Certificate2 merchantCert;
15+
private X509Certificate2 cybsCert;
16+
17+
public CertificateEntry(long lastModifiedTime, X509Certificate2 merchantCert, X509Certificate2 cybsCert)
18+
{
19+
this.lastModifiedTime = lastModifiedTime;
20+
this.merchantCert = merchantCert;
21+
this.cybsCert = cybsCert;
22+
}
23+
24+
public X509Certificate2 MerchantCert
25+
{
26+
get { return merchantCert; }
27+
set
28+
{
29+
merchantCert = value;
30+
31+
}
32+
}
33+
34+
public X509Certificate2 CybsCert
35+
{
36+
get { return cybsCert; }
37+
set
38+
{
39+
cybsCert = value;
40+
41+
}
42+
}
43+
44+
public long LastModifiedTime
45+
{
46+
get { return lastModifiedTime; }
47+
set
48+
{
49+
lastModifiedTime = value;
50+
51+
}
52+
}
53+
}
54+
55+
56+
}

CyberSource/Client/Configuration.cs

+12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class Configuration
2828
internal const string SEND_TO_AKAMAI = "sendToAkamai";
2929
internal const string EFFECTIVE_SERVER_URL = "effectiveServerURL";
3030
internal const string USE_SIGNED_AND_ENCRYPTED = "useSignAndEncrypted";
31+
internal const string CERTIFICATE_CACHE_ENABLED = "certificateCacheEnabled";
3132

3233
/// <summary>
3334
/// Default log file name.
@@ -70,6 +71,7 @@ public class Configuration
7071
private bool sendToAkamai = true;
7172
private int connectionLimit = -1;
7273
private bool useSignedAndEncrypted = false;
74+
private bool certificateCacheEnabled = true;
7375

7476
private bool isSendToProductionSet = false;
7577

@@ -338,6 +340,10 @@ public string LogString
338340
buf += NVP_SEPARATOR + EFFECTIVE_SERVER_URL + NV_SEPARATOR + EffectiveServerURL;
339341
}
340342

343+
buf += NVP_SEPARATOR + CERTIFICATE_CACHE_ENABLED + NV_SEPARATOR + certificateCacheEnabled;
344+
345+
buf += NVP_SEPARATOR + USE_SIGNED_AND_ENCRYPTED + NV_SEPARATOR + useSignedAndEncrypted;
346+
341347
return (buf);
342348
}
343349
}
@@ -414,5 +420,11 @@ public bool UseSignedAndEncrypted
414420
get { return useSignedAndEncrypted; }
415421
set { useSignedAndEncrypted = value; }
416422
}
423+
424+
public bool CertificateCacheEnabled
425+
{
426+
get { return certificateCacheEnabled; }
427+
set { certificateCacheEnabled = value; }
428+
}
417429
}
418430
}

CyberSource/Client/CyberSourceClients.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
<Compile Include="Settings.cs" />
128128
<Compile Include="SoapClient.cs" />
129129
<Compile Include="XmlClient.cs" />
130+
<Compile Include="CertificateEntry.cs" />
130131
</ItemGroup>
131132
<ItemGroup>
132133
<None Include="app.config" />

CyberSource/Client/NVPClient.cs

+89-35
Original file line numberDiff line numberDiff line change
@@ -69,50 +69,104 @@ public static Hashtable RunTransaction(
6969

7070
//Get instance of service
7171
using (proc = new NVPTransactionProcessorClient(currentBinding, endpointAddress))
72-
{
73-
74-
//Set protection level to sign
75-
proc.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;
76-
72+
{
7773
// set the timeout
7874
TimeSpan timeOut = new TimeSpan(0, 0, 0, config.Timeout, 0);
7975
currentBinding.SendTimeout = timeOut;
8076

8177

8278
string keyFilePath = Path.Combine(config.KeysDirectory, config.EffectiveKeyFilename);
83-
proc.ClientCredentials.ClientCertificate.Certificate = new X509Certificate2(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
79+
X509Certificate2 merchantCert = null;
80+
X509Certificate2 cybsCert = null;
81+
DateTime dateFile = File.GetCreationTime(keyFilePath);
82+
if (config.CertificateCacheEnabled)
83+
{
84+
if (!merchantIdentities.ContainsKey(config.MerchantID) || IsMerchantCertExpired(config.MerchantID, dateFile.ToFileTimeUtc(), merchantIdentities))
85+
{
86+
X509Certificate2Collection collection = new X509Certificate2Collection();
87+
collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
8488

85-
proc.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
86-
87-
// Changes for SHA2 certificates support
88-
X509Certificate2Collection collection = new X509Certificate2Collection();
89-
collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
90-
91-
foreach (X509Certificate2 cert1 in collection)
92-
{
93-
if (cert1.Subject.Contains(config.MerchantID))
94-
{
95-
proc.ClientCredentials.ClientCertificate.Certificate = cert1;
96-
proc.ClientCredentials.ServiceCertificate.DefaultCertificate = cert1;
97-
break;
98-
}
99-
}
100-
101-
if (config.UseSignedAndEncrypted)
102-
{
103-
foreach (X509Certificate2 cert2 in collection)
104-
{
105-
//Console.WriteLine(cert1.Subject);
106-
if (cert2.Subject.Contains(CYBERSOURCE_PUBLIC_KEY))
107-
{
108-
//Set protection level to sign & encrypt only
109-
proc.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
110-
proc.ClientCredentials.ServiceCertificate.DefaultCertificate = cert2;
111-
break;
112-
}
113-
}
89+
X509Certificate2 newMerchantCert = null;
90+
X509Certificate2 newCybsCert = null;
91+
92+
foreach (X509Certificate2 cert1 in collection)
93+
{
94+
if (cert1.Subject.Contains(config.MerchantID))
95+
{
96+
newMerchantCert = cert1;
97+
}
98+
99+
if (cert1.Subject.Contains(CYBS_SUBJECT_NAME))
100+
{
101+
newCybsCert = cert1;
102+
}
103+
}
104+
if (merchantIdentities.ContainsKey(config.MerchantID))
105+
{
106+
merchantIdentities.Remove(config.MerchantID);
107+
}
108+
merchantIdentities.Add(config.MerchantID, new CertificateEntry(dateFile.ToFileTimeUtc(), newMerchantCert, newCybsCert));
109+
110+
}
111+
merchantCert = GetOrFindValidMerchantCertFromStore(config.MerchantID, merchantIdentities);
112+
if (config.UseSignedAndEncrypted)
113+
{
114+
cybsCert = GetOrFindValidCybsCertFromStore(config.MerchantID, merchantIdentities);
115+
}
114116
}
117+
else
118+
{
119+
// Changes for SHA2 certificates support
120+
X509Certificate2Collection collection = new X509Certificate2Collection();
121+
collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
115122

123+
foreach (X509Certificate2 cert1 in collection)
124+
{
125+
if (cert1.Subject.Contains(config.MerchantID))
126+
{
127+
merchantCert = cert1;
128+
break;
129+
}
130+
}
131+
132+
if (config.UseSignedAndEncrypted)
133+
{
134+
foreach (X509Certificate2 cert2 in collection)
135+
{
136+
//Console.WriteLine(cert1.Subject);
137+
if (cert2.Subject.Contains(CYBERSOURCE_PUBLIC_KEY))
138+
{
139+
cybsCert = cert2;
140+
break;
141+
}
142+
}
143+
}
144+
}
145+
146+
if (merchantCert == null)
147+
{
148+
throw new ApplicationException(
149+
"CONFIGURATION OR CODE BUG: merchant certificate is missing, check the p12 file");
150+
}
151+
//Set protection level to sign
152+
proc.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;
153+
proc.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
154+
proc.ClientCredentials.ClientCertificate.Certificate = merchantCert;
155+
proc.ClientCredentials.ServiceCertificate.DefaultCertificate = merchantCert;
156+
157+
if (config.UseSignedAndEncrypted)
158+
{
159+
if (cybsCert == null)
160+
{
161+
throw new ApplicationException(
162+
"CONFIGURATION OR CODE BUG: cybs certificate is missing, check the p12 file");
163+
}
164+
165+
//Set protection level to sign & encrypt only
166+
proc.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
167+
proc.ClientCredentials.ServiceCertificate.DefaultCertificate = cybsCert;
168+
}
169+
116170
if (logger != null)
117171
{
118172
logger.LogRequest(request, config.Demo);

0 commit comments

Comments
 (0)