C#在Linux+Mono环境中使用微信支付证书

最近特殊的需求,要把微信平台一个功能页面部署到Linux(CentOS6.5)下,其中涉及到微信支付退款。

鉴于之前实践过mono+jexus+asp.net mvc的部署,于是问题重点在于解决对商户证书的调用问题。

查看微信支付官方文档关于证书的使用说明

  • ◆ apiclient_cert.p12是商户证书文件,除PHP外的开发均使用此证书文件。
  • ◆ 商户如果使用.NET环境开发,请确认Framework版本大于2.0,必须在操作系统上双击安装证书apiclient_cert.p12后才能被正常调用。
  • ◆ 商户证书调用或安装都需要使用到密码,该密码的值为微信商户号(mch_id)
  • ◆ PHP开发环境请使用商户证书文件apiclient_cert.pem和apiclient_key.pem ,rootca.pem是CA证书。

1.使用mono自带工具certmgr导入.p12证书

certmgr -add -c -m -p 密码 My ApiClient_cert.p12

2.编写测试程序

using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public class TlsTest
{
    public static void Main(string[] args)
    {

        HttpWebResponse webreponse;
        string strHtml = "NO";
        try
        {
            //系统必须已经导入cert指向的证书
            string url = "https://api.mch.weixin.qq.com/secapi/pay/refund";            //My即对应”个人“存储区,固定不可改变
            X509Store store = new X509Store("My", StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            X509Certificate cer = store.Certificates.Find(X509FindType.FindByThumbprint, "546FCAA50DB599C5439AC8139C69393D5DB243D0", false)[0];
            //遍历证书查看Hash;windows下可直接双击安装,查看属性
            //Console.WriteLine(store.Certificates.Count);
            //foreach(var c in store.Certificates){
            //    //Console.WriteLine("{0},{1},{2},{3},{4},{5}", c.GetCertHashString(), c.FriendlyName, c.GetFormat(), c.IssuerName, c.SubjectName, c.Thumbprint);
            //    if (c.GetCertHashString() == "546FCAA50DB599C5439AC8139C69393D5DB243D0")
            //    {
            //        Console.WriteLine("found");
            //        cer = c;
            //        break;
            //    }
            //}
            HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url);
            webrequest.ClientCertificates.Add(cer);
            webrequest.Method = "post";
            webrequest.KeepAlive = true;
            webreponse = (HttpWebResponse)webrequest.GetResponse();

            Stream stream = webreponse.GetResponseStream();
            string resp = string.Empty;
            using (StreamReader reader = new StreamReader(stream))
            {
                resp = reader.ReadToEnd();
            }
            strHtml = resp;
        }
        catch (Exception exp)
        {
            strHtml = exp.ToString();

        }

        Console.WriteLine(strHtml);
    }
}

3.运行测试程序

mono SSLtest.exe

结果运行报错

System.Net.WebException: Error: TrustFailure (The authentication or decryption has failed.) ---> System.IO.IOException: The authentication or decryption has failed. ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b0109
  at Mono.Security.Protocol.Tls.RecordProtocol.EndReceiveRecord (IAsyncResult asyncResult) <0x40713980 + 0x0013e> in <filename unknown>:0
  at Mono.Security.Protocol.Tls.SslClientStream.SafeEndReceiveRecord (IAsyncResult ar, Boolean ignoreEmpty) <0x407138b0 + 0x00031> in <filename unknown>:0
  at Mono.Security.Protocol.Tls.SslClientStream.NegotiateAsyncWorker (IAsyncResult result) <0x4070d2d0 + 0x0023a> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslClientStream.EndNegotiateHandshake (IAsyncResult result) <0x40726300 + 0x000f3> in <filename unknown>:0
  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) <0x40726050 + 0x00086> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslStreamBase.EndRead (IAsyncResult asyncResult) <0x4070a990 + 0x00199> in <filename unknown>:0
  at Mono.Net.Security.Private.LegacySslStream.EndAuthenticateAsClient (IAsyncResult asyncResult) <0x4070a8f0 + 0x00042> in <filename unknown>:0
  at Mono.Net.Security.Private.LegacySslStream.AuthenticateAsClient (System.String targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation) <0x40704650 + 0x00055> in <filename unknown>:0
  at Mono.Net.Security.MonoTlsStream.CreateStream (System.Byte[] buffer) <0x40703e00 + 0x00145> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) <0x406fcf50 + 0x001ed> in <filename unknown>:0
  at System.Net.HttpWebRequest.GetResponse () <0x406f5210 + 0x00053> in <filename unknown>:0
  at TlsTest.Main (System.String[] args) <0x406a7d80 + 0x002c6> in <filename unknown>:0

