runliuv

runliuv@cnblogs

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

C# .NET 拉卡拉支付接口解析 付款码支付  条码支付 被扫 反扫 刷卡支付 B扫C。

 

简要:

1.测试环境给的私钥是PKCS8。签名用。

2.CRT证书用X509Certificate2 读取出序列号,签名时用。

3.验证签名的公钥证书是“lkl-apigw-v1-test-.cer”

4.如果termExtInfo对象为null,得处理下JSON字符串,尾部追加“termExtInfo:{}”,否则会报HTTP 400 BAD REQUEST 错误。或者你termExtInfo对象里传个客户端IP即可。

5.退款查询使用交易查询接口,把退款申请号传入ornOrderId即可。

 

测试环境主URL:https://test.wsmsd.cn/sit/labs/txn. 

 

部分代码:

1.读取PKCS8私钥,转为C# .NET RSACryptoServiceProvider 对象,用到了BouncyCastle.Crypto库,用nuget下载最新版本即可:

/// <summary>
        /// PKCS8 文本转RSACryptoServiceProvider 对象
        /// </summary>
        /// <param name="privateKeyPemPkcs8"></param>
        /// <returns></returns>
        public static RSACryptoServiceProvider LoadPrivateKeyPKCS8(string privateKeyPemPkcs8)
        {

            try
            {
                //PKCS8是“BEGIN PRIVATE KEY”
                privateKeyPemPkcs8 = privateKeyPemPkcs8.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
                privateKeyPemPkcs8 = privateKeyPemPkcs8.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

                //pkcs8 文本先转为 .NET XML 私钥字符串
                string privateKeyXml = RSAPrivateKeyJava2DotNet(privateKeyPemPkcs8);

                RSACryptoServiceProvider publicRsa = new RSACryptoServiceProvider();
                publicRsa.FromXmlString(privateKeyXml);
                return publicRsa;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// PKCS8 私钥文本 转 .NET XML 私钥文本
        /// </summary>
        /// <param name="privateKeyPemPkcs8"></param>
        /// <returns></returns>
        public static string RSAPrivateKeyJava2DotNet(string privateKeyPemPkcs8)
        {
            RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKeyPemPkcs8));
            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
            Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
            Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
        }

2.读取接入者公钥的序列号,“cert-.crt”文件:

X509Certificate2 cert = new X509Certificate2(ofdCrt.FileName);
                    txtCertSN.Text = cert.SerialNumber;

3.私钥签名:

public string GetSign(string appid, string serial_no, string bodyJson)
        {
            /*
                 将分配的appId、证书序列号、时间戳、随机字符串、请求报文拼接。拼接报文一共有5行,每一行为一个参数。行尾以\n(换行符,ASCII编码值为0x0A)结束,包括最后一行。

具体格式如下:
${appid}\n+${serialNo}\n+${timeStamp}\n+${nonceStr}\n+${body}\n
                 */

            string timestamp = TimeStampUtil.GetTimeStampTen().ToString();//10位和13位的都可以 
            string nonce_str = Guid.NewGuid().ToString("N").Substring(12);

            StringBuilder sc = new StringBuilder();
            sc.AppendFormat("{0}\n", appid);
            sc.AppendFormat("{0}\n", serial_no);
            sc.AppendFormat("{0}\n", timestamp);
            sc.AppendFormat("{0}\n", nonce_str);
            sc.AppendFormat("{0}\n", bodyJson);
            string fnstr = sc.ToString();

            var rsaP = RsaUtil.LoadPrivateKey(txtPrivateKey.Text, "PKCS8");
            byte[] data = Encoding.UTF8.GetBytes(fnstr);//待签名字符串转成byte数组,UTF8
            byte[] byteSign = rsaP.SignData(data, "SHA256");//对应JAVA的RSAwithSHA256
            string sign = Convert.ToBase64String(byteSign);//签名byte数组转为BASE64字符串

            string schema = "LKLAPI-SHA256withRSA";
            String token = "appid=\"" + appid + "\",serial_no=\"" + serial_no + "\",timestamp=\"" + timestamp
            + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\"";
            String authorization = schema + " " + token;
            return authorization;
        }

 

4.空termExtInfo处理:

 MicroPayReq req = new MicroPayReq();
                req.mercId = txtMchNo.Text.Trim();
                req.termNo = txtTermNo.Text.Trim();
                req.authCode = txtAuthCode.Text.Trim();
                req.amount = total_fee.ToString();
                req.orderId = txtOutTradeNo.Text;
                req.subject = "条码支付-总部";

                LKL_PubReq<MicroPayReq> pub = new LKL_PubReq<MicroPayReq>();                
                pub.reqData = req;

                string bodyJson = JsonConvert.SerializeObject(pub, jsetting);
                bodyJson = bodyJson.Substring(0, bodyJson.Length - 1);
                //空termExtInfo处理
                bodyJson = bodyJson + ",\"termExtInfo\":{}}";

 

5.HTTP 发送报文:

public static string HttpPostJson(string url, string body, IDictionary<string, string> header, int timeOut, IDictionary<string, string> rspHeader)
        {
            string rst = "";
            Encoding enc1 = Encoding.UTF8;
            byte[] content = enc1.GetBytes(body);

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/json;charset=utf-8";
            request.Timeout = timeOut * 1000;
            request.ReadWriteTimeout = timeOut * 1000;
            request.KeepAlive = false;
            request.ServicePoint.Expect100Continue = false;
            request.ContentLength = content.Length;
            //设置请求header
            foreach (var item in header)
            {
                request.Headers.Add(item.Key, item.Value);
            }

            using (Stream reqStream = request.GetRequestStream())
            {
                reqStream.Write(content, 0, content.Length);
            }

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            using (Stream stream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(stream, enc1);
                rst = reader.ReadToEnd();
            }

            //收集响应header
            if (rspHeader == null)
            {
                rspHeader = new Dictionary<string, string>();
            }
            foreach (var item in response.Headers.AllKeys)
            {
                rspHeader.Add(item, response.Headers[item]);
            }

            if (response != null)
            {
                response.Close();
                response = null;
            }
            if (request != null)
            {
                request.Abort();//2018-7-31,增加
                request = null;
            }

            return rst;
        }

 

6.读取公钥证书,转为C# RSACryptoServiceProvider对象 ,用于验证签名。

/// <summary>
        /// 加载公钥证书
        /// </summary>
        /// <param name="publicKeyCert">公钥证书文本内容</param>
        /// <returns></returns>
        public static RSACryptoServiceProvider LoadPublicCert(string publicKeyCert)
        {

            publicKeyCert = publicKeyCert.Replace("-----BEGIN CERTIFICATE-----", "").Replace("-----END CERTIFICATE-----", "").Replace("\r", "").Replace("\n", "").Trim();

            byte[] bytesCerContent = Convert.FromBase64String(publicKeyCert);
            X509Certificate2 x509 = new X509Certificate2(bytesCerContent);
            RSACryptoServiceProvider rsaPub = (RSACryptoServiceProvider)x509.PublicKey.Key;
            return rsaPub;

        }

 

7.验证签名:

/// <summary>
        /// 验证签名
        /// </summary>
        /// <param name="rspHeader">HTTP响应头</param>
        /// <param name="rspBody">响应报文</param>
        /// <param name="pubCert">公钥证书</param>
        /// <returns></returns>
        bool ValidSign(IDictionary<string, string> rspHeader, string rspBody, string pubCert)
        {
            string appid = "";
            string serial_no = "";
            string timestamp = "";
            string nonce_str = "";
            string rspSign = "";

            foreach (var item in rspHeader)
            {
                if (item.Key == "Lklapi-Appid")
                    appid = item.Value;

                if (item.Key == "Lklapi-Serial")
                    serial_no = item.Value;

                if (item.Key == "Lklapi-Timestamp")
                    timestamp = item.Value;

                if (item.Key == "Lklapi-Nonce")
                    nonce_str = item.Value;

                if (item.Key == "Lklapi-Signature")
                    rspSign = item.Value;
            }

            StringBuilder sc = new StringBuilder();
            sc.AppendFormat("{0}\n", appid);
            sc.AppendFormat("{0}\n", serial_no);
            sc.AppendFormat("{0}\n", timestamp);
            sc.AppendFormat("{0}\n", nonce_str);
            sc.AppendFormat("{0}\n", rspBody);
            string fnstr = sc.ToString();

            byte[] byteCon = Encoding.UTF8.GetBytes(fnstr);

            byte[] byteRspSign = Convert.FromBase64String(rspSign);
            var rsaPub = RsaUtil.LoadPublicCert(pubCert);

            bool bRst = rsaPub.VerifyData(byteCon, "SHA256", byteRspSign);

            return bRst;

        }

 

C# .NET 的RSA CryptoServiceProvider对象+SHA256 哈希算法,等效于JAVA的 SHA256withRSA.

完整调用代码:https://gitee.com/runliuv/mypub/tree/master/donetproj/

 

posted on 2021-07-24 15:01  runliuv  阅读(521)  评论(0编辑  收藏  举报