zoukankan      html  css  js  c++  java
  • 记支付宝接口对接,涉及到提取证书SN号的解决方案

    支付宝针对.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

  • 相关阅读:
    将ASCII字符串转换为UNICODE字符串
    GetLastError()返回值大全
    C++构造函数的调用
    DOM – 7.动态创建DOM + 8.innerText innerHTML value
    DOM
    DOM – 4.doucument属性
    用jquery操作xml文件
    請推薦有關網路的書
    Linux命令全称
    轻松架设时时监控工具Cacti
  • 原文地址:https://www.cnblogs.com/xlhblogs/p/11724978.html
Copyright © 2011-2022 走看看