4.发现错误原因是"Invalid certificate received from server",这个问题在Mono文档中已有说明:

That’s probably because you do not trust the site you are connecting to. Note that a default installation of Mono doesn’t trust anyone!

默认情况下,任何站点都不被Mono信任!

同时也给出了4个解决方案:

  • 使用cert-sync工具同步Mono和系统的证书存储区
  • 程序实现ICertificatePolicy自行决定信任规则
  • 使用certmgr.exe导入受信证书
  • 使用mozroots.exe自动导入常用的可信根证书

5.尝试解决方案2

ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);

private static bool CheckValidationResult(object sender, X509Certificate c, X509Chain chain, SslPolicyErrors errors)
{
      return true;
}

报错问题虽然可以解决,但对所有情况均信任不是一个好的解决方法,根据X509Certificate c进行判断理应可行,没有再进一步探索

6.尝试解决方案4

使用mozroots自动下载安装常用的根证书

mozroots --import --sync

显示成功导入100+根证书,重新执行测试程序仍然报错。

这个结果一度使我认为此方法无效,但偶然在本地测试却通过了。

对比发现生效的下载路径是:

https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt

而无效的下载路径是:

http://mxr.mozilla.org/seamonkey/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1

于是手动下载文件并指定mozroots安装指定的文件

wget https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txtmozroots --import --sync --file certdata.txt

这时,执行测试程序运行正常

<xml><return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[post数据为空]]></return_msg>
</xml>

7.总结

使用mozroots是解决问题的简单方法,但由于mono版本问题会导致默认的下载文件并不有效

本地有效版本:mono 4.6.0.0,使用中已提示建议使用cert-sync [WARNING: mozroots is deprecated, please move to cert-sync instead.]

服务器版本:mono 4.4.1.0,默认下载文件无效,这不得不说是个坑。

时间: 2024-10-14 09:03:04

C#在Linux+Mono环境中使用微信支付证书的相关文章

Jar包中读取微信支付证书

通过this.getClass().getResourceAsStream()读入数据 KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(instream, mchId.toCharArray()); 报错: DerInputStream.getLength(): lengthTag=111, too big. 原因:打包时证书文件被篡改 解决办法: 1.spring-boot <build>

人人商城退款提示“未上传完整的微信支付证书,请到【系统设置】-&gt;【支付方式】中上传”

[问题描述]人人商城后台操作买家退款时提示“未上传完整的微信支付证书,请到[系统设置]->[支付方式]中上传” [解决方案]是因为微信支付升级改版之后,不再需要上传root证书,只需要上传“apiclient_cert.pem”,“apiclient_key.pem”两个证书即可.但是支付配置文件还是在判断是否上传root证书,所有需要修改一下支付配置文件.依次找到 addons/ewei_shopv2/core/model 目录下面的 finance.php 文件.把判断root证书的代码去掉

PHP 的解压缩ZipArchive中的extractTo()方法 LINUX+nginx环境中解压zip时文件丢失的问题

