KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > accounting > thirdparty > valuelink > ValueLinkApi


1 /*
2  * $Id: ValueLinkApi.java 5462 2005-08-05 18:35:48Z jonesde $
3  *
4  * Copyright (c) 2003 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  */

25 package org.ofbiz.accounting.thirdparty.valuelink;
26
27 import java.math.BigInteger JavaDoc;
28 import java.security.InvalidAlgorithmParameterException JavaDoc;
29 import java.security.InvalidKeyException JavaDoc;
30 import java.security.KeyFactory JavaDoc;
31 import java.security.KeyPair JavaDoc;
32 import java.security.KeyPairGenerator JavaDoc;
33 import java.security.MessageDigest JavaDoc;
34 import java.security.NoSuchAlgorithmException JavaDoc;
35 import java.security.PrivateKey JavaDoc;
36 import java.security.PublicKey JavaDoc;
37 import java.security.spec.InvalidKeySpecException JavaDoc;
38 import java.text.DecimalFormat JavaDoc;
39 import java.text.ParseException JavaDoc;
40 import java.text.SimpleDateFormat JavaDoc;
41 import java.util.ArrayList JavaDoc;
42 import java.util.Date JavaDoc;
43 import java.util.HashMap JavaDoc;
44 import java.util.List JavaDoc;
45 import java.util.Map JavaDoc;
46 import java.util.Properties JavaDoc;
47 import java.util.Random JavaDoc;
48
49 import javax.crypto.BadPaddingException;
50 import javax.crypto.Cipher;
51 import javax.crypto.IllegalBlockSizeException;
52 import javax.crypto.KeyAgreement;
53 import javax.crypto.KeyGenerator;
54 import javax.crypto.NoSuchPaddingException;
55 import javax.crypto.SecretKey;
56 import javax.crypto.SecretKeyFactory;
57 import javax.crypto.interfaces.DHPrivateKey;
58 import javax.crypto.interfaces.DHPublicKey;
59 import javax.crypto.spec.DESKeySpec;
60 import javax.crypto.spec.DESedeKeySpec;
61 import javax.crypto.spec.DHParameterSpec;
62 import javax.crypto.spec.DHPrivateKeySpec;
63 import javax.crypto.spec.DHPublicKeySpec;
64 import javax.crypto.spec.IvParameterSpec;
65
66 import org.ofbiz.base.util.Debug;
67 import org.ofbiz.base.util.HttpClient;
68 import org.ofbiz.base.util.HttpClientException;
69 import org.ofbiz.base.util.StringUtil;
70 import org.ofbiz.base.util.UtilMisc;
71 import org.ofbiz.base.util.UtilProperties;
72 import org.ofbiz.entity.GenericDelegator;
73 import org.ofbiz.entity.GenericEntityException;
74 import org.ofbiz.entity.GenericValue;
75
76 /**
77  * ValueLinkApi - Implementation of ValueLink Encryption & Transport
78  *
79  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
80  * @version $Rev: 5462 $
81  * @since 3.0
82  */

