📄 valuelinkapi.java
字号:
/*
* $Id: ValueLinkApi.java,v 1.6 2004/03/12 23:22:51 ajzeneski Exp $
*
* 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 org.ofbiz.base.util.*;
import org.ofbiz.entity.GenericDelegator;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.GenericEntityException;
import javax.crypto.*;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DESKeySpec;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
/**
* ValueLinkApi - Implementation of ValueLink Encryption & Transport
*
* @author <a href="mailto:jaz@ofbiz.org">Andy Zeneski</a>
* @version $Revision: 1.6 $
* @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
try {
kekTestC = cipher.doFinal(kekTestB);
} catch (Exception e) {
Debug.logError(e, module);
}
if (!kekOnly) {
// public key (just Y)
BigInteger y = publicKey.getY();
byte[] yBytes = y.toByteArray();
String yHex = StringUtil.toHexString(yBytes);
buf.append("======== Begin Public Key (Y @ " + yBytes.length + " / " + yHex.length() + ") ========\n");
buf.append(yHex + "\n");
buf.append("======== End Public Key ========\n\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -