alipay-sdk-NET沒有getCertSN以及getRootCertSN方法,給同樣採坑的一點幫助

在做支付寶APP支付接口的後臺功能時,發現按DEMO代碼統一下單時根本就走不同,DEMO代碼如下:

IAopClient client = new DefaultAopClient("https://openapi.alipay.com/gateway.do", "app_id", "merchant_private_key", "json", "1.0", "RSA2", "alipay_public_key", "GBK", false);
AlipayTradeAppPayRequest  request= new AlipayTradeAppPayRequest() ;
request.BizContent="{" +
"\"timeout_express\":\"90m\"," +
"\"total_amount\":\"9.00\"," +
"\"product_code\":\"QUICK_MSECURITY_PAY\"," +
"\"body\":\"Iphone6 16G\"," +
"\"subject\":\"大樂透\"," +
"\"out_trade_no\":\"70501111111S001111119\"," +
"\"time_expire\":\"2016-12-31 10:05\"," +
"\"goods_type\":\"0\"," +
"\"promo_params\":\"{\\\"storeIdType\\\":\\\"1\\\"}\"," +
"\"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%3d2016010101111\"," +
"\"extend_params\":{" +
"\"sys_service_provider_id\":\"2088511833207846\"," +
"\"hb_fq_num\":\"3\"," +
"\"hb_fq_seller_percent\":\"100\"," +
"\"industry_reflux_info\":\"{\\\\\\\"scene_code\\\\\\\":\\\\\\\"metro_tradeorder\\\\\\\",\\\\\\\"channel\\\\\\\":\\\\\\\"xxxx\\\\\\\",\\\\\\\"scene_data\\\\\\\":{\\\\\\\"asset_name\\\\\\\":\\\\\\\"ALIPAY\\\\\\\"}}\"," +
"\"card_type\":\"S0JP0000\"" +
"    }," +
"\"merchant_order_no\":\"20161008001\"," +
"\"enable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
"\"store_id\":\"NJ_001\"," +
"\"specified_channel\":\"pcredit\"," +
"\"disable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," +
"      \"goods_detail\":[{" +
"        \"goods_id\":\"apple-01\"," +
"\"alipay_goods_id\":\"20010001\"," +
"\"goods_name\":\"ipad\"," +
"\"quantity\":1," +
"\"price\":2000," +
"\"goods_category\":\"34543238\"," +
"\"categories_tree\":\"124868003|126232002|126252004\"," +
"\"body\":\"特價手機\"," +
"\"show_url\":\"http://www.alipay.com/xxx.jpg\"" +
"        }]," +
"\"ext_user_info\":{" +
"\"name\":\"李明\"," +
"\"mobile\":\"16587658765\"," +
"\"cert_type\":\"IDENTITY_CARD\"," +
"\"cert_no\":\"362334768769238881\"," +
"\"min_age\":\"18\"," +
"\"fix_buyer\":\"F\"," +
"\"need_check_info\":\"F\"" +
"    }," +
"\"business_params\":\"{\\\"data\\\":\\\"123\\\"}\"," +
"\"agreement_sign_params\":{" +
"\"personal_product_code\":\"CYCLE_PAY_AUTH_P\"," +
"\"sign_scene\":\"INDUSTRY|DIGITAL_MEDIA\"," +
"\"external_agreement_no\":\"test20190701\"," +
"\"external_logon_id\":\"13852852877\"," +
"\"access_params\":{" +
"\"channel\":\"ALIPAYAPP\"" +
"      }," +
"\"sub_merchant\":{" +
"\"sub_merchant_id\":\"2088123412341234\"," +
"\"sub_merchant_name\":\"滴滴出行\"," +
"\"sub_merchant_service_name\":\"滴滴出行免密支付\"," +
"\"sub_merchant_service_description\":\"免密付車費,單次最高500\"" +
"      }," +
"\"period_rule_params\":{" +
"\"period_type\":\"DAY\"," +
"\"period\":3," +
"\"execute_time\":\"2019-01-23\"," +
"\"single_amount\":10.99," +
"\"total_amount\":600," +
"\"total_payments\":12" +
"      }" +
"    }" +
"  }";
AlipayTradeAppPayResponse response=client.sdkExecute(request);
Console.WriteLine(response.Body);

重點代碼:AlipayTradeAppPayResponse response=client.sdkExecute(request);

查看DefaultAopClient代碼發現竟然只是把參數給你簽好名然後再直接用參數初始化一個響應對象,根本就沒有發送HTTP請求,其中代碼如下:

public T SdkExecute<T>(IAopRequest<T> request, string appAuthToken) where T : AopResponse
{
    // 構造請求參數
    AopDictionary requestParams = buildRequestParams(request, null, appAuthToken);

    // 字典排序
    IDictionary<string, string> sortedParams = new SortedDictionary<String, String>(requestParams, StringComparer.Ordinal);
    AopDictionary sortedAopDic = new AopDictionary(sortedParams);

    // 參數簽名
    String charset = String.IsNullOrEmpty(this.charset) ? "utf-8" : this.charset;
    String signResult = AopUtils.SignAopRequest(sortedAopDic, privateKeyPem, charset, this.keyFromFile, this.signType);

    // 添加簽名結果參數
    sortedAopDic.Add(SIGN, signResult);

    // 參數拼接
    String signedResult = WebUtils.BuildQuery(sortedAopDic, charset);

    // 構造結果
    T rsp = (T) Activator.CreateInstance(typeof(T));
    rsp.Body = signedResult;
    return rsp;
}

其中關鍵代碼:T rsp = (T) Activator.CreateInstance(typeof(T));

此處他根本沒有發送請求,而是直接調用默認構造函數初始化了一個 T 類的實例(此處的 T 爲 AlipayTradeAppPayRequest 類帶過來的,具體爲:AlipayTradeAppPayResponse)。

無奈,只能自己寫代碼來實現了,,之後的各種看文檔,,接着,,神奇的事發生了。。。。關鍵如下:

此處需要公鑰證書的SN以及根證書的SN,文檔說要根據AntCertificationUtil.getRootCertSN以及AlipaySignature.getCertSN方法來實現,但是找遍SDK的代碼也沒發現有這兩個方法啊。。。

最後無奈又一遍一遍看其他的SDK看有沒得這兩個方法也沒發現(此時還沒看JAVA得SDK,因爲官網提供的Maven項目依賴地址根本就進不去),最後實在沒辦法了,只好百度找下alipay-sdk-java關鍵字,結果發現有github的java版本SDK,然後查看發現有AlipaySignature.getCertSN方法,方法如下:

/**
 * 從公鑰證書中提取公鑰序列號
 *
 * @param certPath 公鑰證書存放路徑,例如:/home/admin/cert.crt
 * @return 公鑰證書序列號
 * @throws AlipayApiException
 */
public static String getCertSN(String certPath) throws AlipayApiException {
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(certPath);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes());
        String certSN = new BigInteger(1, md.digest()).toString(16);
            //BigInteger會把0省略掉,需補全至32位
        certSN = fillMD5(certSN);
        return certSN;

    } catch (NoSuchAlgorithmException e) {
        throw new AlipayApiException(e);
    } catch (IOException e) {
        throw new AlipayApiException(e);
    } catch (CertificateException e) {
        throw new AlipayApiException(e);
    } finally {
        try {
           if (inputStream != null) {
               inputStream.close();
           }
        } catch (IOException e) {
                throw new AlipayApiException(e);
        }
    }
}

單獨提取出來,稍加修改如下:

編譯並運行:

接下來一步一步寫出C#版本,說出我寫C#版本遇到的問題:

1、cert.IssuerName.Name得到的簽發機構名稱中的各個屬性用英文,分隔,但是英文,符號之後會有空格,根據上方圖片得到的信息,java獲取到的簽發機構名稱的屬性分隔符之間是沒有空格的;

2、C#中cert.SerialNumber獲取到的內置序列號是16進制的字符串,根據上方圖片得到的信息,java得到的是10進制的字符串,後來找了好久沒發現C#怎麼把大數據16進制轉換成10進制的方法(從來沒做過大數據類型轉換的業務,所以不知道),後來靈機一動發現java有BigInteger大整數類,C#應該也有的,然後引入System.Numerics.dll順利解決問題。