83 public class ValueLinkApi {
84
85     public static final String JavaDoc module = ValueLinkApi.class.getName();
86
87     // static object cache
88
private static Map JavaDoc objectCache = new HashMap JavaDoc();
89
90     // instance variables
91
protected GenericDelegator delegator = null;
92     protected Properties JavaDoc props = null;
93     protected SecretKey kek = null;
94     protected SecretKey mwk = null;
95     protected String JavaDoc merchantId = null;
96     protected String JavaDoc terminalId = null;
97     protected Long JavaDoc mwkIndex = null;
98     protected boolean debug = false;
99
100     protected ValueLinkApi() {}
101     protected ValueLinkApi(GenericDelegator delegator, Properties JavaDoc props) {
102         String JavaDoc mId = (String JavaDoc) props.get("payment.valuelink.merchantId");
103         String JavaDoc tId = (String JavaDoc) props.get("payment.valuelink.terminalId");
104         this.delegator = delegator;
105         this.merchantId = mId;
106         this.terminalId = tId;
107         this.props = props;
108         if ("Y".equalsIgnoreCase((String JavaDoc) props.get("payment.valuelink.debug"))) {
109             this.debug = true;
110         }
111
112         if (debug) {
113             Debug.log("New ValueLinkApi instance created", module);
114             Debug.log("Merchant ID : " + merchantId, module);
115             Debug.log("Terminal ID : " + terminalId, module);
116         }
117     }
118
119     /**
120      * Obtain an instance of the ValueLinkApi
121      * @param delegator GenericDelegator used to query the encryption keys
122      * @param props Properties to use for the Api (usually payment.properties)
123      * @param reload When true, will replace an existing instance in the cache and reload all properties
124      * @return ValueLinkApi reference
125      */

126     public static ValueLinkApi getInstance(GenericDelegator delegator, Properties JavaDoc props, boolean reload) {
127         String JavaDoc merchantId = (String JavaDoc) props.get("payment.valuelink.merchantId");
128         if (props == null) {
129             throw new IllegalArgumentException JavaDoc("Properties cannot be null");
130         }
131
132         ValueLinkApi api = (ValueLinkApi) objectCache.get(merchantId);
133         if (api == null || reload) {
134             synchronized(ValueLinkApi.class) {
135                 api = (ValueLinkApi) objectCache.get(merchantId);
136                 if (api == null || reload) {
137                     api = new ValueLinkApi(delegator, props);
138                     objectCache.put(merchantId, api);
139                 }
140             }
141         }
142
143         if (api == null) {
144             throw new RuntimeException JavaDoc("Runtime problems with ValueLinkApi; unable to create instance");
145         }
146
147         return api;
148     }
149
150     /**
151      * Obtain an instance of the ValueLinkApi; this method will always return an existing reference if one is available
152      * @param delegator GenericDelegator used to query the encryption keys
153      * @param props Properties to use for the Api (usually payment.properties)
154      * @return
155      */

156     public static ValueLinkApi getInstance(GenericDelegator delegator, Properties JavaDoc props) {
157         return getInstance(delegator, props, false);
158     }
159
160     /**
161      * Encrypt the defined pin using the configured keys
162      * @param pin Plain text String of the pin
163      * @return Hex String of the encrypted pin (EAN) for transmission to ValueLink
164      */

165     public String JavaDoc encryptPin(String JavaDoc pin) {
166         // get the Cipher
167
Cipher mwkCipher = this.getCipher(this.getMwkKey(), Cipher.ENCRYPT_MODE);
168
169         // pin to bytes
170
byte[] pinBytes = pin.getBytes();
171
172         // 7 bytes of random data
173
byte[] random = this.getRandomBytes(7);
174
175         // pin checksum
176
byte[] checkSum = this.getPinCheckSum(pinBytes);
177
178         // put all together
179
byte[] eanBlock = new byte[16];
180         int i;
181         for (i = 0; i < random.length; i++) {
182             eanBlock[i] = random[i];
183         }
184         eanBlock[7] = checkSum[0];
185         for (i = 0; i < pinBytes.length; i++) {
186             eanBlock[i + 8] = pinBytes[i];
187         }
188
189         // encrypy the ean
190
String JavaDoc encryptedEanHex = null;
191         try {
192             byte[] encryptedEan = mwkCipher.doFinal(eanBlock);
193             encryptedEanHex = StringUtil.toHexString(encryptedEan);
194         } catch (IllegalStateException JavaDoc e) {
195             Debug.logError(e, module);
196         } catch (IllegalBlockSizeException e) {
197             Debug.logError(e, module);
198         } catch (BadPaddingException e) {
199             Debug.logError(e, module);
200         }
201
202         if (debug) {
203             Debug.log("encryptPin : " + pin + " / " + encryptedEanHex, module);
204         }
205
206         return encryptedEanHex;
207     }
208
209     /**
210      * Decrypt an encrypted pin using the configured keys
211      * @param pin Hex String of the encrypted pin (EAN)
212      * @return Plain text String of the pin
213      */

214     public String JavaDoc decryptPin(String JavaDoc pin) {
215         // get the Cipher
216
Cipher mwkCipher = this.getCipher(this.getMwkKey(), Cipher.DECRYPT_MODE);
217
218         // decrypt pin
219
String JavaDoc decryptedPinString = null;
220         try {
221             byte[] decryptedEan = mwkCipher.doFinal(StringUtil.fromHexString(pin));
222             byte[] decryptedPin = getByteRange(decryptedEan, 8, 8);
223             decryptedPinString = new String JavaDoc(decryptedPin);
224         } catch (IllegalStateException JavaDoc e) {
225             Debug.logError(e, module);
226         } catch (IllegalBlockSizeException e) {
227             Debug.logError(e, module);
228         } catch (BadPaddingException e) {
229             Debug.logError(e, module);
230         }
231
232         if (debug) {
233             Debug.log("decryptPin : " + pin + " / " + decryptedPinString, module);
234         }
235
236         return decryptedPinString;
237     }
238
239     /**
240      * Transmit a request to ValueLink
241      * @param request Map of request parameters
242      * @return Map of response parameters
243      * @throws HttpClientException
244      */

245     public Map JavaDoc send(Map JavaDoc request) throws HttpClientException {
246         return send((String JavaDoc) props.get("payment.valuelink.url"), request);
247     }
248
249     /**
250      * Transmit a request to ValueLink
251      * @param url override URL from what is defined in the properties
252      * @param request request Map of request parameters
253      * @return Map of response parameters
254      * @throws HttpClientException
255      */

256     public Map JavaDoc send(String JavaDoc url, Map JavaDoc request) throws HttpClientException {
257         if (debug) {
258             Debug.log("Request : " + url + " / " + request, module);
259         }
260
261         // read the timeout value
262
String JavaDoc timeoutString = (String JavaDoc) props.get("payment.valuelink.timeout");
263         int timeout = 34;
264         try {
265             timeout = Integer.parseInt(timeoutString);
266         } catch (NumberFormatException JavaDoc e) {
267             Debug.logError(e, "Unable to set timeout to " + timeoutString + " using default " + timeout);
268         }
269
270         // create the HTTP client
271
HttpClient client = new HttpClient(url, request);
272         client.setTimeout(timeout * 1000);
273         client.setDebug(debug);
274
275         client.setClientCertificateAlias((String JavaDoc) props.get("payment.valuelink.certificateAlias"));
276         String JavaDoc response = client.post();
277
278         // parse the response and return a map
279
return this.parseResponse(response);
280     }
281
282     /**
283      * Output the creation of public/private keys + KEK to the console for manual database update
284      */

285     public StringBuffer JavaDoc outputKeyCreation(boolean kekOnly, String JavaDoc kekTest) {
286         return this.outputKeyCreation(0, kekOnly, kekTest);
287     }
288
289     private StringBuffer JavaDoc outputKeyCreation(int loop, boolean kekOnly, String JavaDoc kekTest) {
290         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
291         loop++;
292
293         if (loop > 100) {
294             // only loop 100 times; then throw an exception
295
throw new IllegalStateException JavaDoc("Unable to create 128 byte keys in 100 tries");
296         }
297
298         // place holder for the keys
299
DHPrivateKey privateKey = null;
300         DHPublicKey publicKey = null;
301
302         if (!kekOnly) {
303             KeyPair JavaDoc keyPair = null;
304             try {
305                 keyPair = this.createKeys();
306             } catch (NoSuchAlgorithmException JavaDoc e) {
307                 Debug.logError(e, module);
308             } catch (InvalidAlgorithmParameterException JavaDoc e) {
309                 Debug.logError(e, module);
310             } catch (InvalidKeySpecException JavaDoc e) {
311                 Debug.logError(e, module);
312             }
313
314             if (keyPair != null) {
315                 publicKey = (DHPublicKey) keyPair.getPublic();
316                 privateKey = (DHPrivateKey) keyPair.getPrivate();
317
318                 if (publicKey == null || publicKey.getY().toByteArray().length != 128) {
319                     // run again until we get a 128 byte public key for VL
320
return this.outputKeyCreation(loop, kekOnly, kekTest);
321                 }
322             } else {
323                 Debug.log("Returned a null KeyPair", module);
324                 return this.outputKeyCreation(loop, kekOnly, kekTest);
325             }
326         } else {
327             // use our existing private key to generate a KEK
328
try {
329                 privateKey = (DHPrivateKey) this.getPrivateKey();
330             } catch (Exception JavaDoc e) {
331                 Debug.logError(e, module);
332             }
333         }
334
335         // the KEK
336
byte[] kekBytes = null;
337         try {
338             kekBytes = this.generateKek(privateKey);
339         } catch (NoSuchAlgorithmException JavaDoc e) {
340             Debug.logError(e, module);
341         } catch (InvalidKeySpecException JavaDoc e) {
342             Debug.logError(e, module);
343         } catch (InvalidKeyException JavaDoc e) {
344             Debug.logError(e, module);
345         }
346
347         // the 3DES KEK value
348
SecretKey loadedKek = this.getDesEdeKey(kekBytes);
349         byte[] loadKekBytes = loadedKek.getEncoded();
350
351         // test the KEK
352
Cipher cipher = this.getCipher(this.getKekKey(), Cipher.ENCRYPT_MODE);
353         byte[] kekTestB = { 0, 0, 0, 0, 0, 0, 0, 0 };
354         byte[] kekTestC = new byte[0];
355         if (kekTest != null) {
356             kekTestB = StringUtil.fromHexString(kekTest);
357         }
358
359         // encrypt the test bytes
360
try {
361             kekTestC = cipher.doFinal(kekTestB);
362         } catch (Exception JavaDoc e) {
363             Debug.logError(e, module);
364         }
365
366         if (!kekOnly) {
367             // public key (just Y)
368
BigInteger JavaDoc y = publicKey.getY();
369             byte[] yBytes = y.toByteArray();
370             String JavaDoc yHex = StringUtil.toHexString(yBytes);
371             buf.append("======== Begin Public Key (Y @ " + yBytes.length + " / " + yHex.length() + ") ========\n");
372             buf.append(yHex + "\n");
373             buf.append("======== End Public Key ========\n\n");
374
375             // private key (just X)
376
BigInteger JavaDoc x = privateKey.getX();
377             byte[] xBytes = x.toByteArray();
378             String JavaDoc xHex = StringUtil.toHexString(xBytes);
379             buf.append("======== Begin Private Key (X @ " + xBytes.length + " / " + xHex.length() + ") ========\n");
380             buf.append(xHex + "\n");
381             buf.append("======== End Private Key ========\n\n");
382
383             // private key (full)
384
byte[] privateBytes = privateKey.getEncoded();
385             String JavaDoc privateHex = StringUtil.toHexString(privateBytes);
386             buf.append("======== Begin Private Key (Full @ " + privateBytes.length + " / " + privateHex.length() + ") ========\n");
387             buf.append(privateHex + "\n");
388             buf.append("======== End Private Key ========\n\n");
389         }
390
391         if (kekBytes != null) {
392             buf.append("======== Begin KEK (" + kekBytes.length + ") ========\n");
393             buf.append(StringUtil.toHexString(kekBytes) + "\n");
394             buf.append("======== End KEK ========\n\n");
395
396             buf.append("======== Begin KEK (DES) (" + loadKekBytes.length + ") ========\n");
397             buf.append(StringUtil.toHexString(loadKekBytes) + "\n");
398             buf.append("======== End KEK (DES) ========\n\n");
399
400             buf.append("======== Begin KEK Test (" + kekTestC.length + ") ========\n");
401             buf.append(StringUtil.toHexString(kekTestC) + "\n");
402             buf.append("======== End KEK Test ========\n\n");
403         } else {
404             Debug.logError("KEK came back empty", module);
405         }
406
407         return buf;
408     }
409
410     /**
411      * Create a set of public/private keys using ValueLinks defined parameters
412      * @return KeyPair object containing both public and private keys
413      * @throws NoSuchAlgorithmException
414      * @throws InvalidAlgorithmParameterException
415      */

416     public KeyPair JavaDoc createKeys() throws NoSuchAlgorithmException JavaDoc, InvalidAlgorithmParameterException JavaDoc, InvalidKeySpecException JavaDoc {
417         // initialize the parameter spec
418
DHPublicKey publicKey = (DHPublicKey) this.getValueLinkPublicKey();
419         DHParameterSpec dhParamSpec = publicKey.getParams();
420         //Debug.log(dhParamSpec.getP().toString() + " / " + dhParamSpec.getG().toString(), module);
421

422         // create the public/private key pair using parameters defined by valuelink
423
KeyPairGenerator JavaDoc keyGen = KeyPairGenerator.getInstance("DH");
424         keyGen.initialize(dhParamSpec);
425         KeyPair JavaDoc keyPair = keyGen.generateKeyPair();
426
427         return keyPair;
428     }
429
430     /**
431      * Generate a key exchange key for use in encrypting the mwk
432      * @param privateKey The private key for the merchant
433      * @return byte array containing the kek
434      * @throws NoSuchAlgorithmException
435      * @throws InvalidKeySpecException
436      * @throws InvalidKeyException
437      */

438     public byte[] generateKek(PrivateKey JavaDoc privateKey) throws NoSuchAlgorithmException JavaDoc, InvalidKeySpecException JavaDoc, InvalidKeyException JavaDoc {
439         // get the ValueLink public key
440
PublicKey JavaDoc vlPublic = this.getValueLinkPublicKey();
441
442         // generate shared secret key
443
KeyAgreement ka = KeyAgreement.getInstance("DH");
444         ka.init(privateKey);
445         ka.doPhase(vlPublic, true);
446         byte[] secretKey = ka.generateSecret();
447
448         if (debug) {
449             Debug.log("Secret Key : " + StringUtil.toHexString(secretKey) + " / " + secretKey.length, module);
450         }
451
452         // generate 3DES from secret key using VL algorithm (KEK)
453
MessageDigest JavaDoc md = MessageDigest.getInstance("SHA1");
454         byte[] digest = md.digest(secretKey);
455         byte[] des2 = getByteRange(digest, 0, 16);
456         byte[] first8 = getByteRange(des2, 0, 8);
457         byte[] kek = copyBytes(des2, first8, 0);
458
459         if (debug) {
460             Debug.log("Generated KEK : " + StringUtil.toHexString(kek) + " / " + kek.length, module);
461         }
462
463         return kek;
464     }
465
466     /**
467      * Get a public key object for the ValueLink supplied public key
468      * @return PublicKey object of ValueLinks's public key
469      * @throws NoSuchAlgorithmException
470      * @throws InvalidKeySpecException
471      */

472     public PublicKey JavaDoc getValueLinkPublicKey() throws NoSuchAlgorithmException JavaDoc, InvalidKeySpecException JavaDoc {
473         // read the valuelink public key
474
String JavaDoc publicValue = (String JavaDoc) props.get("payment.valuelink.publicValue");
475         byte[] publicKeyBytes = StringUtil.fromHexString(publicValue);
476
477         // initialize the parameter spec
478
DHParameterSpec dhParamSpec = this.getDHParameterSpec();
479
480         // load the valuelink public key
481
KeyFactory JavaDoc keyFactory = KeyFactory.getInstance("DH");
482         BigInteger JavaDoc publicKeyInt = new BigInteger JavaDoc(publicKeyBytes);
483         DHPublicKeySpec dhPublicSpec = new DHPublicKeySpec(publicKeyInt, dhParamSpec.getP(), dhParamSpec.getG());
484         PublicKey JavaDoc vlPublic = keyFactory.generatePublic(dhPublicSpec);
485
486         return vlPublic;
487     }
488
489     /**
490      * Get merchant Private Key
491      * @return PrivateKey object for the merchant
492      */

493     public PrivateKey JavaDoc getPrivateKey() throws InvalidKeySpecException JavaDoc, NoSuchAlgorithmException JavaDoc {
494         byte[] privateKeyBytes = this.getPrivateKeyBytes();
495
496         // initialize the parameter spec
497
DHParameterSpec dhParamSpec = this.getDHParameterSpec();
498
499         // load the private key
500
KeyFactory JavaDoc keyFactory = KeyFactory.getInstance("DH");
501         BigInteger JavaDoc privateKeyInt = new BigInteger JavaDoc(privateKeyBytes);
502         DHPrivateKeySpec dhPrivateSpec = new DHPrivateKeySpec(privateKeyInt, dhParamSpec.getP(), dhParamSpec.getG());
503         PrivateKey JavaDoc privateKey = keyFactory.generatePrivate(dhPrivateSpec);
504
505         return privateKey;
506     }
507
508     /**
509      * Generate a new MWK
510      * @return Hex String of the new encrypted MWK ready for transmission to ValueLink
511      */

512     public byte[] generateMwk() {
513         KeyGenerator keyGen = null;
514         try {
515             keyGen = KeyGenerator.getInstance("DES");
516         } catch (NoSuchAlgorithmException JavaDoc e) {
517             Debug.logError(e, module);
518         }
519
520         // generate the DES key 1
521
SecretKey des1 = keyGen.generateKey();
522         SecretKey des2 = keyGen.generateKey();
523
524         if (des1 != null && des2 != null) {
525             byte[] desByte1 = des1.getEncoded();
526             byte[] desByte2 = des2.getEncoded();
527             byte[] desByte3 = des1.getEncoded();
528
529             // check for weak keys
530
try {
531                 if (DESKeySpec.isWeak(des1.getEncoded(), 0) || DESKeySpec.isWeak(des2.getEncoded(), 0)) {
532                     return generateMwk();
533                 }
534             } catch (Exception JavaDoc e) {
535                 Debug.logError(e, module);
536             }
537
538             byte[] des3 = copyBytes(desByte1, copyBytes(desByte2, desByte3, 0), 0);
539             return generateMwk(des3);
540         } else {
541             Debug.log("Null DES keys returned", module);
542         }
543
544         return null;
545     }
546
547     /**
548      * Generate a new MWK
549      * @param desBytes byte array of the DES key (24 bytes)
550      * @return Hex String of the new encrypted MWK ready for transmission to ValueLink
551      */

552     public byte[] generateMwk(byte[] desBytes) {
553         if (debug) {
554             Debug.log("DES Key : " + StringUtil.toHexString(desBytes) + " / " + desBytes.length, module);
555         }
556         SecretKeyFactory skf1 = null;
557         SecretKey mwk = null;
558         try {
559             skf1 = SecretKeyFactory.getInstance("DESede");
560         } catch (NoSuchAlgorithmException JavaDoc e) {
561             Debug.logError(e, module);
562         }
563         DESedeKeySpec desedeSpec2 = null;
564         try {
565             desedeSpec2 = new DESedeKeySpec(desBytes);
566         } catch (InvalidKeyException JavaDoc e) {
567             Debug.logError(e, module);
568         }
569         if (skf1 != null && desedeSpec2 != null) {
570             try {
571                 mwk = skf1.generateSecret(desedeSpec2);
572             } catch (InvalidKeySpecException JavaDoc e) {
573                 Debug.logError(e, module);
574             }
575         }
576         if (mwk != null) {
577             return generateMwk(mwk);
578         } else {
579             return null;
580         }
581     }
582
583     /**
584      * Generate a new MWK
585      * @param mwkdes3 pre-generated DES3 SecretKey
586      * @return Hex String of the new encrypted MWK ready for transmission to ValueLink
587      */

588     public byte[] generateMwk(SecretKey mwkdes3) {
589         // zeros for checksum
590
byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 };
591
592         // 8 bytes random data
593
byte[] random = new byte[8];
594         Random JavaDoc ran = new Random JavaDoc();
595         ran.nextBytes(random);
596
597
598         // open a cipher using the new mwk
599
Cipher cipher = this.getCipher(mwkdes3, Cipher.ENCRYPT_MODE);
600
601         // make the checksum - encrypted 8 bytes of 0's
602
byte[] encryptedZeros = new byte[0];
603         try {
604             encryptedZeros = cipher.doFinal(zeros);
605         } catch (IllegalStateException JavaDoc e) {
606             Debug.logError(e, module);
607         } catch (IllegalBlockSizeException e) {
608             Debug.logError(e, module);
609         } catch (BadPaddingException e) {
610             Debug.logError(e, module);
611         }
612
613         // make the 40 byte MWK - random 8 bytes + key + checksum
614
byte[] newMwk = copyBytes(mwkdes3.getEncoded(), encryptedZeros, 0);
615         newMwk = copyBytes(random, newMwk, 0);
616
617         if (debug) {
618             Debug.log("Random 8 byte : " + StringUtil.toHexString(random), module);
619             Debug.log("Encrypted 0's : " + StringUtil.toHexString(encryptedZeros), module);
620             Debug.log("Decrypted MWK : " + StringUtil.toHexString(mwkdes3.getEncoded()) + " / " + mwkdes3.getEncoded().length, module);
621             Debug.log("Encrypted MWK : " + StringUtil.toHexString(newMwk) + " / " + newMwk.length, module);
622         }
623
624         return newMwk;
625     }
626
627     /**
628      * Use the KEK to encrypt a value usually the MWK
629      * @param content byte array to encrypt
630      * @return encrypted byte array
631      */

