📄 valuelinkapi.java
字号:
/* * $Id: ValueLinkApi.java 5462 2005-08-05 18:35:48Z jonesde $ * * Copyright (c) 2003 The Open For Business Project - www.ofbiz.org * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */package org.ofbiz.accounting.thirdparty.valuelink;import java.math.BigInteger;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.PublicKey;import java.security.spec.InvalidKeySpecException;import java.text.DecimalFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import java.util.Random;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.KeyAgreement;import javax.crypto.KeyGenerator;import javax.crypto.NoSuchPaddingException;import javax.crypto.SecretKey;import javax.crypto.SecretKeyFactory;import javax.crypto.interfaces.DHPrivateKey;import javax.crypto.interfaces.DHPublicKey;import javax.crypto.spec.DESKeySpec;import javax.crypto.spec.DESedeKeySpec;import javax.crypto.spec.DHParameterSpec;import javax.crypto.spec.DHPrivateKeySpec;import javax.crypto.spec.DHPublicKeySpec;import javax.crypto.spec.IvParameterSpec;import org.ofbiz.base.util.Debug;import org.ofbiz.base.util.HttpClient;import org.ofbiz.base.util.HttpClientException;import org.ofbiz.base.util.StringUtil;import org.ofbiz.base.util.UtilMisc;import org.ofbiz.base.util.UtilProperties;import org.ofbiz.entity.GenericDelegator;import org.ofbiz.entity.GenericEntityException;import org.ofbiz.entity.GenericValue;/** * ValueLinkApi - Implementation of ValueLink Encryption & Transport * * @author <a href="mailto:jaz@ofbiz.org">Andy Zeneski</a> * @version $Rev: 5462 $ * @since 3.0 */public class ValueLinkApi { public static final String module = ValueLinkApi.class.getName(); // static object cache private static Map objectCache = new HashMap(); // instance variables protected GenericDelegator delegator = null; protected Properties props = null; protected SecretKey kek = null; protected SecretKey mwk = null; protected String merchantId = null; protected String terminalId = null; protected Long mwkIndex = null; protected boolean debug = false; protected ValueLinkApi() {} protected ValueLinkApi(GenericDelegator delegator, Properties props) { String mId = (String) props.get("payment.valuelink.merchantId"); String tId = (String) props.get("payment.valuelink.terminalId"); this.delegator = delegator; this.merchantId = mId; this.terminalId = tId; this.props = props; if ("Y".equalsIgnoreCase((String) props.get("payment.valuelink.debug"))) { this.debug = true; } if (debug) { Debug.log("New ValueLinkApi instance created", module); Debug.log("Merchant ID : " + merchantId, module); Debug.log("Terminal ID : " + terminalId, module); } } /** * Obtain an instance of the ValueLinkApi * @param delegator GenericDelegator used to query the encryption keys * @param props Properties to use for the Api (usually payment.properties) * @param reload When true, will replace an existing instance in the cache and reload all properties * @return ValueLinkApi reference */ public static ValueLinkApi getInstance(GenericDelegator delegator, Properties props, boolean reload) { String merchantId = (String) props.get("payment.valuelink.merchantId"); if (props == null) { throw new IllegalArgumentException("Properties cannot be null"); } ValueLinkApi api = (ValueLinkApi) objectCache.get(merchantId); if (api == null || reload) { synchronized(ValueLinkApi.class) { api = (ValueLinkApi) objectCache.get(merchantId); if (api == null || reload) { api = new ValueLinkApi(delegator, props); objectCache.put(merchantId, api); } } } if (api == null) { throw new RuntimeException("Runtime problems with ValueLinkApi; unable to create instance"); } return api; } /** * Obtain an instance of the ValueLinkApi; this method will always return an existing reference if one is available * @param delegator GenericDelegator used to query the encryption keys * @param props Properties to use for the Api (usually payment.properties) * @return */ public static ValueLinkApi getInstance(GenericDelegator delegator, Properties props) { return getInstance(delegator, props, false); } /** * Encrypt the defined pin using the configured keys * @param pin Plain text String of the pin * @return Hex String of the encrypted pin (EAN) for transmission to ValueLink */ public String encryptPin(String pin) { // get the Cipher Cipher mwkCipher = this.getCipher(this.getMwkKey(), Cipher.ENCRYPT_MODE); // pin to bytes byte[] pinBytes = pin.getBytes(); // 7 bytes of random data byte[] random = this.getRandomBytes(7); // pin checksum byte[] checkSum = this.getPinCheckSum(pinBytes); // put all together byte[] eanBlock = new byte[16]; int i; for (i = 0; i < random.length; i++) { eanBlock[i] = random[i]; } eanBlock[7] = checkSum[0]; for (i = 0; i < pinBytes.length; i++) { eanBlock[i + 8] = pinBytes[i]; } // encrypy the ean String encryptedEanHex = null; try { byte[] encryptedEan = mwkCipher.doFinal(eanBlock); encryptedEanHex = StringUtil.toHexString(encryptedEan); } catch (IllegalStateException e) { Debug.logError(e, module); } catch (IllegalBlockSizeException e) { Debug.logError(e, module); } catch (BadPaddingException e) { Debug.logError(e, module); } if (debug) { Debug.log("encryptPin : " + pin + " / " + encryptedEanHex, module); } return encryptedEanHex; } /** * Decrypt an encrypted pin using the configured keys * @param pin Hex String of the encrypted pin (EAN) * @return Plain text String of the pin */ public String decryptPin(String pin) { // get the Cipher Cipher mwkCipher = this.getCipher(this.getMwkKey(), Cipher.DECRYPT_MODE); // decrypt pin String decryptedPinString = null; try { byte[] decryptedEan = mwkCipher.doFinal(StringUtil.fromHexString(pin)); byte[] decryptedPin = getByteRange(decryptedEan, 8, 8); decryptedPinString = new String(decryptedPin); } catch (IllegalStateException e) { Debug.logError(e, module); } catch (IllegalBlockSizeException e) { Debug.logError(e, module); } catch (BadPaddingException e) { Debug.logError(e, module); } if (debug) { Debug.log("decryptPin : " + pin + " / " + decryptedPinString, module); } return decryptedPinString; } /** * Transmit a request to ValueLink * @param request Map of request parameters * @return Map of response parameters * @throws HttpClientException */ public Map send(Map request) throws HttpClientException { return send((String) props.get("payment.valuelink.url"), request); } /** * Transmit a request to ValueLink * @param url override URL from what is defined in the properties * @param request request Map of request parameters * @return Map of response parameters * @throws HttpClientException */ public Map send(String url, Map request) throws HttpClientException { if (debug) { Debug.log("Request : " + url + " / " + request, module); } // read the timeout value String timeoutString = (String) props.get("payment.valuelink.timeout"); int timeout = 34; try { timeout = Integer.parseInt(timeoutString); } catch (NumberFormatException e) { Debug.logError(e, "Unable to set timeout to " + timeoutString + " using default " + timeout); } // create the HTTP client HttpClient client = new HttpClient(url, request); client.setTimeout(timeout * 1000); client.setDebug(debug); client.setClientCertificateAlias((String) props.get("payment.valuelink.certificateAlias")); String response = client.post(); // parse the response and return a map return this.parseResponse(response); } /** * Output the creation of public/private keys + KEK to the console for manual database update */ public StringBuffer outputKeyCreation(boolean kekOnly, String kekTest) { return this.outputKeyCreation(0, kekOnly, kekTest); } private StringBuffer outputKeyCreation(int loop, boolean kekOnly, String kekTest) { StringBuffer buf = new StringBuffer(); loop++; if (loop > 100) { // only loop 100 times; then throw an exception throw new IllegalStateException("Unable to create 128 byte keys in 100 tries"); } // place holder for the keys DHPrivateKey privateKey = null; DHPublicKey publicKey = null; if (!kekOnly) { KeyPair keyPair = null; try { keyPair = this.createKeys(); } catch (NoSuchAlgorithmException e) { Debug.logError(e, module); } catch (InvalidAlgorithmParameterException e) { Debug.logError(e, module); } catch (InvalidKeySpecException e) { Debug.logError(e, module); } if (keyPair != null) { publicKey = (DHPublicKey) keyPair.getPublic(); privateKey = (DHPrivateKey) keyPair.getPrivate(); if (publicKey == null || publicKey.getY().toByteArray().length != 128) { // run again until we get a 128 byte public key for VL return this.outputKeyCreation(loop, kekOnly, kekTest); } } else { Debug.log("Returned a null KeyPair", module); return this.outputKeyCreation(loop, kekOnly, kekTest); } } else { // use our existing private key to generate a KEK try { privateKey = (DHPrivateKey) this.getPrivateKey(); } catch (Exception e) { Debug.logError(e, module); } } // the KEK byte[] kekBytes = null; try { kekBytes = this.generateKek(privateKey); } catch (NoSuchAlgorithmException e) { Debug.logError(e, module); } catch (InvalidKeySpecException e) { Debug.logError(e, module); } catch (InvalidKeyException e) { Debug.logError(e, module); } // the 3DES KEK value SecretKey loadedKek = this.getDesEdeKey(kekBytes); byte[] loadKekBytes = loadedKek.getEncoded(); // test the KEK Cipher cipher = this.getCipher(this.getKekKey(), Cipher.ENCRYPT_MODE); byte[] kekTestB = { 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] kekTestC = new byte[0]; if (kekTest != null) { kekTestB = StringUtil.fromHexString(kekTest); } // encrypt the test bytes
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -