建设银行通知到商户个人系统

项目中时常涉及到支付动作,以下是个人在接手建设银行对接部分,查阅了许多资料,整理给大家,希望对大家有用!

业务场景:在项目中通过向建设银行网关缴费,建设银行会跳转到我们在建行商户系统账号上所填写的回调地址,这时我们所要做的就是验签操作,验签成功说明支付成功,接下来是自己业务系统的一系列操作,如账单管理入库,推送消息,保存用户的转账记录等等之类的。

查阅建行的接口对接相关文档,文档上有这样的接口说明:

银行—>商户接口参数

银行—>商户接口包含两部分,即服务器通知和页面通知,商户可以登录商户后台独立设置,也可以由分行业务人员在分行后台设置。

这里写图片描述

站点间接口的参数传送仍然采用普通的URL方式,信息包含在CGI参数,具体如下所示:
HTTP://MERCHANT.WEB.SITE/MERCHANT_CGI?POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=500.00&CURCODE=01&REMARK1=19991101&REMARK2=merchantname&SUCCESS=Y&SIGN=4b3ef029516193b7d969ac1840083635a3e0901b8cd526caa44c1a07
2f496d7f0d4bca3942c0d9030bede37c7809b835cec787eb39e18b7596a724fba9805b24714dfbb0f4a3fb430b32e075254a114d4c38a0ac
52ef46a0ad33dec3fbfc15417402a1399e65e46996c0cf49fc7ffca9222f8cd693c8376b6f928828967bec42

注:?前的URL由商户在签约时提供

参与签名运算的字符及其顺序如下
POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=500.00&CURCODE=01&REMARK1=19991101&REMARK2=merchantname&SUCCESS=Y
注:字符串中变量名必须是大写字母。

如果商户的程序将MERCHANTID, POSID,BRANCHID, ORDERID, PAYMENT, CURCODE,TXCODE和MAC作为隐藏域(hidden),
然后使用SUBMIT按纽,注意在 FORM的METHOD中使用“GET”的方式。

验签java代码:

package com.wlkj.common.config;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;

import netpay.merchant.crypto.ABAProvider;
import netpay.merchant.crypto.RSAPrivKeyCrt;
import netpay.merchant.crypto.RSAPubKey;

public class RSASig{
    private String priKey;
    private String pubKey;

    public boolean generateKeys(){

        PublicKey       keyPub;
        PrivateKey      keyPri;
        SecureRandom        rand;

        Security.addProvider(new ABAProvider());

        rand = new SecureRandom();

        rand.setSeed(System.currentTimeMillis());

        try
        {
            KeyPairGenerator    fact;
            KeyPair         keyPair;

            fact = KeyPairGenerator.getInstance("RSA", "ABA");

            fact.initialize(1024, rand);

            keyPair = fact.generateKeyPair();

            keyPub = keyPair.getPublic();

            keyPri = keyPair.getPrivate();

            pubKey = bytesToHexStr(keyPub.getEncoded());

            priKey = bytesToHexStr(keyPri.getEncoded());
        }
        catch (Exception e)
        {
            return false;
        }
        return true;
    }


    public String getPublicKey(){
        return pubKey;
    }

    public String getPrivateKey(){
        return priKey;
    }

    public void setPublicKey(String pkey){
        pubKey = pkey;
    }

    public void setPrivateKey(String pkey){
        priKey = pkey;
    }

    public String generateSigature(String src){
        try{
        Security.addProvider(new ABAProvider());
        Signature sigEng = Signature.getInstance("MD5withRSA","ABA");

        byte[] pribyte = hexStrToBytes(priKey.trim());
        sigEng.initSign(new RSAPrivKeyCrt(pribyte));
        sigEng.update(src.getBytes());

        byte[] signature = sigEng.sign();
        return bytesToHexStr(signature);
        }catch(Exception e){
            return null;
        }
    }

    public boolean verifySigature(String sign,String src){
        try{
        Security.addProvider(new ABAProvider());
        Signature sigEng = Signature.getInstance("MD5withRSA","ABA");

        byte[] pubbyte = hexStrToBytes(pubKey.trim());
        sigEng.initVerify(new RSAPubKey(pubbyte));
        sigEng.update(src.getBytes());

        byte[] sign1 = hexStrToBytes(sign);
        if (sigEng.verify(sign1))
        {
            return true;
        }
        else
        {
            return false;
        }

        }catch(Exception e){
            return false;
        }
    }

    /** * Transform the specified byte into a Hex String form. */
    public static final String bytesToHexStr(
        byte[] bcd)
    {
        StringBuffer s = new StringBuffer(bcd.length * 2);

        for (int i = 0; i < bcd.length; i++)
        {
            s.append(bcdLookup[(bcd[i] >>> 4) & 0x0f]);
            s.append(bcdLookup[bcd[i] & 0x0f]);
        }

        return s.toString();
    }


    /** * Transform the specified Hex String into a byte array. */
    public static final byte[] hexStrToBytes(
        String  s)
    {
        byte[]  bytes;

        bytes = new byte[s.length() / 2];

        for (int i = 0; i < bytes.length; i++)
        {
            bytes[i] = (byte)Integer.parseInt(
                    s.substring(2 * i, 2 * i + 2), 16);
        }

        return bytes;
    }

    private static final char[] bcdLookup =
    {
        '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
    };
}
import net.sf.json.JSONObject;

import org.apache.log4j.Logger;

import com.jfinal.core.Controller;
import com.jfinal.kit.HttpKit;

/** * 支付回调类 * @author 黄宝康 */
public class PayAction extends Controller{
    private static final Logger logger = Logger.getLogger(PayAction.class);
    private String URL = "";
    private String PUBKEY = "";
    /** * 获取支付回调方法 */
    public void index(){
        String param = HttpKit.readData(getRequest());
        JSONObject jo = JSONObject.fromObject(param);
        String posid = getPara("POSID");
// 业务参数
        String POSID = jo.getString("POSID");
        String BRANCHID = jo.getString("BRANCHID");
        String ORDERID = jo.getString("ORDERID");
        String PAYMENT = jo.getString("PAYMENT");
        String CURCODE = jo.getString("CURCODE");
        String REMARK1 = jo.getString("REMARK1");
        String REMARK2 = jo.getString("REMARK2");
        String SUCCESS = jo.getString("SUCCESS");
        String SIGN = jo.getString("SIGN");

        String strSrc = "POSID="+POSID+"&BRANCHID="+BRANCHID+"&ORDERID="+ORDERID+"&PAYMENT="+PAYMENT+"&CURCODE="+CURCODE+"&REMARK1="+REMARK1+"&REMARK2="+REMARK2+"&SUCCESS="+SUCCESS;
        String strSign = SIGN;
        RSASig rsa=new RSASig();
        rsa.setPublicKey(PUBKEY);
        String strRet;
        if(rsa.verifySigature(strSign, strSrc)){
            strRet = "Y";
        }else{
            strRet = "N";
        }
        renderText("调用成功!");
    }
}

验签操作主要是考虑到安全性,公钥公开,sign参数是经过加密过来的,用自己的私钥加密的。以下是加签,解签的测试。

package signtest;
import CCBSign.RSASig; 

