using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 { using SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants; using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings; using SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities; public static class WechatTenpayClientRequestEncryptionExtensions { /// /// 加密请求中传入的敏感数据。该方法会改变传入的请求模型对象。 /// /// /// /// public static TRequest EncryptRequestSensitiveProperty(this WechatTenpayClient client, TRequest request) where TRequest : WechatTenpayRequest { if (client is null) throw new ArgumentNullException(nameof(client)); if (request is null) throw new ArgumentNullException(nameof(request)); try { bool requireEncrypt = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute)); if (!requireEncrypt) return request; string signScheme = client.Credentials.SignScheme; string algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密 SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_RSA : SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_SM2 : throw new WechatTenpayException($"Failed to encrypt request. Unsupported signing scheme: \"{signScheme}\"."); ReflectionHelper.ReplaceObjectStringProperties(request, (_, currentProp, oldValue) => { if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute))) return (false, oldValue); WechatTenpaySensitivePropertyAttribute? attribute = currentProp .GetCustomAttributes() .FirstOrDefault(attr => attr.Scheme == signScheme); if (attribute is null) return (false, oldValue); string certificate; if (string.IsNullOrEmpty(request.WechatpayCertificateSerialNumber)) { // 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的 IEnumerable entries = client.PlatformCertificateManager.AllEntries() .Where(e => e.AlgorithmType == algorithmType) .OrderByDescending(e => e.ExpireTime); if (!entries.Any()) { throw new WechatTenpayException("Failed to encrypt request, because the platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first."); } CertificateEntry entry = entries.First(); certificate = entry.Certificate; request.WechatpayCertificateSerialNumber = entry.SerialNumber; } else { // 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值 CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(request.WechatpayCertificateSerialNumber!); if (!entry.HasValue) { throw new WechatTenpayException($"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \"{request.WechatpayCertificateSerialNumber}\". Please make sure you have downloaded platform (NOT merchant) certificates first."); } certificate = entry.Value.Certificate; } string newValue = GenerateEncryptedValueByCertificate(attribute.Algorithm, certificate, oldValue); return (true, newValue); }); return request; } catch (WechatTenpayException) { throw; } catch (Exception ex) { throw new WechatTenpayException("Failed to encrypt request. Please see the inner exception for more details.", ex); } } /// /// 异步加密请求中传入的敏感数据。该方法会改变传入的请求模型对象。 /// /// /// /// /// public static Task EncryptRequestSensitivePropertyAsync(this WechatTenpayClient client, TRequest request, CancellationToken cancellationToken = default) where TRequest : WechatTenpayRequest { if (client is null) throw new ArgumentNullException(nameof(client)); if (request is null) throw new ArgumentNullException(nameof(request)); if (client.PlatformCertificateManager is not ICertificateManagerAsync) { // 降级为同步调用 return Task.FromResult(EncryptRequestSensitiveProperty(client, request)); } try { bool requireEncrypt = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute)); if (!requireEncrypt) return Task.FromResult(request); string signScheme = client.Credentials.SignScheme; string algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密 SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_RSA : SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_SM2 : throw new WechatTenpayException($"Failed to encrypt request. Unsupported signing scheme: \"{signScheme}\"."); ReflectionHelper.ReplaceObjectStringProperties(request, (_, currentProp, oldValue) => { if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute))) return (false, oldValue); WechatTenpaySensitivePropertyAttribute? attribute = currentProp .GetCustomAttributes() .FirstOrDefault(attr => attr.Scheme == signScheme); if (attribute is null) return (false, oldValue); string certificate; if (string.IsNullOrEmpty(request.WechatpayCertificateSerialNumber)) { if (client.PlatformCertificateManager is null) throw new WechatTenpayException("Failed to encrypt request, because the platform certificate manager is not initialized."); // 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的 IEnumerable entries = ((ICertificateManagerAsync)client.PlatformCertificateManager) .AllEntriesAsync(cancellationToken) .ConfigureAwait(false) .GetAwaiter() .GetResult() .Where(e => e.AlgorithmType == algorithmType) .OrderByDescending(e => e.ExpireTime); if (!entries.Any()) { throw new WechatTenpayException("Failed to encrypt request, because the platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first."); } CertificateEntry entry = entries.First(); certificate = entry.Certificate; request.WechatpayCertificateSerialNumber = entry.SerialNumber; } else { // 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值 CertificateEntry? entry = ((ICertificateManagerAsync)client.PlatformCertificateManager) .GetEntryAsync(request.WechatpayCertificateSerialNumber!, cancellationToken) .ConfigureAwait(false) .GetAwaiter() .GetResult(); if (!entry.HasValue) { throw new WechatTenpayException($"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \"{request.WechatpayCertificateSerialNumber}\". Please make sure you have downloaded platform (NOT merchant) certificates first."); } certificate = entry.Value.Certificate; } string newValue = GenerateEncryptedValueByCertificate(attribute.Algorithm, certificate, oldValue); return (true, newValue); }); return Task.FromResult(request); } catch (WechatTenpayException) { throw; } catch (Exception ex) { throw new WechatTenpayException("Failed to encrypt request. Please see the inner exception for more details.", ex); } } private static string GenerateEncryptedValueByCertificate(string algorithm, string certificate, string value) { switch (algorithm) { case EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1: return RSAUtility.EncryptWithECBByCertificate( certificatePem: certificate, plainData: value, paddingMode: RSAUtility.PADDING_MODE_OAEPWITHSHA1ANDMGF1 )!; case EncryptionAlgorithms.RSA_2048_ECB_PKCS1: return RSAUtility.EncryptWithECBByCertificate( certificatePem: certificate, plainData: value, paddingMode: RSAUtility.PADDING_MODE_PKCS1 )!; case EncryptionAlgorithms.SM2_C1C3C2_ASN1: return SM2Utility.EncryptByCertificate( certificatePem: certificate, plainData: value, asn1Encoding: true )!; default: throw new WechatTenpayException($"Failed to encrypt request. Unsupported encryption algorithm: \"{algorithm}\"."); } } } }