Source Code Cross Referenced for ValueLinkApi.java in  » ERP-CRM-Financial » SourceTap-CRM » org » ofbiz » accounting » thirdparty » valuelink » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » ERP CRM Financial » SourceTap CRM » org.ofbiz.accounting.thirdparty.valuelink 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $Id: ValueLinkApi.java,v 1.6 2004/03/12 23:22:51 ajzeneski Exp $
0003:         *
0004:         * Copyright (c) 2003 The Open For Business Project - www.ofbiz.org
0005:         *
0006:         * Permission is hereby granted, free of charge, to any person obtaining a
0007:         * copy of this software and associated documentation files (the "Software"),
0008:         * to deal in the Software without restriction, including without limitation
0009:         * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0010:         * and/or sell copies of the Software, and to permit persons to whom the
0011:         * Software is furnished to do so, subject to the following conditions:
0012:         *
0013:         * The above copyright notice and this permission notice shall be included
0014:         * in all copies or substantial portions of the Software.
0015:         *
0016:         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
0017:         * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0018:         * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
0019:         * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
0020:         * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
0021:         * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
0022:         * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0023:         *
0024:         */
0025:        package org.ofbiz.accounting.thirdparty.valuelink;
0026:
0027:        import org.ofbiz.base.util.*;
0028:        import org.ofbiz.entity.GenericDelegator;
0029:        import org.ofbiz.entity.GenericValue;
0030:        import org.ofbiz.entity.GenericEntityException;
0031:
0032:        import javax.crypto.*;
0033:        import javax.crypto.Cipher;
0034:        import javax.crypto.IllegalBlockSizeException;
0035:        import javax.crypto.KeyGenerator;
0036:        import javax.crypto.SecretKey;
0037:        import javax.crypto.interfaces.DHPublicKey;
0038:        import javax.crypto.interfaces.DHPrivateKey;
0039:        import javax.crypto.spec.IvParameterSpec;
0040:        import javax.crypto.spec.DESedeKeySpec;
0041:        import javax.crypto.spec.DHParameterSpec;
0042:        import javax.crypto.spec.DHPublicKeySpec;
0043:        import javax.crypto.spec.DHPrivateKeySpec;
0044:        import javax.crypto.spec.DESKeySpec;
0045:        import java.security.*;
0046:        import java.security.spec.InvalidKeySpecException;
0047:        import java.security.spec.X509EncodedKeySpec;
0048:        import java.util.*;
0049:        import java.math.BigInteger;
0050:        import java.text.SimpleDateFormat;
0051:        import java.text.DecimalFormat;
0052:        import java.text.ParseException;
0053:
0054:        /**
0055:         * ValueLinkApi - Implementation of ValueLink Encryption & Transport
0056:         *
0057:         * @author     <a href="mailto:jaz@ofbiz.org">Andy Zeneski</a>
0058:         * @version    $Revision: 1.6 $
0059:         * @since      3.0
0060:         */
0061:        public class ValueLinkApi {
0062:
0063:            public static final String module = ValueLinkApi.class.getName();
0064:
0065:            // static object cache
0066:            private static Map objectCache = new HashMap();
0067:
0068:            // instance variables
0069:            protected GenericDelegator delegator = null;
0070:            protected Properties props = null;
0071:            protected SecretKey kek = null;
0072:            protected SecretKey mwk = null;
0073:            protected String merchantId = null;
0074:            protected String terminalId = null;
0075:            protected Long mwkIndex = null;
0076:            protected boolean debug = false;
0077:
0078:            protected ValueLinkApi() {
0079:            }
0080:
0081:            protected ValueLinkApi(GenericDelegator delegator, Properties props) {
0082:                String mId = (String) props.get("payment.valuelink.merchantId");
0083:                String tId = (String) props.get("payment.valuelink.terminalId");
0084:                this .delegator = delegator;
0085:                this .merchantId = mId;
0086:                this .terminalId = tId;
0087:                this .props = props;
0088:                if ("Y".equalsIgnoreCase((String) props
0089:                        .get("payment.valuelink.debug"))) {
0090:                    this .debug = true;
0091:                }
0092:
0093:                if (debug) {
0094:                    Debug.log("New ValueLinkApi instance created", module);
0095:                    Debug.log("Merchant ID : " + merchantId, module);
0096:                    Debug.log("Terminal ID : " + terminalId, module);
0097:                }
0098:            }
0099:
0100:            /**
0101:             * Obtain an instance of the ValueLinkApi
0102:             * @param delegator GenericDelegator used to query the encryption keys
0103:             * @param props Properties to use for the Api (usually payment.properties)
0104:             * @param reload When true, will replace an existing instance in the cache and reload all properties
0105:             * @return ValueLinkApi reference
0106:             */
0107:            public static ValueLinkApi getInstance(GenericDelegator delegator,
0108:                    Properties props, boolean reload) {
0109:                String merchantId = (String) props
0110:                        .get("payment.valuelink.merchantId");
0111:                if (props == null) {
0112:                    throw new IllegalArgumentException(
0113:                            "Properties cannot be null");
0114:                }
0115:
0116:                ValueLinkApi api = (ValueLinkApi) objectCache.get(merchantId);
0117:                if (api == null || reload) {
0118:                    synchronized (ValueLinkApi.class) {
0119:                        api = (ValueLinkApi) objectCache.get(merchantId);
0120:                        if (api == null || reload) {
0121:                            api = new ValueLinkApi(delegator, props);
0122:                            objectCache.put(merchantId, api);
0123:                        }
0124:                    }
0125:                }
0126:
0127:                if (api == null) {
0128:                    throw new RuntimeException(
0129:                            "Runtime problems with ValueLinkApi; unable to create instance");
0130:                }
0131:
0132:                return api;
0133:            }
0134:
0135:            /**
0136:             * Obtain an instance of the ValueLinkApi; this method will always return an existing reference if one is available
0137:             * @param delegator GenericDelegator used to query the encryption keys
0138:             * @param props Properties to use for the Api (usually payment.properties)
0139:             * @return
0140:             */
0141:            public static ValueLinkApi getInstance(GenericDelegator delegator,
0142:                    Properties props) {
0143:                return getInstance(delegator, props, false);
0144:            }
0145:
0146:            /**
0147:             * Encrypt the defined pin using the configured keys
0148:             * @param pin Plain text String of the pin
0149:             * @return Hex String of the encrypted pin (EAN) for transmission to ValueLink
0150:             */
0151:            public String encryptPin(String pin) {
0152:                // get the Cipher
0153:                Cipher mwkCipher = this .getCipher(this .getMwkKey(),
0154:                        Cipher.ENCRYPT_MODE);
0155:
0156:                // pin to bytes
0157:                byte[] pinBytes = pin.getBytes();
0158:
0159:                // 7 bytes of random data
0160:                byte[] random = this .getRandomBytes(7);
0161:
0162:                // pin checksum
0163:                byte[] checkSum = this .getPinCheckSum(pinBytes);
0164:
0165:                // put all together
0166:                byte[] eanBlock = new byte[16];
0167:                int i;
0168:                for (i = 0; i < random.length; i++) {
0169:                    eanBlock[i] = random[i];
0170:                }
0171:                eanBlock[7] = checkSum[0];
0172:                for (i = 0; i < pinBytes.length; i++) {
0173:                    eanBlock[i + 8] = pinBytes[i];
0174:                }
0175:
0176:                // encrypy the ean
0177:                String encryptedEanHex = null;
0178:                try {
0179:                    byte[] encryptedEan = mwkCipher.doFinal(eanBlock);
0180:                    encryptedEanHex = StringUtil.toHexString(encryptedEan);
0181:                } catch (IllegalStateException e) {
0182:                    Debug.logError(e, module);
0183:                } catch (IllegalBlockSizeException e) {
0184:                    Debug.logError(e, module);
0185:                } catch (BadPaddingException e) {
0186:                    Debug.logError(e, module);
0187:                }
0188:
0189:                if (debug) {
0190:                    Debug.log("encryptPin : " + pin + " / " + encryptedEanHex,
0191:                            module);
0192:                }
0193:
0194:                return encryptedEanHex;
0195:            }
0196:
0197:            /**
0198:             * Decrypt an encrypted pin using the configured keys
0199:             * @param pin Hex String of the encrypted pin (EAN)
0200:             * @return Plain text String of the pin
0201:             */
0202:            public String decryptPin(String pin) {
0203:                // get the Cipher
0204:                Cipher mwkCipher = this .getCipher(this .getMwkKey(),
0205:                        Cipher.DECRYPT_MODE);
0206:
0207:                // decrypt pin
0208:                String decryptedPinString = null;
0209:                try {
0210:                    byte[] decryptedEan = mwkCipher.doFinal(StringUtil
0211:                            .fromHexString(pin));
0212:                    byte[] decryptedPin = getByteRange(decryptedEan, 8, 8);
0213:                    decryptedPinString = new String(decryptedPin);
0214:                } catch (IllegalStateException e) {
0215:                    Debug.logError(e, module);
0216:                } catch (IllegalBlockSizeException e) {
0217:                    Debug.logError(e, module);
0218:                } catch (BadPaddingException e) {
0219:                    Debug.logError(e, module);
0220:                }
0221:
0222:                if (debug) {
0223:                    Debug.log("decryptPin : " + pin + " / "
0224:                            + decryptedPinString, module);
0225:                }
0226:
0227:                return decryptedPinString;
0228:            }
0229:
0230:            /**
0231:             * Transmit a request to ValueLink
0232:             * @param request Map of request parameters
0233:             * @return Map of response parameters
0234:             * @throws HttpClientException
0235:             */
0236:            public Map send(Map request) throws HttpClientException {
0237:                return send((String) props.get("payment.valuelink.url"),
0238:                        request);
0239:            }
0240:
0241:            /**
0242:             * Transmit a request to ValueLink
0243:             * @param url override URL from what is defined in the properties
0244:             * @param request request Map of request parameters
0245:             * @return Map of response parameters
0246:             * @throws HttpClientException
0247:             */
0248:            public Map send(String url, Map request) throws HttpClientException {
0249:                if (debug) {
0250:                    Debug.log("Request : " + url + " / " + request, module);
0251:                }
0252:
0253:                // read the timeout value
0254:                String timeoutString = (String) props
0255:                        .get("payment.valuelink.timeout");
0256:                int timeout = 34;
0257:                try {
0258:                    timeout = Integer.parseInt(timeoutString);
0259:                } catch (NumberFormatException e) {
0260:                    Debug.logError(e, "Unable to set timeout to "
0261:                            + timeoutString + " using default " + timeout);
0262:                }
0263:
0264:                // create the HTTP client
0265:                HttpClient client = new HttpClient(url, request);
0266:                client.setTimeout(timeout * 1000);
0267:                client.setDebug(debug);
0268:
0269:                client.setClientCertificateAlias((String) props
0270:                        .get("payment.valuelink.certificateAlias"));
0271:                String response = client.post();
0272:
0273:                // parse the response and return a map
0274:                return this .parseResponse(response);
0275:            }
0276:
0277:            /**
0278:             * Output the creation of public/private keys + KEK to the console for manual database update
0279:             */
0280:            public StringBuffer outputKeyCreation(boolean kekOnly,
0281:                    String kekTest) {
0282:                return this .outputKeyCreation(0, kekOnly, kekTest);
0283:            }
0284:
0285:            private StringBuffer outputKeyCreation(int loop, boolean kekOnly,
0286:                    String kekTest) {
0287:                StringBuffer buf = new StringBuffer();
0288:                loop++;
0289:
0290:                if (loop > 100) {
0291:                    // only loop 100 times; then throw an exception
0292:                    throw new IllegalStateException(
0293:                            "Unable to create 128 byte keys in 100 tries");
0294:                }
0295:
0296:                // place holder for the keys
0297:                DHPrivateKey privateKey = null;
0298:                DHPublicKey publicKey = null;
0299:
0300:                if (!kekOnly) {
0301:                    KeyPair keyPair = null;
0302:                    try {
0303:                        keyPair = this .createKeys();
0304:                    } catch (NoSuchAlgorithmException e) {
0305:                        Debug.logError(e, module);
0306:                    } catch (InvalidAlgorithmParameterException e) {
0307:                        Debug.logError(e, module);
0308:                    } catch (InvalidKeySpecException e) {
0309:                        Debug.logError(e, module);
0310:                    }
0311:
0312:                    if (keyPair != null) {
0313:                        publicKey = (DHPublicKey) keyPair.getPublic();
0314:                        privateKey = (DHPrivateKey) keyPair.getPrivate();
0315:
0316:                        if (publicKey == null
0317:                                || publicKey.getY().toByteArray().length != 128) {
0318:                            // run again until we get a 128 byte public key for VL
0319:                            return this .outputKeyCreation(loop, kekOnly,
0320:                                    kekTest);
0321:                        }
0322:                    } else {
0323:                        Debug.log("Returned a null KeyPair", module);
0324:                        return this .outputKeyCreation(loop, kekOnly, kekTest);
0325:                    }
0326:                } else {
0327:                    // use our existing private key to generate a KEK
0328:                    try {
0329:                        privateKey = (DHPrivateKey) this .getPrivateKey();
0330:                    } catch (Exception e) {
0331:                        Debug.logError(e, module);
0332:                    }
0333:                }
0334:
0335:                // the KEK
0336:                byte[] kekBytes = null;
0337:                try {
0338:                    kekBytes = this .generateKek(privateKey);
0339:                } catch (NoSuchAlgorithmException e) {
0340:                    Debug.logError(e, module);
0341:                } catch (InvalidKeySpecException e) {
0342:                    Debug.logError(e, module);
0343:                } catch (InvalidKeyException e) {
0344:                    Debug.logError(e, module);
0345:                }
0346:
0347:                // the 3DES KEK value
0348:                SecretKey loadedKek = this .getDesEdeKey(kekBytes);
0349:                byte[] loadKekBytes = loadedKek.getEncoded();
0350:
0351:                // test the KEK
0352:                Cipher cipher = this .getCipher(this .getKekKey(),
0353:                        Cipher.ENCRYPT_MODE);
0354:                byte[] kekTestB = { 0, 0, 0, 0, 0, 0, 0, 0 };
0355:                byte[] kekTestC = new byte[0];
0356:                if (kekTest != null) {
0357:                    kekTestB = StringUtil.fromHexString(kekTest);
0358:                }
0359:
0360:                // encrypt the test bytes
0361:                try {
0362:                    kekTestC = cipher.doFinal(kekTestB);
0363:                } catch (Exception e) {
0364:                    Debug.logError(e, module);
0365:                }
0366:
0367:                if (!kekOnly) {
0368:                    // public key (just Y)
0369:                    BigInteger y = publicKey.getY();
0370:                    byte[] yBytes = y.toByteArray();
0371:                    String yHex = StringUtil.toHexString(yBytes);
0372:                    buf.append("======== Begin Public Key (Y @ "
0373:                            + yBytes.length + " / " + yHex.length()
0374:                            + ") ========\n");
0375:                    buf.append(yHex + "\n");
0376:                    buf.append("======== End Public Key ========\n\n");
0377:
0378:                    // private key (just X)
0379:                    BigInteger x = privateKey.getX();
0380:                    byte[] xBytes = x.toByteArray();
0381:                    String xHex = StringUtil.toHexString(xBytes);
0382:                    buf.append("======== Begin Private Key (X @ "
0383:                            + xBytes.length + " / " + xHex.length()
0384:                            + ") ========\n");
0385:                    buf.append(xHex + "\n");
0386:                    buf.append("======== End Private Key ========\n\n");
0387:
0388:                    // private key (full)
0389:                    byte[] privateBytes = privateKey.getEncoded();
0390:                    String privateHex = StringUtil.toHexString(privateBytes);
0391:                    buf.append("======== Begin Private Key (Full @ "
0392:                            + privateBytes.length + " / " + privateHex.length()
0393:                            + ") ========\n");
0394:                    buf.append(privateHex + "\n");
0395:                    buf.append("======== End Private Key ========\n\n");
0396:                }
0397:
0398:                if (kekBytes != null) {
0399:                    buf.append("======== Begin KEK (" + kekBytes.length
0400:                            + ") ========\n");
0401:                    buf.append(StringUtil.toHexString(kekBytes) + "\n");
0402:                    buf.append("======== End KEK ========\n\n");
0403:
0404:                    buf.append("======== Begin KEK (DES) ("
0405:                            + loadKekBytes.length + ") ========\n");
0406:                    buf.append(StringUtil.toHexString(loadKekBytes) + "\n");
0407:                    buf.append("======== End KEK (DES) ========\n\n");
0408:
0409:                    buf.append("======== Begin KEK Test (" + kekTestC.length
0410:                            + ") ========\n");
0411:                    buf.append(StringUtil.toHexString(kekTestC) + "\n");
0412:                    buf.append("======== End KEK Test ========\n\n");
0413:                } else {
0414:                    Debug.logError("KEK came back empty", module);
0415:                }
0416:
0417:                return buf;
0418:            }
0419:
0420:            /**
0421:             * Create a set of public/private keys using ValueLinks defined parameters
0422:             * @return KeyPair object containing both public and private keys
0423:             * @throws NoSuchAlgorithmException
0424:             * @throws InvalidAlgorithmParameterException
0425:             */
0426:            public KeyPair createKeys() throws NoSuchAlgorithmException,
0427:                    InvalidAlgorithmParameterException, InvalidKeySpecException {
0428:                // initialize the parameter spec
0429:                DHPublicKey publicKey = (DHPublicKey) this 
0430:                        .getValueLinkPublicKey();
0431:                DHParameterSpec dhParamSpec = publicKey.getParams();
0432:                //Debug.log(dhParamSpec.getP().toString() + " / " + dhParamSpec.getG().toString(), module);
0433:
0434:                // create the public/private key pair using parameters defined by valuelink
0435:                KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
0436:                keyGen.initialize(dhParamSpec);
0437:                KeyPair keyPair = keyGen.generateKeyPair();
0438:
0439:                return keyPair;
0440:            }
0441:
0442:            /**
0443:             * Generate a key exchange key for use in encrypting the mwk
0444:             * @param privateKey The private key for the merchant
0445:             * @return byte array containing the kek
0446:             * @throws NoSuchAlgorithmException
0447:             * @throws InvalidKeySpecException
0448:             * @throws InvalidKeyException
0449:             */
0450:            public byte[] generateKek(PrivateKey privateKey)
0451:                    throws NoSuchAlgorithmException, InvalidKeySpecException,
0452:                    InvalidKeyException {
0453:                // get the ValueLink public key
0454:                PublicKey vlPublic = this .getValueLinkPublicKey();
0455:
0456:                // generate shared secret key
0457:                KeyAgreement ka = KeyAgreement.getInstance("DH");
0458:                ka.init(privateKey);
0459:                ka.doPhase(vlPublic, true);
0460:                byte[] secretKey = ka.generateSecret();
0461:
0462:                if (debug) {
0463:                    Debug.log("Secret Key : "
0464:                            + StringUtil.toHexString(secretKey) + " / "
0465:                            + secretKey.length, module);
0466:                }
0467:
0468:                // generate 3DES from secret key using VL algorithm (KEK)
0469:                MessageDigest md = MessageDigest.getInstance("SHA1");
0470:                byte[] digest = md.digest(secretKey);
0471:                byte[] des2 = getByteRange(digest, 0, 16);
0472:                byte[] first8 = getByteRange(des2, 0, 8);
0473:                byte[] kek = copyBytes(des2, first8, 0);
0474:
0475:                if (debug) {
0476:                    Debug.log("Generated KEK : " + StringUtil.toHexString(kek)
0477:                            + " / " + kek.length, module);
0478:                }
0479:
0480:                return kek;
0481:            }
0482:
0483:            /**
0484:             * Get a public key object for the ValueLink supplied public key
0485:             * @return PublicKey object of ValueLinks's public key
0486:             * @throws NoSuchAlgorithmException
0487:             * @throws InvalidKeySpecException
0488:             */
0489:            public PublicKey getValueLinkPublicKey()
0490:                    throws NoSuchAlgorithmException, InvalidKeySpecException {
0491:                // read the valuelink public key
0492:                String publicValue = (String) props
0493:                        .get("payment.valuelink.publicValue");
0494:                byte[] publicKeyBytes = StringUtil.fromHexString(publicValue);
0495:
0496:                // initialize the parameter spec
0497:                DHParameterSpec dhParamSpec = this .getDHParameterSpec();
0498:
0499:                // load the valuelink public key
0500:                KeyFactory keyFactory = KeyFactory.getInstance("DH");
0501:                BigInteger publicKeyInt = new BigInteger(publicKeyBytes);
0502:                DHPublicKeySpec dhPublicSpec = new DHPublicKeySpec(
0503:                        publicKeyInt, dhParamSpec.getP(), dhParamSpec.getG());
0504:                PublicKey vlPublic = keyFactory.generatePublic(dhPublicSpec);
0505:
0506:                return vlPublic;
0507:            }
0508:
0509:            /**
0510:             * Get merchant Private Key
0511:             * @return PrivateKey object for the merchant
0512:             */
0513:            public PrivateKey getPrivateKey() throws InvalidKeySpecException,
0514:                    NoSuchAlgorithmException {
0515:                byte[] privateKeyBytes = this .getPrivateKeyBytes();
0516:
0517:                // initialize the parameter spec
0518:                DHParameterSpec dhParamSpec = this .getDHParameterSpec();
0519:
0520:                // load the private key
0521:                KeyFactory keyFactory = KeyFactory.getInstance("DH");
0522:                BigInteger privateKeyInt = new BigInteger(privateKeyBytes);
0523:                DHPrivateKeySpec dhPrivateSpec = new DHPrivateKeySpec(
0524:                        privateKeyInt, dhParamSpec.getP(), dhParamSpec.getG());
0525:                PrivateKey privateKey = keyFactory
0526:                        .generatePrivate(dhPrivateSpec);
0527:
0528:                return privateKey;
0529:            }
0530:
0531:            /**
0532:             * Generate a new MWK
0533:             * @return Hex String of the new encrypted MWK ready for transmission to ValueLink
0534:             */
0535:            public byte[] generateMwk() {
0536:                KeyGenerator keyGen = null;
0537:                try {
0538:                    keyGen = KeyGenerator.getInstance("DES");
0539:                } catch (NoSuchAlgorithmException e) {
0540:                    Debug.logError(e, module);
0541:                }
0542:
0543:                // generate the DES key 1
0544:                SecretKey des1 = keyGen.generateKey();
0545:                SecretKey des2 = keyGen.generateKey();
0546:
0547:                if (des1 != null && des2 != null) {
0548:                    byte[] desByte1 = des1.getEncoded();
0549:                    byte[] desByte2 = des2.getEncoded();
0550:                    byte[] desByte3 = des1.getEncoded();
0551:
0552:                    // check for weak keys
0553:                    try {
0554:                        if (DESKeySpec.isWeak(des1.getEncoded(), 0)
0555:                                || DESKeySpec.isWeak(des2.getEncoded(), 0)) {
0556:                            return generateMwk();
0557:                        }
0558:                    } catch (Exception e) {
0559:                        Debug.logError(e, module);
0560:                    }
0561:
0562:                    byte[] des3 = copyBytes(desByte1, copyBytes(desByte2,
0563:                            desByte3, 0), 0);
0564:                    return generateMwk(des3);
0565:                } else {
0566:                    Debug.log("Null DES keys returned", module);
0567:                }
0568:
0569:                return null;
0570:            }
0571:
0572:            /**
0573:             * Generate a new MWK
0574:             * @param desBytes byte array of the DES key (24 bytes)
0575:             * @return Hex String of the new encrypted MWK ready for transmission to ValueLink
0576:             */
0577:            public byte[] generateMwk(byte[] desBytes) {
0578:                if (debug) {
0579:                    Debug.log("DES Key : " + StringUtil.toHexString(desBytes)
0580:                            + " / " + desBytes.length, module);
0581:                }
0582:                SecretKeyFactory skf1 = null;
0583:                SecretKey mwk = null;
0584:                try {
0585:                    skf1 = SecretKeyFactory.getInstance("DESede");
0586:                } catch (NoSuchAlgorithmException e) {
0587:                    Debug.logError(e, module);
0588:                }
0589:                DESedeKeySpec desedeSpec2 = null;
0590:                try {
0591:                    desedeSpec2 = new DESedeKeySpec(desBytes);
0592:                } catch (InvalidKeyException e) {
0593:                    Debug.logError(e, module);
0594:                }
0595:                if (skf1 != null && desedeSpec2 != null) {
0596:                    try {
0597:                        mwk = skf1.generateSecret(desedeSpec2);
0598:                    } catch (InvalidKeySpecException e) {
0599:                        Debug.logError(e, module);
0600:                    }
0601:                }
0602:                if (mwk != null) {
0603:                    return generateMwk(mwk);
0604:                } else {
0605:                    return null;
0606:                }
0607:            }
0608:
0609:            /**
0610:             * Generate a new MWK
0611:             * @param mwkdes3 pre-generated DES3 SecretKey
0612:             * @return Hex String of the new encrypted MWK ready for transmission to ValueLink
0613:             */
0614:            public byte[] generateMwk(SecretKey mwkdes3) {
0615:                // zeros for checksum
0616:                byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 };
0617:
0618:                // 8 bytes random data
0619:                byte[] random = new byte[8];
0620:                Random ran = new Random();
0621:                ran.nextBytes(random);
0622:
0623:                // open a cipher using the new mwk
0624:                Cipher cipher = this .getCipher(mwkdes3, Cipher.ENCRYPT_MODE);
0625:
0626:                // make the checksum - encrypted 8 bytes of 0's
0627:                byte[] encryptedZeros = new byte[0];
0628:                try {
0629:                    encryptedZeros = cipher.doFinal(zeros);
0630:                } catch (IllegalStateException e) {
0631:                    Debug.logError(e, module);
0632:                } catch (IllegalBlockSizeException e) {
0633:                    Debug.logError(e, module);
0634:                } catch (BadPaddingException e) {
0635:                    Debug.logError(e, module);
0636:                }
0637:
0638:                // make the 40 byte MWK - random 8 bytes + key + checksum
0639:                byte[] newMwk = copyBytes(mwkdes3.getEncoded(), encryptedZeros,
0640:                        0);
0641:                newMwk = copyBytes(random, newMwk, 0);
0642:
0643:                if (debug) {
0644:                    Debug.log("Random 8 byte : "
0645:                            + StringUtil.toHexString(random), module);
0646:                    Debug.log("Encrypted 0's : "
0647:                            + StringUtil.toHexString(encryptedZeros), module);
0648:                    Debug.log("Decrypted MWK : "
0649:                            + StringUtil.toHexString(mwkdes3.getEncoded())
0650:                            + " / " + mwkdes3.getEncoded().length, module);
0651:                    Debug.log("Encrypted MWK : "
0652:                            + StringUtil.toHexString(newMwk) + " / "
0653:                            + newMwk.length, module);
0654:                }
0655:
0656:                return newMwk;
0657:            }
0658:
0659:            /**
0660:             * Use the KEK to encrypt a value usually the MWK
0661:             * @param content byte array to encrypt
0662:             * @return encrypted byte array
0663:             */
0664:            public byte[] encryptViaKek(byte[] content) {
0665:                return cryptoViaKek(content, Cipher.ENCRYPT_MODE);
0666:            }
0667:
0668:            /**
0669:             * Ue the KEK to decrypt a value
0670:             * @param content byte array to decrypt
0671:             * @return decrypted byte array
0672:             */
0673:            public byte[] decryptViaKek(byte[] content) {
0674:                return cryptoViaKek(content, Cipher.DECRYPT_MODE);
0675:            }
0676:
0677:            /**
0678:             * Returns a date string formatted as directed by ValueLink
0679:             * @return ValueLink formatted date String
0680:             */
0681:            public String getDateString() {
0682:                String format = (String) props
0683:                        .get("payment.valuelink.timestamp");
0684:                SimpleDateFormat sdf = new SimpleDateFormat(format);
0685:                return sdf.format(new Date());
0686:            }
0687:
0688:            /**
0689:             * Returns the current working key index
0690:             * @return Long number of the current working key index
0691:             */
0692:            public Long getWorkingKeyIndex() {
0693:                if (this .mwkIndex == null) {
0694:                    synchronized (this ) {
0695:                        if (this .mwkIndex == null) {
0696:                            this .mwkIndex = this .getGenericValue().getLong(
0697:                                    "workingKeyIndex");
0698:                        }
0699:                    }
0700:                }
0701:
0702:                if (debug) {
0703:                    Debug.log("Current Working Key Index : " + this .mwkIndex,
0704:                            module);
0705:                }
0706:
0707:                return this .mwkIndex;
0708:            }
0709:
0710:            /**
0711:             * Returns a ValueLink formatted amount String
0712:             * @param amount Double value to format
0713:             * @return Formatted String
0714:             */
0715:            public String getAmount(Double amount) {
0716:                if (amount == null) {
0717:                    return "0.00";
0718:                }
0719:                String currencyFormat = UtilProperties.getPropertyValue(
0720:                        "general.properties", "currency.decimal.format",
0721:                        "##0.00");
0722:                DecimalFormat formatter = new DecimalFormat(currencyFormat);
0723:                String amountString = formatter.format(amount.doubleValue());
0724:                Double newAmount = null;
0725:                try {
0726:                    newAmount = new Double(formatter.parse(amountString)
0727:                            .doubleValue());
0728:                } catch (ParseException e) {
0729:                    Debug.logError(e, "Unable to parse amount Double");
0730:                }
0731:
0732:                String formattedString = null;
0733:                if (newAmount != null) {
0734:                    double amountDouble = newAmount.doubleValue() * 100;
0735:                    formattedString = new String(new Integer(new Double(
0736:                            amountDouble).intValue()).toString());
0737:                }
0738:                return formattedString;
0739:            }
0740:
0741:            /**
0742:             * Returns a Double from a ValueLink formatted amount String
0743:             * @param amount The ValueLink formatted amount String
0744:             * @return Double object
0745:             */
0746:            public Double getAmount(String amount) {
0747:                if (amount == null) {
0748:                    return new Double(0.00);
0749:                }
0750:                Double doubleAmount = new Double(amount);
0751:                return new Double(doubleAmount.doubleValue() / 100);
0752:            }
0753:
0754:            public String getCurrency(String currency) {
0755:                return "840"; // todo make this multi-currency
0756:            }
0757:
0758:            /**
0759:             * Creates a Map of initial request values (MerchID, AltMerchNo, Modes, MerchTime, TermTxnNo, EncryptID)
0760:             * Note: For 2010 (assign working key) transaction, the EncryptID will need to be adjusted
0761:             * @return Map containing the inital request values
0762:             */
0763:            public Map getInitialRequestMap(Map context) {
0764:                Map request = new HashMap();
0765:
0766:                // merchant information
0767:                request.put("MerchID", merchantId + terminalId);
0768:                request.put("AltMerchNo", props
0769:                        .get("payment.valuelink.altMerchantId"));
0770:
0771:                // mode settings
0772:                String modes = (String) props.get("payment.valuelink.modes");
0773:                if (modes != null && modes.length() > 0) {
0774:                    request.put("Modes", modes);
0775:                }
0776:
0777:                // merchant timestamp
0778:                String merchTime = (String) context.get("MerchTime");
0779:                if (merchTime == null) {
0780:                    merchTime = this .getDateString();
0781:                }
0782:                request.put("MerchTime", merchTime);
0783:
0784:                // transaction number
0785:                String termTxNo = (String) context.get("TermTxnNo");
0786:                if (termTxNo == null) {
0787:                    termTxNo = delegator.getNextSeqId("ValueLinkKey")
0788:                            .toString();
0789:                }
0790:                request.put("TermTxnNo", termTxNo);
0791:
0792:                // current working key index
0793:                request.put("EncryptID", this .getWorkingKeyIndex());
0794:
0795:                if (debug) {
0796:                    Debug.log("Created Initial Request Map : " + request,
0797:                            module);
0798:                }
0799:
0800:                return request;
0801:            }
0802:
0803:            /**
0804:             * Gets the cached value object for this merchant's keys
0805:             * @return Cached GenericValue object
0806:             */
0807:            public GenericValue getGenericValue() {
0808:                GenericValue value = null;
0809:                try {
0810:                    value = delegator.findByPrimaryKeyCache("ValueLinkKey",
0811:                            UtilMisc.toMap("merchantId", merchantId));
0812:                } catch (GenericEntityException e) {
0813:                    Debug.logError(e, module);
0814:                }
0815:                if (value == null) {
0816:                    throw new RuntimeException(
0817:                            "No ValueLinkKey record found for Merchant ID : "
0818:                                    + merchantId);
0819:                }
0820:                return value;
0821:            }
0822:
0823:            /**
0824:             * Reloads the keys in the object cache; use this when re-creating keys
0825:             */
0826:            public void reload() {
0827:                this .kek = null;
0828:                this .mwk = null;
0829:                this .mwkIndex = null;
0830:            }
0831:
0832:            // using the prime and generator provided by valuelink; create a parameter object
0833:            protected DHParameterSpec getDHParameterSpec() {
0834:                String primeHex = (String) props.get("payment.valuelink.prime");
0835:                String genString = (String) props
0836:                        .get("payment.valuelink.generator");
0837:
0838:                // convert the p/g hex values
0839:                byte[] primeByte = StringUtil.fromHexString(primeHex);
0840:                BigInteger prime = new BigInteger(1, primeByte); // force positive (unsigned)
0841:                BigInteger generator = new BigInteger(genString);
0842:
0843:                // initialize the parameter spec
0844:                DHParameterSpec dhParamSpec = new DHParameterSpec(prime,
0845:                        generator, 1024);
0846:
0847:                return dhParamSpec;
0848:            }
0849:
0850:            // actual kek encryption/decryption code
0851:            protected byte[] cryptoViaKek(byte[] content, int mode) {
0852:                // open a cipher using the kek for transport
0853:                Cipher cipher = this .getCipher(this .getKekKey(), mode);
0854:                byte[] dec = new byte[0];
0855:                try {
0856:                    dec = cipher.doFinal(content);
0857:                } catch (IllegalStateException e) {
0858:                    Debug.logError(e, module);
0859:                } catch (IllegalBlockSizeException e) {
0860:                    Debug.logError(e, module);
0861:                } catch (BadPaddingException e) {
0862:                    Debug.logError(e, module);
0863:                }
0864:                return dec;
0865:            }
0866:
0867:            // return a cipher for a key - DESede/CBC/NoPadding IV = 0
0868:            protected Cipher getCipher(SecretKey key, int mode) {
0869:                byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 };
0870:                IvParameterSpec iv = new IvParameterSpec(zeros);
0871:
0872:                // create the Cipher - DESede/CBC/NoPadding
0873:                Cipher mwkCipher = null;
0874:                try {
0875:                    mwkCipher = Cipher.getInstance("DESede/CBC/NoPadding");
0876:                } catch (NoSuchAlgorithmException e) {
0877:                    Debug.logError(e, module);
0878:                    return null;
0879:                } catch (NoSuchPaddingException e) {
0880:                    Debug.logError(e, module);
0881:                }
0882:                try {
0883:                    mwkCipher.init(mode, key, iv);
0884:                } catch (InvalidKeyException e) {
0885:                    Debug.logError(e, "Invalid key", module);
0886:                } catch (InvalidAlgorithmParameterException e) {
0887:                    Debug.logError(e, module);
0888:                }
0889:                return mwkCipher;
0890:            }
0891:
0892:            protected byte[] getPinCheckSum(byte[] pinBytes) {
0893:                byte[] checkSum = new byte[1];
0894:                checkSum[0] = 0;
0895:                for (int i = 0; i < pinBytes.length; i++) {
0896:                    checkSum[0] += pinBytes[i];
0897:                }
0898:                return checkSum;
0899:            }
0900:
0901:            protected byte[] getRandomBytes(int length) {
0902:                Random rand = new Random();
0903:                byte[] randomBytes = new byte[length];
0904:                rand.nextBytes(randomBytes);
0905:                return randomBytes;
0906:            }
0907:
0908:            protected SecretKey getMwkKey() {
0909:                if (mwk == null) {
0910:                    mwk = this .getDesEdeKey(getByteRange(getMwk(), 8, 24));
0911:                }
0912:
0913:                if (debug) {
0914:                    Debug.log("Raw MWK : " + StringUtil.toHexString(getMwk()),
0915:                            module);
0916:                    Debug.log("MWK : "
0917:                            + StringUtil.toHexString(mwk.getEncoded()), module);
0918:                }
0919:
0920:                return mwk;
0921:            }
0922:
0923:            protected SecretKey getKekKey() {
0924:                if (kek == null) {
0925:                    kek = this .getDesEdeKey(getKek());
0926:                }
0927:
0928:                if (debug) {
0929:                    Debug.log("Raw KEK : " + StringUtil.toHexString(getKek()),
0930:                            module);
0931:                    Debug.log("KEK : "
0932:                            + StringUtil.toHexString(kek.getEncoded()), module);
0933:                }
0934:
0935:                return kek;
0936:            }
0937:
0938:            protected SecretKey getDesEdeKey(byte[] rawKey) {
0939:                SecretKeyFactory skf = null;
0940:                try {
0941:                    skf = SecretKeyFactory.getInstance("DESede");
0942:                } catch (NoSuchAlgorithmException e) {
0943:                    // should never happen since DESede is a standard algorithm
0944:                    Debug.logError(e, module);
0945:                    return null;
0946:                }
0947:
0948:                // load the raw key
0949:                if (rawKey.length > 0) {
0950:                    DESedeKeySpec desedeSpec1 = null;
0951:                    try {
0952:                        desedeSpec1 = new DESedeKeySpec(rawKey);
0953:                    } catch (InvalidKeyException e) {
0954:                        Debug.logError(e, "Not a valid DESede key", module);
0955:                        return null;
0956:                    }
0957:
0958:                    // create the SecretKey Object
0959:                    SecretKey key = null;
0960:                    try {
0961:                        key = skf.generateSecret(desedeSpec1);
0962:                    } catch (InvalidKeySpecException e) {
0963:                        Debug.logError(e, module);
0964:                    }
0965:                    return key;
0966:                } else {
0967:                    throw new RuntimeException("No valid DESede key available");
0968:                }
0969:            }
0970:
0971:            protected byte[] getMwk() {
0972:                return StringUtil.fromHexString(this .getGenericValue()
0973:                        .getString("workingKey"));
0974:            }
0975:
0976:            protected byte[] getKek() {
0977:                return StringUtil.fromHexString(this .getGenericValue()
0978:                        .getString("exchangeKey"));
0979:            }
0980:
0981:            protected byte[] getPrivateKeyBytes() {
0982:                return StringUtil.fromHexString(this .getGenericValue()
0983:                        .getString("privateKey"));
0984:            }
0985:
0986:            protected Map parseResponse(String response) {
0987:                if (debug) {
0988:                    Debug.log("Raw Response : " + response, module);
0989:                }
0990:
0991:                // covert to all lowercase and trim off the html header
0992:                String subResponse = response.toLowerCase();
0993:                int firstIndex = subResponse.indexOf("<tr>");
0994:                int lastIndex = subResponse.lastIndexOf("</tr>");
0995:                subResponse = subResponse.substring(firstIndex, lastIndex);
0996:
0997:                // check for a history table
0998:                String history = null;
0999:                List historyMapList = null;
1000:                if (subResponse.indexOf("<table") > -1) {
1001:                    int startHistory = subResponse.indexOf("<table");
1002:                    int endHistory = subResponse.indexOf("</table>") + 8;
1003:                    history = subResponse.substring(startHistory, endHistory);
1004:
1005:                    // replace the subResponse string so it doesn't conflict
1006:                    subResponse = StringUtil.replaceString(subResponse,
1007:                            history, "[_HISTORY_]");
1008:
1009:                    // parse the history into a list of maps
1010:                    historyMapList = this .parseHistoryResponse(history);
1011:                }
1012:
1013:                // replace all end rows with | this is the name delimiter
1014:                subResponse = StringUtil.replaceString(subResponse, "</tr>",
1015:                        "|");
1016:
1017:                // replace all </TD><TD> with = this is the value delimiter
1018:                subResponse = StringUtil.replaceString(subResponse,
1019:                        "</td><td>", "=");
1020:
1021:                // clean off a bunch of other useless stuff
1022:                subResponse = StringUtil.replaceString(subResponse, "<tr>", "");
1023:                subResponse = StringUtil.replaceString(subResponse, "<td>", "");
1024:                subResponse = StringUtil
1025:                        .replaceString(subResponse, "</td>", "");
1026:
1027:                // make the map
1028:                Map responseMap = StringUtil.strToMap(subResponse, true);
1029:
1030:                // add the raw html back in just in case we need it later
1031:                responseMap.put("_rawHtmlResponse", response);
1032:
1033:                // if we have a history add it back in
1034:                if (history != null) {
1035:                    responseMap.put("_rawHistoryHtml", history);
1036:                    responseMap.put("history", historyMapList);
1037:                }
1038:
1039:                if (debug) {
1040:                    Debug.log("Response Map : " + responseMap, module);
1041:                }
1042:
1043:                return responseMap;
1044:            }
1045:
1046:            private List parseHistoryResponse(String response) {
1047:                if (debug) {
1048:                    Debug.log("Raw History : " + response, module);
1049:                }
1050:
1051:                // covert to all lowercase and trim off the html header
1052:                String subResponse = response.toLowerCase();
1053:                int firstIndex = subResponse.indexOf("<tr>");
1054:                int lastIndex = subResponse.lastIndexOf("</tr>");
1055:                subResponse = subResponse.substring(firstIndex, lastIndex);
1056:
1057:                // clean up the html and replace the delimiters with '|'
1058:                subResponse = StringUtil.replaceString(subResponse, "<td>", "");
1059:                subResponse = StringUtil.replaceString(subResponse, "</td>",
1060:                        "|");
1061:
1062:                // test the string to make sure we have fields to parse
1063:                String testResponse = StringUtil.replaceString(subResponse,
1064:                        "<tr>", "");
1065:                testResponse = StringUtil.replaceString(testResponse, "</tr>",
1066:                        "");
1067:                testResponse = StringUtil.replaceString(testResponse, "|", "");
1068:                testResponse = testResponse.trim();
1069:                if (testResponse.length() == 0) {
1070:                    if (debug) {
1071:                        Debug
1072:                                .log(
1073:                                        "History did not contain any fields, returning null",
1074:                                        module);
1075:                    }
1076:                    return null;
1077:                }
1078:
1079:                // break up the keys from the values
1080:                int valueStart = subResponse.indexOf("</tr>");
1081:                String keys = subResponse.substring(4, valueStart - 1);
1082:                String values = subResponse.substring(valueStart + 9,
1083:                        subResponse.length() - 6);
1084:
1085:                // split sets of values up
1086:                values = StringUtil.replaceString(values, "|</tr><tr>", "&");
1087:                List valueList = StringUtil.split(values, "&");
1088:
1089:                // create a List of Maps for each set of values
1090:                List valueMap = new ArrayList();
1091:                for (int i = 0; i < valueList.size(); i++) {
1092:                    valueMap.add(StringUtil.createMap(StringUtil.split(keys,
1093:                            "|"), StringUtil.split((String) valueList.get(i),
1094:                            "|")));
1095:                }
1096:
1097:                if (debug) {
1098:                    Debug.log("History Map : " + valueMap, module);
1099:                }
1100:
1101:                return valueMap;
1102:            }
1103:
1104:            /**
1105:             * Returns a new byte[] from the offset of the defined byte[] with a specific number of bytes
1106:             * @param bytes The byte[] to extract from
1107:             * @param offset The starting postition
1108:             * @param length The number of bytes to copy
1109:             * @return a new byte[]
1110:             */
1111:            public static byte[] getByteRange(byte[] bytes, int offset,
1112:                    int length) {
1113:                byte[] newBytes = new byte[length];
1114:                for (int i = 0; i < length; i++) {
1115:                    newBytes[i] = bytes[offset + i];
1116:                }
1117:                return newBytes;
1118:            }
1119:
1120:            /**
1121:             * Copies a byte[] into another byte[] starting at a specific position
1122:             * @param source byte[] to copy from
1123:             * @param target byte[] coping into
1124:             * @param position the position on target where source will be copied to
1125:             * @return a new byte[]
1126:             */
1127:            public static byte[] copyBytes(byte[] source, byte[] target,
1128:                    int position) {
1129:                byte[] newBytes = new byte[target.length + source.length];
1130:                for (int i = 0, n = 0, x = 0; i < newBytes.length; i++) {
1131:                    if (i < position || i > (position + source.length - 2)) {
1132:                        newBytes[i] = target[n];
1133:                        n++;
1134:                    } else {
1135:                        for (; x < source.length; x++) {
1136:                            newBytes[i] = source[x];
1137:                            if (source.length - 1 > x) {
1138:                                i++;
1139:                            }
1140:                        }
1141:                    }
1142:                }
1143:                return newBytes;
1144:            }
1145:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.