public class SignTest {
    public static void main(String []args){
        String strSrc;
        String strPubKey;
        String strPriKey;
        String strSign;

        strPubKey="30819d300d06092a864886f70d010101050003818b00308187028181009ba4951169c5deecf03a8ddb2fd934f53747c03a211f63bccc84773182bdd8f7159634705041087e4c9053df05326952a143e1aab5e8ba75ed891a91c2db484b66a064abba6605418944d8763814ff23c161101948ec9ef2dfac735b4bb7c7dac18fbf87157b424780eb7080a3e7c9e79dd4841e44a001edfe497b9e3d2181b9020111";
        strPriKey="30820277020100300d06092a864886f70d0101010500048202613082025d020100028181009ba4951169c5deecf03a8ddb2fd934f53747c03a211f63bccc84773182bdd8f7159634705041087e4c9053df05326952a143e1aab5e8ba75ed891a91c2db484b66a064abba6605418944d8763814ff23c161101948ec9ef2dfac735b4bb7c7dac18fbf87157b424780eb7080a3e7c9e79dd4841e44a001edfe497b9e3d2181b9020111028181008954fc004e452e1c5b7ef5a348563dc94ee4f4e7ff1bb25b4b0b783abea783345e575b7228b1da51529d772e31c311a342ffa90009eb7758fec4449ebafdb84126d1d2443dbcec07d9807638ef32cb91bf18eaaa46f6db84de5eba05edfe70ad029449a4cb4de7a95f5c903d6a3fa301f1cc0fe3e29ac72eeab68737f3b2f57d024100d428be0e1463c6b25cc493f23777135a9251b8092f3439c9604d61df8aadb958b947222fd60a489e5de44c379e806015edb0b15030a22cbc5e0ff693fd5bedcf024100bbce1eb6b55f5530f1bb7a437a0f0512f0153d0ada5c5b4ea57c3ea83bd89fe0166d5af1d07f153e83c05eae1585b113c03c8d989bb4d151c96aa78691fac1f7024100bb33020c6c5809ac6ff8bec6a9691113ae481adaed6a511b18bcbfc53e20d0b7b28a0f1b26454f2252d87f7c5ead81f53b236f46c180095ae9959d556714e0e3024100b0c1feca141d7d5b3ddda03f81f004c6879b84beeba237d18cb12be9a1bcd2b4c9d055984bc2e6d16cf14a0d416ec4c74b8449081a1397d48155526089647a51024100bcfe9b05b25578d5d96f80229e015aa58a0af5b0c0aa3ad695fe0d270c4818a737a7abc2f59cf1ea22c7155e06b7d26fba2594e29cb7fd02bd9b6e24b49e425a";
        strSrc="POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=500.00&CURCODE=01&REMARK1=19991101&REMARK2=北京商户&SUCCESS=Y&ACC_TYPE=11";
        RSASig rsa=new RSASig();
        rsa.setPrivateKey(strPriKey);
        strSign=rsa.generateSigature(strSrc);

        System.out.println(strSign);

        rsa.setPublicKey(strPubKey);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign fail");
        }


        //修改了源串后再验签 注意比较POSID的编号
        strSrc="POSID=000000001&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=500.00&CURCODE=01&REMARK1=19991101&REMARK2=北京商户&SUCCESS=Y&ACC_TYPE=11";
        if( rsa.verifySigature(strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign failed");
        }

        //修改签名串后再验签
        strSrc="POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=500.00&CURCODE=01&REMARK1=19991101&REMARK2=北京商户&SUCCESS=Y&ACC_TYPE=11";
        strSign='a'+strSign.substring(1,1)+strSign.substring(1,strSign.length());
        System.out.println(strSign);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign failed");
        }