在项目中要用ZipArchive解压ZIP文件,起初測试环境在WINDOWS平台中,測试通过,换到 LINUX+nginx 的环境中时 就出问题了(ZIP包中有文件和目录一共3百多个文件,大部分是带汉字的文件名称),问题的现象是:不带汉字的文件解压没有问题,另外有部分带汉字和数字字母的文件解压没有问题,然后其它纯文字的文件名称就丢失了,也没有报错,最后把问题定位到了extractTo()方法,这种方法尼玛是个封装的方法,看不到实际的源码. 可是,发现 for($i = 0; $i < $zip-

iOS中 最新微信支付/最全的微信支付教程具体解释 韩俊强的博客

亲们, 首先让我们来看一下微信支付的流程吧. 1. 注冊微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些參数. 2. 下载微信支付sdk 3. client请求订单,后台与微信后台交互.返回给client支付參数 4. 调用微信client.由微信client和微信server打交道: 5. client和服务端都会收到支付结果:(前台消息不可靠.我们须要去后台验证,假设后台没有收到支付通知.后台去微信server验证然后将结果返回给client)

微信商城中使用微信支付接口获取用户地址

授人以鱼不如授人以渔 微信支付获取用户地址 使用微信获取地址信息是和微信支付一道申请的,微信支付申请通过,就可以使用该功能. 微信商城中,使用微信支付获取用户的收货地址,可以省略用户输入地址信息的繁复流程,提高用户体验. 但是可能是因为牵扯到用户隐私,所以在使用过程中,需要用户自己主动选择使用该功能,并且是通过点击的操作,我们才可以获取到用户的收货地址,这一点是要注意的. 操作流程如下: 1.用户打开购物车页面,点击结算,跳转到一个微信的oauth2的页面,地址为:https://open.we

在douphp中加入微信支付教程

本教程结合推送模板消息效果更佳!如果您在用douphp的订单会员模块并且在微信端使用,那么在处理订单的时候可以使用微信付款!前提条件:开通微信支付微信公众号会员关注了你的微信公众号 直接使用微信提供的sdk就可以,具体的操作办法如下! 1.下载微信提供的公众号内支付的sdk文件!下载地址:https://pay.weixin.qq.com/wiki/doc/api/download/WxpayAPI_php_v3.zip2.解压后我们放在m/目录下即可(按照sdk/doc文件夹内的word文档修

iOS中 最新微信支付/最全的微信支付教程详解 韩俊强的博客

每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 亲们, 首先让我们来看一下微信支付的流程吧. 1. 注册微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些参数. 2. 下载微信支付sdk 3. 客户端请求订单,后台与微信后台交互,返回给客户端支付参数 4. 调用微信客户端,由微信客户端和微信服务器打交道: 5. 客户端和服务端都会收到支付结果:(前台消息不可靠,我们需要去后台验证,如果后台没有收到支付通知,后台去微

Cordova - 彻底搞定安卓中的微信支付插件!

你看到这个标题肯定会惊讶,一个Cordova的微信支付插件,有这么夸张吗?信不信由你,我相信,最终你会回来看这篇文章的! 一,不要使用的微信支付插件:https://github.com/xu-li/cordova-plugin-wechat 上面的插件,是目前大家在网上搜索文章时候,推荐最多的插件,但是,插件开发者,已经不再维护这个插件了,从最新版插件的代码看,确实是那样,其中从2.1.0开始的bug,到了最终的2.3.0也没有修复,而且还增加了bug!如果你不相信我的话,真的想使用上面这个插

linux生产环境中替换jdk

声明一下,为了给大家演示以及对生产环境的保密,我在本地vm中搭建了跟生产环境一模一样的虚拟机来给大家操作,其中截图来自本地虚拟机.操作步骤严格按照生产环境替换来做. 首先,给大家介绍下生产环境的大体架构,前端采用nginx分发,后端两个tomcat处理请求应用服务器,这只是最基本也是最常见的负载均衡架构. 下面开始实际操作:(注:请使用root账户操作) 第一步:首先看一下系统的java版本.系统采用openJDK,并且为1.7.0_85版本 第二步:安装sunjdk 1使用命令新建一个文件夹s