JAVA建行银企直连报文加解密

接上一篇密钥交换之后获得银行提供的:银行RSA签名公钥RSA.bank.public.key,接受报文验签使用;银行DES加密私钥DES.bank.private.key,加密报文使用。还有之前自己生成的RSA签名私钥RSA.private.key,报文签名使用。下面以签到为例把报文签名、加密以及接受报文后解密、验签写上:
签到http入口:

	/**
	 * @Auther qijw
	 * @Date 2020/11/16
	 * @Description:建行银企直连签到
	 * @Version 1.0
	 **/
    @ApiOperation(value = "建行银企直连签到", notes = "建行银企直连签到")
    @GetMapping("/CCBSignIn")
    public ResponseModel<JSONObject> signIn(){
        try {
            log.info("建行银企直连签到");
            RspSign signIn = signServiceImpl.signIn();
            JSONObject joo = new JSONObject();
            joo.put("signIn", signIn);
            return new ResponseModel<>(joo);
        } catch (Exception e){
            log.error("建行银企直连签到异常",e);
            return new ResponseModel<>(HttpStatus.INTERNAL_SERVER_ERROR.value(),HttpStatus.INTERNAL_SERVER_ERROR.name());
        }
    }

签到构造报文、签名、解析

	/**
	 * @Auther qijw
	 * @Date 2020/11/16
	 * @Description:签到构造报文、签名、解析
	 * @Version 1.0
	 **/
public RspSign sign(String sysCode) {
        try {
            log.info("建行银企直连签到sysCode:{}", sysCode);
            //构造请求报文
            String reqXml = getSign(sysCode);
            //签名,加密,发送请求
            String resultXml = signAndEncryp(reqXml);
            //解析响应
            return JaxbUtil.converyToJavaBean(resultXml, RspSign.class);
        } catch (Exception e) {
            log.error("建行银企直连签到异常", e);
            return null;
        }
    }

构造接口签到的请求报文,使用实体类bean传入P1OPME001、电子银行合约编号、时间等必要字段

    /**
     * 构造接口签到的请求报文
     */
    public String getSign(String sysCode) {
        try {
            log.info("建行银企直连签到报文");
            BankProperties BankProperties = new BankProperties();
            CCBSign cCBSign = new CCBSign();
            Head head = BankProperties.createHead();
            //签到P1OPME001,签退P1OPME002
            head.setSysTxCode(sysCode);
            cCBSign.setHead(head);
            String xmlStr = JaxbUtil.ojbectToXmlWithCDATA(BankConstants.elements, CCBSign.class, cCBSign);
            return xmlStr;
        } catch (Exception e) {
            log.error("建行银企直连签到报文出错,错误信息:" + e.getMessage(), e);
            return null;
        }
    }

签名,加密,发送请求,解析报文,验签

    /**
     * 签名,加密,发送请求,解析报文,验签
     */
    public String signAndEncryp(String reqXml) {
        try {
            //数字签名算法为:MD5withRSA
            String signature = RSASignUtil.sign(reqXml, bankKeyConfig.priRSAKey);
            //DESede (3DES):DES 对称密钥由银行生成,用于对交易报文和文件内容的加解密
            String data = DESedeUtil.encryptWithDESede(reqXml, bankKeyConfig.Des_KEY);
            //向建行银企直连发送请求
            String reqs = httpUtil.sendRequest(data, signature, CommonBankConstants.REQUEST_CHARSET_UTF8);
            if (null != reqs) {
                return reqs;
            } else {
                log.info("建行银企直连签名,加密,发送请求异常");
            }
        } catch (Exception e) {
            log.error("建行银企直连签名,加密,发送请求错误", e);
            return null;
        }
        return null;
    }

MD5withRSA签名

    /**
     * MD5withRSA签名
     *
     * @param data 待签名数据
     * @return 签名
     */
    public static String sign(String data , String private_Key) throws Exception {
        byte[] keyBytes = getPrivateKey(private_Key).getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64.encodeBase64(signature.sign()));
    }

DESede加密,还原密钥方法网上有。不知道的自己搜一下


    public static final String KEY_ALGORITHM = "DESede";

    /**
     * 加密/解密算法 /工作模式 /填充方式
     * Java 6支持PKCS5PADDING填充方式
     * Bouncy Castle支持PKCS7Padding填充方式
     */
    public static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS5Padding";
        /**
     * DESede/ECB/PKCS5Padding加密
     * @param data 待加密数据
     * @param key 密钥
     * @return byte[] 加密数据
     * @throws Exception
     */
    public static String encrypt(String data, String key) throws Exception{
        try {
            //还原密钥
            Key k = toKey(key);
            //加密
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, k);
            return Base64Util.getInstance().base64Encode(cipher.doFinal(data.getBytes("UTF-8")));
        } catch (Exception e){
            log.error("DESede加密错误",e);
        }
        return null;
    }