        //修改公钥后再验签 删除一位公钥
        //strPubKey="30819d300d06092a864886f70d010101050003818b00308187028181009ba4951169c5deecf03a8ddb2fd934f53747c03a211f63bccc84773182bdd8f7159634705041087e4c9053df05326952a143e1aab5e8ba75ed891a91c2db484b66a064abba6605418944d8763814ff23c161101948ec9ef2dfac735b4bb7c7dac18fbf87157b424780eb7080a3e7c9e79dd4841e44a001edfe497b9e3d2181b9020111";
        strPubKey="3019d300d06092a864886f70d010101050003818b00308187028181009ba4951169c5deecf03a8ddb2fd934f53747c03a211f63bccc84773182bdd8f7159634705041087e4c9053df05326952a143e1aab5e8ba75ed891a91c2db484b66a064abba6605418944d8763814ff23c161101948ec9ef2dfac735b4bb7c7dac18fbf87157b424780eb7080a3e7c9e79dd4841e44a001edfe497b9e3d2181b9020111";
        strSign=rsa.generateSigature( strSrc);
        rsa.setPublicKey( strPubKey);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign ok");
        }
        else{
            System.out.println("Sign failed");
        }

        //接下来是一些成功的签名验证
        //1
        strSrc="POSID=000000000&BRANCHID=330000000&ORDERID=2004010061&PAYMENT=0.01&CURCODE=01&REMARK1=&REMARK2=&SUCCESS=N";
        strSign="5bf88c409a13963286904e8954a4d825108f9b5bb60a8c8e5cfc05355fe4e247c777b521c7d68b8d51968285d51d1a0da0c5bd55e19268949a20dd7bd14f17422e41f3e6f7446d2136e10e796abc8b8a6f752bed5091374551d84d02f185aa3f9b516ac77ca319b06a8269389de6d7f677c619bfc0c89ccbcb125ae6dd7cc646";
        strPubKey="30819c300d06092a864886f70d010101050003818a003081860281807d1e98e9c10625239ad9116488accf18a95125c83f5ac52f055be47614087b1bc55f1d475ddb0516b6339f7c2a8fd4def86519087cc6ecd8ea4657a5cef26d84890d00772d216e95d0aba1ea9fd39fb02202c82b71333b104e715da5de65be4cf5b83e3c0ba459777fe83a39485f145fccc94b471981348db5beab735c5889f1020111";
        rsa.setPublicKey(strPubKey);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign failed");
        }

        //2
        strSrc="POSID=000000000&BRANCHID=110000000&ORDERID=20041031&PAYMENT=0.01&CURCODE=01&REMARK1=ccb&REMARK2=test&SUCCESS=Y";
        strSign="43680d00f5097caae18b7af3fc936cc79feb621fb166e25affbb52721e2c5c1e656f030dff46e6f0298ef82cf2fd10b6cef34fb2aa270716c30708aeb1abf0520418449614562e891cd5aede8f83b1dd65f76cc81ad5aabfd4aba409da3523ef8e82a7d19055dbb6d9241171893bf282bf64f239677ecd84abbe55fd855f48f3";
        strPubKey="30819f300d06092a864886f70d010101050003818d0030818902818100b466e3a0fa097b57a1bc63c1fd5d97d4ef8d270d538a5aee3d1061f579f02a19cf1543701d94d81f46ce56adb84dca440a7e8f5af40538bb7a88efaf9991ead0fabc63d48fd1f12de658229e30e38ccbd9a631ec9c2d95b8590ea1a01d0931221e062544023a1ed2eb7050853fe56bf8cfd0f18243192d38855a36a87badba790203010001";
        rsa.setPublicKey(strPubKey);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign failed");
        }

        //3
        strSrc="POSID=000000000&BRANCHID=110000000&ORDERID=20041031&PAYMENT=0.01&CURCODE=01&REMARK1=ccb&REMARK2=test&SUCCESS=Y";
        strSign="3183a60f887937846008f4ecfea725af5d65ecaefebea828459193343df7d0943f0fa9e44a298cc9a8e335bece72f8bfce8da3975e21fe4ce4d6c96894d5428e05e896b7da03f7519551b8a09bf1286ea48975b3cd49978eefbb628cc98f4f064feb898518dfb783acdd25eb6f5507fc00c16d1ae69d801a8cb970c4b7e0959b";
        strPubKey="30819f300d06092a864886f70d010101050003818d0030818902818100d0e57a2ebbc82801980de2ad7101c67dc137432bb6ced45882b8d41cbfec7519ae8bf18b2584ae460d7d437aec069ec907935e4b39c72a6291e43a6a88c3405565357dc23c46b7072e6e50b1da4cd9cfdec616cb6ad43f0b013040307973d63b889e78fdd1389714adec663acefe5c974e513a063ba9acb96f590139b0fc571b0203010001";
        rsa.setPublicKey(strPubKey);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign failed");
        }

        //接下来是一些错误的验签
        //1
        strSrc="POSID=000000000&BRANCHID=330000000&ORDERID=120040915091516271800047&PAYMENT=0.01&CURCODE=01&REMARK1=&REMARK2=&SUCCESS=Y";
        strSign="2732e323aa1d4f460bed516a79944001fbad2c93bbdb0d22c71066f4a69528c8699da27c13893bfcefea14dabb6f3cfa93e1414e8782124c99ffc8b059aad5f7f543993f28d262147b5206aea8d2b72aa34a256a4a5bb9c90c9aa8d2897eae90b581e7e6091fcfbb7f4885711b75b95ba7982f7519dc166e20d8b8294e4af2ad";
        strPubKey="30819c300d06092a864886f70d010101050003818a003081860281807e4caba7c0ff9f593bb03ac8e64fcc76ebdf728b3b54493c3f62c7c94e8663d8505da39b08b00df4320c5a49d54c7774044fcc42937a0fb6a3706f724f872fe5f998cc48eb20875902b4b935e14df77b7aeb9224a5cf2db765b20fe56d8f4d5a9e03ab7943a41a179f8240e5311b3957971921fe9ccb9c24c828e99f91cc33f1020111";
        rsa.setPublicKey(strPubKey);
        if( rsa.verifySigature( strSign,strSrc) ){
            System.out.println("Sign OK");
        }
        else{
            System.out.println("Sign failed");
        }
    }
}
阅读更多

更多精彩内容