mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2026-02-11 18:26:20 +08:00
refactor(tenpayv3): 优化加解密及哈希算法工具类
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientEventDecryptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -14,7 +15,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
public static WechatTenpayEvent DeserializeEvent(this WechatTenpayClient client, string webhookJson)
|
||||
{
|
||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||
if (string.IsNullOrEmpty(webhookJson)) throw new ArgumentNullException(webhookJson);
|
||||
if (webhookJson is null) throw new ArgumentNullException(webhookJson);
|
||||
|
||||
return client.JsonSerializer.Deserialize<WechatTenpayEvent>(webhookJson);
|
||||
}
|
||||
@@ -56,11 +57,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
try
|
||||
{
|
||||
plainJson = Utilities.AESUtility.DecryptWithGCM(
|
||||
key: client.Credentials.MerchantV3Secret,
|
||||
nonce: webhookEventResource.Nonce,
|
||||
aad: webhookEventResource.AssociatedData,
|
||||
cipherText: webhookEventResource.CipherText
|
||||
);
|
||||
encodingKey: new EncodedString(client.Credentials.MerchantV3Secret, EncodingKinds.Literal),
|
||||
encodingNonce: new EncodedString(webhookEventResource.Nonce, EncodingKinds.Literal),
|
||||
encodingAssociatedData: new EncodedString(webhookEventResource.AssociatedData, EncodingKinds.Literal),
|
||||
encodingCipher: new EncodedString(webhookEventResource.CipherText, EncodingKinds.Base64)
|
||||
)!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -75,17 +76,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
// REF: https://pay.weixin.qq.com/docs/merchant/development/shangmi/guide.html
|
||||
// 由于 SM4 密钥长度的限制,密钥由 APIv3 密钥通过国密 SM3 Hash 计算生成。SM4 密钥取其摘要(256bit)的前 128bit。
|
||||
byte[] secretBytes = Utilities.SM3Utility.Hash(Encoding.UTF8.GetBytes(client.Credentials.MerchantV3Secret));
|
||||
byte[] secretBytes = Utilities.SM3Utility.Hash(EncodedString.FromLiteralString(client.Credentials.MerchantV3Secret));
|
||||
byte[] keyBytes = new byte[16];
|
||||
Array.Copy(secretBytes, keyBytes, keyBytes.Length);
|
||||
|
||||
byte[] plainBytes = Utilities.SM4Utility.DecryptWithGCM(
|
||||
keyBytes: keyBytes,
|
||||
nonceBytes: Encoding.UTF8.GetBytes(webhookEventResource.Nonce),
|
||||
aadBytes: webhookEventResource.AssociatedData is null ? null : Encoding.UTF8.GetBytes(webhookEventResource.AssociatedData),
|
||||
cipherBytes: Convert.FromBase64String(webhookEventResource.CipherText)
|
||||
nonceBytes: EncodedString.FromLiteralString(webhookEventResource.Nonce),
|
||||
associatedDataBytes: EncodedString.FromLiteralString(webhookEventResource.AssociatedData),
|
||||
cipherBytes: EncodedString.FromBase64String(webhookEventResource.CipherText)
|
||||
);
|
||||
plainJson = Encoding.UTF8.GetString(plainBytes);
|
||||
plainJson = EncodedString.ToLiteralString(plainBytes).Value!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@ using Flurl.Http;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientExecuteEcommerceExtensions
|
||||
{
|
||||
#region Account
|
||||
@@ -64,7 +66,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
|
||||
|
||||
@@ -6,6 +6,8 @@ using Flurl.Http;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientExecuteMarketingBankExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -26,7 +28,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".txt";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes ?? Array.Empty<byte>())).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes ?? Array.Empty<byte>())).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "text/plain";
|
||||
|
||||
@@ -6,6 +6,8 @@ using Flurl.Http;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientExecuteMarketingMediaExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -30,7 +32,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
|
||||
|
||||
@@ -6,6 +6,8 @@ using Flurl.Http;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientExecuteMarketingShoppingReceiptExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -25,7 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
|
||||
|
||||
@@ -6,6 +6,8 @@ using Flurl.Http;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientExecuteMerchantMediaExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -27,7 +29,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
|
||||
@@ -56,7 +58,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".mp4";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
|
||||
|
||||
@@ -7,6 +7,8 @@ using Flurl.Http;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
public static class WechatTenpayClientExecuteMerchantServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -250,7 +252,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
|
||||
|
||||
if (request.FileHash is null)
|
||||
request.FileHash = BitConverter.ToString(Utilities.SHA256Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
|
||||
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
|
||||
|
||||
if (request.FileContentType is null)
|
||||
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
|
||||
|
||||
@@ -33,10 +33,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
string timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString();
|
||||
string nonce = Guid.NewGuid().ToString("N");
|
||||
string package = $"prepay_id={prepayId}";
|
||||
string sign = Utilities.RSAUtility.SignWithSHA256(
|
||||
privateKey: client.Credentials.MerchantCertificatePrivateKey,
|
||||
string sign = Utilities.RSAUtility.Sign(
|
||||
privateKeyPem: client.Credentials.MerchantCertificatePrivateKey,
|
||||
message: $"{appId}\n{timestamp}\n{nonce}\n{package}\n"
|
||||
);
|
||||
)!;
|
||||
|
||||
return new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()
|
||||
{
|
||||
@@ -85,10 +85,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
|
||||
string timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString();
|
||||
string nonce = Guid.NewGuid().ToString("N");
|
||||
string sign = Utilities.RSAUtility.SignWithSHA256(
|
||||
privateKey: client.Credentials.MerchantCertificatePrivateKey,
|
||||
string sign = Utilities.RSAUtility.Sign(
|
||||
privateKeyPem: client.Credentials.MerchantCertificatePrivateKey,
|
||||
message: $"{appId}\n{timestamp}\n{nonce}\n{prepayId}\n"
|
||||
);
|
||||
)!;
|
||||
|
||||
return new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()
|
||||
{
|
||||
|
||||
@@ -82,29 +82,30 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
case Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1:
|
||||
{
|
||||
newValue = RSAUtility.EncryptWithECBByCertificate(
|
||||
certificate: certificate,
|
||||
plainText: oldValue
|
||||
);
|
||||
certificatePem: certificate,
|
||||
plainData: oldValue,
|
||||
paddingMode: RSAUtility.PADDING_MODE_OAEPWITHSHA1ANDMGF1
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
case Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS1:
|
||||
{
|
||||
newValue = RSAUtility.EncryptWithECBByCertificate(
|
||||
certificate: certificate,
|
||||
plainText: oldValue,
|
||||
paddingMode: "PKCS1PADDING"
|
||||
);
|
||||
certificatePem: certificate,
|
||||
plainData: oldValue,
|
||||
paddingMode: RSAUtility.PADDING_MODE_PKCS1
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
case Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1:
|
||||
{
|
||||
newValue = SM2Utility.EncryptByCertificate(
|
||||
certificate: certificate,
|
||||
plainText: oldValue,
|
||||
certificatePem: certificate,
|
||||
plainData: oldValue,
|
||||
asn1Encoding: true
|
||||
);
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
|
||||
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities;
|
||||
|
||||
@@ -37,11 +37,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
throw new WechatTenpayException("Failed to decrypt response, because the merchant private key is not set.");
|
||||
|
||||
certificate.EncryptCertificate.CipherText = AESUtility.DecryptWithGCM(
|
||||
key: client.Credentials.MerchantV3Secret,
|
||||
nonce: certificate.EncryptCertificate.Nonce,
|
||||
aad: certificate.EncryptCertificate.AssociatedData,
|
||||
cipherText: certificate.EncryptCertificate.CipherText
|
||||
);
|
||||
encodingKey: new EncodedString(client.Credentials.MerchantV3Secret, EncodingKinds.Literal),
|
||||
encodingNonce: new EncodedString(certificate.EncryptCertificate.Nonce, EncodingKinds.Literal),
|
||||
encodingAssociatedData: new EncodedString(certificate.EncryptCertificate.AssociatedData, EncodingKinds.Literal),
|
||||
encodingCipher: new EncodedString(certificate.EncryptCertificate.CipherText, EncodingKinds.Base64)
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -52,17 +52,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
|
||||
// REF: https://pay.weixin.qq.com/docs/merchant/development/shangmi/guide.html
|
||||
// 由于 SM4 密钥长度的限制,密钥由 APIv3 密钥通过国密 SM3 Hash 计算生成。SM4 密钥取其摘要(256bit)的前 128bit。
|
||||
byte[] secretBytes = SM3Utility.Hash(Encoding.UTF8.GetBytes(client.Credentials.MerchantV3Secret));
|
||||
byte[] secretBytes = SM3Utility.Hash(EncodedString.FromLiteralString(client.Credentials.MerchantV3Secret));
|
||||
byte[] keyBytes = new byte[16];
|
||||
Array.Copy(secretBytes, keyBytes, keyBytes.Length);
|
||||
|
||||
byte[] plainBytes = SM4Utility.DecryptWithGCM(
|
||||
keyBytes: keyBytes,
|
||||
nonceBytes: Encoding.UTF8.GetBytes(certificate.EncryptCertificate.Nonce),
|
||||
aadBytes: Encoding.UTF8.GetBytes(certificate.EncryptCertificate.AssociatedData ?? string.Empty),
|
||||
cipherBytes: Convert.FromBase64String(certificate.EncryptCertificate.CipherText)
|
||||
nonceBytes: EncodedString.FromLiteralString(certificate.EncryptCertificate.Nonce),
|
||||
associatedDataBytes: EncodedString.FromLiteralString(certificate.EncryptCertificate.AssociatedData),
|
||||
cipherBytes: EncodedString.FromBase64String(certificate.EncryptCertificate.CipherText)
|
||||
);
|
||||
certificate.EncryptCertificate.CipherText = Encoding.UTF8.GetString(plainBytes);
|
||||
certificate.EncryptCertificate.CipherText = EncodedString.ToLiteralString(plainBytes).Value!;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -123,29 +123,30 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
case Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1:
|
||||
{
|
||||
newValue = RSAUtility.DecryptWithECB(
|
||||
privateKey: client.Credentials.MerchantCertificatePrivateKey,
|
||||
cipherText: oldValue
|
||||
);
|
||||
privateKeyPem: client.Credentials.MerchantCertificatePrivateKey,
|
||||
encodingCipher: new EncodedString(oldValue, EncodingKinds.Base64),
|
||||
paddingMode: RSAUtility.PADDING_MODE_OAEPWITHSHA1ANDMGF1
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
case Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS1:
|
||||
{
|
||||
newValue = RSAUtility.DecryptWithECB(
|
||||
privateKey: client.Credentials.MerchantCertificatePrivateKey,
|
||||
cipherText: oldValue,
|
||||
paddingMode: "PKCS1PADDING"
|
||||
);
|
||||
privateKeyPem: client.Credentials.MerchantCertificatePrivateKey,
|
||||
encodingCipher: new EncodedString(oldValue, EncodingKinds.Base64),
|
||||
paddingMode: RSAUtility.PADDING_MODE_PKCS1
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
case Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1:
|
||||
{
|
||||
newValue = SM2Utility.Decrypt(
|
||||
privateKey: client.Credentials.MerchantCertificatePrivateKey,
|
||||
cipherText: oldValue,
|
||||
privateKeyPem: client.Credentials.MerchantCertificatePrivateKey,
|
||||
encodingCipher: new EncodedString(oldValue, EncodingKinds.Base64),
|
||||
asn1Encoding: true
|
||||
);
|
||||
)!;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
|
||||
|
||||
internal static class WechatTenpayClientSigningExtensions
|
||||
@@ -36,10 +37,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
return Utilities.RSAUtility.VerifyWithSHA256ByCertificate(
|
||||
certificate: entry.Value.Certificate,
|
||||
return Utilities.RSAUtility.VerifyByCertificate(
|
||||
certificatePem: entry.Value.Certificate,
|
||||
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
||||
signature: strSignature
|
||||
encodingSignature: new EncodedString(strSignature, EncodingKinds.Base64)
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -74,9 +75,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
try
|
||||
{
|
||||
return Utilities.SM2Utility.VerifyWithSM3ByCertificate(
|
||||
certificate: entry.Value.Certificate,
|
||||
certificatePem: entry.Value.Certificate,
|
||||
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
||||
signature: strSignature
|
||||
encodingSignature: new EncodedString(strSignature, EncodingKinds.Base64)
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Interceptors
|
||||
{
|
||||
try
|
||||
{
|
||||
signText = Utilities.RSAUtility.SignWithSHA256(_mchCertPk, msgText);
|
||||
signText = Utilities.RSAUtility.Sign(_mchCertPk, msgText).Value!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -69,7 +69,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Interceptors
|
||||
{
|
||||
try
|
||||
{
|
||||
signText = Utilities.SM2Utility.SignWithSM3(_mchCertPk, msgText);
|
||||
signText = Utilities.SM2Utility.SignWithSM3(_mchCertPk, msgText).Value!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -75,16 +75,16 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings
|
||||
case ALGORITHM_TYPE_RSA:
|
||||
{
|
||||
SerialNumber = Utilities.RSAUtility.ExportSerialNumberFromCertificate(certificate).ToUpper();
|
||||
EffectiveTime = Utilities.RSAUtility.ExportEffectiveTimeFromCertificate(certificate);
|
||||
ExpireTime = Utilities.RSAUtility.ExportExpireTimeFromCertificate(certificate);
|
||||
EffectiveTime = Utilities.RSAUtility.ExportValidFromDateFromCertificate(certificate);
|
||||
ExpireTime = Utilities.RSAUtility.ExportValidToDateFromCertificate(certificate);
|
||||
}
|
||||
break;
|
||||
|
||||
case ALGORITHM_TYPE_SM2:
|
||||
{
|
||||
SerialNumber = Utilities.SM2Utility.ExportSerialNumberFromCertificate(certificate).ToUpper();
|
||||
EffectiveTime = Utilities.SM2Utility.ExportEffectiveTimeFromCertificate(certificate);
|
||||
ExpireTime = Utilities.SM2Utility.ExportExpireTimeFromCertificate(certificate);
|
||||
EffectiveTime = Utilities.SM2Utility.ExportValidFromDateFromCertificate(certificate);
|
||||
ExpireTime = Utilities.SM2Utility.ExportValidToDateFromCertificate(certificate);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// AES 算法工具类。
|
||||
/// </summary>
|
||||
public static class AESUtility
|
||||
{
|
||||
private const string AES_CIPHER_ALGORITHM_GCM = "AES/GCM";
|
||||
private const string AES_CIPHER_PADDING_NOPADDING = "NoPadding";
|
||||
/// <summary>
|
||||
/// 填充模式:NoPadding。
|
||||
/// </summary>
|
||||
public const string PADDING_MODE_NOPADDING = "NoPadding";
|
||||
|
||||
/// <summary>
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="keyBytes">AES 密钥字节数组。</param>
|
||||
/// <param name="nonceBytes">加密使用的初始化向量字节数组。</param>
|
||||
/// <param name="aadBytes">加密使用的附加数据包字节数组。</param>
|
||||
/// <param name="nonceBytes">初始化向量字节数组。</param>
|
||||
/// <param name="associatedDataBytes">附加数据字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密数据字节数组。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="AES_CIPHER_PADDING_NOPADDING"/>)</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] nonceBytes, byte[]? aadBytes, byte[] cipherBytes, string paddingMode = AES_CIPHER_PADDING_NOPADDING)
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] nonceBytes, byte[]? associatedDataBytes, byte[] cipherBytes, string paddingMode = PADDING_MODE_NOPADDING)
|
||||
{
|
||||
const int KEY_LENGTH_BYTE = 32;
|
||||
const int NONCE_LENGTH_BYTE = 12;
|
||||
@@ -36,12 +39,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
|
||||
if (cipherBytes.Length < TAG_LENGTH_BYTE) throw new ArgumentException($"Invalid cipher byte length (expected: more than {TAG_LENGTH_BYTE}, actual: {cipherBytes.Length}).", nameof(cipherBytes));
|
||||
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher(string.Format("{0}/{1}", AES_CIPHER_ALGORITHM_GCM, paddingMode));
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher($"AES/GCM/{paddingMode}");
|
||||
ICipherParameters cipherParams = new AeadParameters(
|
||||
new KeyParameter(keyBytes),
|
||||
TAG_LENGTH_BYTE * 8,
|
||||
nonceBytes,
|
||||
aadBytes
|
||||
associatedDataBytes
|
||||
);
|
||||
cipher.Init(false, cipherParams);
|
||||
byte[] plainBytes = new byte[cipher.GetOutputSize(cipherBytes.Length)];
|
||||
@@ -53,26 +56,26 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="key">AES 密钥。</param>
|
||||
/// <param name="nonce">加密使用的初始化向量。</param>
|
||||
/// <param name="aad">加密使用的附加数据包。</param>
|
||||
/// <param name="cipherText">经 Base64 编码后的待解密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="AES_CIPHER_PADDING_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的文本数据。</returns>
|
||||
public static string DecryptWithGCM(string key, string nonce, string? aad, string cipherText, string paddingMode = AES_CIPHER_PADDING_NOPADDING)
|
||||
/// <param name="encodingKey">经过编码后的(通常为 Base64)AES 密钥。</param>
|
||||
/// <param name="encodingNonce">经过编码后的(通常为 Base64)初始化向量。</param>
|
||||
/// <param name="encodingAssociatedData">经过编码后的(通常为 Base64)附加数据。</param>
|
||||
/// <param name="encodingCipher">经过编码后的(通常为 Base64)待解密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的数据。</returns>
|
||||
public static EncodedString DecryptWithGCM(EncodedString encodingKey, EncodedString encodingNonce, EncodedString encodingAssociatedData, EncodedString encodingCipher, string paddingMode = PADDING_MODE_NOPADDING)
|
||||
{
|
||||
if (key is null) throw new ArgumentNullException(nameof(key));
|
||||
if (nonce is null) throw new ArgumentNullException(nameof(nonce));
|
||||
if (cipherText is null) throw new ArgumentNullException(nameof(cipherText));
|
||||
if (encodingKey.Value is null) throw new ArgumentNullException(nameof(encodingKey));
|
||||
if (encodingNonce.Value is null) throw new ArgumentNullException(nameof(encodingNonce));
|
||||
if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher));
|
||||
|
||||
byte[] plainBytes = DecryptWithGCM(
|
||||
keyBytes: Encoding.UTF8.GetBytes(key),
|
||||
nonceBytes: Encoding.UTF8.GetBytes(nonce),
|
||||
aadBytes: aad is not null ? Encoding.UTF8.GetBytes(aad) : null,
|
||||
cipherBytes: Convert.FromBase64String(cipherText),
|
||||
keyBytes: EncodedString.FromString(encodingKey, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
nonceBytes: EncodedString.FromString(encodingNonce, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
associatedDataBytes: encodingAssociatedData.Value is not null ? EncodedString.FromString(encodingAssociatedData, fallbackEncodingKind: EncodingKinds.Base64) : null,
|
||||
cipherBytes: EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
paddingMode: paddingMode
|
||||
);
|
||||
return Encoding.UTF8.GetString(plainBytes);
|
||||
return EncodedString.ToLiteralString(plainBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,143 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.X509;
|
||||
using Org.BouncyCastle.Asn1.Pkcs;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// RSA 算法工具类。
|
||||
/// </summary>
|
||||
public static class RSAUtility
|
||||
{
|
||||
private const string RSA_CIPHER_ALGORITHM_ECB = "RSA/ECB";
|
||||
private const string RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1 = "OAEPWITHSHA1ANDMGF1PADDING";
|
||||
private const string RSA_SIGNER_ALGORITHM_SHA256 = "SHA-256withRSA";
|
||||
/// <summary>
|
||||
/// 填充模式:OAEPwithSHA-256andMGF1Padding。
|
||||
/// </summary>
|
||||
public const string PADDING_MODE_OAEPWITHSHA1ANDMGF1 = "OAEPWITHSHA1ANDMGF1PADDING";
|
||||
|
||||
private static byte[] ConvertPrivateKeyPkcs8PemToByteArray(string privateKey)
|
||||
/// <summary>
|
||||
/// 填充模式:PKCS1Padding。
|
||||
/// </summary>
|
||||
public const string PADDING_MODE_PKCS1 = "PKCS1PADDING";
|
||||
|
||||
/// <summary>
|
||||
/// 签名算法:SHA-256withRSA。
|
||||
/// </summary>
|
||||
public const string DIGEST_ALGORITHM_SHA256 = "SHA-256withRSA";
|
||||
|
||||
private static byte[] ConvertPrivateKeyPemToByteArray(string privateKeyPem)
|
||||
{
|
||||
privateKey = privateKey
|
||||
if (!privateKeyPem.StartsWith("-----BEGIN PRIVATE KEY-----"))
|
||||
{
|
||||
using (TextReader textReader = new StringReader(privateKeyPem))
|
||||
using (PemReader pemReader = new PemReader(textReader))
|
||||
{
|
||||
object pemObject = pemReader.ReadObject();
|
||||
|
||||
if (pemObject is AsymmetricCipherKeyPair)
|
||||
{
|
||||
// PKCS#1 格式
|
||||
AsymmetricCipherKeyPair cipherKeyPair = (AsymmetricCipherKeyPair)pemObject;
|
||||
using (TextWriter textWriter = new StringWriter())
|
||||
using (PemWriter pemWriter = new PemWriter(textWriter))
|
||||
{
|
||||
Pkcs8Generator pkcs8 = new Pkcs8Generator(cipherKeyPair.Private);
|
||||
pemWriter.WriteObject(pkcs8);
|
||||
pemWriter.Writer.Close();
|
||||
|
||||
privateKeyPem = textWriter.ToString()!;
|
||||
}
|
||||
}
|
||||
else if (pemObject is RsaPrivateCrtKeyParameters)
|
||||
{
|
||||
// PKCS#8 格式
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Private key format is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
privateKeyPem = privateKeyPem
|
||||
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
|
||||
.Replace("-----END PRIVATE KEY-----", string.Empty);
|
||||
privateKey = Regex.Replace(privateKey, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(privateKey);
|
||||
privateKeyPem = Regex.Replace(privateKeyPem, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(privateKeyPem);
|
||||
}
|
||||
|
||||
private static byte[] ConvertPublicKeyPkcs8PemToByteArray(string publicKey)
|
||||
private static byte[] ConvertPublicKeyPemToByteArray(string publicKeyPem)
|
||||
{
|
||||
publicKey = publicKey
|
||||
if (!publicKeyPem.StartsWith("-----BEGIN PUBLIC KEY-----"))
|
||||
{
|
||||
using (TextReader textReader = new StringReader(publicKeyPem))
|
||||
using (PemReader pemReader = new PemReader(textReader))
|
||||
{
|
||||
object pemObject = pemReader.ReadObject();
|
||||
if (pemObject is RsaKeyParameters)
|
||||
{
|
||||
// PKCS#1 或 PKCS#8 格式
|
||||
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)pemObject;
|
||||
using (TextWriter textWriter = new StringWriter())
|
||||
using (PemWriter pemWriter = new PemWriter(textWriter))
|
||||
{
|
||||
pemWriter.WriteObject(rsaKeyParams);
|
||||
pemWriter.Writer.Close();
|
||||
|
||||
publicKeyPem = textWriter.ToString()!;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Public key format is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publicKeyPem = publicKeyPem
|
||||
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
|
||||
.Replace("-----END PUBLIC KEY-----", string.Empty);
|
||||
publicKey = Regex.Replace(publicKey, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(publicKey);
|
||||
publicKeyPem = Regex.Replace(publicKeyPem, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(publicKeyPem);
|
||||
}
|
||||
|
||||
private static X509Certificate ParseCertificatePemToX509(string certificate)
|
||||
private static X509Certificate ParseCertificatePemToX509(string certificatePem)
|
||||
{
|
||||
using (TextReader sreader = new StringReader(certificate))
|
||||
using (TextReader sreader = new StringReader(certificatePem))
|
||||
{
|
||||
PemReader pemReader = new PemReader(sreader);
|
||||
return (X509Certificate)pemReader.ReadObject();
|
||||
}
|
||||
}
|
||||
|
||||
private static RsaKeyParameters ParsePrivateKeyPemToPrivateKeyParameters(byte[] privateKeyBytes)
|
||||
private static RsaKeyParameters ParsePrivateKeyToParameters(byte[] privateKeyBytes)
|
||||
{
|
||||
return (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
|
||||
}
|
||||
|
||||
private static RsaKeyParameters ParsePublicKeyPemToPublicKeyParameters(byte[] publicKeyBytes)
|
||||
private static RsaKeyParameters ParsePublicKeyToParameters(byte[] publicKeyBytes)
|
||||
{
|
||||
return (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
|
||||
}
|
||||
|
||||
private static byte[] SignWithSHA256(RsaKeyParameters rsaPrivateKeyParams, byte[] msgBytes)
|
||||
private static byte[] Sign(RsaKeyParameters rsaPrivateKeyParams, byte[] msgBytes, string digestAlgorithm)
|
||||
{
|
||||
ISigner signer = SignerUtilities.GetSigner(RSA_SIGNER_ALGORITHM_SHA256);
|
||||
ISigner signer = SignerUtilities.GetSigner(digestAlgorithm);
|
||||
signer.Init(true, rsaPrivateKeyParams);
|
||||
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
|
||||
return signer.GenerateSignature();
|
||||
}
|
||||
|
||||
private static bool VerifyWithSHA256(RsaKeyParameters rsaPublicKeyParams, byte[] msgBytes, byte[] signBytes)
|
||||
private static bool Verify(RsaKeyParameters rsaPublicKeyParams, byte[] msgBytes, byte[] signBytes, string digestAlgorithm)
|
||||
{
|
||||
ISigner signer = SignerUtilities.GetSigner(RSA_SIGNER_ALGORITHM_SHA256);
|
||||
ISigner signer = SignerUtilities.GetSigner(digestAlgorithm);
|
||||
signer.Init(false, rsaPublicKeyParams);
|
||||
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
|
||||
return signer.VerifySignature(signBytes);
|
||||
@@ -74,182 +145,187 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
|
||||
private static byte[] DecryptWithECB(RsaKeyParameters rsaPrivateKeyParams, byte[] cipherBytes, string paddingMode)
|
||||
{
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher($"{RSA_CIPHER_ALGORITHM_ECB}/{paddingMode}");
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher($"RSA/ECB/{paddingMode}");
|
||||
cipher.Init(false, rsaPrivateKeyParams);
|
||||
return cipher.DoFinal(cipherBytes);
|
||||
}
|
||||
|
||||
private static byte[] EncryptWithECB(RsaKeyParameters rsaPublicKeyParams, byte[] plainBytes, string paddingMode)
|
||||
{
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher($"{RSA_CIPHER_ALGORITHM_ECB}/{paddingMode}");
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher($"RSA/ECB/{paddingMode}");
|
||||
cipher.Init(true, rsaPublicKeyParams);
|
||||
return cipher.DoFinal(plainBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SHA-256 算法生成签名。
|
||||
/// 使用私钥生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="privateKeyBytes">PKCS#1/PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static byte[] SignWithSHA256(byte[] privateKeyBytes, byte[] msgBytes)
|
||||
public static byte[] Sign(byte[] privateKeyBytes, byte[] msgBytes, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
|
||||
{
|
||||
if (privateKeyBytes is null) throw new ArgumentNullException(nameof(privateKeyBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyPemToPrivateKeyParameters(privateKeyBytes);
|
||||
return SignWithSHA256(rsaPrivateKeyParams, msgBytes);
|
||||
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyToParameters(privateKeyBytes);
|
||||
return Sign(rsaPrivateKeyParams, msgBytes, digestAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SHA-256 算法生成签名。
|
||||
/// 使用私钥生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="message">待签名的文本数据。</param>
|
||||
/// <returns>经 Base64 编码的签名。</returns>
|
||||
public static string SignWithSHA256(string privateKey, string message)
|
||||
/// <param name="privateKeyPem">PKCS#1/PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="message">待签名的数据。</param>
|
||||
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
|
||||
/// <returns>经过 Base64 编码的签名。</returns>
|
||||
public static EncodedString Sign(string privateKeyPem, string message, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
|
||||
{
|
||||
if (privateKey is null) throw new ArgumentNullException(nameof(privateKey));
|
||||
if (privateKeyPem is null) throw new ArgumentNullException(nameof(privateKeyPem));
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] signBytes = SignWithSHA256(privateKeyBytes, msgBytes);
|
||||
return Convert.ToBase64String(signBytes);
|
||||
byte[] privateKeyBytes = ConvertPrivateKeyPemToByteArray(privateKeyPem);
|
||||
byte[] msgBytes = EncodedString.FromLiteralString(message);
|
||||
byte[] signBytes = Sign(privateKeyBytes, msgBytes, digestAlgorithm);
|
||||
return EncodedString.ToBase64String(signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SHA-256 算法验证签名。
|
||||
/// 使用公钥验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="publicKeyBytes">PKCS#1/PKCS#8 公钥字节数组。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSHA256(byte[] publicKeyBytes, byte[] msgBytes, byte[] signBytes)
|
||||
public static bool Verify(byte[] publicKeyBytes, byte[] msgBytes, byte[] signBytes, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
|
||||
{
|
||||
if (publicKeyBytes is null) throw new ArgumentNullException(nameof(publicKeyBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
if (signBytes is null) throw new ArgumentNullException(nameof(signBytes));
|
||||
|
||||
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
|
||||
return VerifyWithSHA256(rsaPublicKeyParams, msgBytes, signBytes);
|
||||
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyToParameters(publicKeyBytes);
|
||||
return Verify(rsaPublicKeyParams, msgBytes, signBytes, digestAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SHA-256 算法验证签名。
|
||||
/// 使用公钥验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <param name="publicKeyPem">PKCS#1/PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的数据。</param>
|
||||
/// <param name="encodingSignature">经过编码后的(通常为 Base64)签名。</param>
|
||||
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSHA256(string publicKey, string message, string signature)
|
||||
public static bool Verify(string publicKeyPem, string message, EncodedString encodingSignature, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
|
||||
{
|
||||
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
|
||||
if (publicKeyPem is null) throw new ArgumentNullException(nameof(publicKeyPem));
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
if (signature is null) throw new ArgumentNullException(nameof(signature));
|
||||
if (encodingSignature.Value is null) throw new ArgumentNullException(nameof(encodingSignature));
|
||||
|
||||
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] signBytes = Convert.FromBase64String(signature);
|
||||
return VerifyWithSHA256(publicKeyBytes, msgBytes, signBytes);
|
||||
byte[] publicKeyBytes = ConvertPublicKeyPemToByteArray(publicKeyPem);
|
||||
byte[] msgBytes = EncodedString.FromLiteralString(message);
|
||||
byte[] signBytes = EncodedString.FromString(encodingSignature, fallbackEncodingKind: EncodingKinds.Base64);
|
||||
return Verify(publicKeyBytes, msgBytes, signBytes, digestAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用证书基于 SHA-256 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的数据。</param>
|
||||
/// <param name="encodingSignature">经过编码后的(通常为 Base64)签名。</param>
|
||||
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSHA256ByCertificate(string certificate, string message, string signature)
|
||||
public static bool VerifyByCertificate(string certificatePem, string message, EncodedString encodingSignature, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
string publicKey = ExportPublicKeyFromCertificate(certificate);
|
||||
return VerifyWithSHA256(publicKey, message, signature);
|
||||
string publicKeyPem = ExportPublicKeyFromCertificate(certificatePem);
|
||||
return Verify(publicKeyPem, message, encodingSignature, digestAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 ECB 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数据。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1"/>)</param>
|
||||
/// <param name="privateKeyBytes">PKCS#1/PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数组。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_OAEPWITHSHA1ANDMGF1"/>)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] DecryptWithECB(byte[] privateKeyBytes, byte[] cipherBytes, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
|
||||
public static byte[] DecryptWithECB(byte[] privateKeyBytes, byte[] cipherBytes, string paddingMode = PADDING_MODE_OAEPWITHSHA1ANDMGF1)
|
||||
{
|
||||
if (privateKeyBytes is null) throw new ArgumentNullException(nameof(privateKeyBytes));
|
||||
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
|
||||
|
||||
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyPemToPrivateKeyParameters(privateKeyBytes);
|
||||
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyToParameters(privateKeyBytes);
|
||||
return DecryptWithECB(rsaPrivateKeyParams, cipherBytes, paddingMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 ECB 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="cipherText">经 Base64 编码的待解密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1"/>)</param>
|
||||
/// <returns>解密后的文本数据。</returns>
|
||||
public static string DecryptWithECB(string privateKey, string cipherText, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
|
||||
/// <param name="privateKeyPem">PKCS#1/PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="encodingCipher">经过编码后的(通常为 Base64)待解密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_OAEPWITHSHA1ANDMGF1"/>)</param>
|
||||
/// <returns>解密后的数据。</returns>
|
||||
public static EncodedString DecryptWithECB(string privateKeyPem, EncodedString encodingCipher, string paddingMode = PADDING_MODE_OAEPWITHSHA1ANDMGF1)
|
||||
{
|
||||
if (privateKey is null) throw new ArgumentNullException(nameof(privateKey));
|
||||
if (cipherText is null) throw new ArgumentNullException(nameof(cipherText));
|
||||
if (privateKeyPem is null) throw new ArgumentNullException(nameof(privateKeyPem));
|
||||
if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher));
|
||||
|
||||
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
|
||||
byte[] cipherBytes = Convert.FromBase64String(cipherText);
|
||||
byte[] privateKeyBytes = ConvertPrivateKeyPemToByteArray(privateKeyPem);
|
||||
byte[] cipherBytes = EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64);
|
||||
byte[] plainBytes = DecryptWithECB(privateKeyBytes, cipherBytes, paddingMode);
|
||||
return Encoding.UTF8.GetString(plainBytes);
|
||||
return EncodedString.ToLiteralString(plainBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 ECB 模式加密数据。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1"/>)</param>
|
||||
/// <param name="publicKeyBytes">PKCS#1/PKCS#8 公钥字节数组。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数组。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_OAEPWITHSHA1ANDMGF1"/>)</param>
|
||||
/// <returns>加密后的数据字节数组。</returns>
|
||||
public static byte[] EncryptWithECB(byte[] publicKeyBytes, byte[] plainBytes, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
|
||||
public static byte[] EncryptWithECB(byte[] publicKeyBytes, byte[] plainBytes, string paddingMode = PADDING_MODE_OAEPWITHSHA1ANDMGF1)
|
||||
{
|
||||
if (publicKeyBytes is null) throw new ArgumentNullException(nameof(publicKeyBytes));
|
||||
if (plainBytes is null) throw new ArgumentNullException(nameof(plainBytes));
|
||||
|
||||
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
|
||||
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyToParameters(publicKeyBytes);
|
||||
return EncryptWithECB(rsaPublicKeyParams, plainBytes, paddingMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 ECB 模式加密数据。
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="plainText">待加密的文本数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1"/>)</param>
|
||||
/// <returns>经 Base64 编码的加密数据。</returns>
|
||||
public static string EncryptWithECB(string publicKey, string plainText, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
|
||||
/// <param name="publicKeyPem">PKCS#1/PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="plainData">待加密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_OAEPWITHSHA1ANDMGF1"/>)</param>
|
||||
/// <returns>经过 Base64 编码的加密数据。</returns>
|
||||
public static EncodedString EncryptWithECB(string publicKeyPem, string plainData, string paddingMode = PADDING_MODE_OAEPWITHSHA1ANDMGF1)
|
||||
{
|
||||
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
|
||||
if (plainText is null) throw new ArgumentNullException(nameof(plainText));
|
||||
if (publicKeyPem is null) throw new ArgumentNullException(nameof(publicKeyPem));
|
||||
if (plainData is null) throw new ArgumentNullException(nameof(plainData));
|
||||
|
||||
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] publicKeyBytes = ConvertPublicKeyPemToByteArray(publicKeyPem);
|
||||
byte[] plainBytes = EncodedString.FromLiteralString(plainData);
|
||||
byte[] cipherBytes = EncryptWithECB(publicKeyBytes, plainBytes, paddingMode);
|
||||
return Convert.ToBase64String(cipherBytes);
|
||||
return EncodedString.ToBase64String(cipherBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用证书基于 ECB 模式加密数据。
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="plainText">待加密的文本数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1"/>)</param>
|
||||
/// <returns>经 Base64 编码的加密数据。</returns>
|
||||
public static string EncryptWithECBByCertificate(string certificate, string plainText, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
|
||||
/// <param name="certificatePem">证书(PEM 格式)。</param>
|
||||
/// <param name="plainData">待加密的数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_OAEPWITHSHA1ANDMGF1"/>)</param>
|
||||
/// <returns>经过 Base64 编码的加密数据。</returns>
|
||||
public static EncodedString EncryptWithECBByCertificate(string certificatePem, string plainData, string paddingMode = PADDING_MODE_OAEPWITHSHA1ANDMGF1)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
string publicKey = ExportPublicKeyFromCertificate(certificate);
|
||||
return EncryptWithECB(publicKey, plainText, paddingMode);
|
||||
string publicKeyPem = ExportPublicKeyFromCertificate(certificatePem);
|
||||
return EncryptWithECB(publicKeyPem, plainData, paddingMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -259,7 +335,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 转为 -----BEGIN PUBLIC KEY----- ..... -----END PUBLIC KEY-----
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="certificate">证书内容(PEM 格式)。</param>
|
||||
/// <returns>PKCS#8 公钥(PEM 格式)。</returns>
|
||||
public static string ExportPublicKeyFromCertificate(string certificate)
|
||||
{
|
||||
@@ -279,7 +355,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书序列号。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="certificate">证书内容(PEM 格式)。</param>
|
||||
/// <returns>证书序列号。</returns>
|
||||
public static string ExportSerialNumberFromCertificate(string certificate)
|
||||
{
|
||||
@@ -290,11 +366,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书颁发时间。</para>
|
||||
/// <para>从 CRT/CER 证书中导出证书有效期的开始时间。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书颁发时间。</returns>
|
||||
public static DateTimeOffset ExportEffectiveTimeFromCertificate(string certificate)
|
||||
/// <param name="certificate">证书内容(PEM 格式)。</param>
|
||||
/// <returns>证书有效期的开始时间。</returns>
|
||||
public static DateTimeOffset ExportValidFromDateFromCertificate(string certificate)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
|
||||
@@ -303,11 +379,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书过期时间。</para>
|
||||
/// <para>从 CRT/CER 证书中导出证书有效期的结束时间。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书过期时间。</returns>
|
||||
public static DateTimeOffset ExportExpireTimeFromCertificate(string certificate)
|
||||
/// <param name="certificate">证书内容(PEM 格式)。</param>
|
||||
/// <returns>证书有效期的结束时间。</returns>
|
||||
public static DateTimeOffset ExportValidToDateFromCertificate(string certificate)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 算法工具类。
|
||||
/// </summary>
|
||||
public static class SHA256Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取 SHA-256 信息摘要。
|
||||
/// 计算 SHA-256 哈希值。
|
||||
/// </summary>
|
||||
/// <param name="bytes">信息字节数组。</param>
|
||||
/// <returns>信息摘要字节数组。</returns>
|
||||
/// <param name="bytes">要计算哈希值的信息字节数组。</param>
|
||||
/// <returns>哈希值字节数组。</returns>
|
||||
public static byte[] Hash(byte[] bytes)
|
||||
{
|
||||
if (bytes is null) throw new ArgumentNullException(nameof(bytes));
|
||||
|
||||
using SHA256 sha = SHA256.Create();
|
||||
return sha.ComputeHash(bytes);
|
||||
#if NET5_0_OR_GREATER
|
||||
return SHA256.HashData(bytes);
|
||||
#else
|
||||
using SHA256 sha256 = SHA256.Create();
|
||||
return sha256.ComputeHash(bytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 SHA-256 信息摘要。
|
||||
/// 计算 SHA-256 哈希值。
|
||||
/// </summary>
|
||||
/// <param name="message">文本信息。</param>
|
||||
/// <returns>信息摘要。</returns>
|
||||
public static string Hash(string message)
|
||||
/// <param name="message">要计算哈希值的信息。</param>
|
||||
/// <returns>经过十六进制编码的哈希值。</returns>
|
||||
public static EncodedString Hash(string message)
|
||||
{
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] msgBytes = EncodedString.FromLiteralString(message);
|
||||
byte[] hashBytes = Hash(msgBytes);
|
||||
return BitConverter.ToString(hashBytes).Replace("-", string.Empty);
|
||||
return EncodedString.ToHexString(hashBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Asn1.GM;
|
||||
using Org.BouncyCastle.Asn1.X9;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Math;
|
||||
@@ -20,6 +18,8 @@ using Org.BouncyCastle.X509;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// SM2 算法工具类。
|
||||
/// <para>此实现遵循国家标准 GM/T 0009-2012 的有关规定。</para>
|
||||
@@ -33,59 +33,51 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
private const int SM2_C3_LENGTH = 32;
|
||||
private const int SM2_RS_LENGTH = 32;
|
||||
|
||||
private static byte[] ConvertPrivateKeyPkcs8PemToByteArray(string privateKey)
|
||||
private static byte[] ConvertPrivateKeyPemToByteArray(string privateKeyPem)
|
||||
{
|
||||
privateKey = privateKey
|
||||
privateKeyPem = privateKeyPem
|
||||
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
|
||||
.Replace("-----END PRIVATE KEY-----", string.Empty);
|
||||
privateKey = Regex.Replace(privateKey, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(privateKey);
|
||||
privateKeyPem = Regex.Replace(privateKeyPem, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(privateKeyPem);
|
||||
}
|
||||
|
||||
private static byte[] ConvertPublicKeyPkcs8PemToByteArray(string publicKey)
|
||||
private static byte[] ConvertPublicKeyPemToByteArray(string publicKeyPem)
|
||||
{
|
||||
publicKey = publicKey
|
||||
publicKeyPem = publicKeyPem
|
||||
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
|
||||
.Replace("-----END PUBLIC KEY-----", string.Empty);
|
||||
publicKey = Regex.Replace(publicKey, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(publicKey);
|
||||
publicKeyPem = Regex.Replace(publicKeyPem, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(publicKeyPem);
|
||||
}
|
||||
|
||||
private static X509Certificate ConvertCertificatePemToX509(string certificate)
|
||||
private static X509Certificate ConvertCertificatePemToX509(string certificatePem)
|
||||
{
|
||||
using (TextReader sreader = new StringReader(certificate))
|
||||
using (TextReader sreader = new StringReader(certificatePem))
|
||||
{
|
||||
PemReader pemReader = new PemReader(sreader);
|
||||
return (X509Certificate)pemReader.ReadObject();
|
||||
}
|
||||
}
|
||||
|
||||
private static ECPrivateKeyParameters ParsePrivateKeyPemToPrivateKeyParameters(string privateKey)
|
||||
{
|
||||
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
|
||||
return ParsePrivateKeyPemToPrivateKeyParameters(privateKeyBytes);
|
||||
}
|
||||
|
||||
private static ECPrivateKeyParameters ParsePrivateKeyPemToPrivateKeyParameters(byte[] privateKeyBytes)
|
||||
private static ECPrivateKeyParameters ParsePrivateKeyToParameters(byte[] privateKeyBytes)
|
||||
{
|
||||
return (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
|
||||
}
|
||||
|
||||
private static ECPrivateKeyParameters ParseECPrivateKeyToPrivateKeyParameters(string ecPrivateKeyHex)
|
||||
private static ECPrivateKeyParameters ParseECPrivateKeyToParameters(byte[] ecPrivateKeyBytes)
|
||||
{
|
||||
BigInteger ecPrivateKeyParamsD = new BigInteger(ecPrivateKeyHex, 16);
|
||||
BigInteger ecPrivateKeyParamsD = new BigInteger(Hex.ToHexString(ecPrivateKeyBytes), 16);
|
||||
return new ECPrivateKeyParameters(ecPrivateKeyParamsD, SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
private static ECPublicKeyParameters ParsePublicKeyPemToPublicKeyParameters(byte[] publicKeyBytes)
|
||||
private static ECPublicKeyParameters ParsePublicKeyToParameters(byte[] publicKeyBytes)
|
||||
{
|
||||
return (ECPublicKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
|
||||
}
|
||||
|
||||
private static ECPublicKeyParameters ParseECPublicKeyToPublicKeyParameters(string ecPublicKeyHex)
|
||||
private static ECPublicKeyParameters ParseECPublicKeyToParameters(byte[] ecPublicKeyBytes)
|
||||
{
|
||||
byte[] ecPublicKeyBytes = Hex.Decode(ecPublicKeyHex);
|
||||
|
||||
const int KEY_BYTE_LENGTH = 64;
|
||||
|
||||
bool unzipped = ecPublicKeyBytes.FirstOrDefault() == 0x04;
|
||||
@@ -287,7 +279,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (uidBytes is null) throw new ArgumentNullException(nameof(uidBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParsePrivateKeyPemToPrivateKeyParameters(privateKeyBytes);
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParsePrivateKeyToParameters(privateKeyBytes);
|
||||
return SignWithSM3(sm2PrivateKeyParams, uidBytes, msgBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
@@ -311,27 +303,27 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="message">待签名的文本数据。</param>
|
||||
/// <param name="privateKeyPem">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="message">待签名的数据。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>经 Base64 编码的签名。</returns>
|
||||
public static string SignWithSM3(string privateKey, string message, bool asn1Encoding = true)
|
||||
/// <returns>经过 Base64 编码的签名。</returns>
|
||||
public static EncodedString SignWithSM3(string privateKeyPem, string message, bool asn1Encoding = true)
|
||||
{
|
||||
if (privateKey is null) throw new ArgumentNullException(nameof(privateKey));
|
||||
if (privateKeyPem is null) throw new ArgumentNullException(nameof(privateKeyPem));
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
byte[] signBytes = SignWithSM3(
|
||||
privateKeyBytes: ConvertPrivateKeyPkcs8PemToByteArray(privateKey),
|
||||
msgBytes: Encoding.UTF8.GetBytes(message),
|
||||
privateKeyBytes: ConvertPrivateKeyPemToByteArray(privateKeyPem),
|
||||
msgBytes: EncodedString.FromLiteralString(message),
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
return Convert.ToBase64String(signBytes);
|
||||
return EncodedString.ToBase64String(signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制私钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPrivateKeyBytes">EC 私钥字节数据。</param>
|
||||
/// <param name="ecPrivateKeyBytes">EC 私钥字节数组。</param>
|
||||
/// <param name="uidBytes">用户标识符字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
@@ -342,18 +334,14 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (uidBytes is null) throw new ArgumentNullException(nameof(uidBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
return SignWithSM3ByECPrivateKey(
|
||||
ecPrivateKeyHex: Hex.ToHexString(ecPrivateKeyBytes),
|
||||
uidBytes: uidBytes,
|
||||
msgBytes: msgBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParseECPrivateKeyToParameters(ecPrivateKeyBytes);
|
||||
return SignWithSM3(sm2PrivateKeyParams, uidBytes, msgBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制私钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPrivateKeyBytes">EC 私钥字节数据。</param>
|
||||
/// <param name="ecPrivateKeyBytes">EC 私钥字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
@@ -368,34 +356,38 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制私钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPrivateKeyHex">EC 私钥(十六进制格式)。</param>
|
||||
/// <param name="encodingECPrivateKey">经过编码后的(通常为十六进制)EC 私钥。</param>
|
||||
/// <param name="uidBytes">用户标识符字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static byte[] SignWithSM3ByECPrivateKey(string ecPrivateKeyHex, byte[] uidBytes, byte[] msgBytes, bool asn1Encoding = true)
|
||||
public static byte[] SignWithSM3ByECPrivateKey(EncodedString encodingECPrivateKey, byte[] uidBytes, byte[] msgBytes, bool asn1Encoding = true)
|
||||
{
|
||||
if (ecPrivateKeyHex is null) throw new ArgumentNullException(nameof(ecPrivateKeyHex));
|
||||
if (encodingECPrivateKey.Value is null) throw new ArgumentNullException(nameof(encodingECPrivateKey));
|
||||
if (uidBytes is null) throw new ArgumentNullException(nameof(uidBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
ECPrivateKeyParameters ecPrivateKeyParams = ParseECPrivateKeyToPrivateKeyParameters(ecPrivateKeyHex);
|
||||
return SignWithSM3(ecPrivateKeyParams, uidBytes, msgBytes, asn1Encoding);
|
||||
return SignWithSM3ByECPrivateKey(
|
||||
ecPrivateKeyBytes: EncodedString.FromString(encodingECPrivateKey, fallbackEncodingKind: EncodingKinds.Hex),
|
||||
uidBytes: uidBytes,
|
||||
msgBytes: msgBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制私钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPrivateKeyHex">EC 私钥(十六进制格式)。</param>
|
||||
/// <param name="encodingECPrivateKey">经过编码后的(通常为十六进制)EC 私钥。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static byte[] SignWithSM3ByECPrivateKey(string ecPrivateKeyHex, byte[] msgBytes, bool asn1Encoding = true)
|
||||
public static byte[] SignWithSM3ByECPrivateKey(EncodedString encodingECPrivateKey, byte[] msgBytes, bool asn1Encoding = true)
|
||||
{
|
||||
return SignWithSM3ByECPrivateKey(
|
||||
ecPrivateKeyHex: ecPrivateKeyHex,
|
||||
encodingECPrivateKey: encodingECPrivateKey,
|
||||
uidBytes: SM2_DEFAULT_UID,
|
||||
msgBytes: msgBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
@@ -405,10 +397,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数组。</param>
|
||||
/// <param name="uidBytes">用户标识符字节数组。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3(byte[] publicKeyBytes, byte[] uidBytes, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
@@ -418,16 +410,16 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
if (signBytes is null) throw new ArgumentNullException(nameof(signBytes));
|
||||
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParsePublicKeyToParameters(publicKeyBytes);
|
||||
return VerifyWithSM3(sm2PublicKeyParams, uidBytes, msgBytes, signBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数组。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3(byte[] publicKeyBytes, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
@@ -444,21 +436,21 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <param name="publicKeyPem">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的数据。</param>
|
||||
/// <param name="encodingSignature">经过编码后的(通常为 Base64)签名。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3(string publicKey, string message, string signature, bool asn1Encoding = true)
|
||||
public static bool VerifyWithSM3(string publicKeyPem, string message, EncodedString encodingSignature, bool asn1Encoding = true)
|
||||
{
|
||||
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
|
||||
if (publicKeyPem is null) throw new ArgumentNullException(nameof(publicKeyPem));
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
if (signature is null) throw new ArgumentNullException(nameof(signature));
|
||||
if (encodingSignature.Value is null) throw new ArgumentNullException(nameof(encodingSignature));
|
||||
|
||||
return VerifyWithSM3(
|
||||
publicKeyBytes: ConvertPublicKeyPkcs8PemToByteArray(publicKey),
|
||||
msgBytes: Encoding.UTF8.GetBytes(message),
|
||||
signBytes: Convert.FromBase64String(signature),
|
||||
publicKeyBytes: ConvertPublicKeyPemToByteArray(publicKeyPem),
|
||||
msgBytes: EncodedString.FromLiteralString(message),
|
||||
signBytes: EncodedString.FromString(encodingSignature, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
@@ -466,31 +458,31 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 使用证书基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <param name="message">待验证的数据。</param>
|
||||
/// <param name="encodingSignature">经过编码后的(通常为 Base64)签名。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3ByCertificate(string certificate, string message, string signature, bool asn1Encoding = true)
|
||||
public static bool VerifyWithSM3ByCertificate(string certificatePem, string message, EncodedString encodingSignature, bool asn1Encoding = true)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
string publicKey = ExportPublicKeyFromCertificate(certificate);
|
||||
string publicKeyPem = ExportPublicKeyFromCertificate(certificatePem);
|
||||
return VerifyWithSM3(
|
||||
publicKey: publicKey,
|
||||
publicKeyPem: publicKeyPem,
|
||||
message: message,
|
||||
signature: signature,
|
||||
encodingSignature: encodingSignature,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制公钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 公钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPublicKeyBytes">EC 公钥字节数据。</param>
|
||||
/// <param name="ecPublicKeyBytes">EC 公钥字节数组。</param>
|
||||
/// <param name="uidBytes">用户标识符字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static bool VerifyWithSM3ByECPublicKey(byte[] ecPublicKeyBytes, byte[] uidBytes, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
@@ -499,21 +491,16 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (uidBytes is null) throw new ArgumentNullException(nameof(uidBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
return VerifyWithSM3ByECPublicKey(
|
||||
ecPublicKeyHex: Hex.ToHexString(ecPublicKeyBytes),
|
||||
uidBytes: uidBytes,
|
||||
msgBytes: msgBytes,
|
||||
signBytes: signBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParseECPublicKeyToParameters(ecPublicKeyBytes);
|
||||
return VerifyWithSM3(sm2PublicKeyParams, uidBytes, msgBytes, signBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制公钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 公钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPublicKeyBytes">EC 公钥字节数据。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="ecPublicKeyBytes">EC 公钥字节数组。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static bool VerifyWithSM3ByECPublicKey(byte[] ecPublicKeyBytes, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
@@ -528,36 +515,41 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制公钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 公钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPublicKeyHex">EC 公钥(十六进制格式)。</param>
|
||||
/// <param name="encodingECPublicKey">经过编码后的(通常为十六进制)EC 公钥。</param>
|
||||
/// <param name="uidBytes">用户标识符字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static bool VerifyWithSM3ByECPublicKey(string ecPublicKeyHex, byte[] uidBytes, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
public static bool VerifyWithSM3ByECPublicKey(EncodedString encodingECPublicKey, byte[] uidBytes, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
{
|
||||
if (ecPublicKeyHex is null) throw new ArgumentNullException(nameof(ecPublicKeyHex));
|
||||
if (encodingECPublicKey.Value is null) throw new ArgumentNullException(nameof(encodingECPublicKey));
|
||||
if (uidBytes is null) throw new ArgumentNullException(nameof(uidBytes));
|
||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
ECPublicKeyParameters ecPublicKeyParams = ParseECPublicKeyToPublicKeyParameters(ecPublicKeyHex);
|
||||
return VerifyWithSM3(ecPublicKeyParams, uidBytes, msgBytes, signBytes, asn1Encoding);
|
||||
return VerifyWithSM3ByECPublicKey(
|
||||
ecPublicKeyBytes: EncodedString.FromString(encodingECPublicKey, fallbackEncodingKind: EncodingKinds.Hex),
|
||||
uidBytes: uidBytes,
|
||||
msgBytes: msgBytes,
|
||||
signBytes: signBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制公钥基于 SM3 算法生成签名。
|
||||
/// 使用 EC 公钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="ecPublicKeyHex">EC 公钥(十六进制格式)。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <param name="encodingECPublicKey">经过编码后的(通常为十六进制)EC 公钥。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数组。</param>
|
||||
/// <param name="signBytes">签名字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示签名结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static bool VerifyWithSM3ByECPublicKey(string ecPublicKeyHex, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
public static bool VerifyWithSM3ByECPublicKey(EncodedString encodingECPublicKey, byte[] msgBytes, byte[] signBytes, bool asn1Encoding = true)
|
||||
{
|
||||
return VerifyWithSM3ByECPublicKey(
|
||||
ecPublicKeyHex: ecPublicKeyHex,
|
||||
encodingECPublicKey: encodingECPublicKey,
|
||||
uidBytes: SM2_DEFAULT_UID,
|
||||
msgBytes: msgBytes,
|
||||
signBytes: signBytes,
|
||||
@@ -568,8 +560,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 使用私钥解密数据。
|
||||
/// </summary>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数据。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数据。</param>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] Decrypt(byte[] privateKeyBytes, byte[] cipherBytes, bool asn1Encoding = true)
|
||||
@@ -577,68 +569,68 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (privateKeyBytes is null) throw new ArgumentNullException(nameof(privateKeyBytes));
|
||||
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
|
||||
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParsePrivateKeyPemToPrivateKeyParameters(privateKeyBytes);
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParsePrivateKeyToParameters(privateKeyBytes);
|
||||
return Decrypt(sm2PrivateKeyParams, cipherBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥解密数据。
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="cipherText">经 Base64 编码的待解密数据。</param>
|
||||
/// <param name="privateKeyPem">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="encodingCipher">经过编码后的(通常为 Base64)待解密数据。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>解密后的文本数据。</returns>
|
||||
public static string Decrypt(string privateKey, string cipherText, bool asn1Encoding = true)
|
||||
/// <returns>解密后的数据。</returns>
|
||||
public static EncodedString Decrypt(string privateKeyPem, EncodedString encodingCipher, bool asn1Encoding = true)
|
||||
{
|
||||
if (privateKey is null) throw new ArgumentNullException(nameof(privateKey));
|
||||
if (cipherText is null) throw new ArgumentNullException(nameof(cipherText));
|
||||
if (privateKeyPem is null) throw new ArgumentNullException(nameof(privateKeyPem));
|
||||
if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher));
|
||||
|
||||
byte[] plainBytes = Decrypt(
|
||||
privateKeyBytes: ConvertPrivateKeyPkcs8PemToByteArray(privateKey),
|
||||
cipherBytes: Convert.FromBase64String(cipherText),
|
||||
privateKeyBytes: ConvertPrivateKeyPemToByteArray(privateKeyPem),
|
||||
cipherBytes: EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
return Encoding.UTF8.GetString(plainBytes);
|
||||
return EncodedString.ToLiteralString(plainBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制私钥解密数据。
|
||||
/// 使用 EC 私钥解密数据。
|
||||
/// </summary>
|
||||
/// <param name="ecPrivateKeyBytes">EC 私钥字节数据。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数据。</param>
|
||||
/// <param name="ecPrivateKeyBytes">EC 私钥字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] DecryptByECPrivateKey(byte[] ecPrivateKeyBytes, byte[] cipherBytes, bool asn1Encoding = true)
|
||||
{
|
||||
if (ecPrivateKeyBytes is null) throw new ArgumentNullException(nameof(ecPrivateKeyBytes));
|
||||
|
||||
ECPrivateKeyParameters ecPrivateKeyParams = ParseECPrivateKeyToParameters(ecPrivateKeyBytes);
|
||||
return Decrypt(ecPrivateKeyParams, cipherBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 私钥解密数据。
|
||||
/// </summary>
|
||||
/// <param name="encodingECPrivateKey">经过编码后的(通常为十六进制)EC 私钥。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>解密后的数据。</returns>
|
||||
public static byte[] DecryptByECPrivateKey(EncodedString encodingECPrivateKey, byte[] cipherBytes, bool asn1Encoding = true)
|
||||
{
|
||||
if (encodingECPrivateKey.Value is null) throw new ArgumentNullException(nameof(encodingECPrivateKey));
|
||||
|
||||
return DecryptByECPrivateKey(
|
||||
ecPrivateKeyHex: Hex.ToHexString(ecPrivateKeyBytes),
|
||||
ecPrivateKeyBytes: EncodedString.FromString(encodingECPrivateKey, fallbackEncodingKind: EncodingKinds.Hex),
|
||||
cipherBytes: cipherBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制私钥解密数据。
|
||||
/// </summary>
|
||||
/// <param name="ecPrivateKeyHex">EC 私钥(十六进制格式)。</param>
|
||||
/// <param name="cipherBytes">待解密的数据字节数据。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>解密后的文本数据。</returns>
|
||||
public static byte[] DecryptByECPrivateKey(string ecPrivateKeyHex, byte[] cipherBytes, bool asn1Encoding = true)
|
||||
{
|
||||
if (ecPrivateKeyHex is null) throw new ArgumentNullException(nameof(ecPrivateKeyHex));
|
||||
|
||||
ECPrivateKeyParameters ecPrivateKeyParams = ParseECPrivateKeyToPrivateKeyParameters(ecPrivateKeyHex);
|
||||
return Decrypt(ecPrivateKeyParams, cipherBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥加密数据。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数据。</param>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数组。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>加密后的数据字节数组。</returns>
|
||||
public static byte[] Encrypt(byte[] publicKeyBytes, byte[] plainBytes, bool asn1Encoding = true)
|
||||
@@ -646,77 +638,77 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (publicKeyBytes is null) throw new ArgumentNullException(nameof(publicKeyBytes));
|
||||
if (plainBytes is null) throw new ArgumentNullException(nameof(plainBytes));
|
||||
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParsePublicKeyToParameters(publicKeyBytes);
|
||||
return Encrypt(sm2PublicKeyParams, plainBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥加密数据。
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="plainText">待加密的文本数据。</param>
|
||||
/// <param name="publicKeyPem">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="plainData">待加密的数据。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>经 Base64 编码的加密数据。</returns>
|
||||
public static string Encrypt(string publicKey, string plainText, bool asn1Encoding = true)
|
||||
/// <returns>经过 Base64 编码的加密数据。</returns>
|
||||
public static EncodedString Encrypt(string publicKeyPem, string plainData, bool asn1Encoding = true)
|
||||
{
|
||||
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
|
||||
if (plainText is null) throw new ArgumentNullException(nameof(plainText));
|
||||
if (publicKeyPem is null) throw new ArgumentNullException(nameof(publicKeyPem));
|
||||
if (plainData is null) throw new ArgumentNullException(nameof(plainData));
|
||||
|
||||
byte[] cipherBytes = Encrypt(
|
||||
publicKeyBytes: ConvertPublicKeyPkcs8PemToByteArray(publicKey),
|
||||
plainBytes: Encoding.UTF8.GetBytes(plainText),
|
||||
publicKeyBytes: ConvertPublicKeyPemToByteArray(publicKeyPem),
|
||||
plainBytes: EncodedString.FromLiteralString(plainData),
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
return Convert.ToBase64String(cipherBytes);
|
||||
return EncodedString.ToBase64String(cipherBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用证书加密数据。
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="plainText">待加密的文本数据。</param>
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <param name="plainData">待加密的数据。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>经 Base64 编码的加密数据。</returns>
|
||||
public static string EncryptByCertificate(string certificate, string plainText, bool asn1Encoding = true)
|
||||
/// <returns>经过 Base64 编码的加密数据。</returns>
|
||||
public static EncodedString EncryptByCertificate(string certificatePem, string plainData, bool asn1Encoding = true)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
return Encrypt(
|
||||
publicKey: ExportPublicKeyFromCertificate(certificate),
|
||||
plainText: plainText,
|
||||
publicKeyPem: ExportPublicKeyFromCertificate(certificatePem),
|
||||
plainData: plainData,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制公钥加密数据。
|
||||
/// 使用 EC 公钥加密数据。
|
||||
/// </summary>
|
||||
/// <param name="ecPublicKeyBytes">EC 公钥字节数据。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数据。</param>
|
||||
/// <param name="ecPublicKeyBytes">EC 公钥字节数组。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>加密后的数据字节数组。</returns>
|
||||
public static byte[] EncryptByECPublicKey(byte[] ecPublicKeyBytes, byte[] plainBytes, bool asn1Encoding = true)
|
||||
{
|
||||
return EncryptByECPublicKey(
|
||||
ecPublicKeyHex: Hex.ToHexString(ecPublicKeyBytes),
|
||||
plainBytes: plainBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
ECPublicKeyParameters ecPublicKeyParams = ParseECPublicKeyToParameters(ecPublicKeyBytes);
|
||||
return Encrypt(ecPublicKeyParams, plainBytes, asn1Encoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 EC 十六进制公钥加密数据。
|
||||
/// 使用 EC 公钥加密数据。
|
||||
/// </summary>
|
||||
/// <param name="ecPublicKeyHex">EC 公钥(十六进制格式)。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数据。</param>
|
||||
/// <param name="encodingECPublicKey">EC 公钥(十六进制格式)。</param>
|
||||
/// <param name="plainBytes">待加密的数据字节数组。</param>
|
||||
/// <param name="asn1Encoding">指示加密结果是否为 ASN.1 编码的形式。(默认值:true)</param>
|
||||
/// <returns>加密后的数据字节数组。</returns>
|
||||
public static byte[] EncryptByECPublicKey(string ecPublicKeyHex, byte[] plainBytes, bool asn1Encoding = true)
|
||||
public static byte[] EncryptByECPublicKey(EncodedString encodingECPublicKey, byte[] plainBytes, bool asn1Encoding = true)
|
||||
{
|
||||
if (ecPublicKeyHex is null) throw new ArgumentNullException(nameof(ecPublicKeyHex));
|
||||
if (encodingECPublicKey.Value is null) throw new ArgumentNullException(nameof(encodingECPublicKey));
|
||||
|
||||
ECPublicKeyParameters ecPublicKeyParams = ParseECPublicKeyToPublicKeyParameters(ecPublicKeyHex);
|
||||
return Encrypt(ecPublicKeyParams, plainBytes, asn1Encoding);
|
||||
return EncryptByECPublicKey(
|
||||
ecPublicKeyBytes: EncodedString.FromString(encodingECPublicKey, fallbackEncodingKind: EncodingKinds.Hex),
|
||||
plainBytes: plainBytes,
|
||||
asn1Encoding: asn1Encoding
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -726,15 +718,15 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 转为 -----BEGIN PUBLIC KEY----- ..... -----END PUBLIC KEY-----
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <returns>PKCS#8 公钥(PEM 格式)。</returns>
|
||||
public static string ExportPublicKeyFromCertificate(string certificate)
|
||||
public static string ExportPublicKeyFromCertificate(string certificatePem)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
using (TextWriter swriter = new StringWriter())
|
||||
{
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificate);
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificatePem);
|
||||
ECPublicKeyParameters exPublicKeyParams = (ECPublicKeyParameters)x509cert.GetPublicKey();
|
||||
PemWriter pemWriter = new PemWriter(swriter);
|
||||
pemWriter.WriteObject(exPublicKeyParams);
|
||||
@@ -746,39 +738,39 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书序列号。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <returns>证书序列号。</returns>
|
||||
public static string ExportSerialNumberFromCertificate(string certificate)
|
||||
public static string ExportSerialNumberFromCertificate(string certificatePem)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificate);
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificatePem);
|
||||
return x509cert.SerialNumber.ToString(16);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书颁发时间。</para>
|
||||
/// <para>从 CRT/CER 证书中导出证书有效期的开始时间。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书颁发时间。</returns>
|
||||
public static DateTimeOffset ExportEffectiveTimeFromCertificate(string certificate)
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <returns>证书有效期的开始时间。</returns>
|
||||
public static DateTimeOffset ExportValidFromDateFromCertificate(string certificatePem)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificate);
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificatePem);
|
||||
return new DateTimeOffset(x509cert.NotBefore);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书过期时间。</para>
|
||||
/// <para>从 CRT/CER 证书中导出证书有效期的结束时间。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书过期时间。</returns>
|
||||
public static DateTimeOffset ExportExpireTimeFromCertificate(string certificate)
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <returns>证书有效期的结束时间。</returns>
|
||||
public static DateTimeOffset ExportValidToDateFromCertificate(string certificatePem)
|
||||
{
|
||||
if (certificate is null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (certificatePem is null) throw new ArgumentNullException(nameof(certificatePem));
|
||||
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificate);
|
||||
X509Certificate x509cert = ConvertCertificatePemToX509(certificatePem);
|
||||
return new DateTimeOffset(x509cert.NotAfter);
|
||||
}
|
||||
|
||||
@@ -789,12 +781,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 转为 04+X|Y 结构的十六进制字符串。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="certificatePem">证书内容(PEM 格式)。</param>
|
||||
/// <returns>EC 公钥(十六进制格式)。</returns>
|
||||
public static string ExportECPublicKeyFromCertificate(string certificate)
|
||||
public static string ExportECPublicKeyFromCertificate(string certificatePem)
|
||||
{
|
||||
string publicKey = ExportPublicKeyFromCertificate(certificate);
|
||||
return ExportECPublicKeyFromPublicKey(publicKey);
|
||||
string publicKeyPem = ExportPublicKeyFromCertificate(certificatePem);
|
||||
return ExportECPublicKeyFromPublicKey(publicKeyPem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -804,12 +796,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 转为 04+X|Y 结构的 130(128+2) 位的十六进制字符串。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="publicKeyPem">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <returns>EC 公钥(十六进制格式)。</returns>
|
||||
public static string ExportECPublicKeyFromPublicKey(string publicKey)
|
||||
public static string ExportECPublicKeyFromPublicKey(string publicKeyPem)
|
||||
{
|
||||
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
|
||||
byte[] publicKeyBytes = ConvertPublicKeyPemToByteArray(publicKeyPem);
|
||||
ECPublicKeyParameters sm2PublicKeyParams = ParsePublicKeyToParameters(publicKeyBytes);
|
||||
ECPoint ecPublicKeyPoint = sm2PublicKeyParams.Q;
|
||||
string ecPublicKeyX = ecPublicKeyPoint.XCoord.ToBigInteger().ToString(16);
|
||||
string ecPublicKeyY = ecPublicKeyPoint.YCoord.ToBigInteger().ToString(16);
|
||||
@@ -823,11 +815,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 转为 64 位的十六进制字符串。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="privateKeyPem">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <returns>EC 私钥(十六进制格式)。</returns>
|
||||
public static string ExportECPrivateKeyFromPrivateKey(string privateKey)
|
||||
public static string ExportECPrivateKeyFromPrivateKey(string privateKeyPem)
|
||||
{
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParsePrivateKeyPemToPrivateKeyParameters(privateKey);
|
||||
byte[] privateKeyBytes = ConvertPrivateKeyPemToByteArray(privateKeyPem);
|
||||
ECPrivateKeyParameters sm2PrivateKeyParams = ParsePrivateKeyToParameters(privateKeyBytes);
|
||||
return sm2PrivateKeyParams.D.ToString(16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// SM3 算法工具类。
|
||||
/// </summary>
|
||||
public static class SM3Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取 SM3 哈希值。
|
||||
/// 计算 SM3 哈希值。
|
||||
/// </summary>
|
||||
/// <param name="bytes">信息字节数组。</param>
|
||||
/// <returns>哈希字节数组。</returns>
|
||||
/// <param name="bytes">要计算哈希值的信息字节数组。</param>
|
||||
/// <returns>哈希值字节数组。</returns>
|
||||
public static byte[] Hash(byte[] bytes)
|
||||
{
|
||||
if (bytes is null) throw new ArgumentNullException(nameof(bytes));
|
||||
@@ -26,17 +27,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 SM3 哈希值。
|
||||
/// 计算 SM3 哈希值。
|
||||
/// </summary>
|
||||
/// <param name="message">文本信息。</param>
|
||||
/// <returns>哈希值。</returns>
|
||||
public static string Hash(string message)
|
||||
/// <param name="message">要计算哈希值的信息。</param>
|
||||
/// <returns>经过十六进制编码的哈希值。</returns>
|
||||
public static EncodedString Hash(string message)
|
||||
{
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] msgBytes = EncodedString.FromLiteralString(message);
|
||||
byte[] hashBytes = Hash(msgBytes);
|
||||
return BitConverter.ToString(hashBytes).Replace("-", string.Empty);
|
||||
return EncodedString.ToHexString(hashBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// SM4 算法工具类。
|
||||
/// </summary>
|
||||
public static class SM4Utility
|
||||
{
|
||||
private const string SM4_CIPHER_ALGORITHM_GCM = "SM4/GCM";
|
||||
private const string SM4_CIPHER_PADDING_NOPADDING = "NoPadding";
|
||||
/// <summary>
|
||||
/// 填充模式:NoPadding。
|
||||
/// </summary>
|
||||
private const string PADDING_MODE_NOPADDING = "NoPadding";
|
||||
|
||||
/// <summary>
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="keyBytes">SM4 密钥字节数组。</param>
|
||||
/// <param name="nonceBytes">加密使用的初始化向量字节数组。</param>
|
||||
/// <param name="aadBytes">加密使用的附加数据包字节数组。</param>
|
||||
/// <param name="nonceBytes">初始化向量字节数组。</param>
|
||||
/// <param name="associatedDataBytes">附加数据字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密数据字节数组。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="SM4_CIPHER_PADDING_NOPADDING"/>)</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] nonceBytes, byte[]? aadBytes, byte[] cipherBytes, string paddingMode = SM4_CIPHER_PADDING_NOPADDING)
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] nonceBytes, byte[]? associatedDataBytes, byte[] cipherBytes, string paddingMode = PADDING_MODE_NOPADDING)
|
||||
{
|
||||
const int KEY_LENGTH_BYTE = 16;
|
||||
const int NONCE_LENGTH_BYTE = 12;
|
||||
@@ -36,12 +39,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
|
||||
if (cipherBytes.Length < TAG_LENGTH_BYTE) throw new ArgumentException($"Invalid cipher byte length (expected: more than {TAG_LENGTH_BYTE}, actual: {cipherBytes.Length}).", nameof(cipherBytes));
|
||||
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher(string.Format("{0}/{1}", SM4_CIPHER_ALGORITHM_GCM, paddingMode));
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher($"SM4/GCM/{paddingMode}");
|
||||
ICipherParameters cipherParams = new AeadParameters(
|
||||
new KeyParameter(keyBytes),
|
||||
TAG_LENGTH_BYTE * 8,
|
||||
nonceBytes,
|
||||
aadBytes
|
||||
associatedDataBytes
|
||||
);
|
||||
cipher.Init(false, cipherParams);
|
||||
byte[] plainBytes = new byte[cipher.GetOutputSize(cipherBytes.Length)];
|
||||
@@ -53,26 +56,26 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// <summary>
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="key">SM4 密钥。</param>
|
||||
/// <param name="nonce">加密使用的初始化向量。</param>
|
||||
/// <param name="aad">加密使用的附加数据包。</param>
|
||||
/// <param name="cipherText">经 Base64 编码后的待解密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="SM4_CIPHER_PADDING_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的文本数据。</returns>
|
||||
public static string DecryptWithGCM(string key, string nonce, string? aad, string cipherText, string paddingMode = SM4_CIPHER_PADDING_NOPADDING)
|
||||
/// <param name="encodingKey">经过编码后的(通常为 Base64)SM4 密钥。</param>
|
||||
/// <param name="encodingNonce">经过编码后的(通常为 Base64)初始化向量。</param>
|
||||
/// <param name="encodingAssociatedData">经过编码后的(通常为 Base64)附加数据。</param>
|
||||
/// <param name="encodingCipher">经过编码后的(通常为 Base64)待解密数据。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="PADDING_MODE_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的数据。</returns>
|
||||
public static EncodedString DecryptWithGCM(EncodedString encodingKey, EncodedString encodingNonce, EncodedString encodingAssociatedData, EncodedString encodingCipher, string paddingMode = PADDING_MODE_NOPADDING)
|
||||
{
|
||||
if (key is null) throw new ArgumentNullException(nameof(key));
|
||||
if (nonce is null) throw new ArgumentNullException(nameof(nonce));
|
||||
if (cipherText is null) throw new ArgumentNullException(nameof(cipherText));
|
||||
if (encodingKey.Value is null) throw new ArgumentNullException(nameof(encodingKey));
|
||||
if (encodingNonce.Value is null) throw new ArgumentNullException(nameof(encodingNonce));
|
||||
if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher));
|
||||
|
||||
byte[] plainBytes = DecryptWithGCM(
|
||||
keyBytes: Encoding.UTF8.GetBytes(key),
|
||||
nonceBytes: Encoding.UTF8.GetBytes(nonce),
|
||||
aadBytes: aad is null ? null : Encoding.UTF8.GetBytes(aad),
|
||||
cipherBytes: Convert.FromBase64String(cipherText),
|
||||
keyBytes: EncodedString.FromString(encodingKey, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
nonceBytes: EncodedString.FromString(encodingNonce, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
associatedDataBytes: encodingAssociatedData.Value is not null ? EncodedString.FromString(encodingAssociatedData, fallbackEncodingKind: EncodingKinds.Base64) : null,
|
||||
cipherBytes: EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64),
|
||||
paddingMode: paddingMode
|
||||
);
|
||||
return Encoding.UTF8.GetString(plainBytes);
|
||||
return EncodedString.ToLiteralString(plainBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user