From 115d3744495f87bb5520ff76c5bc104d3fff1b5c Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 14 Nov 2022 13:25:07 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Advanced_EventDataDeserialization.md | 2 +- ...Advanced_RequestSensitiveDataEncryption.md | 2 + docs/WechatTenpayV3/Advanced_SMAlgorithm.md | 37 ++++++++++++++++ docs/WechatTenpayV3/README.md | 6 ++- .../Advanced_EventDataDeserialization.md | 2 +- .../TestCase_CertificateManagerTests.cs | 43 ++++++++++++++++++- 6 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 docs/WechatTenpayV3/Advanced_SMAlgorithm.md diff --git a/docs/WechatApi/Advanced_EventDataDeserialization.md b/docs/WechatApi/Advanced_EventDataDeserialization.md index a42ee659..5c3ca6a5 100644 --- a/docs/WechatApi/Advanced_EventDataDeserialization.md +++ b/docs/WechatApi/Advanced_EventDataDeserialization.md @@ -44,7 +44,7 @@ switch (msgType) var callbackModel = client.DeserializeEventFromXml(callbackXml); } break; - // 省略其他情况 + // 其他情况略 } ``` diff --git a/docs/WechatTenpayV3/Advanced_RequestSensitiveDataEncryption.md b/docs/WechatTenpayV3/Advanced_RequestSensitiveDataEncryption.md index 40f9d72e..57fa6405 100644 --- a/docs/WechatTenpayV3/Advanced_RequestSensitiveDataEncryption.md +++ b/docs/WechatTenpayV3/Advanced_RequestSensitiveDataEncryption.md @@ -164,6 +164,7 @@ public class RedisCertificateManager : CertificateManager IDictionary map = values.ToDictionary(k => k.Name.ToString(), v => v.Value.ToString()); return new CertificateEntry( + algorithmType: map[nameof(CertificateEntry.AlgorithmType)], serialNumber: map[nameof(CertificateEntry.SerialNumber)], certificate: map[nameof(CertificateEntry.Certificate)], effectiveTime: DateTimeOffset.Parse(map[nameof(CertificateEntry.EffectiveTime)]), @@ -175,6 +176,7 @@ public class RedisCertificateManager : CertificateManager { return new HashEntry[] { + new HashEntry(nameof(CertificateEntry.AlgorithmType), entry.AlgorithmType), new HashEntry(nameof(CertificateEntry.SerialNumber), entry.SerialNumber), new HashEntry(nameof(CertificateEntry.Certificate), entry.Certificate), new HashEntry(nameof(CertificateEntry.EffectiveTime), entry.EffectiveTime.ToString()), diff --git a/docs/WechatTenpayV3/Advanced_SMAlgorithm.md b/docs/WechatTenpayV3/Advanced_SMAlgorithm.md new file mode 100644 index 00000000..6e43b1a1 --- /dev/null +++ b/docs/WechatTenpayV3/Advanced_SMAlgorithm.md @@ -0,0 +1,37 @@ +## 如何接入国密算法? + +--- + +> 请先自行阅读: +> +> [《微信支付开发者文档 - 国密接入指引》](https://pay.weixin.qq.com/docs/merchant/development/shangmi/introduction.html) + +从 v2.14.0 版本起,本库支持接入微信支付平台基于国密证书和使用 SM2/SM3/SM4 算法的 API v3 接口。 + +--- + +### 接入指引 + +在微信商户平台开通国密接入权限后,你需要在原有的项目代码基础上做出如下两点调整。 + +首先,构造得到 `WechatTenpayClient` 对象的方式与原有方式基本一致,只需将 `MerchantCertificateSerialNumber`、`MerchantCertificatePrivateKey` 替换为相应的国密证书内容即可,并指定签名认证方式: + +```csharp +var options = new WechatTenpayClientOptions() +{ + // 其他配置项略 + SignScheme = Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3 +}; +var client = new WechatTenpayClient(options); +``` + +此外,获取平台证书并存入平台证书管理器 `PlatformCertificateManager` 时,需指定证书的算法类型: + +```csharp +// 如果是 RSA 证书 +manager.AddEntry(new CertificateEntry("RSA", "RSA 证书序列号", "RSA 证书内容", "RSA 证书生效时间", "RSA 证书过期时间")); +// 如果是 SM2 证书 +manager.AddEntry(new CertificateEntry("SM2", "SM2 证书序列号", "SM2 证书内容", "SM2 证书生效时间", "SM2 证书过期时间")); +``` + +这样,就已经完成了接入国密算法的全部流程。请求自动签名、响应验证签名、加解密敏感数据字段、解析回调通知事件模型等相关的扩展方法调用方式与原有方式完全一致。 diff --git a/docs/WechatTenpayV3/README.md b/docs/WechatTenpayV3/README.md index a3a6d4de..8553d47f 100644 --- a/docs/WechatTenpayV3/README.md +++ b/docs/WechatTenpayV3/README.md @@ -12,9 +12,9 @@ - 支持直连商户、服务商两种模式。 -- 请求时自动生成签名,无需开发者手动干预。 +- 请求时自动生成签名(同时支持国际 RSA 算法或国密 SM 算法),无需开发者手动干预。 -- 提供了微信支付所需的 RSA、AES、SHA-256 等算法工具类。 +- 提供了微信支付所需的 RSA、AES、SM2/SM3/SM4、SHA-256 等算法工具类。 - 提供了生成调起支付签名、加密请求中敏感数据、解密响应中敏感数据、解析回调通知事件敏感数据等扩展方法。 @@ -114,6 +114,8 @@ else - [如何生成客户端调起支付时所需的参数及签名?](./Advanced_Parameters.md) +- [如何接入国密算法?](./Advanced_SMAlgorithm.md) + - [如何扩展额外的 API?](./Advanced_Extensions.md) --- diff --git a/docs/WechatWork/Advanced_EventDataDeserialization.md b/docs/WechatWork/Advanced_EventDataDeserialization.md index f0a2cb25..2042b41b 100644 --- a/docs/WechatWork/Advanced_EventDataDeserialization.md +++ b/docs/WechatWork/Advanced_EventDataDeserialization.md @@ -35,7 +35,7 @@ switch (msgType) var callbackModel = client.DeserializeEventFromXml(callbackXml); } break; - // 省略其他情况 + // 其他情况略 } ``` diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_CertificateManagerTests.cs b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_CertificateManagerTests.cs index 9ec3ecc6..8d7b134d 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_CertificateManagerTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_CertificateManagerTests.cs @@ -5,10 +5,19 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests { public class TestCase_CertificateManagerTests { + private const string CERT_RSA_SN = "3050f72e2dce03e7efe49557dd231d176649afbd"; + private const string CERT_RSA_PEM = "-----BEGIN CERTIFICATE-----\nMIIFRzCCAy8CFDBQ9y4tzgPn7+SVV90jHRdmSa+9MA0GCSqGSIb3DQEBCwUAMGAx\nCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwIU2hhbmdo\nYWkxDTALBgNVBAoMBFNLSVQxDTALBgNVBAsMBFNLSVQxDTALBgNVBAMMBFNLSVQw\nHhcNMjExMTI1MTgzNzQ4WhcNMjExMjI1MTgzNzQ4WjBgMQswCQYDVQQGEwJDTjER\nMA8GA1UECAwIU2hhbmdoYWkxETAPBgNVBAcMCFNoYW5naGFpMQ0wCwYDVQQKDART\nS0lUMQ0wCwYDVQQLDARTS0lUMQ0wCwYDVQQDDARTS0lUMIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEA52DszUZzPKPo1d9Hi5Hjlu7OINwADaeXifA4rvmJ\nJaA+jm4DCMwrAMzyS12EiW31xCAF8LZ/xkrFHO5CZgvK87Y+kY9DmhvNX6FVYsn4\nay7KER0zo87zqQjC+njUu1rYuKnio7MYb354PitwQ3SWNv2qTCbCNCXTN9pJXNhl\nCudWCEWrNrYc4/hKz3bqu1DjpY0oHuuKPk/iRr2TTUIAwahNkNQheQNB2a8hL7L2\nOG1Sn1vaDWe+5RJYlMRZ3NgYDTqoy8GMs+6q091MQMDlQ90jtW/JEoM5DUyI8zfQ\nfDLGnU7FuY0rrZ/+6OQT/o7ISf0OR5TISS0lqnDN3vVaph0ftDGRdGqJk2SJAHIo\nxp5gt410rfWS9kpSDFJs3Pvt4rtNZBYvkGD8obSm91brAkoX4+u1Y4p1qZpWJ4LI\nKw8oyeieqlLZtF/VGKOtKxe/IKn8GwoQJLx4dUGFOqM7HPwR9cyjMaC1o3V1NQG+\n1wD9TLtGh3WXUFJRYDmePaSp39GFPupTMlPRbD0RK80B6xv2rYTyYyd8s2LN6P6H\nh/nFIkc1rekIf9JhPy0WKzrXdmnfjSHKPxmz0WSYN8FxKasqcJhncOdhLTzzVEhj\n9xHSI8ejP2fJ4v+ARoD3GURPD9H7KMa7xmzRSAZ8A8LM3uvdJNhbKBwWqvo45ncz\n+7cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAVTS6oMfDA3XTwEel0BvaXMCdo7yM\ns5ueM87151eywnPlConYDXeqhfF0OCSBnY2g7Fpmn+YAUoa/L+FNOx/gMC9QV/lP\nHhhAcWpiCRy52RX/IyTDxFD6OqtH0BaBtDTb+QBXZuFypMUkPy6EUYs5Cl9qYepy\nHcgGVomx7tcwWcvI4o/KZtj8hXC5wu/k4Y0GGUriTt8xmnJ+RTRedZ6hzAFVHtXm\n/YIT9Lc1IIYZuHVyCbX/HXwa0E4r8lghwZRg94HUvpbfabNA3obt5auwtJUfW1tK\n2ERgFrtBRBWf9EGb8TstXqksqYZ04U4OjLm/3ZJhSSYKNbriRLlSEzAlHikNVW+t\n6cTh+sasrGt/qNIRMs5PiipwmV/T3z1LbyoiU7fXZ4GqiWBnZARFC9KiPPTzLszh\nBKJGYHaC8wkGb3WfNWFBqVRfFL8kdME+shLB8/ETQ31gIFeudnW1QlujJ7ZSZtwz\nxT3HxzZIIbNEqLFP+d37kmuKjRmI4KWc+pKOUw9BOl4g/TJH6ySljSNs8LSDWwQY\n76Dsnr+ovz8ZVLNUCmedZCyumeJo2tLkJmsPo5GuMnXpL94mhqpCoUS4l4JbJl44\nT2lmqp1Ueoz+Qlkqyt2lj3heTv9bvB7NO9KHTsDy1hhWHOG1QyXzajyWETU+1XdW\nx1hGvYxtpQPLUE8=\n-----END CERTIFICATE-----"; + private const string CERT_RSA_START_DATE = "2021-11-26 02:37:48"; + private const string CERT_RSA_END_DATE = "2021-12-26 02:37:48"; + private const string CERT_SM2_PEM = "-----BEGIN CERTIFICATE-----\nMIICNzCCAdygAwIBAgIJAOWoGwJCnY0IMAoGCCqBHM9VAYN1MGcxCzAJBgNVBAYT\nAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdIYWlEaWFuMRMwEQYDVQQK\nDApHTUNlcnQub3JnMR8wHQYDVQQDDBZHTUNlcnQgR00gUm9vdCBDQSAtIDAxMB4X\nDTIyMTEwOTEzMTIyMFoXDTIzMTEwOTEzMTIyMFowSzEtMCsGA1UEAwwkU0tJVC5G\nbHVybEh0dHBDbGllbnQuV2VjaGF0LlRlbnBheVYzMQ0wCwYDVQQKDARTS0lUMQsw\nCQYDVQQGEwJDTjBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABMXP1hZc2zBzreRN\nZgOR9hklE01tw10RDUfj176EXcVoVOvITMENJ3HREQtDPlOfz8i1SXCQEwclYyxI\n2KcTdKqjgYwwgYkwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCA/gwLAYJYIZIAYb4\nQgENBB8WHUdNQ2VydC5vcmcgU2lnbmVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRj\nIhoxmSgP84XT/scjkQNSWylMFTAfBgNVHSMEGDAWgBR/Wl47AIRZKg+YvqEObzmV\nQxBNBzAKBggqgRzPVQGDdQNJADBGAiEAnXykM0qDOWay2EMB6+c6YJ7h4n7Wbju7\nXuT5RkuM/3ICIQDAA3sLba/dQMhmKkCoJl31iZwYKz7NP+0aq6NhWDommQ==\n-----END CERTIFICATE-----"; + private const string CERT_SM2_SN = "e5a81b02429d8d08"; + private const string CERT_SM2_START_DATE = "2022-11-09 21:12:20"; + private const string CERT_SM2_END_DATE = "2023-11-09 21:12:20"; + [Fact(DisplayName = "测试用例:`CertificateEntry` 序列化")] public void TestCertificateEntrySerialization() { - var entry = new Settings.CertificateEntry("RSA", "FAKE SERIAL NUMBER", "-----BEGIN CERTIFICATE-----FAKE CERTIFICATE-----END CERTIFICATE-----", DateTimeOffset.Now, DateTimeOffset.Now); + var entry = new Settings.CertificateEntry(Settings.CertificateEntry.ALGORITHM_TYPE_RSA, CERT_RSA_SN, CERT_RSA_PEM, DateTimeOffset.Parse(CERT_RSA_START_DATE), DateTimeOffset.Parse(CERT_RSA_END_DATE)); var serialized1 = Newtonsoft.Json.JsonConvert.SerializeObject(entry); var deserialized1 = Newtonsoft.Json.JsonConvert.DeserializeObject(serialized1); @@ -28,5 +37,37 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests Assert.Equal(entry.EffectiveTime, deserialized2.EffectiveTime); Assert.Equal(entry.ExpireTime, deserialized2.ExpireTime); } + + [Fact(DisplayName = "测试用例:`CertificateEntry` 构造器")] + public void TestCertificateEntryConstructor() + { + var certRSA = new Settings.CertificateEntry( + new Models.QueryCertificatesResponse.Types.Certificate() + { + SerialNumber = CERT_RSA_SN, + EncryptCertificate = new Models.QueryCertificatesResponse.Types.Certificate.Types.EncryptCertificate() + { + CipherText = CERT_RSA_PEM + }, + EffectiveTime = DateTimeOffset.Parse(CERT_RSA_START_DATE), + ExpireTime = DateTimeOffset.Parse(CERT_RSA_END_DATE) + } + ); + Assert.Equal(Settings.CertificateEntry.ALGORITHM_TYPE_RSA, certRSA.AlgorithmType); + + var certSM2 = new Settings.CertificateEntry( + new Models.QueryCertificatesResponse.Types.Certificate() + { + SerialNumber = CERT_SM2_SN, + EncryptCertificate = new Models.QueryCertificatesResponse.Types.Certificate.Types.EncryptCertificate() + { + CipherText = CERT_SM2_PEM + }, + EffectiveTime = DateTimeOffset.Parse(CERT_SM2_START_DATE), + ExpireTime = DateTimeOffset.Parse(CERT_SM2_END_DATE) + } + ); + Assert.Equal(Settings.CertificateEntry.ALGORITHM_TYPE_SM2, certSM2.AlgorithmType); + } } }