632     public byte[] encryptViaKek(byte[] content) {
633         return cryptoViaKek(content, Cipher.ENCRYPT_MODE);
634     }
635
636     /**
637      * Ue the KEK to decrypt a value
638      * @param content byte array to decrypt
639      * @return decrypted byte array
640      */

641     public byte[] decryptViaKek(byte[] content) {
642         return cryptoViaKek(content, Cipher.DECRYPT_MODE);
643     }
644
645     /**
646      * Returns a date string formatted as directed by ValueLink
647      * @return ValueLink formatted date String
648      */

649     public String JavaDoc getDateString() {
650         String JavaDoc format = (String JavaDoc) props.get("payment.valuelink.timestamp");
651         SimpleDateFormat JavaDoc sdf = new SimpleDateFormat JavaDoc(format);
652         return sdf.format(new Date JavaDoc());
653     }
654
655     /**
656      * Returns the current working key index
657      * @return Long number of the current working key index
658      */

659     public Long JavaDoc getWorkingKeyIndex() {
660         if (this.mwkIndex == null) {
661             synchronized(this) {
662                 if (this.mwkIndex == null) {
663                     this.mwkIndex = this.getGenericValue().getLong("workingKeyIndex");
664                 }
665             }
666         }
667
668         if (debug) {
669             Debug.log("Current Working Key Index : " + this.mwkIndex, module);
670         }
671
672         return this.mwkIndex;
673     }
674
675     /**
676      * Returns a ValueLink formatted amount String
677      * @param amount Double value to format
678      * @return Formatted String
679      */

