支付宝针对.NET SDK并未封装有提取证书SN序列号的方法,仅针对Java平台才有对应的方法(赤裸裸的歧视啊~~)

要想在提取这个SN序列号有两种方案:

1. 直接用Java SDK包来提取SN

2. 根据Java代码转换成C#代码来提取

支付宝的签名指导上有如下提示:

我两种方案都有使用,提取应用证书的SN用的是Java来提取的,没有问题;但是在通过Java SDK来提取支付宝根证书的SN时,返回错误;

因为对Java代码不熟悉,改不动,直接换成用C#代码来提取SN;

代码是从网上找的,这里记录下:

using Org.BouncyCastle.Math;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml; namespace Qcnt.ThreeParty.Payment
{
/// <summary>
/// 三方支付相关的静态帮助类。
/// </summary>
public static class Alipay_Crt_SN_Helper
{
/// <summary>
/// 获取系统当前时间的时间戳。
/// </summary>
public static string Timestamp
{
get
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString();
}
} /// <summary>
/// 获取指定长度的随机字符串。
/// </summary>
/// <param name="length">待生成的随机字符串长度。</param>
/// <returns>返回生成好的随机字符串。</returns>
public static string GetRandomString(int length)
{
string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; StringBuilder result = new StringBuilder(); Random rd = new Random(); while (length > 0)
{
result.Append(str[rd.Next(str.Length)]); length--;
} return result.ToString();
} /// <summary>
/// 对字符串进行MD5加密。
/// </summary>
/// <param name="str">需要加密的字符串。</param>
/// <param name="encoding">字符串使用的编码集。</param>
/// <param name="delimiter">分隔符。</param>
/// <returns>返回加密后的字符串。</returns>
public static string MD5Encrypt(string str, Encoding encoding, char? delimiter)
{
if (string.IsNullOrEmpty(str))
return null; byte[] result = encoding.GetBytes(str); MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = md5.ComputeHash(result); if (delimiter != null && delimiter.HasValue)
return BitConverter.ToString(output).ToUpper().Replace('-', delimiter.Value); return BitConverter.ToString(output).ToUpper().Replace("-", "");
} /// <summary>
/// 模拟发送HTTP POST请求。
/// </summary>
/// <param name="url">需要请求的URL地址。</param>
/// <param name="data">请求参数字符串。</param>
/// <param name="encoding">请求所使用的字符串编码集。</param>
/// <returns>返回请求结果。</returns>
public static string HttpPost(string url, string data, Encoding encoding)
{
return HttpPost(url, data, null, null, encoding);
} /// <summary>
/// 模拟发送HTTP POST请求。
/// </summary>
/// <param name="url">需要请求的URL地址。</param>
/// <param name="data">请求参数字符串。</param>
/// <param name="certPath">附加的证书路径(使用前先双击安装该证书)。</param>
/// <param name="certPassword">附加的证书密码。</param>
/// <param name="encoding">请求所使用的字符串编码集。</param>
/// <returns>返回请求结果。</returns>
public static string HttpPost(string url, string data, string certPath, string certPassword, Encoding encoding)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 10000;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded"; if (!string.IsNullOrWhiteSpace(certPath) && !string.IsNullOrWhiteSpace(certPassword) && File.Exists(certPath))
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((sender, certificate, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
return true; return false;
}); request.ClientCertificates.Add(new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable));
} if (!string.IsNullOrWhiteSpace(data))
{
byte[] dataBytes = encoding.GetBytes(data); request.ContentLength = dataBytes.Length; using (Stream stream = request.GetRequestStream())
{
stream.Write(dataBytes, 0, dataBytes.Length);
}
} using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("gb2312")))
{
return streamReader.ReadToEnd();
}
}
}
} /// <summary>
/// 获取根据XmlNode名称匹配到第一个XmlNode的InnerText值。
/// </summary>
/// <param name="node">匹配XmlNode的根XmlNode。</param>
/// <param name="nodeName">要获取InnerText值的XmlNode名称。</param>
/// <returns>若找到指定的XmlNode则返回该XmlNode的InnerText值,否则返回null。</returns>
public static string GetNodeInnerText(this XmlNode node, string nodeName)
{
if (node == null)
return null; XmlNode resultNode = node.SelectSingleNode(nodeName); if (resultNode == null)
return null; return resultNode.InnerText;
} /// <summary>
/// 使用指定的参数集生成MD5签名。
/// </summary>
/// <param name="pars">生成签名所用到的参数集合。</param>
/// <param name="key">生成签名所用到的Key。</param>
/// <returns>返回生成好的签名字符串。</returns>
public static string CreateMD5Sign(Dictionary<string, string> pars, string key)
{
List<string> sortedKey = new List<string>(pars.Keys); sortedKey.Sort(); StringBuilder parsString = new StringBuilder(); foreach (string itemKey in sortedKey)
{
parsString.AppendFormat("{0}={1}&", itemKey, pars[itemKey]);
} parsString.AppendFormat("key={0}", key); return MD5Encrypt(parsString.ToString(), Encoding.UTF8, null);
} /// <summary>
/// 使用指定的参数集生成RSA2签名。
/// </summary>
/// <param name="pars">生成签名所用到的参数集合。</param>
/// <param name="privateKey">生成签名所用到的私有秘钥。</param>
/// <returns>返回生成好的签名字符串。</returns>
public static string CreateRSA2Sign(Dictionary<string, string> pars, string privateKey)
{
List<string> sortedKey = new List<string>(pars.Keys); sortedKey.Sort(); StringBuilder parsString = new StringBuilder(); int i = 0; foreach (string key in sortedKey)
{
if (i > 0)
parsString.Append("&"); parsString.AppendFormat("{0}={1}", key, pars[key]); i++;
} return RSA2Encrypt(parsString.ToString(), privateKey);
} /// <summary>
/// 将指定的Xml字符串转换成<see cref="Dictionary{TKey, TValue}"/>类型的参数集。
/// </summary>
/// <param name="xmlString">待转换的Xml字符串。</param>
/// <returns>若转换成功则返回转换后的参数集。</returns>
public static Dictionary<string, string> XmlToParameters(string xmlString)
{
if (string.IsNullOrWhiteSpace(xmlString))
return null; XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlString); XmlNode xmlRoot = xml.DocumentElement; Dictionary<string, string> result = new Dictionary<string, string>(); foreach (XmlNode childNode in xmlRoot.ChildNodes)
{
result.Add(childNode.Name, childNode.InnerText);
} return result;
} /// <summary>
/// 将指定的参数集转换成Xml格式的字符串。
/// </summary>
/// <param name="pars">待转换的参数集。</param>
/// <returns>返回转换后的Xml字符串。</returns>
public static string ParametersToXmlString(Dictionary<string, string> pars)
{
StringBuilder parsString = new StringBuilder("<xml>"); foreach (string key in pars.Keys)
{
parsString.AppendFormat("<{0}>{1}</{0}>", key, pars[key].Replace("<", "<").Replace(">", ">").Replace("&", "&").Replace("\"", """));
} parsString.Append("</xml>"); return parsString.ToString();
} /// <summary>
/// 将指定的参数集转换成POST数据字符串。
/// </summary>
/// <param name="pars">待转换的参数集。</param>
/// <returns>返回转换后的POST数据字符串。</returns>
public static string ParametersToPostDataString(Dictionary<string, string> pars)
{
StringBuilder postDataString = new StringBuilder(); int i = 0; foreach (string key in pars.Keys)
{
if (i > 0)
postDataString.Append("&"); postDataString.AppendFormat("{0}={1}", key, System.Web.HttpUtility.UrlEncode(pars[key])); i++;
} return postDataString.ToString();
} /// <summary>
/// 对AES加密字符串方向进行AES解密操作。
/// </summary>
/// <param name="decryptStr">待解密字符串。</param>
/// <param name="key">AES解密Key。</param>
/// <returns>返回解密后的字符串。</returns>
public static string AESDecrypt(string decryptStr, string key)
{
byte[] keyByte = Encoding.UTF8.GetBytes(key); byte[] encryptByte = Convert.FromBase64String(decryptStr); RijndaelManaged rijndaelManaged = new RijndaelManaged(); rijndaelManaged.Key = keyByte; rijndaelManaged.Mode = CipherMode.ECB; rijndaelManaged.Padding = PaddingMode.PKCS7; ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor(); byte[] resultByte = cryptoTransform.TransformFinalBlock(encryptByte, 0, encryptByte.Length); return Encoding.UTF8.GetString(resultByte);
} /// <summary>
/// 使用RSA2加密算法加密字符串。
/// </summary>
/// <param name="str">待加密字符串。</param>
/// <param name="privateKey">加密私有秘钥。</param>
/// <returns>获取成功则返回该CRT证书的序列号,否则返回null。</returns>
private static string RSA2Encrypt(string str, string privateKey)
{
RSACryptoServiceProvider rsaCrypto = GetRSACryptoFromPrivateKey(privateKey); return Convert.ToBase64String(rsaCrypto.SignData(Encoding.UTF8.GetBytes(str), "SHA256"));
} /// <summary>
/// 从RSA私有秘钥字符串获取一个RSA加密对象。
/// </summary>
/// <param name="privateKey">私有秘钥字符串。</param>
/// <returns>返回获取到的RSA加密对象。</returns>
private static RSACryptoServiceProvider GetRSACryptoFromPrivateKey(string privateKey)
{
byte[] modulus, exponent, d, p, q, dp, dq, inverseQ; MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(privateKey)); BinaryReader binaryReader = new BinaryReader(memoryStream); switch (binaryReader.ReadUInt16())
{
case 0x8130:
binaryReader.ReadByte();
break;
case 0x8230:
binaryReader.ReadInt16();
break;
} if (binaryReader.ReadUInt16() != 0x0102)
return null; if (binaryReader.ReadByte() != 0x00)
return null; int elementCount = GetIntegerSize(binaryReader);
modulus = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
exponent = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
d = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
p = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
q = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
dp = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
dq = binaryReader.ReadBytes(elementCount); elementCount = GetIntegerSize(binaryReader);
inverseQ = binaryReader.ReadBytes(elementCount); CspParameters cspParameters = new CspParameters(); cspParameters.Flags = CspProviderFlags.UseMachineKeyStore; RSACryptoServiceProvider rsaCrypto = new RSACryptoServiceProvider(2048, cspParameters); RSAParameters rsaParams = new RSAParameters(); rsaParams.Modulus = modulus;
rsaParams.Exponent = exponent;
rsaParams.D = d;
rsaParams.P = p;
rsaParams.Q = q;
rsaParams.DP = dp;
rsaParams.DQ = dq;
rsaParams.InverseQ = inverseQ; rsaCrypto.ImportParameters(rsaParams); return rsaCrypto;
} /// <summary>
/// 获取下一个基本部分的字节长度。
/// </summary>
/// <param name="binaryReader">二进制读取器。</param>
/// <returns>返回下一个基本部分的字节长度。</returns>
private static int GetIntegerSize(BinaryReader binaryReader)
{
byte byteValue = 0;
byte lowByte = 0x00;
byte highByte = 0x00;
int count = 0; byteValue = binaryReader.ReadByte(); if (byteValue != 0x02)
return 0; byteValue = binaryReader.ReadByte(); if (byteValue == 0x81)
{
count = binaryReader.ReadByte();
}
else
{
if (byteValue == 0x82)
{
highByte = binaryReader.ReadByte(); lowByte = binaryReader.ReadByte(); byte[] modelInt = { lowByte, highByte, 0x00, 0x00 }; count = BitConverter.ToInt32(modelInt, 0);
}
else
{
count = byteValue;
}
} while (binaryReader.ReadByte() == 0x00)
{
count -= 1;
} binaryReader.BaseStream.Seek(-1, SeekOrigin.Current); return count;
} /// <summary>
/// 获取指定CRT根证书的序列号。
/// </summary>
/// <param name="rootCertPath">根证书路径。</param>
/// <returns>返回该根证书序列号。</returns>
public static string GetRootCertSN(string rootCertPath)
{
List<List<byte>> allCertByte = new List<List<byte>>();
string beginText = "-----BEGIN CERTIFICATE-----";
string endText = "-----END CERTIFICATE-----";
Encoding encoding = Encoding.UTF8; using (StreamReader streamRead = new StreamReader(rootCertPath, encoding))
{
List<byte> bytes = null; while (!streamRead.EndOfStream)
{
string lineText = streamRead.ReadLine(); if (lineText.StartsWith(beginText))
{
bytes = new List<byte>(); allCertByte.Add(bytes); if (lineText.Length > beginText.Length)
bytes.AddRange(encoding.GetBytes(lineText.Replace(beginText, "").Trim()));
}
else if (!lineText.StartsWith(endText))
{
bytes.AddRange(encoding.GetBytes(lineText.Trim()));
}
}
} StringBuilder resultSN = new StringBuilder(); int index = 0; foreach (List<byte> item in allCertByte)
{
X509Certificate2 certificate = new X509Certificate2(item.ToArray()); if (certificate.SignatureAlgorithm.Value.StartsWith("1.2.840.113549.1.1"))
{
if (index > 0)
resultSN.Append("_"); resultSN.Append(GetCertSN(certificate)); index++;
}
} return resultSN.ToString();
} /// <summary>
/// 获取指定CRT证书文件的序列号。
/// </summary>
/// <param name="certPath">证书路径。</param>
/// <returns>返回该证书序列号。</returns>
public static string GetCertSN(string certPath)
{
return GetCertSN(new X509Certificate2(certPath));
} /// <summary>
/// 获取指定CRT证书的序列号。
/// </summary>
/// <param name="cert">证书对象。</param>
/// <returns>返回该证书序列号。</returns>
private static string GetCertSN(X509Certificate2 cert)
{
string issuerName = new Regex(@"\, +C=").Replace(new Regex(@"\, +O=").Replace(new Regex(@"\, +OU=").Replace(cert.IssuerName.Name, ",OU="), ",O="), ",C="); return MD5Encrypt(issuerName + System.Numerics.BigInteger.Parse(string.Format("0{0}", cert.SerialNumber), System.Globalization.NumberStyles.AllowHexSpecifier), Encoding.UTF8, null).ToLower();
}
}
}

  

代码是CSDN上一位网友的提供的,原文链接:https://blog.csdn.net/aaa907638015/article/details/101246654

记支付宝接口对接,涉及到提取证书SN号的解决方案的更多相关文章

  1. thinkphp3.2.3 成功对接支付宝接口

    一.首先下载支付宝官方接口,下载地址: https://b.alipay.com/order/productDetail.htm?productId=2012111200373124&tabI ...

  2. MVC支付宝PC网站接口对接

    PC网站支付接口,请参考支付宝官方文档:https://b.alipay.com/signing/productSet.htm?navKey=all 1.需要提供签约账号.商户密钥 2.代码实现: 支 ...

  3. 支付宝PC网站接口对接

    PC网站支付接口,请参考支付宝官方文档:https://b.alipay.com/signing/productSet.htm?navKey=all 1.需要提供签约账号.商户密钥 2.代码实现: 支 ...

  4. 记录用友T+接口对接的心酸历程

    前言:公司的业务主要是对接财务系统做单据传输或者凭证处理的,难免少不了和各大财务软件做数据对接,其中当然是必须通过接口来传递数据了.于是乎,用友T+的版本来了,对接的工作自然是我来做,可没想到就是这样 ...

  5. CodeIgniter 开发,支付宝接口调用实例

    准备:1.alipay官方下载最新接口类库2.解压后,将目录"\即时到账交易接口-create_direct_pay_by_user\demo\create_direct_pay_by_us ...

  6. 【转载】关于Alipay支付宝接口(Java版)

    转载自:http://blog.163.com/lai_chao/blog/static/70340789201412724619514/ 1.alipay 双功能支付简介 2.alipay 提交支付 ...

  7. android应用程序如何调用支付宝接口

    最近在做一个关于购物商城的项目,项目里面付款这块我选的是调用支付宝的接口,因为用的人比较多. 在网上搜索了以下,有很多这方面的教程,但大部分教程过于陈旧,而且描述的过于简单.而且支付宝提供的接口一直在 ...

  8. android应用程序如何调用支付宝接口(转)

    最近在做一个关于购物商城的项目,项目里面付款这块我选的是调用支付宝的接口,因为用的人比较多. 在网上搜索了以下,有很多这方面的教程,但大部分教程过于陈旧,而且描述的过于简单.而且支付宝提供的接口一直在 ...

  9. wemall app商城源码Android之支付宝接口公用函数

    wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之  ...

随机推荐

  1. Web.Config配置文件中customErrors元素的使用方法

    在Web.Config配置文件中,customErrors元素提供有关ASP.NET 应用程序自定义错误消息的信息. 先看一下配置结构的示例: <configuration>   < ...

  2. Java基础 三目运算符 在嵌套时,使用括号

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  3. Win 10 你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问....

    按window+R键输入gpedit.msc 来启动本地组策略编辑器 依次找到“计算机配置-管理模板-网络-Lanman工作站”这个节点,在右侧内容区可以看到“启用不安全的来宾登录”这一条策略设置.状 ...

  4. VC++6.0/VC6使用c99的stdint.h

    如果使用https://github.com/mattn/gntp-send/blob/master/include/msinttypes/stdint.h会报错: error C2733: seco ...

  5. EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器同步输出http-flv协议流是怎么实现的?

    http-flv是什么 http-flv是以http为传输协议,flv媒体格式为内容的方式实时下载flv音视频帧.为什么选择flv格式而非mp4?原因是mp4必须要有moov box或者moof bo ...

  6. bugclose使用—bug管理工具

    1.前言 bugclose管理的是BUG,而不仅仅是bug.BUG概念是bugclose的创造,代表团队工作的对象,包括缺陷,任务和需求. 2.bugclose访问地址 访问地址:https://ww ...

  7. (转)在高分辨率下eclipse,STS,等软件工具栏图标过小的问题方法总结

    背景:在高分辨率情况下,sts工具栏图标超小,肉眼看不清.按照方法二能够满足需求,开心 https://blog.csdn.net/u012687923/article/details/8032437 ...

  8. [Oracle] - 查看数据库中每个表占用空间大小,及进行表压缩

    查询用户创建的表 select * from user_tab_comments; -- 查询本用户的表,视图等. select * from user_col_comments; -- 查询本用户的 ...

  9. stm32F103片上串口USART1通信实验

    硬件说明:如原理图所示, 103的PA10 PA11分别接CP2102usb转串口芯片的TXD RXD引脚.CP2102芯片是将串口和电脑USB接口进行了转接,方便应用灵活. 片上串口驱动代码如下. ...

  10. 爬虫请求库之requests库

    一.介绍 介绍:使用requests可以模拟浏览器的请求,比之前的urllib库使用更加方便 注意:requests库发送请求将网页内容下载下来之后,并不会执行js代码,这需要我们自己分析目标站点然后 ...