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/