数据加密、解密:签名、验签

做项目时,会经常要与第三方进行交互,比如与支付宝、微信等做交互。

为了保证交互时数据的安全,一般情况下我们会进行签名、验签。

我们假设商户AAA与某第三方XXX进行支付对接,以商户为主语:

商户AAA有自己的私钥,并把AAA的公钥提供给XXX;

支付XXX有自己的私钥,并把XXX公钥提供给AAA;

签名:用AAA的私钥做签名,并把AAA的公钥提供给XXX,XXX使用AAA给的公钥验证签名,确认是AAA发送过来的数据,且没有被篡改过。

验签:XXX收到AAA发送过来的数据后,会回复一串数据,并用XXX自己的私钥进行签名;同上,AAA用XXX给的XXX的公钥进行签名验签。


我们举个例子:

1、商户发送数据:

商户发给支付方的数据要求是xml格式的,比如是如下格式的:

<?xml version="1.0" encoding="GBK"?>
<AIPG>
    <INFO>
        <TRX_CODE>100001</TRX_CODE>
        <VERSION>02</VERSION>
        <DATA_TYPE>2</DATA_TYPE>
        <LEVEL>0</LEVEL>
        <USER_NAME>test</USER_NAME>
        <USER_PASS>test</USER_PASS>
        <REQ_SN>2009041611084101</REQ_SN>
        <SIGNED_MSG></SIGNED_MSG>
    </INFO>
    <BODY>
        <TRANS_SUM>
                <BUSINESS_CODE>00600</BUSINESS_CODE>
                <MERCHANT_ID>0010530001</MERCHANT_ID>
                <SUBMIT_TIME>20090416120000</SUBMIT_TIME>
                <TOTAL_ITEM>2</TOTAL_ITEM>
                <TOTAL_SUM>2</TOTAL_SUM>
        </TRANS_SUM>
    </BODY>
</AIPG>

我们需要给以上的数据做签名,签名放在<SIGNED_MSG></SIGNED_MSG>中进行传输。

要求是:做签名的原始数据不能包括<SIGNED_MSG></SIGNED_MSG>标签,所以我们需要在做签名时将<SIGNED_MSG></SIGNED_MSG>标签删除,签名完成后再添加<SIGNED_MSG></SIGNED_MSG>标签到数据中。

做签名的代码如下:

/**
 * 报文签名
 * @param msg:原始XML数据
 */
public String signMsg(String xml) throws Exception{
    xml=XmlTools.signMsg(xml, tranxContants.pfxPath,tranxContants.pfxPassword);
    return xml;
}
其中pfxPath是商户的私钥证书,加密时会读取该文件,pfxPassword是一个字符串常量,加密使用。XmlTools.signMsg定义如下:

public static String signMsg(String strData, String pathPfx, String pass) throws Exception{
    return signMsg(strData, pathPfx, pass, prvd);
}
其中prvd是java.security.Provider,关于这个对象的作用,可自行搜索,它是jdk引入的密码服务提供者概念,实现了Java安全性的一部分或者全部。Provider 可能实现的服务包括: 算法(如DES、RSA、MD5);密钥的生成、转换和管理。上面的signMsg方法如下:

private static String signMsg(String strData, String pathPfx, String pass,Provider prv) throws Exception{
    final String IDD_STR="<SIGNED_MSG></SIGNED_MSG>";
    String strMsg = strData.replaceAll(IDD_STR, "");
    String signedMsg = signPlain(strMsg, pathPfx, pass,prv);
    String strRnt = strData.replaceAll(IDD_STR, "<SIGNED_MSG>" + signedMsg + "</SIGNED_MSG>");
    return strRnt;
}
这一步分三个要点:

1、去除xml格式数据中的<SIGNED_MSG></SIGNED_MSG>;

2、使用1中的数据做签名;

3、将2中做的签名放入<SIGNED_MSG></SIGNED_MSG>中,并放回原xml数据中,至此,要发送的数据已经做好的签名。

String signedMsg = signPlain(strMsg, pathPfx, pass,prv);
此处是得到签名字符串的逻辑,由商户和第三方协商好如何处理,在此不做论述。

上述的只是做签名,总体并没有对发送的数据进行加密,这意味着,此种方式只能确保数据在发送的过程中,如果被篡改,接收方能发现数据被篡改。因此,如果发送的数据涉及到密码等,还需要对整体发送的数据再进行一次加密。


2、商户接收返回的数据:

 我们现在讨论商户接收到第三方的返回数据后,如何处理:返回的数据是resp

String resp=XmlTools.send(url,xml);
boolean flag= this.verifyMsg(resp, tranxContants.tltcerPath,isFront);
我们需要对返回的数据进行校验,确保数据是第三方返回的且没有被篡改,处理逻辑在verifyMsg()方法中,tranxContants.tltcerPath是第三方给商户的公钥证书。

verifyMsg方法逻辑如下:

int iStart = strXML.indexOf("<SIGNED_MSG>");
if(iStart==-1) throw new Exception("XML报文中不存在<SIGNED_MSG>");
int end = strXML.indexOf("</SIGNED_MSG>");
if(end==-1) throw new Exception("XML报文中不存在</SIGNED_MSG>");
String signedMsg = strXML.substring(iStart + 12, end);
String strMsg = strXML.substring(0, iStart) + strXML.substring(end + 13);
System.out.println(strMsg);
System.out.println(strMsg.length());
System.out.println(signedMsg.toLowerCase());
return crypt.VerifyMsg(signedMsg.toLowerCase(), strMsg,cerFile);

其中:

   signMsg是第三方返回的数据中的签名数据,这个数据功能类似于商户发送数据给第三方时做的signMsg;

   strMsg是去除了返回的数据中的<SIGNED_MSG>签名信息</SIGNED_MSG>后的数据;

我们用第三方给的公钥(cerFile)+strMsg做签名,并将签名的结果和signMsg做对比,若一致,说明返回的数据是正确的、未被篡改的。

return crypt.VerifyMsg(signedMsg.toLowerCase(), strMsg,cerFile);就是商户做签名验签的逻辑,在此不做论述。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值