最終寫出C#代碼如下(此處MD5Encrypt函數沒有提供):

/// <summary>
/// 獲取指定CRT證書文件的序列號。
/// </summary>
/// <param name="certPath">證書路徑。</param>
/// <returns>返回該證書序列號。</returns>
public static string GetCertSN(String certPath)
{
     X509Certificate2 cert = new X509Certificate2(certPath);

     string issuerName = new Regex(@"\, +C=").Replace(new Regex(@"\, +O=").Replace(new Regex(@"\, +OU=").Replace(cert.IssuerName.Name, ",OU="), ",O="), ",C=");

     return MD5Encrypt(issuerName + BigInteger.Parse(string.Format("0{0}", cert.SerialNumber), System.Globalization.NumberStyles.AllowHexSpecifier), Encoding.UTF8, null).ToLower();
}

 

 

同樣的,找到java sdk的AntCertificationUtil.getRootCertSN函數,代碼如下:

/**
 * 獲取根證書序列號
 *
 * @param rootCertContent
 * @return
 */
public static String getRootCertSN(String rootCertContent) {
    String rootCertSN = null;
    try {
        X509Certificate[] x509Certificates = readPemCertChain(rootCertContent);
        MessageDigest md = MessageDigest.getInstance("MD5");
        for (X509Certificate c : x509Certificates) {
            if (c.getSigAlgOID().startsWith("1.2.840.113549.1.1")) {
                md.update((c.getIssuerX500Principal().getName() + c.getSerialNumber()).getBytes());
                String certSN = new BigInteger(1, md.digest()).toString(16);
                //BigInteger會把0省略掉,需補全至32位
                certSN = fillMD5(certSN);
                if (StringUtils.isEmpty(rootCertSN)) {
                    rootCertSN = certSN;
                } else {
                    rootCertSN = rootCertSN + "_" + certSN;
                }
            }

        }
    } catch (Exception e) {
        AlipayLogger.logBizError(("提取根證書失敗"));
    }
    return rootCertSN;
}

發現X509Certificate[] x509Certificates = readPemCertChain(rootCertContent);代碼,其中他自己寫了個readPemCertChain函數,查看如下:

private static X509Certificate[] readPemCertChain(String cert) throws CertificateException {
    ByteArrayInputStream inputStream = new ByteArrayInputStream(cert.getBytes());
    CertificateFactory factory = CertificateFactory.getInstance("X.509", provider);
    Collection<? extends Certificate> certificates = factory.generateCertificates(inputStream);
    return (X509Certificate[]) certificates.toArray(new X509Certificate[certificates.size()]);
}

然後C#找了各種函數沒有發現對應的方法,無奈只能百度找資料了,其中發現該處文章:https://blog.csdn.net/xiangguiwang/article/details/76400805

關鍵點如下:

於是乎用記事本打開了阿里雲的alipayRootCert.crt查看了一下,發現內部是這樣的:

根據百度查到的資料,得出結論:阿里雲的CRT根證書就是ASCII編碼的PEM證書,只是其中包含了很多個PEM證書(不知道這樣描述對不對,本人對證書內部原理一點不知道)

然後再根據java裏面的代碼得出結論:首先讀取根證書內部所有的證書內容,然後循環獲取內部的每一個SN號,然後用_拼接

最終寫出C#代碼如下:

/// <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();
}

完整的Helper.cs代碼:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Numerics;
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 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("<", "&lt;").Replace(">", "&gt;").Replace("&", "&amp;").Replace("\"", "&quot;"));
            }

            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 + BigInteger.Parse(string.Format("0{0}", cert.SerialNumber), System.Globalization.NumberStyles.AllowHexSpecifier), Encoding.UTF8, null).ToLower();
        }
    }
}

 

有需要的朋友可以直接拿去使用,SDK還未完全寫出來,所以本處的代碼不保證完全正確。寫出來之後再把微信和支付寶做一個集成的SDK發出來吧。。先把遇到最大的問題給寫出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章