发送请求报文,获得返回报文,解密、验签

/**
     * 建行银企直连,发送请求报文,获得返回报文,解密、验签
     *
     * @param data
     * @return
     */
    public String sendRequest(String data, String signature, String charset) {
        log.info("请求建行银企直连请求报文请求参数:{}", data);
        HttpClient myclient = null;
        PostMethod httppost = null;
        String repcontent = "";
        try {
            myclient = new HttpClient(); // 构建http客户端
            httppost = new PostMethod(bankKeyConfig.bankServerUrl); // 加密端口
            httppost.addParameter("chanl_cust_no", bankKeyConfig.CHANL_CUST_NO);
            httppost.addParameter("xml", data);
            httppost.addParameter("signature", signature);
            log.info("发送给银企直连服务数据:{}",data);
            // 获得http返回码
            int returnFlag = myclient.executeMethod(httppost);
            InputStream postResult = httppost.getResponseBodyAsStream();
            byte[] bytes = inputStream2byte(postResult);
            byte[] len_byte = new byte[10];
            System.arraycopy(bytes,0,len_byte,0,10);
            repcontent = new String(len_byte);
            log.info("请求建行银企直连秘钥传输报文返回结果:{}", repcontent);
            //成功响应: 数字签名的长度(固定长度 10 个字节) +数字签名数据+加密后的数据。解密时应先根据前 10 位算出数字签名的长度,然后再截取数字签名及报文密文。
            if (null != bytes  && repcontent.startsWith("00")) {
                //数字签名的长度(固定长度 10 个字节) +数字签名数据+加密后的数据
                int signLenth = Integer.valueOf(repcontent.substring(0, BankConstants.LENGTH));
                byte[] signByte = new byte[signLenth];
                System.arraycopy(bytes,10,signByte,0,signLenth);
                //返回后的签名
                String sign = new String(Base64.encodeBase64(signByte),"UTF-8");
                //加密后报文的长度
                int srcLength = bytes.length - signLenth - 10;
                byte[] scrByte = new byte[srcLength];
                System.arraycopy(bytes,signLenth + 10,scrByte,0,srcLength);
                //返回的解密后的报文
                repcontent = DESedeUtil.decrypt(scrByte, bankKeyConfig.Des_KEY);
                //将解密后的报文与签名验签
                if (null != repcontent && RSASignUtil.verify(sign, repcontent, bankKeyConfig.RSA_KEY)) {
                    return repcontent;
                } else {
                    log.error("请求建行银企直连验签出错:{} :{}", sign, repcontent);
                }
            }else {//失败响应: 6 位错误代码+错误信息,所有信息均不需要进行加密,以字节流返回。
                log.error("请求建行银企直连秘钥传输报文出错:",repcontent);
                return repcontent;
            }
            log.info("请求建行银企直连报文返回结果:{}", bytes);
            return repcontent;
        } catch (Exception e) {
            log.error("请求建行银企直连报文出错,错误信息:" + e.getMessage(), e);
        } finally {
            try {
                // 释放http连接
                httppost.releaseConnection();
                myclient.getHttpConnectionManager().closeIdleConnections(0);
            } catch (Exception e2) {
                log.error("银企直联释放http连接报错:{}", e2.getMessage());
            }
            myclient = null;
            httppost = null;
        }
        return null;
    }

byte[]数组用的比较少,如果有更好更简洁的方法请联系我,谢谢!
DES解密

    /**
     * 解密
     * @param data 待解密数据
     * @param key 密钥
     * @return byte[] 解密数据
     * @throws Exception
     */
    public static String decrypt(byte[] data, String key){
        try {
            //还原密钥
            Key k = toKey(key);
            //加密
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, k);
            return new String(cipher.doFinal(data));
        } catch (Exception e){
            log.error("DESede解密错误",e);
            System.out.printf("");
        }
        return null;
    }

RSA验签

    /**
     * 验签
     *
     * @param srcData 原始字符串
     * @param sign 签名
     * @return 是否验签通过
     */
    public static boolean verify(String sign, String srcData ,String public_Key) throws Exception {
        byte[] keyBytes = getPublicKey(public_Key).getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

签到签退调试好了之后别的接口也很简单了,有什么不懂的可以给我留言!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值