支付与签名原串的那些事,但选择排序生成签名原串 您所在的位置:网站首页 zby是什么意思 支付与签名原串的那些事,但选择排序生成签名原串

支付与签名原串的那些事,但选择排序生成签名原串

2023-04-06 22:56| 来源: 网络整理| 查看: 265

引题

【备注】签名原串的源码放在git上了,请大家参看:项目源码

笔者最近在做支付、调用天猫优惠券、绑定银行卡相关的业务,在这些业务中,我们都需要将数据加密。然而,数据的加密方式不同,绑定银行卡用md5加密,这不涉及金钱上的往来,使用MD5加密没问题。然而,一旦涉及了金钱,比如支付业务,那么,这种方式并不好。因为黑客很有可能截取报文,修改密码后盗取金额,因而,我们采用RSA的加密方式。这里以连连支付为讲解示例。

讲解连连支付之前,需要介绍非对称加密算法。

非对称加密

我们在通过ip传输数据时,如果采用对称加密,即一个主站和用户之间可以使用相同的密钥对传输内容进行加密,主站和用户之间是知道彼此的密钥。然而,ip报文就好比在官道上与数粮草、黄金、物资,虽然相对来说比较安全,但很容易被人盯上。密钥本身如果被盗,那么,再复杂的密钥也无济于事。自然的想法是在密钥上再加密,这就是递归的穷举问题了。

这并不是最好的办法,有没有一种方式,即报文被截取之后,黑客依然无计可施。这就出现了一种全新的算法,即RSA加密算法。它把密码革命性地分成公钥和私钥,由于两个密钥并不相同。

首先通过openssl genrsa -out rsa_private_key.pem 1028 生成pkcs1格式的1028个字节的私钥(适合PHP等前端),即:

MIICXgIBAAKBgQsyeT57L81ie1Lm1hEb7RVa9JszkhmuNAu7garMbmHInXRJBkqjGWMqRFp0KQWYGGRYRqG59XVXYub3KuTE/9FamifG+d+EyUNFbwcG9H1g+kSnm868MhBp1wr2zec/s47Bbx0fbtRYPXeQrkdzz6oAxVLoNDp+7eRixvlTe6c0LwIDAQABAoGBCx+1vBD9yHlSM2YIvS6VNmYKJDXzq3eZVR6PD3PRJWv8oQ37JiMqkY3oIkTMjDYx5V6drQXliRGru/FJt8TOsNM7nmu1sGQH2Ae6WPHnqWHDJpSlEQ/rSzAv4XYxWZtYWq/6ToT25foJ7e+BL2uMKKAq/64deiLt+K7hQWUi6nTBAkEDlqt/j/cYEGnTeY2GBRTbLLLJGZ+c3hSHSS84n82l0U2qnNA3zrxshZc7hU6NTPrrQzmjIl0MGimPVbDNwC59qQJBAx7IQx6ec1OoNA+chz1Xh/ipklcximKdPNW6QByEZ8B6lp74l2SJaISeqe+WCHvnk6FVpOTqC3rWmQWsVje42hcCQQGOZL9EKq8X5xzbuOEm8P1/q+UEJLD9qj9lIIJY4vEHDLxxluas1A/n+0bHr+IdQS+njqZNb7ag3ecYDT2dG0xJAkB6Fv/zUSKtebsjW7hsDtHwlvKQMzlEo2XmAQbFlRNKnzIgcDyrmDkKdDnjLdp0Hcw5z55ZgtBoYR6YeGPhNnbXAkEC/hvl31bulAqTGdZsVYY6FEVn9TXbsF9mTFSyFbGHXjjILiDu9dQasPVBP5vLNt+ClGJJJ36ffVaX7FSbHVs7iA==

然而,我们后台使用的是Java,需要将其转为pkcs8格式的私钥,即:

MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBCzJ5PnsvzWJ7UubWERvtFVr0mzOSGa40C7uBqsxuYciddEkGSqMZYypEWnQpBZgYZFhGobn1dVdi5vcq5MT/0VqaJ8b534TJQ0VvBwb0fWD6RKebzrwyEGnXCvbN5z+zjsFvHR9u1Fg9d5CuR3PPqgDFUug0On7t5GLG+VN7pzQvAgMBAAECgYELH7W8EP3IeVIzZgi9LpU2ZgokNfOrd5lVHo8Pc9Ela/yhDfsmIyqRjegiRMyMNjHlXp2tBeWJEau78Um3xM6w0zuea7WwZAfYB7pY8eepYcMmlKURD+tLMC/hdjFZm1har/pOhPbl+gnt74Eva4wooCr/rh16Iu34ruFBZSLqdMECQQOWq3+P9xgQadN5jYYFFNsssskZn5zeFIdJLzifzaXRTaqc0DfOvGyFlzuFTo1M+utDOaMiXQwaKY9VsM3ALn2pAkEDHshDHp5zU6g0D5yHPVeH+KmSVzGKYp081bpAHIRnwHqWnviXZIlohJ6p75YIe+eToVWk5OoLetaZBaxWN7jaFwJBAY5kv0QqrxfnHNu44Sbw/X+r5QQksP2qP2Ugglji8QcMvHGW5qzUD+f7Rsev4h1BL6eOpk1vtqDd5xgNPZ0bTEkCQHoW//NRIq15uyNbuGwO0fCW8pAzOUSjZeYBBsWVE0qfMiBwPKuYOQp0OeMt2nQdzDnPnlmC0GhhHph4Y+E2dtcCQQL+G+XfVu6UCpMZ1mxVhjoURWf1NduwX2ZMVLIVsYdeOMguIO711Bqw9UE/m8s234KUYkknfp99VpfsVJsdWzuI

我们将pkcs8格式的私钥转化为公钥,即

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQsyeT57L81ie1Lm1hEb7RVa9JszkhmuNAu7garMbmHInXRJBkqjGWMqRFp0KQWYGGRYRqG59XVXYub3KuTE/9FamifG+d+EyUNFbwcG9H1g+kSnm868MhBp1wr2zec/s47Bbx0fbtRYPXeQrkdzz6oAxVLoNDp+7eRixvlTe6c0LwIDAQAB

你会发现,不论是pkcs1的私钥,还是pkcs8格式的私钥,其合公钥并不相等。因为, 这就是所谓的非对称加密。私钥是用来对公钥加密的信息解密的,需要保密。而公钥是对信息进行机密,任何都可以知道,包括hack。我们在传输的时候,双方都遵守这个契约:

甲该诉乙,使用RSA算法进行加密,乙说,好的。甲和乙分别根据RSA生成一对密钥,互相发送公钥。甲使用乙的公钥给乙加密报文信息。乙收到信息,并用自己的密钥进行解密。乙使用同样的方式给甲发送消息,甲使用相同方式进行解密。

其实,我们在使用连连支付时,也遵守这个规则。我们首先生成一对公私钥。将生成的公钥上传到连连商户站的后台,连连那边就接收到了我们的公钥。我们再从连连商户站的后台下载连连公钥,我们将私钥和签名原串共同加密生成签名,这就是加签。价钱后的数据和连连公钥再次加密,通过HttpClient调用连连支付的接口,将加签后的信息传递给连连。连连验签通过后,给我们回传他们加签后的签名信息,我们这边进行验签。这样的加密方式是比较安全的。

上面提到了两次加密和签名原串,那么,签名原串到底是什么?

签名原串、加签

我们调用连连支付时,肯定涉及到金额,商户号,签名方式,银行卡名称的。这些就是支付请求对象,假设,我们现在有一个请求支付的javabean类:

/*** 这是支付父类的bean*/ public class BaseRequestBean {private String oid_partner;private String sign;private String sign_type;}@Data @AllArgsConstructor @NoArgsConstructor public class PaymentRequestBean extends BaseRequestBean {private String api_version;private String card_no;private String flag_card;private String notify_url;private String no_order;private String dt_order;public String money_order;private String acct_name;private String bank_name;private String info_order;private String memo;private String brabank_name; }

在上面的父类中有一个sign属性,这里存储的是签名原串加密后的数据。

什么是前面原串?

即上面各个属性(但不包含sign属性)的值,按照一定格式,拼接而成的字符串。

为什么除去sign属性?

sign属性存储的将签名原串加密后的字符串。

我们首先要讲支付请求对象赋值,如图所示:赋值后的支付请求对象

我们通过一系列的操作,将其转变为如下格式的字符串,按照首字母由低到高的方式排名,如果首字母相同,再比较第二个,以此类推。。。具体怎么生成的,下面会提到。

acct_name=jack&api_version=1.2&bank_name=工商银行&brabank_name=中国工商银行&card_no=123456677756&dt_order=20190302023423&flag_card=1212121&info_order=提现支付&memo=ceshi&money_order=12.00¬ify_url=https://域名/项目名/接口&no_o... 我们第一次使用支付请求对象,是为了将其生成签名原串。签名原串和我们生成的pkcs8格式的私钥加签,第一次加密(加签)涉及到我们自己生成的私钥,如代码所示: /*** 签名处理** @param prikeyvalue:私钥* @param sign_str:签名原串* @return*/public static String sign(String prikeyvalue, String sign_str) {try {//【1】获取私钥KeyFactory keyFactory = KeyFactory.getInstance(PaymentConstant.SIGN_TYPE);//将BASE64编码的私钥字符串进行解码BASE64Decoder decoder = new BASE64Decoder();byte[] encodeByte = decoder.decodeBuffer(prikeyvalue);//生成私钥对象PrivateKey privatekey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodeByte));//【2】使用私钥// 获取Signature实例,指定签名算法(本例使用SHA1WithRSA)Signature signature = Signature.getInstance(PaymentConstant.MD5_WITH_RSA);//加载私钥signature.initSign(privatekey);//更新待签名的数据signature.update(sign_str.getBytes(BaseConstant.CHARSET));//进行签名byte[] signed = signature.sign();//将加密后的字节数组,转换成BASE64编码的字符串,作为最终的签名数据return new String(org.apache.commons.codec.binary.Base64.encodeBase64(signed));} catch (Exception e) {e.printStackTrace();}return null;} 我们将加签后的数据放置在请求对象的sign中,如图所示

获取加签后的数据

我们第二次使用支付请求对象,这次对象中的sign已经存值。我们此时可以将加签后的请求对象和连连公钥共同加密。这次涉及到的是我们从商户站下载下来的连连公钥。调用连连的支付接口,如图所示:

加签后的支付请求对象和公钥共同加密

书写签名原串

我们上面一直在提签名原串,其实怎么生成的呢,我采用的是选择排序算法,如代码所示:

public static void main(String[] args) {JSONObject jsonObject = new JSONObject();jsonObject.put("oid_partner", "12121212121");jsonObject.put("api_version", "1.2");jsonObject.put("sign_type", "rsa");jsonObject.put("flag_card", "1212121");jsonObject.put("notify_url", "https://域名/项目名/接口");jsonObject.put("no_order", "20190302023423zby");jsonObject.put("dt_order", "20190302023423");jsonObject.put("money_order", "12.00");jsonObject.put("card_no", "123456677756");jsonObject.put("acct_name", "jack");jsonObject.put("bank_name", "工商银行");jsonObject.put("info_order", "提现支付");jsonObject.put("memo", "ceshi");jsonObject.put("brabank_name", "中国工商银行");System.out.println(concatString(jsonObject,null));}/*** Created By zby on 15:07 2019/3/6* 拼接字符串*/public static String concatString(JSONObject jsonObject, String type) {List keys = keysSort(jsonObject);if (null == keys && keys.size() = 'a' && c = 'A' && c


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有