附录 1
安全控制
响应页面 SHValue
商户和 PayMaster 将共享一个用于创建交易数据的 密钥。
- 密钥应从 PayMaster 网关获取。
支付响应消息将包括 SHAlgorithmType
和 SHValue
。商户应使用这两个值验证 PayMaster 发送的响应消息,以确保数据未被篡改。
为了验证,商户需要从响应消息中删除尾部字段(t001_SHT
和 t002_SHV
),将消息附加上商户密钥,然后使用 SHA-256 HMAC 哈希算法对字符串进行编码。
一旦获得编码结果,将其与 SHValue
中的值进行比较。
- 两个值必须相等。
示例 1
步骤 1:删除尾部字段
从 PayMaster 网关返回的响应消息中删除尾部字段(最后两行)。
h001_MTI=0290&h002_VNO=06&h003_TDT=20230811&h004_TTM=16241856&f001_MID=000010000012639&f004_PAN=545301XXXXXX5323&f005_ExpDate=2710&f006_TxnDtTm=20230811162418&f007_TxnAmt=000000000100&f009_RespCode=00&f010_CurrCode=458&f011_AuthIDResp=774585&f019_ExpTxnAmt=2&f023_RRN=322382167159&f024_OrgRespCode=00&f254_DDRespCode=00&f256_FICode=&f257_PGRN=230811162420AC069FNX&f258_TxnStatDetCde=0000&f259_TxnStatMsg=SUCCESS&f260_ServID=FNX&f261_HostID=&f262_SessID=&f263_MRN=20230811162418FNX&f270_ORN=&f277_DDRN=&f283_UPP_PM=00&f286_OrgDDRespCode=22&f350_CrdTyp=MST&f352_AcqBank=&f353_IPPTenure=&f325_ECommMercInd=1&f339_TokenFlg=N&f344_MercCustId=&f346_Token=&f347_TokenShrtNm=&f348_MaskPAN=&f365_POSEnvFlg=&t001_SHT=SH2&t002_SHV=FC63B34696B77153044E6D353D0241088A44EC13CCF5C414FFC30CE815C96614
h001_MTI=0290&h002_VNO=06&h003_TDT=20230811&h004_TTM=16241856&f001_MID=000010000012639&f004_PAN=545301XXXXXX5323&f005_ExpDate=2710&f006_TxnDtTm=20230811162418&f007_TxnAmt=000000000100&f009_RespCode=00&f010_CurrCode=458&f011_AuthIDResp=774585&f019_ExpTxnAmt=2&f023_RRN=322382167159&f024_OrgRespCode=00&f254_DDRespCode=00&f256_FICode=&f257_PGRN=230811162420AC069FNX&f258_TxnStatDetCde=0000&f259_TxnStatMsg=SUCCESS&f260_ServID=FNX&f261_HostID=&f262_SessID=&f263_MRN=20230811162418FNX&f270_ORN=&f277_DDRN=&f283_UPP_PM=00&f286_OrgDDRespCode=22&f350_CrdTyp=MST&f352_AcqBank=&f353_IPPTenure=&f325_ECommMercInd=1&f339_TokenFlg=N&f344_MercCustId=&f346_Token=&f347_TokenShrtNm=&f348_MaskPAN=&f365_POSEnvFlg=
步骤 2:将密钥附加到响应消息
在删除尾部字段之后,将密钥附加到响应消息。您将得到一个没有尾部字段 + 密钥的响应消息字符串。使用 SHAlgorithmType
中指定的算法对字符串进行编码。在这个例子中,使用 SHA-256。
步骤 3:将编码结果与哈希值比较
将编码结果与 SHValue
中的哈希值进行比较。两者 必须 相匹配。
在这个例子中:
- 假设密钥为
00112233445566778899AABBCCDDEEFF
,哈希值为E73CEDA9D9A8D1AAF59BDB919EF7C82D52671A4B457CE816BCE91AFF31485259
。
商户必须拒绝不匹配的结果并向 PayMaster 报告。
示例代码
public void resPayment() throws Exception {
String secretKey = "B96856FA91749A259F71340E3C6BC3478524320F218587D22071A35DD4E7BXXX";
String response = "h001_MTI=0290&h002_VNO=06&h003_TDT=20230811&h004_TTM=16241856&f001_MID=000010000012639&
f004_PAN=545301XXXXXX5323&f005_ExpDate=2710&f006_TxnDtTm=20230811162418&f007_TxnAmt=000000000100&f009_Resp
Code=00&f010_CurrCode=458&f011_AuthIDResp=774585&f019_ExpTxnAmt=2&f023_RRN=322382167159&f024_OrgRespCode=0
0&f254_DDRespCode=00&f256_FICode=&f257_PGRN=230811162420AC069FNX&f258_TxnStatDetCde=0000&f259_TxnStatMsg=S
UCCESS&f260_ServID=FNX&f261_HostID=&f262_SessID=&f263_MRN=20230811162418FNX&f270_ORN=&f277_DDRN=&f283_UPP_
PM=00&f286_OrgDDRespCode=22&f350_CrdTyp=MST&f352_AcqBank=&f353_IPPTenure=&f325_ECommMercInd=1&f339_TokenFl
g=N&f344_MercCustId=&f346_Token=&f347_TokenShrtNm=&f348_MaskPAN=&f365_POSEnvFlg=" + secretKey;
SecretKeySpec byteSecretKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
Mac sha256_MAC = Mac.getInstance("HmacSHA256");
sha256_MAC.init(byteSecretKey);
String hashed = this.bytesToHex(sha256_MAC.doFinal(response.getBytes()));
System.out.println(hashed.equalsIgnoreCase("FCXX3469XX7715304XX6XX53XX241088XX4EXX3CCXXC41XXFXX0CXX15XX6614"));
}
public String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
byte[] var6 = bytes;
int var5 = bytes.length;
for(int var4 = 0; var4 < var5; ++var4) {
byte b = var6[var4];
sb.append(String.format("%02x", b));
}
return sb.toString();// 31
}
示例 2
Portal 用户 ID (PortalUserID
) 的加密算法是 Triple DES 在 ECB 模式下,如以下步骤所示:
- 加密密钥与安全哈希密钥相同。
参数
- Portal 用户 ID >
7180024846
- 加密密钥 >
00112233445566778899AABBCCDDEEFF
步骤 1:取密钥值
对加密密钥进行消息摘要,并将前 24 字节作为密钥。
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digestOfPassword = md.digest(encryptionKey.getBytes("utf-8"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
十六进制表示的密钥 > 20AFF5A341131D58C07A76E062D895E0968727664B8769B7
步骤 2:初始化密码对象
使用密钥初始化密码对象。
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
步骤 3:填充 Portal 用户 ID
填充 Portal 用户 ID 并返回为字符串。
PortalUserID = padStringByMultiplier(customerID,8);
public static String padStringByMultiplier(String in, int multiplier) {
byte[] dest = null;
while (in.length() % multiplier != 0) {
int finalLength = in.length() + 1;
dest = new byte[finalLength]; byte[] src = in.getBytes();
System.arraycopy(src,0,dest,0,Math.min(src.length,dest.length));
Arrays.fill(dest, src.length, dest.length, (byte) 0x20);
in = new String(dest);
} return in;
}
步骤 4:加密填充的 Portal 用户 ID
使用 Triple DES(使用 ECB 模式)加密填充的 Portal 用户 ID。
byte[] plainTextBytes = customerID.getBytes("utf-8");
byte[] buf = cipher.doFinal(plainTextBytes);
十六进制表示的加密 PortalUserID
> 20AFF5A341131D58C07A76E062D895E0968727664B8769B7
步骤 5:编码加密的 Portal 用户 ID
对步骤 4 中的加密 PortalUserID
进行 Base64 编码。
byte[] plainTextBytes = customerID.getBytes("utf-8");
byte[] buf = cipher.doFinal(plainTextBytes);
十六进制表示的加密 PortalUserID
> 20AFF5A341131D58C07A76E062D895E0968727664B8769B7
最终值
最终输出值将为:
O5ACzHbvQNm3iNAwdvi1Sg==
编程指南
加载 Java 密钥库文件
Password > mystorepassword
keyStoreFile > mykeystore.jks
KeyStore keystore = KeyStore.getInstance("JKS");
char[] storePass = password.toCharArray();
FileInputStream fileInputStream = new FileInputStream(keyStoreFile);
keystore.load(fileInputStream, storePass);
fileInputStream.close();
从 Java 密钥库文件获取私钥
alias > mySignCert
KeyStore.ProtectionParameter keyPass = new KeyStore.PasswordProtection(storePass);
KeyStore.PrivateKeyEntry privKeyEntry = (KeyStore.PrivateKeyEntry)keystore.getEntry(alias, keyPass);
PrivateKey privateKey = privKeyEntry.getPrivateKey();