@@ -6,6 +6,8 @@ var cache = require('memory-cache');
6
6
var path = require ( 'path' ) ;
7
7
var Constants = require ( './Constants' ) ;
8
8
var ApiException = require ( './ApiException' ) ;
9
+ var Logger = require ( '../logging/Logger' ) ;
10
+ var Utility = require ( './Utility' ) ;
9
11
10
12
11
13
/**
@@ -77,3 +79,169 @@ exports.fetchPEMFileForNetworkTokenization = function(merchantConfig) {
77
79
}
78
80
return cache . get ( "privateKeyFromPEMFile" ) ;
79
81
}
82
+
83
+
84
+ exports . getRequestMLECertFromCache = function ( merchantConfig ) {
85
+ var logger = Logger . getLogger ( merchantConfig , 'Cache' ) ;
86
+ var merchantId = merchantConfig . getMerchantID ( ) ;
87
+ var cacheKey = null ;
88
+ var mleCertPath = null ;
89
+ if ( merchantConfig . getMleForRequestPublicCertPath ( ) !== null && merchantConfig . getMleForRequestPublicCertPath ( ) !== undefined ) {
90
+ cacheKey = merchantId + Constants . MLE_CACHE_IDENTIFIER_FOR_CONFIG_CERT ;
91
+ mleCertPath = merchantConfig . getMleForRequestPublicCertPath ( ) ;
92
+ } else if ( Constants . JWT === merchantConfig . getAuthenticationType ( ) . toLowerCase ( ) ) {
93
+ mleCertPath = path . resolve ( path . join ( merchantConfig . getKeysDirectory ( ) , merchantConfig . getKeyFileName ( ) + '.p12' ) ) ;
94
+ try {
95
+ fs . accessSync ( mleCertPath , fs . constants . R_OK ) ;
96
+ } catch ( err ) {
97
+ logger . warn ( "MLE certificate file not found or not readable: " + mleCertPath ) ;
98
+ return null ;
99
+ }
100
+ cacheKey = merchantId + Constants . MLE_CACHE_IDENTIFIER_FOR_P12_CERT ;
101
+ } else {
102
+ logger . debug ( "The certificate to use for MLE for requests is not provided in the merchant configuration. Please ensure that the certificate path is provided." ) ;
103
+ return null ;
104
+ }
105
+ return getMLECertBasedOnCacheKey ( merchantConfig , cacheKey , mleCertPath ) ;
106
+
107
+ }
108
+
109
+ function getMLECertBasedOnCacheKey ( merchantConfig , cacheKey , mleCertPath ) {
110
+ var cachedMLECert = cache . get ( cacheKey ) ;
111
+ var logger = Logger . getLogger ( merchantConfig , 'Cache' ) ;
112
+ if ( cachedMLECert === null || cachedMLECert === undefined || cachedMLECert . fileLastModifiedTime !== fs . statSync ( mleCertPath ) . mtimeMs ) {
113
+ logger . debug ( "MLE certificate not found in cache or has been modified. Loading from file: " + mleCertPath ) ;
114
+ setupMLECache ( merchantConfig , cacheKey , mleCertPath ) ;
115
+ } else {
116
+ logger . debug ( "MLE certificate found in cache for key: " + cacheKey ) ;
117
+ }
118
+ return cache . get ( cacheKey ) . mleCert ;
119
+ }
120
+
121
+ function setupMLECache ( merchantConfig , cacheKey , mleCertPath ) {
122
+ var fileLastModifiedTime = fs . statSync ( mleCertPath ) . mtimeMs ;
123
+ var mleCert = null ;
124
+ if ( cacheKey . endsWith ( Constants . MLE_CACHE_IDENTIFIER_FOR_CONFIG_CERT ) ) {
125
+ mleCert = loadCertificateFromPem ( merchantConfig , mleCertPath ) ;
126
+ }
127
+ else if ( cacheKey . endsWith ( Constants . MLE_CACHE_IDENTIFIER_FOR_P12_CERT ) ) {
128
+ mleCert = loadCertificateFromP12 ( merchantConfig , mleCertPath ) ;
129
+ }
130
+ cache . put ( cacheKey , {
131
+ mleCert : mleCert ,
132
+ fileLastModifiedTime : fileLastModifiedTime
133
+ } ) ;
134
+ validateCertificateExpiry ( mleCert , merchantConfig . getMleKeyAlias ( ) , cacheKey , merchantConfig ) ;
135
+ }
136
+
137
+
138
+ function loadCertificateFromP12 ( merchantConfig , mleCertPath ) {
139
+ const logger = Logger . getLogger ( merchantConfig , 'Cache' ) ;
140
+ try {
141
+ // Read the P12 file as before
142
+ var p12Buffer = fs . readFileSync ( mleCertPath ) ;
143
+ var p12Der = forge . util . binary . raw . encode ( new Uint8Array ( p12Buffer ) ) ;
144
+ var p12Asn1 = forge . asn1 . fromDer ( p12Der ) ;
145
+ var p12Cert = forge . pkcs12 . pkcs12FromAsn1 ( p12Asn1 , false , merchantConfig . getKeyPass ( ) ) ;
146
+
147
+ // Extract the certificate from the P12 container
148
+ var certBags = p12Cert . getBags ( { bagType : forge . pki . oids . certBag } ) ;
149
+ if ( certBags && certBags [ forge . pki . oids . certBag ] && certBags [ forge . pki . oids . certBag ] . length > 0 ) {
150
+ // Process all certificates in the P12 file
151
+ var certs = [ ] ;
152
+ for ( var i = 0 ; i < certBags [ forge . pki . oids . certBag ] . length ; i ++ ) {
153
+ var cert = certBags [ forge . pki . oids . certBag ] [ i ] . cert ;
154
+ var certPem = forge . pki . certificateToPem ( cert ) ;
155
+ certs . push ( certPem ) ;
156
+ }
157
+
158
+ // Try to find the certificate by alias among all certificates
159
+ var mleCert = Utility . findCertificateByAlias ( certs , merchantConfig . getMleKeyAlias ( ) ) ;
160
+ return forge . pki . certificateFromPem ( mleCert ) ;
161
+ } else {
162
+ throw new Error ( "No certificate found in P12 file" ) ;
163
+ }
164
+ } catch ( error ) {
165
+ ApiException . ApiException ( error . message + ". " + Constants . INCORRECT_KEY_PASS , logger ) ;
166
+ }
167
+ }
168
+
169
+ function loadCertificateFromPem ( merchantConfig , mleCertPath ) {
170
+ try {
171
+ const logger = Logger . getLogger ( merchantConfig , 'Cache' ) ;
172
+ var pemData = fs . readFileSync ( mleCertPath , 'utf8' ) ;
173
+ var certs = Utility . loadPemCertificates ( pemData ) ;
174
+ var mleCert = null ;
175
+ if ( ! certs || certs . length === 0 ) {
176
+ throw new Error ( "No valid PEM certificates found in the provided path : " + mleCertPath ) ;
177
+ }
178
+ try {
179
+ mleCert = Utility . findCertificateByAlias ( certs , merchantConfig . getMleKeyAlias ( ) ) ;
180
+
181
+ } catch ( error ) {
182
+ logger . warn ( "No certificate found for the specified mleKeyAlias '" + merchantConfig . getMleKeyAlias ( ) + "'. Using the first certificate from file " + mleCertPath + " as the MLE request certificate." ) ;
183
+ mleCert = certs [ 0 ] ;
184
+ }
185
+ // Use node forge to parse the PEM certificate
186
+ var forgeCert = forge . pki . certificateFromPem ( mleCert ) ;
187
+ return forgeCert ;
188
+ } catch ( error ) {
189
+ ApiException . AuthException ( "Error occurred while loading MLE certificate from PEM file : " + error . message ) ;
190
+ }
191
+ }
192
+
193
+ function validateCertificateExpiry ( certificate , keyAlias , cacheKey , merchantConfig ) {
194
+ var logger = Logger . getLogger ( merchantConfig , 'Cache' ) ;
195
+
196
+ var warningMessageForNoExpiryDate = "Certificate does not have expiry date" ;
197
+ var warningMessageForCertificateExpiringSoon = "Certificate with alias {} is going to expire on {}. Please update the certificate before then." ;
198
+ var warningMessageForExpiredCertificate = "Certificate with alias {} is expired as of {}. Please update the certificate." ;
199
+
200
+ if ( cacheKey . endsWith ( Constants . MLE_CACHE_IDENTIFIER_FOR_CONFIG_CERT ) ) {
201
+ warningMessageForNoExpiryDate = "Certificate for MLE Requests does not have expiry date from mleForRequestPublicCertPath in merchant configuration." ;
202
+ warningMessageForCertificateExpiringSoon = "Certificate for MLE Requests with alias {} is going to expire on {}. Please update the certificate provided in mleForRequestPublicCertPath in merchant configuration before then." ;
203
+ warningMessageForExpiredCertificate = "Certificate for MLE Requests with alias {} is expired as of {}. Please update the certificate provided in mleForRequestPublicCertPath in merchant configuration." ;
204
+ }
205
+
206
+ if ( cacheKey . endsWith ( Constants . MLE_CACHE_IDENTIFIER_FOR_P12_CERT ) ) {
207
+ warningMessageForNoExpiryDate = "Certificate for MLE Requests does not have expiry date in the P12 file." ;
208
+ warningMessageForCertificateExpiringSoon = "Certificate for MLE Requests with alias {} is going to expire on {}. Please update the P12 file before then." ;
209
+ warningMessageForExpiredCertificate = "Certificate for MLE Requests with alias {} is expired as of {}. Please update the P12 file." ;
210
+ }
211
+
212
+ // Get the certificate's notAfter date (expiry date)
213
+ var notAfter = null ;
214
+ try {
215
+ // All certificates are now in PEM format
216
+ if ( certificate . validity && certificate . validity . notAfter ) {
217
+ notAfter = certificate . validity . notAfter ;
218
+ } else {
219
+ logger . warn ( "Unknown certificate format. Cannot extract expiry date." ) ;
220
+ }
221
+ } catch ( error ) {
222
+ logger . warn ( "Error extracting certificate expiry date: " + error . message ) ;
223
+ return ;
224
+ }
225
+
226
+ if ( ! notAfter ) {
227
+ // Certificate does not have an expiry date
228
+ logger . warn ( warningMessageForNoExpiryDate ) ;
229
+ } else {
230
+ var now = new Date ( ) ;
231
+
232
+ if ( notAfter < now ) {
233
+ // Certificate is already expired
234
+ var expiredMessage = warningMessageForExpiredCertificate . replace ( "{}" , keyAlias ) . replace ( "{}" , notAfter . toISOString ( ) . split ( 'T' ) [ 0 ] ) ;
235
+ logger . warn ( expiredMessage ) ;
236
+ } else {
237
+ // Calculate days until expiry
238
+ var timeToExpire = notAfter . getTime ( ) - now . getTime ( ) ;
239
+ var daysToExpire = Math . floor ( timeToExpire / Constants . FACTOR_DAYS_TO_MILLISECONDS ) ;
240
+
241
+ if ( daysToExpire < Constants . CERTIFICATE_EXPIRY_DATE_WARNING_DAYS ) {
242
+ var expiringMessage = warningMessageForCertificateExpiringSoon . replace ( "{}" , keyAlias ) . replace ( "{}" , notAfter . toISOString ( ) . split ( 'T' ) [ 0 ] ) ;
243
+ logger . warn ( expiringMessage ) ;
244
+ }
245
+ }
246
+ }
247
+ } ;
0 commit comments