680     public String JavaDoc getAmount(Double JavaDoc amount) {
681         if (amount == null) {
682             return "0.00";
683         }
684         String JavaDoc currencyFormat = UtilProperties.getPropertyValue("general.properties", "currency.decimal.format", "##0.00");
685         DecimalFormat JavaDoc formatter = new DecimalFormat JavaDoc(currencyFormat);
686         String JavaDoc amountString = formatter.format(amount.doubleValue());
687         Double JavaDoc newAmount = null;
688         try {
689             newAmount = new Double JavaDoc(formatter.parse(amountString).doubleValue());
690         } catch (ParseException JavaDoc e) {
691             Debug.logError(e, "Unable to parse amount Double");
692         }
693
694         String JavaDoc formattedString = null;
695         if (newAmount != null) {
696             double amountDouble = newAmount.doubleValue() * 100;
697             formattedString = new String JavaDoc(new Integer JavaDoc(new Double JavaDoc(amountDouble).intValue()).toString());
698         }
699         return formattedString;
700     }
701
702     /**
703      * Returns a Double from a ValueLink formatted amount String
704      * @param amount The ValueLink formatted amount String
705      * @return Double object
706      */

707     public Double JavaDoc getAmount(String JavaDoc amount) {
708         if (amount == null) {
709             return new Double JavaDoc(0.00);
710         }
711         Double JavaDoc doubleAmount = new Double JavaDoc(amount);
712         return new Double JavaDoc(doubleAmount.doubleValue() / 100);
713     }
714     
715     public String JavaDoc getCurrency(String JavaDoc currency) {
716         return "840"; // todo make this multi-currency
717
}
718
719     /**
720      * Creates a Map of initial request values (MerchID, AltMerchNo, Modes, MerchTime, TermTxnNo, EncryptID)
721      * Note: For 2010 (assign working key) transaction, the EncryptID will need to be adjusted
722      * @return Map containing the inital request values
723      */

724     public Map JavaDoc getInitialRequestMap(Map JavaDoc context) {
725         Map JavaDoc request = new HashMap JavaDoc();
726
727         // merchant information
728
request.put("MerchID", merchantId + terminalId);
729         request.put("AltMerchNo", props.get("payment.valuelink.altMerchantId"));
730
731         // mode settings
732
String JavaDoc modes = (String JavaDoc) props.get("payment.valuelink.modes");
733         if (modes != null && modes.length() > 0) {
734             request.put("Modes", modes);
735         }
736
737         // merchant timestamp
738
String JavaDoc merchTime = (String JavaDoc) context.get("MerchTime");
739