mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-07-16 16:50:43 +08:00
feat(tenpayv3): 新增 SM4 算法工具类
This commit is contained in:
parent
3558bf8231
commit
2c37745113
@ -18,26 +18,32 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="keyBytes">AES 密钥字节数组。</param>
|
||||
/// <param name="ivBytes">加密使用的初始化向量字节数组。</param>
|
||||
/// <param name="nonceBytes">加密使用的初始化向量字节数组。</param>
|
||||
/// <param name="aadBytes">加密使用的附加数据包字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密数据字节数组。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="AES_CIPHER_PADDING_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] ivBytes, byte[] aadBytes, byte[] cipherBytes)
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] nonceBytes, byte[]? aadBytes, byte[] cipherBytes, string paddingMode = AES_CIPHER_PADDING_NOPADDING)
|
||||
{
|
||||
if (keyBytes == null) throw new ArgumentNullException(nameof(keyBytes));
|
||||
if (ivBytes == null) throw new ArgumentNullException(nameof(ivBytes));
|
||||
if (aadBytes == null) throw new ArgumentNullException(nameof(aadBytes));
|
||||
if (cipherBytes == null) throw new ArgumentNullException(nameof(cipherBytes));
|
||||
const int KEY_LENGTH_BYTE = 32;
|
||||
const int NONCE_LENGTH_BYTE = 12;
|
||||
const int TAG_LENGTH_BYTE = 16;
|
||||
|
||||
const int TAG_LENGTH_BIT = 128;
|
||||
IBufferedCipher cipher = CipherUtilities.GetCipher(string.Format("{0}/{1}", AES_CIPHER_ALGORITHM_GCM, AES_CIPHER_PADDING_NOPADDING));
|
||||
ICipherParameters aeadParams = new AeadParameters(
|
||||
if (keyBytes == null) throw new ArgumentNullException(nameof(keyBytes));
|
||||
if (keyBytes.Length != KEY_LENGTH_BYTE) throw new ArgumentException($"Invalid key byte length (expected: {KEY_LENGTH_BYTE}, actual: {keyBytes.Length}).", nameof(keyBytes));
|
||||
if (nonceBytes == null) throw new ArgumentNullException(nameof(nonceBytes));
|
||||
if (nonceBytes.Length != NONCE_LENGTH_BYTE) throw new ArgumentException($"Invalid nonce byte length (expected: {NONCE_LENGTH_BYTE}, actual: {nonceBytes.Length}).", nameof(nonceBytes));
|
||||
if (cipherBytes == 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));
|
||||
ICipherParameters cipherParams = new AeadParameters(
|
||||
new KeyParameter(keyBytes),
|
||||
TAG_LENGTH_BIT,
|
||||
ivBytes,
|
||||
TAG_LENGTH_BYTE * 8,
|
||||
nonceBytes,
|
||||
aadBytes
|
||||
);
|
||||
cipher.Init(false, aeadParams);
|
||||
cipher.Init(false, cipherParams);
|
||||
byte[] plainBytes = new byte[cipher.GetOutputSize(cipherBytes.Length)];
|
||||
int len = cipher.ProcessBytes(cipherBytes, 0, cipherBytes.Length, plainBytes, 0);
|
||||
cipher.DoFinal(plainBytes, len);
|
||||
@ -48,21 +54,23 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="key">AES 密钥。</param>
|
||||
/// <param name="iv">加密使用的初始化向量。</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 iv, string? aad, string cipherText)
|
||||
public static string DecryptWithGCM(string key, string nonce, string? aad, string cipherText, string paddingMode = AES_CIPHER_PADDING_NOPADDING)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
if (iv == null) throw new ArgumentNullException(nameof(iv));
|
||||
if (nonce == null) throw new ArgumentNullException(nameof(nonce));
|
||||
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
|
||||
|
||||
byte[] plainBytes = DecryptWithGCM(
|
||||
keyBytes: Encoding.UTF8.GetBytes(key),
|
||||
ivBytes: Encoding.UTF8.GetBytes(iv),
|
||||
nonceBytes: Encoding.UTF8.GetBytes(nonce),
|
||||
aadBytes: Encoding.UTF8.GetBytes(aad ?? string.Empty),
|
||||
cipherBytes: Convert.FromBase64String(cipherText)
|
||||
cipherBytes: Convert.FromBase64String(cipherText),
|
||||
paddingMode: paddingMode
|
||||
);
|
||||
return Encoding.UTF8.GetString(plainBytes);
|
||||
}
|
||||
|
@ -54,19 +54,19 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
return (RsaKeyParameters)cert.GetPublicKey();
|
||||
}
|
||||
|
||||
private static byte[] SignWithSHA256(RsaKeyParameters rsaPrivateKeyParams, byte[] plainBytes)
|
||||
private static byte[] SignWithSHA256(RsaKeyParameters rsaPrivateKeyParams, byte[] msgBytes)
|
||||
{
|
||||
ISigner signer = SignerUtilities.GetSigner(RSA_SIGNER_ALGORITHM_SHA256);
|
||||
signer.Init(true, rsaPrivateKeyParams);
|
||||
signer.BlockUpdate(plainBytes, 0, plainBytes.Length);
|
||||
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
|
||||
return signer.GenerateSignature();
|
||||
}
|
||||
|
||||
private static bool VerifyWithSHA256(RsaKeyParameters rsaPublicKeyParams, byte[] plainBytes, byte[] signBytes)
|
||||
private static bool VerifyWithSHA256(RsaKeyParameters rsaPublicKeyParams, byte[] msgBytes, byte[] signBytes)
|
||||
{
|
||||
ISigner signer = SignerUtilities.GetSigner(RSA_SIGNER_ALGORITHM_SHA256);
|
||||
signer.Init(false, rsaPublicKeyParams);
|
||||
signer.BlockUpdate(plainBytes, 0, plainBytes.Length);
|
||||
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
|
||||
return signer.VerifySignature(signBytes);
|
||||
}
|
||||
|
||||
@ -88,31 +88,31 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 使用私钥基于 SHA-256 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="plainBytes">待签名的数据字节数组。</param>
|
||||
/// <param name="msgBytes">待签名的数据字节数组。</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static byte[] SignWithSHA256(byte[] privateKeyBytes, byte[] plainBytes)
|
||||
public static byte[] SignWithSHA256(byte[] privateKeyBytes, byte[] msgBytes)
|
||||
{
|
||||
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
|
||||
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
|
||||
if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
|
||||
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
|
||||
return SignWithSHA256(rsaKeyParams, plainBytes);
|
||||
return SignWithSHA256(rsaKeyParams, msgBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SHA-256 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="plainText">待签名的文本数据。</param>
|
||||
/// <param name="message">待签名的文本数据。</param>
|
||||
/// <returns>经 Base64 编码的签名。</returns>
|
||||
public static string SignWithSHA256(string privateKey, string plainText)
|
||||
public static string SignWithSHA256(string privateKey, string message)
|
||||
{
|
||||
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
|
||||
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
byte[] privateKeyBytes = ConvertPkcs8PrivateKeyToByteArray(privateKey);
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] signBytes = SignWithSHA256(privateKeyBytes, plainBytes);
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] signBytes = SignWithSHA256(privateKeyBytes, msgBytes);
|
||||
return Convert.ToBase64String(signBytes);
|
||||
}
|
||||
|
||||
@ -120,55 +120,55 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
/// 使用公钥基于 SHA-256 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="plainBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="msgBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSHA256(byte[] publicKeyBytes, byte[] plainBytes, byte[] signBytes)
|
||||
public static bool VerifyWithSHA256(byte[] publicKeyBytes, byte[] msgBytes, byte[] signBytes)
|
||||
{
|
||||
if (publicKeyBytes == null) throw new ArgumentNullException(nameof(publicKeyBytes));
|
||||
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
|
||||
if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
|
||||
if (signBytes == null) throw new ArgumentNullException(nameof(signBytes));
|
||||
|
||||
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
|
||||
return VerifyWithSHA256(rsaKeyParams, plainBytes, signBytes);
|
||||
return VerifyWithSHA256(rsaKeyParams, msgBytes, signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SHA-256 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="plainText">待验证的文本数据。</param>
|
||||
/// <param name="message">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSHA256(string publicKey, string plainText, string signature)
|
||||
public static bool VerifyWithSHA256(string publicKey, string message, string signature)
|
||||
{
|
||||
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
|
||||
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
if (signature == null) throw new ArgumentNullException(nameof(signature));
|
||||
|
||||
byte[] publicKeyBytes = ConvertPkcs8PublicKeyToByteArray(publicKey);
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] signBytes = Convert.FromBase64String(signature);
|
||||
return VerifyWithSHA256(publicKeyBytes, plainBytes, signBytes);
|
||||
return VerifyWithSHA256(publicKeyBytes, msgBytes, signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用证书基于 SHA-256 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <param name="plainText">待验证的文本数据。</param>
|
||||
/// <param name="message">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSHA256ByCertificate(string certificate, string plainText, string signature)
|
||||
public static bool VerifyWithSHA256ByCertificate(string certificate, string message, string signature)
|
||||
{
|
||||
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
|
||||
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
if (signature == null) throw new ArgumentNullException(nameof(signature));
|
||||
|
||||
RsaKeyParameters rsaKeyParams = ConvertCertificateToPublicKeyParams(certificate);
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
||||
byte[] signBytes = Convert.FromBase64String(signature);
|
||||
return VerifyWithSHA256(rsaKeyParams, plainBytes, signBytes);
|
||||
return VerifyWithSHA256(rsaKeyParams, msgBytes, signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
599
src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Utilities/SM2Utility.cs
Normal file
599
src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Utilities/SM2Utility.cs
Normal file
@ -0,0 +1,599 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Signers;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Math.EC;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.X509;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// SM2 算法工具类。
|
||||
/// </summary>
|
||||
public static partial class SM2Utility
|
||||
{
|
||||
// 默认用户身份标识:1234567812345678
|
||||
private static readonly byte[] DEFAULT_USERID_BYTES = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
|
||||
|
||||
private static byte[] ConvertPkcs8PrivateKeyToByteArray(string privateKey)
|
||||
{
|
||||
privateKey = privateKey
|
||||
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
|
||||
.Replace("-----END PRIVATE KEY-----", string.Empty);
|
||||
privateKey = Regex.Replace(privateKey, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(privateKey);
|
||||
}
|
||||
|
||||
private static byte[] ConvertPkcs8PublicKeyToByteArray(string publicKey)
|
||||
{
|
||||
publicKey = publicKey
|
||||
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
|
||||
.Replace("-----END PUBLIC KEY-----", string.Empty);
|
||||
publicKey = Regex.Replace(publicKey, "\\s+", string.Empty);
|
||||
return Convert.FromBase64String(publicKey);
|
||||
}
|
||||
|
||||
private static X509Certificate ParseX509Certificate(string certificate)
|
||||
{
|
||||
using (TextReader sreader = new StringReader(certificate))
|
||||
{
|
||||
PemReader pemReader = new PemReader(sreader);
|
||||
return (X509Certificate)pemReader.ReadObject();
|
||||
}
|
||||
}
|
||||
|
||||
private static ECPublicKeyParameters ConvertCertificateToPublicKeyParams(string certificate)
|
||||
{
|
||||
X509Certificate cert = ParseX509Certificate(certificate);
|
||||
return (ECPublicKeyParameters)cert.GetPublicKey();
|
||||
}
|
||||
|
||||
private static byte[] SignWithSM3(ECPrivateKeyParameters ecPrivateKeyParams, byte[] userIdBytes, byte[] plainBytes)
|
||||
{
|
||||
ISigner signer = new SM2Signer(new SM3Digest());
|
||||
ICipherParameters idParams = new ParametersWithID(new ParametersWithRandom(ecPrivateKeyParams), userIdBytes);
|
||||
signer.Init(true, idParams);
|
||||
signer.BlockUpdate(plainBytes, 0, plainBytes.Length);
|
||||
return signer.GenerateSignature();
|
||||
}
|
||||
|
||||
private static bool VerifyWithSM3(ECPublicKeyParameters ecPublicKeyParams, byte[] userIdBytes, byte[] plainBytes, byte[] signBytes)
|
||||
{
|
||||
ISigner signer = new SM2Signer(new SM3Digest());
|
||||
ICipherParameters idParams = new ParametersWithID(new ParametersWithRandom(ecPublicKeyParams), userIdBytes);
|
||||
signer.Init(false, idParams);
|
||||
signer.BlockUpdate(plainBytes, 0, plainBytes.Length);
|
||||
return signer.VerifySignature(signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="plainBytes">待签名的数据字节数组。</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static byte[] SignWithSM3(byte[] privateKeyBytes, byte[] plainBytes)
|
||||
{
|
||||
return SignWithSM3(
|
||||
privateKeyBytes: privateKeyBytes,
|
||||
userIdBytes: DEFAULT_USERID_BYTES,
|
||||
plainBytes: plainBytes
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKeyBytes">PKCS#8 私钥字节数组。</param>
|
||||
/// <param name="userIdBytes">用户身份标识字节数组。</param>
|
||||
/// <param name="plainBytes">待签名的数据字节数组。</param>
|
||||
/// <returns>签名字节数组。</returns>
|
||||
public static byte[] SignWithSM3(byte[] privateKeyBytes, byte[] userIdBytes, byte[] plainBytes)
|
||||
{
|
||||
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
|
||||
if (userIdBytes == null) throw new ArgumentNullException(nameof(userIdBytes));
|
||||
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
|
||||
|
||||
//ECPrivateKeyParameters ecKeyParams = (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
|
||||
//return SignWithSM3(ecKeyParams, userIdBytes, plainBytes);
|
||||
|
||||
BigInteger userD = new BigInteger(1, privateKeyBytes);
|
||||
SM2Factory sm2Factory = new SM2Factory();
|
||||
|
||||
ECPoint userKey = sm2Factory.ecc_point_g.Multiply(userD);
|
||||
SM3Digest sm3Digest = new SM3Digest();
|
||||
var z = sm2Factory.Sm2GetZ(userIdBytes, userKey);
|
||||
sm3Digest.BlockUpdate(z, 0, z.Length);
|
||||
sm3Digest.BlockUpdate(plainBytes, 0, plainBytes.Length);
|
||||
var md = new byte[32];
|
||||
sm3Digest.DoFinal(md, 0);
|
||||
|
||||
var result = sm2Factory.Sm2Sign(md, userD, userKey);
|
||||
return result.ToByteArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用私钥基于 SM3 算法生成签名。
|
||||
/// </summary>
|
||||
/// <param name="privateKey">PKCS#8 私钥(PEM 格式)。</param>
|
||||
/// <param name="plainText">待签名的文本数据。</param>
|
||||
/// <returns>经 Base64 编码的签名。</returns>
|
||||
public static string SignWithSM3(string privateKey, string plainText)
|
||||
{
|
||||
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
|
||||
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
|
||||
|
||||
byte[] privateKeyBytes = ConvertPkcs8PrivateKeyToByteArray(privateKey);
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] signBytes = SignWithSM3(privateKeyBytes, plainBytes);
|
||||
return Convert.ToBase64String(signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="plainBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3(byte[] publicKeyBytes, byte[] plainBytes, byte[] signBytes)
|
||||
{
|
||||
return VerifyWithSM3(
|
||||
publicKeyBytes: publicKeyBytes,
|
||||
userIdBytes: DEFAULT_USERID_BYTES,
|
||||
plainBytes: plainBytes,
|
||||
signBytes: signBytes
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKeyBytes">PKCS#8 公钥字节数据。</param>
|
||||
/// <param name="userIdBytes">用户身份标识字节数组。</param>
|
||||
/// <param name="plainBytes">待验证的数据字节数据。</param>
|
||||
/// <param name="signBytes">待验证的签名字节数据。</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3(byte[] publicKeyBytes, byte[] userIdBytes, byte[] plainBytes, byte[] signBytes)
|
||||
{
|
||||
if (publicKeyBytes == null) throw new ArgumentNullException(nameof(publicKeyBytes));
|
||||
if (userIdBytes == null) throw new ArgumentNullException(nameof(userIdBytes));
|
||||
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
|
||||
if (signBytes == null) throw new ArgumentNullException(nameof(signBytes));
|
||||
|
||||
ECPublicKeyParameters ecPublicKeyParams = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
|
||||
return VerifyWithSM3(ecPublicKeyParams, userIdBytes, plainBytes, signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用公钥基于 SM3 算法验证签名。
|
||||
/// </summary>
|
||||
/// <param name="publicKey">PKCS#8 公钥(PEM 格式)。</param>
|
||||
/// <param name="plainText">待验证的文本数据。</param>
|
||||
/// <param name="signature">经 Base64 编码的待验证的签名。</param>
|
||||
/// <returns>验证结果。</returns>
|
||||
public static bool VerifyWithSM3(string publicKey, string plainText, string signature)
|
||||
{
|
||||
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
|
||||
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
|
||||
if (signature == null) throw new ArgumentNullException(nameof(signature));
|
||||
|
||||
byte[] publicKeyBytes = ConvertPkcs8PublicKeyToByteArray(publicKey);
|
||||
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
byte[] signBytes = Convert.FromBase64String(signature);
|
||||
return VerifyWithSM3(publicKeyBytes, plainBytes, signBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出 PKCS#8 公钥。</para>
|
||||
/// <para>
|
||||
/// 即从 -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----
|
||||
/// 转为 -----BEGIN PUBLIC KEY----- ..... -----END PUBLIC KEY-----
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>PKCS#8 公钥(PEM 格式)。</returns>
|
||||
public static string ExportPublicKey(string certificate)
|
||||
{
|
||||
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
|
||||
|
||||
using (TextWriter swriter = new StringWriter())
|
||||
{
|
||||
ECKeyParameters ecKeyParams = ConvertCertificateToPublicKeyParams(certificate);
|
||||
PemWriter pemWriter = new PemWriter(swriter);
|
||||
pemWriter.WriteObject(ecKeyParams);
|
||||
pemWriter.Writer.Flush();
|
||||
return swriter.ToString()!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书序列号。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书序列号。</returns>
|
||||
public static string ExportSerialNumber(string certificate)
|
||||
{
|
||||
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
|
||||
|
||||
X509Certificate cert = ParseX509Certificate(certificate);
|
||||
return cert.SerialNumber.ToString(16);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书颁发时间。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书颁发时间。</returns>
|
||||
public static DateTimeOffset ExportEffectiveTime(string certificate)
|
||||
{
|
||||
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
|
||||
|
||||
X509Certificate cert = ParseX509Certificate(certificate);
|
||||
return new DateTimeOffset(cert.NotBefore);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>从 CRT/CER 证书中导出证书过期时间。</para>
|
||||
/// </summary>
|
||||
/// <param name="certificate">证书(PEM 格式)。</param>
|
||||
/// <returns>证书过期时间。</returns>
|
||||
public static DateTimeOffset ExportExpireTime(string certificate)
|
||||
{
|
||||
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
|
||||
|
||||
X509Certificate cert = ParseX509Certificate(certificate);
|
||||
return new DateTimeOffset(cert.NotAfter);
|
||||
}
|
||||
}
|
||||
|
||||
partial class SM2Utility
|
||||
{
|
||||
internal sealed class Cipher
|
||||
{
|
||||
private int ct = 1;
|
||||
|
||||
private ECPoint? p2;
|
||||
private SM3Digest? sm3keybase;
|
||||
private SM3Digest? sm3c3;
|
||||
|
||||
private byte[] key = new byte[32];
|
||||
private byte keyOff = 0;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
sm3keybase = new SM3Digest();
|
||||
sm3c3 = new SM3Digest();
|
||||
|
||||
|
||||
byte[] p;
|
||||
|
||||
|
||||
p = p2.Normalize().XCoord.ToBigInteger().ToByteArray32();
|
||||
|
||||
sm3keybase.BlockUpdate(p, 0, p.Length);
|
||||
sm3c3.BlockUpdate(p, 0, p.Length);
|
||||
|
||||
|
||||
p = p2.Normalize().YCoord.ToBigInteger().ToByteArray32();
|
||||
|
||||
sm3keybase.BlockUpdate(p, 0, p.Length);
|
||||
|
||||
|
||||
ct = 1;
|
||||
NextKey();
|
||||
}
|
||||
|
||||
private void NextKey()
|
||||
{
|
||||
|
||||
SM3Digest sm3keycur = new SM3Digest(sm3keybase);
|
||||
sm3keycur.Update((byte)(ct >> 24 & 0xff));
|
||||
sm3keycur.Update((byte)(ct >> 16 & 0xff));
|
||||
sm3keycur.Update((byte)(ct >> 8 & 0xff));
|
||||
sm3keycur.Update((byte)(ct & 0xff));
|
||||
sm3keycur.DoFinal(key, 0);
|
||||
var testKey = HexUtility.ConvertBytesToHex(key);
|
||||
|
||||
keyOff = 0;
|
||||
ct++;
|
||||
}
|
||||
|
||||
public ECPoint Init_enc(SM2Factory sm2, ECPoint userKey)
|
||||
{
|
||||
BigInteger k = null;
|
||||
ECPoint c1 = null;
|
||||
|
||||
|
||||
AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.GenerateKeyPair();
|
||||
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;
|
||||
ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;
|
||||
k = ecpriv.D;
|
||||
c1 = ecpub.Q;
|
||||
|
||||
|
||||
p2 = userKey.Multiply(k);
|
||||
Reset();
|
||||
|
||||
|
||||
return c1;
|
||||
}
|
||||
|
||||
public void Encrypt(byte[] data)
|
||||
{
|
||||
sm3c3.BlockUpdate(data, 0, data.Length);
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
if (keyOff == key.Length)
|
||||
NextKey();
|
||||
|
||||
|
||||
data[i] ^= key[keyOff++];
|
||||
}
|
||||
}
|
||||
|
||||
public void Init_dec(BigInteger userD, ECPoint c1)
|
||||
{
|
||||
p2 = c1.Multiply(userD);
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Decrypt(byte[] data)
|
||||
{
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
if (keyOff == key.Length)
|
||||
NextKey();
|
||||
|
||||
|
||||
data[i] ^= key[keyOff++];
|
||||
}
|
||||
sm3c3.BlockUpdate(data, 0, data.Length);
|
||||
}
|
||||
|
||||
public void Dofinal(byte[] c3)
|
||||
{
|
||||
byte[] p = p2.Normalize().YCoord.ToBigInteger().ToByteArray32();
|
||||
sm3c3.BlockUpdate(p, 0, p.Length);
|
||||
sm3c3.DoFinal(c3, 0);
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SM2Factory
|
||||
{
|
||||
public static readonly string[] SM2_PARAMS = {
|
||||
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", // p,0
|
||||
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", // a,1
|
||||
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", // b,2
|
||||
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", // n,3
|
||||
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", // gx,4
|
||||
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5
|
||||
};
|
||||
|
||||
public string[] ecc_param = SM2_PARAMS;
|
||||
|
||||
public readonly BigInteger ecc_p;
|
||||
public readonly BigInteger ecc_a;
|
||||
public readonly BigInteger ecc_b;
|
||||
public readonly BigInteger ecc_n;
|
||||
public readonly BigInteger ecc_gx;
|
||||
public readonly BigInteger ecc_gy;
|
||||
|
||||
public readonly ECCurve ecc_curve;
|
||||
public readonly ECPoint ecc_point_g;
|
||||
|
||||
public readonly ECDomainParameters ecc_bc_spec;
|
||||
|
||||
public readonly ECKeyPairGenerator ecc_key_pair_generator;
|
||||
|
||||
public SM2Factory()
|
||||
{
|
||||
ecc_param = SM2_PARAMS;
|
||||
|
||||
ECFieldElement ecc_gx_fieldelement;
|
||||
ECFieldElement ecc_gy_fieldelement;
|
||||
|
||||
ecc_p = new BigInteger(ecc_param[0], 16);
|
||||
ecc_a = new BigInteger(ecc_param[1], 16);
|
||||
ecc_b = new BigInteger(ecc_param[2], 16);
|
||||
ecc_n = new BigInteger(ecc_param[3], 16);
|
||||
ecc_gx = new BigInteger(ecc_param[4], 16);
|
||||
ecc_gy = new BigInteger(ecc_param[5], 16);
|
||||
|
||||
|
||||
ecc_gx_fieldelement = new FpFieldElement(ecc_p, ecc_gx);
|
||||
ecc_gy_fieldelement = new FpFieldElement(ecc_p, ecc_gy);
|
||||
|
||||
ecc_curve = new FpCurve(ecc_p, ecc_a, ecc_b);
|
||||
ecc_point_g = new FpPoint(ecc_curve, ecc_gx_fieldelement, ecc_gy_fieldelement);
|
||||
|
||||
ecc_bc_spec = new ECDomainParameters(ecc_curve, ecc_point_g, ecc_n);
|
||||
|
||||
ECKeyGenerationParameters ecc_ecgenparam;
|
||||
ecc_ecgenparam = new ECKeyGenerationParameters(ecc_bc_spec, new SecureRandom());
|
||||
|
||||
ecc_key_pair_generator = new ECKeyPairGenerator();
|
||||
ecc_key_pair_generator.Init(ecc_ecgenparam);
|
||||
}
|
||||
|
||||
public SM2Signature Sm2Sign(byte[] md, BigInteger userD, ECPoint userKey)
|
||||
{
|
||||
BigInteger e = new BigInteger(1, md);
|
||||
BigInteger k = null;
|
||||
ECPoint kp = null;
|
||||
BigInteger r = null;
|
||||
BigInteger s = null;
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
// 正式环境
|
||||
AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.GenerateKeyPair();
|
||||
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keypair.Private;
|
||||
ECPublicKeyParameters ecpub = (ECPublicKeyParameters)keypair.Public;
|
||||
k = ecpriv.D;
|
||||
kp = ecpub.Q;
|
||||
//System.out.println("BigInteger:" + k + "\nECPoint:" + kp);
|
||||
|
||||
//System.out.println("计算曲线点X1: "+ kp.getXCoord().toBigInteger().toString(16));
|
||||
//System.out.println("计算曲线点Y1: "+ kp.getYCoord().toBigInteger().toString(16));
|
||||
//System.out.println("");
|
||||
// r
|
||||
r = e.Add(kp.XCoord.ToBigInteger());
|
||||
r = r.Mod(this.ecc_n);
|
||||
} while (r.Equals(BigInteger.Zero) || r.Add(k).Equals(this.ecc_n) || r.ToString(16).Length != 64);
|
||||
|
||||
// (1 + dA)~-1
|
||||
BigInteger da_1 = userD.Add(BigInteger.One);
|
||||
da_1 = da_1.ModInverse(this.ecc_n);
|
||||
// s
|
||||
s = r.Multiply(userD);
|
||||
s = k.Subtract(s).Mod(this.ecc_n);
|
||||
s = da_1.Multiply(s).Mod(this.ecc_n);
|
||||
} while (s.Equals(BigInteger.Zero) || s.ToString(16).Length != 64);
|
||||
var sM2Signature = new SM2Signature
|
||||
{
|
||||
R = r.ToByteArray32(),
|
||||
S = s.ToByteArray32()
|
||||
};
|
||||
return sM2Signature;
|
||||
}
|
||||
|
||||
public SM2Result Sm2Verify(byte[] md, ECPoint userKey, BigInteger r, BigInteger s)
|
||||
{
|
||||
var sm2Result = new SM2Result();
|
||||
|
||||
BigInteger e = new BigInteger(1, md);
|
||||
BigInteger t = r.Add(s).Mod(this.ecc_n);
|
||||
if (t.Equals(BigInteger.Zero))
|
||||
{
|
||||
return sm2Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
ECPoint x1y1 = ecc_point_g.Multiply(s);
|
||||
//System.out.println("计算曲线点X0: "+ x1y1.normalize().getXCoord().toBigInteger().toString(16));
|
||||
//System.out.println("计算曲线点Y0: "+ x1y1.normalize().getYCoord().toBigInteger().toString(16));
|
||||
//System.out.println("");
|
||||
|
||||
x1y1 = x1y1.Add(userKey.Multiply(t));
|
||||
//System.out.println("计算曲线点X1: "+ x1y1.normalize().getXCoord().toBigInteger().toString(16));
|
||||
//System.out.println("计算曲线点Y1: "+ x1y1.normalize().getYCoord().toBigInteger().toString(16));
|
||||
//System.out.println("");
|
||||
sm2Result.R = e.Add(x1y1.Normalize().XCoord.ToBigInteger()).Mod(this.ecc_n);
|
||||
//System.out.println("R: " + sm2Result.R.toString(16));
|
||||
return sm2Result;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Sm2GetZ(byte[] userId, ECPoint userKey)
|
||||
{
|
||||
SM3Digest sm3 = new SM3Digest();
|
||||
byte[] p;
|
||||
// userId length
|
||||
int len = userId.Length * 8;
|
||||
sm3.Update((byte)(len >> 8 & 0x00ff));
|
||||
sm3.Update((byte)(len & 0x00ff));
|
||||
|
||||
// userId
|
||||
sm3.BlockUpdate(userId, 0, userId.Length);
|
||||
|
||||
// a,b
|
||||
p = ecc_a.ToByteArray32();
|
||||
sm3.BlockUpdate(p, 0, p.Length);
|
||||
p = ecc_b.ToByteArray32();
|
||||
sm3.BlockUpdate(p, 0, p.Length);
|
||||
// gx,gy
|
||||
p = ecc_gx.ToByteArray32();
|
||||
sm3.BlockUpdate(p, 0, p.Length);
|
||||
p = ecc_gy.ToByteArray32();
|
||||
sm3.BlockUpdate(p, 0, p.Length);
|
||||
|
||||
// x,y
|
||||
p = userKey.Normalize().XCoord.ToBigInteger().ToByteArray32();
|
||||
sm3.BlockUpdate(p, 0, p.Length);
|
||||
p = userKey.Normalize().YCoord.ToBigInteger().ToByteArray32();
|
||||
sm3.BlockUpdate(p, 0, p.Length);
|
||||
|
||||
// Z
|
||||
byte[] md = new byte[sm3.GetDigestSize()];
|
||||
sm3.DoFinal(md, 0);
|
||||
|
||||
return md;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SM2Signature
|
||||
{
|
||||
public byte[] R { get; set; }
|
||||
|
||||
public byte[] S { get; set; }
|
||||
|
||||
public SM2Signature()
|
||||
{
|
||||
R = Array.Empty<byte>();
|
||||
S = Array.Empty<byte>();
|
||||
}
|
||||
|
||||
public SM2Signature(byte[] signBytes)
|
||||
: this()
|
||||
{
|
||||
if (signBytes.Length == 65)
|
||||
{
|
||||
signBytes = signBytes.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
if (signBytes.Length == 64)
|
||||
{
|
||||
R = signBytes.Take(32).ToArray();
|
||||
S = signBytes.Skip(32).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid signature data.", nameof(signBytes));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
return R.Concat(S).ToArray();
|
||||
}
|
||||
|
||||
public byte[] ToByteArray04()
|
||||
{
|
||||
byte[] temp = new byte[65];
|
||||
temp[0] = 0x04;
|
||||
|
||||
Array.Copy(this.R, 0, temp, 1, 32);
|
||||
Array.Copy(this.S, 0, temp, 33, 32);
|
||||
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SM2Result
|
||||
{
|
||||
public BigInteger R;
|
||||
public BigInteger S;
|
||||
|
||||
public byte[] sa;
|
||||
public byte[] sb;
|
||||
public byte[] s1;
|
||||
public byte[] s2;
|
||||
|
||||
public ECPoint keyra;
|
||||
public ECPoint keyrb;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
/// <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>
|
||||
/// 基于 GCM 模式解密数据。
|
||||
/// </summary>
|
||||
/// <param name="keyBytes">SM4 密钥字节数组。</param>
|
||||
/// <param name="nonceBytes">加密使用的初始化向量字节数组。</param>
|
||||
/// <param name="aadBytes">加密使用的附加数据包字节数组。</param>
|
||||
/// <param name="cipherBytes">待解密数据字节数组。</param>
|
||||
/// <param name="paddingMode">填充模式。(默认值:<see cref="SM4_CIPHER_PADDING_NOPADDING"/>)</param>
|
||||
/// <returns>解密后的数据字节数组。</returns>
|
||||
public static byte[] DecryptWithGCM(byte[] keyBytes, byte[] nonceBytes, byte[]? aadBytes, byte[] cipherBytes, string paddingMode = SM4_CIPHER_PADDING_NOPADDING)
|
||||
{
|
||||
const int KEY_LENGTH_BYTE = 16;
|
||||
const int NONCE_LENGTH_BYTE = 12;
|
||||
const int TAG_LENGTH_BYTE = 16;
|
||||
|
||||
if (keyBytes == null) throw new ArgumentNullException(nameof(keyBytes));
|
||||
if (keyBytes.Length != KEY_LENGTH_BYTE) throw new ArgumentException($"Invalid key byte length (expected: {KEY_LENGTH_BYTE}, actual: {keyBytes.Length}).", nameof(keyBytes));
|
||||
if (nonceBytes == null) throw new ArgumentNullException(nameof(nonceBytes));
|
||||
if (nonceBytes.Length != NONCE_LENGTH_BYTE) throw new ArgumentException($"Invalid nonce byte length (expected: {NONCE_LENGTH_BYTE}, actual: {nonceBytes.Length}).", nameof(nonceBytes));
|
||||
if (cipherBytes == 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));
|
||||
ICipherParameters cipherParams = new AeadParameters(
|
||||
new KeyParameter(keyBytes),
|
||||
TAG_LENGTH_BYTE * 8,
|
||||
nonceBytes,
|
||||
aadBytes
|
||||
);
|
||||
cipher.Init(false, cipherParams);
|
||||
byte[] plainBytes = new byte[cipher.GetOutputSize(cipherBytes.Length)];
|
||||
int len = cipher.ProcessBytes(cipherBytes, 0, cipherBytes.Length, plainBytes, 0);
|
||||
cipher.DoFinal(plainBytes, len);
|
||||
return plainBytes;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
if (nonce == null) throw new ArgumentNullException(nameof(nonce));
|
||||
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
|
||||
|
||||
byte[] plainBytes = DecryptWithGCM(
|
||||
keyBytes: Encoding.UTF8.GetBytes(key),
|
||||
nonceBytes: Encoding.UTF8.GetBytes(nonce),
|
||||
aadBytes: Encoding.UTF8.GetBytes(aad ?? string.Empty),
|
||||
cipherBytes: Convert.FromBase64String(cipherText),
|
||||
paddingMode: paddingMode
|
||||
);
|
||||
return Encoding.UTF8.GetString(plainBytes);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using Xunit;
|
||||
using Xunit;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
{
|
||||
@ -8,11 +8,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
public void TestAESGCMDecrypt()
|
||||
{
|
||||
string key = "f09b03a7a1902b5b4913856f1fd07ab1";
|
||||
string iv = "aae8c2e79c5b";
|
||||
string nonce = "aae8c2e79c5b";
|
||||
string aad = "certificate";
|
||||
string cipherText = "x9kkL5w1JuaypcjhrYIP+kVNlN8o8uN4yJyJjy5lg+PyPnQL2Zn//ORaXAyzdaK/WBMVd3u/Y9hLaTBLMyRXzowsrkJ5PT37johye48N7BAJQ0PJwW++d1RdhOOPjoqfmws6rSV5Gv2qhfdKjmpxVjr8xr71dtBt8J2wu+bAV99HHQoAynm/Pp9OQYZgpOQ+1cyFHd43TAxOoFfmixKrXr3HP8lJot0XCUSq4qkr1Hs44FV2KuzntSk8eqKr5N17UcuPF3VYnnnF/AvQ7HuLKWwrHhUbaXfkwy0Q2n36UJMfBj7344S97E8BnS89ojgOPQi+olBPyNgrtDWHgsJAKu7HA6PV/FgmXcrZirje/AH1u25es4z5xItHscm//6rDvALgf7greV5OJzMsSl/KVDtbkjDSzim0j4ZTduIfzh7l6jfOz115ITcNILT9ef2KkcMyBBc89GZlMGeHTbgsBHzGeLawX3dXFjqt5aMnHM3VWCovA2aUM4c//rqkZGf+Va86OEFoJQLiSTFpkOrKxcIxcrbKPLTgiDWRT3wzmnUDg7kSPbluzt3ROvMFq9lB8bO/pBd7TD2w87sfUdKLj69FniX6s37SBeRVhw8GSIvBf5lpLUhqL5zKYlbuAWePuz0wVV4NTtkaVKZlmm8KTODyZDFpsyKPubDDcwT1ftRf5aSVM4x04I/1B7GkNz/TOy+zpJ0h0B7VHdxyO5JYiI/1qsatX/FE2aJdQYMYOtqfDH7ZH5UUKIqo538OKvE2M4MlBR/aVE4z4QDKfE/1kYrOfvVGfDzF/FWHfUrcqB8kdQMvk8vCoM8yYZsX4KE1aoJbNM2pWv2tpr9JE8b/VQgUyHOgPYAha+UOmZki4Sfl9H62687EIWdbM57ZxmwIiBp60SrJLiBfZon9JqXKdtJOKj0CRokQiBnONNXCVerLFeBNQfeKRw8tgJXf+QPohMGYkSDdc8hTgdbmhTwB1Vv01stlYK12QMNRCovlp/fcmpB72Phlq+/3p5pqMzknw+qm5QAz7JnpZJCFHit52gHwAkKRkVPB3HF2rfLrdTYz5c9Bok1ICAY9My47eLFdduIe99V52cjMLQuUmNFBPrDdyZKVqIHJ/wtWO/wIFpAVGSJMHctyEKmeJVc1IQN74Wm00PrpPackHdO3G41bBmkp5pqUdsSgSkwdfNVqv0cMcSe04NrRGNKMcZ7TA/CMaP1YnhxvVE+z8aksJqSJ+gdplvuwl40y5C8UEHeAi1V8Q0Bf4YvYRgOVIWm2Lzjdn2z9PWLGcStUj11/1hthk2li4V3mgm2Cr2IZme2sn4rZmJ6dexGP1nk+ZYOq0xLE8F7oex9gyDN+A/6zHqnuhO/X08qye0gochMr8U89Qvj2c0L3P2mjCea2H1mEriAJPqMPMKIinh1lQJEZufnfCcPxbZLKTtl6zHtHgOztejd1gV/nUyCVKD4MCMfBDy9C/Af8pWx6akOg/QSQNIGA2AI6zprHn9zEjpFIzXJYvruVI22Yt6oF9Xnt7Ki82wRK2M96r4kj6cwSs4exMPGv8fWMrFTm0Br6p6T+HZsxyyn2ChuPIgpfisnce/ZaU/0xCZhK/K79+TK2GeeChq5oEpua/1tx4+kDHi7H9381pLJmy2oXW060c2mmwA9+EpcuwEDhr8fsnghbv41u7b1NhEmWNVUy29Dwaz61PPGUdh5DsvaKLWC+raZ/6UEKPw+tiABJ5o6u2jAWgmEYmmJCKapNgtfPc6D+O0aHH9oqh6u4+8NRAhusPZzDGWBr6AT4pexgWFeEhZhn6bXM9HhpUe0IhOTw5D+tqXrTlNon4kjYibiMUFy1h2YyYS3IEdu1J4xqvo0rFyCxF1C+P6ubc0tClRPkXg==";
|
||||
|
||||
string actualPlain = Utilities.AESUtility.DecryptWithGCM(key: key, iv: iv, aad: aad, cipherText: cipherText);
|
||||
string actualPlain = Utilities.AESUtility.DecryptWithGCM(key: key, nonce: nonce, aad: aad, cipherText: cipherText);
|
||||
string expectedPlain = "-----BEGIN CERTIFICATE-----\nMIID3DCCAsSgAwIBAgIUGZNrTcamx3sFSJsQli3v9C6gZe8wDQYJKoZIhvcNAQEL\nBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT\nFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg\nQ0EwHhcNMjAwODAxMDczNTE4WhcNMjUwNzMxMDczNTE4WjBuMRgwFgYDVQQDDA9U\nZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl\nbnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo\nZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOAYHqxCqaRzoTIvgV\nixaYJJvmvHbiczbx5MQ9XL1ITSFxkTsNsk7RKHnO7eBS5imheJgQwd22Ky+XclSe\n7B4odssu/l/+gHo2gooTYrrCpQrOkpvGMf8R8aI56BQIF+vsomDvVq1NojHV2Fql\njMwFXzhj2EmU6p6gDv9iL7q1NrfnxFx8iJe4OhIB5Ek4qn1xXxrTUhiULd2vXlbI\nXhRetZSNcJsLt5Rw7D7c8F+aX2JchfeqsZECwKW7bSjMbVWWC6M9MgkB/aId8P0y\n7qEiiXFJkfkg1I/E1ud2apopsid5tdCyRRR6+MhhX2EC8S04MN4soUT7haqNNxX2\nrKHnAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBc\nMFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3Js\nP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJ\nKoZIhvcNAQELBQADggEBAJyg2z4oLQmPfftLQWyzbUc9ONhRMtfA+tVlVBgtLLKn\nWuDlsmEntheM07fu84F4pcfs3yHzjD7pAOFbO4Yt1yhQ50DK35sjbRWepPdWJZLl\nni7KBctcmm0o4zq37oB7vonmBEbFqYs9DaINYOjgI3J25iSBkPVC7dBbvFj2xB0L\ncIcXipq30tDdC/oUem27MNzwZAt49WthKhw6u3HSkcE5cO4LyYTsJhSyG/7LXwvV\nMgX4Jyzo0SSiGOU1/beaZssTVI8sTPJVlHWjhNE3Lc2SaAlKGfGwvt0X3cEZEF+7\noEZIFTkkAF2JhqfnpR3gST0G8Umq1SaVtCPP/zVI8x0=\n-----END CERTIFICATE-----";
|
||||
|
||||
Assert.Equal(expectedPlain, actualPlain);
|
||||
|
@ -0,0 +1,22 @@
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
{
|
||||
public class TestCase_SM4UtilityTests
|
||||
{
|
||||
[Fact(DisplayName = "测试用例:SM4-GCM 解密")]
|
||||
public void TestSM4GCMDecrypt()
|
||||
{
|
||||
string key = "f09b03a7a1902b5b";
|
||||
string nonce = "aae8c2e79c5b";
|
||||
string aad = "certificate";
|
||||
string cipherText = "fB6kij5HTmN4mXBIu6MaZIjDp8jRt2iziXIGF34yLSHo0Gkt6Y6CgjdWZvCxht2UfC52VbHfeNMS4kOBRtpZ/LO9pIoA/V7Qs/V5RD3iqxYBgbCIdgGNmjJN9mcT7VZbBYLnKAp8PQsbVbmHxmmXTpY/xj+geUuF3ELIhTjLcjOB4UW0/FNUjtM9taYriGwVUKo9cJ+652QkFs3QBerxMzqPEObG3NS2h99WrtV1LmayBN2m9ncJJ8Kjk2oSdn/P+ua2CpqM4G6f8AFeitfdRdS94js3A/6GEZGY8WFHMrslvfc8YMj72MxPJ1qv3zF15BBzEKrRcoZWdywRyXDGPpTIXrlGFOiGzxS07FdekYj5V6qW7pD0wuqVug3fyU5wt+Jx4Yvk8dG2voywkqxFzRQfbTL/Wv0+54a+AFK2HFZjY9oXXgiVHpqPK0fv+meTSMoVyQTbVXwfZvNGm5sROJlfM5tiV6UKRbBUx+/6+39H2unwnItIFIMANKJPiS9x8B3Vkxuc8ZdpZe8owfrL6otEPzWSXsDK0zKYvvK+Mxmb40cZZKiaLzXmsEiVTQ9JJX9ycE6xFiCQTjd91zy5Os7xaY6vLPLegdWCbbOq4bpIpX0vt0RPHLWvG3vyzX6tPiy77z9AxOohd4hqLpiH1nA2Ua3+AjcYRYjU7NA1PnbFThBulH7O1TMFtShpLdlZAGl4op926iZVRYZjGAdfbkLCrHmoggsB15DjFWbNwEuuZzpuX5tcIZLXqxNdOrECrwXAYM6K1HtBC/wDpqlNfFb7LEaKRhv++KHE3K6UXekLpPR5h22ttIMbMAjZrBdjXVA9fagPtB1gmY0fH4j0b8Wd6j6Fj8MjjhirvTmRAZHV0Zq1dqw8QDFELOVE1bczZJ2vUDgGoL5cCNgAsjOCejJCsSe2MWiWQgvM8zmwv2eSKHSsVuQ6E4urovXmVdoEdDdar10juldAVVQ5FfSbtZrNpVmK2EHx2s7rBKySR+KWpf1DXNQWa9YmSzxQH4RrVaUB5OSwqhDyxOH4i9HeNzHsK56y/6tumToKA/QCUp2CqZp++OrLNkgy7n3bx1NN3Qm8uIIWQVT5HKJoI0CmvzuSrHAxQQVTkcZ5dMsUF/RQy0lN8ztuF0yTEqRWl4efUoX8RViGJkTyGkQngjZX4XnH7S4mAuvHFBTGFzFmSGUfxeWO9lIXhPUN1yslWxQBxCmGIp7YGmB9k+8WF8z1OgRaYbCbql7DveTcegNyoZXzfpjF3YJ4AQXuvSlKIwK7qsLEKBwmawxwt2yT0SjWuO41AgpeLH9s2JRLPZ+ZY+1RWPG8a4lfl+fMchIQH8qmULGgaqlzbq7VZmRnmeyYOz75v1mvSqNFijCrnlzB4QZcSNI05ylTtvRE6C/WezeY3dc9uVdBRWp1i8jzm6RhFid40C9SCtxrvbv/O3RjfiwsibZp4JWNbIPBmt08aUn0nMjpoaXcQXPTIDLtiYCziECD0Mlf5mz2DaK0ecltI5CfsFT/Yi0zmrbmOARiOP3zkYiCP8bVcWu2sOyC1HB5Dlf3ak8ver5lOJIq43dIG0++LwwxGzgIiQx5MPdWCs1dKD6cREKNdtyjcW689N6Q0hwC7Lj4od2W6oYe2cOOfp6IriKlYUP//a1lAnBI6xaHcy50YoJud77Et3nCUWWyZLS75fMaqNqG+cioe1arnk/n9vIpibZqOyKbPJvNcbTAimshZZY8BJgeRTbm/Jjaka79WFXKxq9gwnl+jUjuxpEhMlkoRAezH4EGK/hdGjnfSk5L54ppxxEGMLBlpjbEaUA+9mSj5OcSqz1iSHUvI8v9RHVyXFixRXCvv9EHsJew";
|
||||
|
||||
string actualPlain = Utilities.SM4Utility.DecryptWithGCM(key: key, nonce: nonce, aad: aad, cipherText: cipherText);
|
||||
string expectedPlain = "-----BEGIN CERTIFICATE-----MIID3DCCAsSgAwIBAgIUGZNrTcamx3sFSJsQli3v9C6gZe8wDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsTFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3QgQ0EwHhcNMjAwODAxMDczNTE4WhcNMjUwNzMxMDczNTE4WjBuMRgwFgYDVQQDDA9UZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRlbnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpoZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOAYHqxCqaRzoTIvgVixaYJJvmvHbiczbx5MQ9XL1ITSFxkTsNsk7RKHnO7eBS5imheJgQwd22Ky+XclSe7B4odssu/l/+gHo2gooTYrrCpQrOkpvGMf8R8aI56BQIF+vsomDvVq1NojHV2FqljMwFXzhj2EmU6p6gDv9iL7q1NrfnxFx8iJe4OhIB5Ek4qn1xXxrTUhiULd2vXlbIXhRetZSNcJsLt5Rw7D7c8F+aX2JchfeqsZECwKW7bSjMbVWWC6M9MgkB/aId8P0y7qEiiXFJkfkg1I/E1ud2apopsid5tdCyRRR6+MhhX2EC8S04MN4soUT7haqNNxX2rKHnAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBcMFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJKoZIhvcNAQELBQADggEBAJyg2z4oLQmPfftLQWyzbUc9ONhRMtfA+tVlVBgtLLKnWuDlsmEntheM07fu84F4pcfs3yHzjD7pAOFbO4Yt1yhQ50DK35sjbRWepPdWJZLlni7KBctcmm0o4zq37oB7vonmBEbFqYs9DaINYOjgI3J25iSBkPVC7dBbvFj2xB0LcIcXipq30tDdC/oUem27MNzwZAt49WthKhw6u3HSkcE5cO4LyYTsJhSyG/7LXwvVMgX4Jyzo0SSiGOU1/beaZssTVI8sTPJVlHWjhNE3Lc2SaAlKGfGwvt0X3cEZEF+7oEZIFTkkAF2JhqfnpR3gST0G8Umq1SaVtCPP/zVI8x0=-----END CERTIFICATE-----";
|
||||
|
||||
Assert.Equal(expectedPlain, actualPlain);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user