📄 valuelinkapi.java
字号:
// merchant information
request.put("MerchID", merchantId + terminalId);
request.put("AltMerchNo", props.get("payment.valuelink.altMerchantId"));
// mode settings
String modes = (String) props.get("payment.valuelink.modes");
if (modes != null && modes.length() > 0) {
request.put("Modes", modes);
}
// merchant timestamp
String merchTime = (String) context.get("MerchTime");
if (merchTime == null) {
merchTime = this.getDateString();
}
request.put("MerchTime", merchTime);
// transaction number
String termTxNo = (String) context.get("TermTxnNo");
if (termTxNo == null) {
termTxNo = delegator.getNextSeqId("ValueLinkKey").toString();
}
request.put("TermTxnNo", termTxNo);
// current working key index
request.put("EncryptID", this.getWorkingKeyIndex());
if (debug) {
Debug.log("Created Initial Request Map : " + request, module);
}
return request;
}
/**
* Gets the cached value object for this merchant's keys
* @return Cached GenericValue object
*/
public GenericValue getGenericValue() {
GenericValue value = null;
try {
value = delegator.findByPrimaryKeyCache("ValueLinkKey", UtilMisc.toMap("merchantId", merchantId));
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (value == null) {
throw new RuntimeException("No ValueLinkKey record found for Merchant ID : " + merchantId);
}
return value;
}
/**
* Reloads the keys in the object cache; use this when re-creating keys
*/
public void reload() {
this.kek = null;
this.mwk = null;
this.mwkIndex = null;
}
// using the prime and generator provided by valuelink; create a parameter object
protected DHParameterSpec getDHParameterSpec() {
String primeHex = (String) props.get("payment.valuelink.prime");
String genString = (String) props.get("payment.valuelink.generator");
// convert the p/g hex values
byte[] primeByte = StringUtil.fromHexString(primeHex);
BigInteger prime = new BigInteger(1, primeByte); // force positive (unsigned)
BigInteger generator = new BigInteger(genString);
// initialize the parameter spec
DHParameterSpec dhParamSpec = new DHParameterSpec(prime, generator, 1024);
return dhParamSpec;
}
// actual kek encryption/decryption code
protected byte[] cryptoViaKek(byte[] content, int mode) {
// open a cipher using the kek for transport
Cipher cipher = this.getCipher(this.getKekKey(), mode);
byte[] dec = new byte[0];
try {
dec = cipher.doFinal(content);
} catch (IllegalStateException e) {
Debug.logError(e, module);
} catch (IllegalBlockSizeException e) {
Debug.logError(e, module);
} catch (BadPaddingException e) {
Debug.logError(e, module);
}
return dec;
}
// return a cipher for a key - DESede/CBC/NoPadding IV = 0
protected Cipher getCipher(SecretKey key, int mode) {
byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec iv = new IvParameterSpec(zeros);
// create the Cipher - DESede/CBC/NoPadding
Cipher mwkCipher = null;
try {
mwkCipher = Cipher.getInstance("DESede/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
Debug.logError(e, module);
return null;
} catch (NoSuchPaddingException e) {
Debug.logError(e, module);
}
try {
mwkCipher.init(mode, key, iv);
} catch (InvalidKeyException e) {
Debug.logError(e, "Invalid key", module);
} catch (InvalidAlgorithmParameterException e) {
Debug.logError(e, module);
}
return mwkCipher;
}
protected byte[] getPinCheckSum(byte[] pinBytes) {
byte[] checkSum = new byte[1];
checkSum[0] = 0;
for (int i = 0; i < pinBytes.length; i++) {
checkSum[0] += pinBytes[i];
}
return checkSum;
}
protected byte[] getRandomBytes(int length) {
Random rand = new Random();
byte[] randomBytes = new byte[length];
rand.nextBytes(randomBytes);
return randomBytes;
}
protected SecretKey getMwkKey() {
if (mwk == null) {
mwk = this.getDesEdeKey(getByteRange(getMwk(), 8, 24));
}
if (debug) {
Debug.log("Raw MWK : " + StringUtil.toHexString(getMwk()), module);
Debug.log("MWK : " + StringUtil.toHexString(mwk.getEncoded()), module);
}
return mwk;
}
protected SecretKey getKekKey() {
if (kek == null) {
kek = this.getDesEdeKey(getKek());
}
if (debug) {
Debug.log("Raw KEK : " + StringUtil.toHexString(getKek()), module);
Debug.log("KEK : " + StringUtil.toHexString(kek.getEncoded()), module);
}
return kek;
}
protected SecretKey getDesEdeKey(byte[] rawKey) {
SecretKeyFactory skf = null;
try {
skf = SecretKeyFactory.getInstance("DESede");
} catch (NoSuchAlgorithmException e) {
// should never happen since DESede is a standard algorithm
Debug.logError(e, module);
return null;
}
// load the raw key
if (rawKey.length > 0) {
DESedeKeySpec desedeSpec1 = null;
try {
desedeSpec1 = new DESedeKeySpec(rawKey);
} catch (InvalidKeyException e) {
Debug.logError(e, "Not a valid DESede key", module);
return null;
}
// create the SecretKey Object
SecretKey key = null;
try {
key = skf.generateSecret(desedeSpec1);
} catch (InvalidKeySpecException e) {
Debug.logError(e, module);
}
return key;
} else {
throw new RuntimeException("No valid DESede key available");
}
}
protected byte[] getMwk() {
return StringUtil.fromHexString(this.getGenericValue().getString("workingKey"));
}
protected byte[] getKek() {
return StringUtil.fromHexString(this.getGenericValue().getString("exchangeKey"));
}
protected byte[] getPrivateKeyBytes() {
return StringUtil.fromHexString(this.getGenericValue().getString("privateKey"));
}
protected Map parseResponse(String response) {
if (debug) {
Debug.log("Raw Response : " + response, module);
}
// covert to all lowercase and trim off the html header
String subResponse = response.toLowerCase();
int firstIndex = subResponse.indexOf("<tr>");
int lastIndex = subResponse.lastIndexOf("</tr>");
subResponse = subResponse.substring(firstIndex, lastIndex);
// check for a history table
String history = null;
List historyMapList = null;
if (subResponse.indexOf("<table") > -1) {
int startHistory = subResponse.indexOf("<table");
int endHistory = subResponse.indexOf("</table>") + 8;
history = subResponse.substring(startHistory, endHistory);
// replace the subResponse string so it doesn't conflict
subResponse = StringUtil.replaceString(subResponse, history, "[_HISTORY_]");
// parse the history into a list of maps
historyMapList = this.parseHistoryResponse(history);
}
// replace all end rows with | this is the name delimiter
subResponse = StringUtil.replaceString(subResponse, "</tr>", "|");
// replace all </TD><TD> with = this is the value delimiter
subResponse = StringUtil.replaceString(subResponse, "</td><td>", "=");
// clean off a bunch of other useless stuff
subResponse = StringUtil.replaceString(subResponse, "<tr>", "");
subResponse = StringUtil.replaceString(subResponse, "<td>", "");
subResponse = StringUtil.replaceString(subResponse, "</td>", "");
// make the map
Map responseMap = StringUtil.strToMap(subResponse, true);
// add the raw html back in just in case we need it later
responseMap.put("_rawHtmlResponse", response);
// if we have a history add it back in
if (history != null) {
responseMap.put("_rawHistoryHtml", history);
responseMap.put("history", historyMapList);
}
if (debug) {
Debug.log("Response Map : " + responseMap, module);
}
return responseMap;
}
private List parseHistoryResponse(String response) {
if (debug) {
Debug.log("Raw History : " + response, module);
}
// covert to all lowercase and trim off the html header
String subResponse = response.toLowerCase();
int firstIndex = subResponse.indexOf("<tr>");
int lastIndex = subResponse.lastIndexOf("</tr>");
subResponse = subResponse.substring(firstIndex, lastIndex);
// clean up the html and replace the delimiters with '|'
subResponse = StringUtil.replaceString(subResponse, "<td>", "");
subResponse = StringUtil.replaceString(subResponse, "</td>", "|");
// test the string to make sure we have fields to parse
String testResponse = StringUtil.replaceString(subResponse, "<tr>", "");
testResponse = StringUtil.replaceString(testResponse, "</tr>", "");
testResponse = StringUtil.replaceString(testResponse, "|", "");
testResponse = testResponse.trim();
if (testResponse.length() == 0) {
if (debug) {
Debug.log("History did not contain any fields, returning null", module);
}
return null;
}
// break up the keys from the values
int valueStart = subResponse.indexOf("</tr>");
String keys = subResponse.substring(4, valueStart - 1);
String values = subResponse.substring(valueStart + 9, subResponse.length() - 6);
// split sets of values up
values = StringUtil.replaceString(values, "|</tr><tr>", "&");
List valueList = StringUtil.split(values, "&");
// create a List of Maps for each set of values
List valueMap = new ArrayList();
for (int i = 0; i < valueList.size(); i++) {
valueMap.add(StringUtil.createMap(StringUtil.split(keys, "|"), StringUtil.split((String) valueList.get(i), "|")));
}
if (debug) {
Debug.log("History Map : " + valueMap, module);
}
return valueMap;
}
/**
* Returns a new byte[] from the offset of the defined byte[] with a specific number of bytes
* @param bytes The byte[] to extract from
* @param offset The starting postition
* @param length The number of bytes to copy
* @return a new byte[]
*/
public static byte[] getByteRange(byte[] bytes, int offset, int length) {
byte[] newBytes = new byte[length];
for (int i = 0; i < length; i++) {
newBytes[i] = bytes[offset + i];
}
return newBytes;
}
/**
* Copies a byte[] into another byte[] starting at a specific position
* @param source byte[] to copy from
* @param target byte[] coping into
* @param position the position on target where source will be copied to
* @return a new byte[]
*/
public static byte[] copyBytes(byte[] source, byte[] target, int position) {
byte[] newBytes = new byte[target.length + source.length];
for (int i = 0, n = 0, x = 0; i < newBytes.length; i++) {
if (i < position || i > (position + source.length - 2)) {
newBytes[i] = target[n];
n++;
} else {
for (; x < source.length; x++) {
newBytes[i] = source[x];
if (source.length - 1 > x) {
i++;
}
}
}
}
return newBytes;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -