feat(tenpayv3): 新增 SM2 算法工具类

This commit is contained in:
Fu Diwei 2022-11-11 22:45:03 +08:00
parent 8eee6803f2
commit 74a4c72f78
5 changed files with 913 additions and 511 deletions

View File

@ -16,7 +16,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
private const string RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1 = "OAEPWITHSHA1ANDMGF1PADDING";
private const string RSA_SIGNER_ALGORITHM_SHA256 = "SHA-256withRSA";
private static byte[] ConvertPkcs8PrivateKeyToByteArray(string privateKey)
private static byte[] ConvertPrivateKeyPkcs8PemToByteArray(string privateKey)
{
privateKey = privateKey
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
@ -25,7 +25,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
return Convert.FromBase64String(privateKey);
}
private static byte[] ConvertPkcs8PublicKeyToByteArray(string publicKey)
private static byte[] ConvertPublicKeyPkcs8PemToByteArray(string publicKey)
{
publicKey = publicKey
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
@ -75,8 +75,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return SignWithSHA256(rsaKeyParams, msgBytes);
RsaKeyParameters rsaPrivateKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return SignWithSHA256(rsaPrivateKeyParams, msgBytes);
}
/// <summary>
@ -90,7 +90,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (message == null) throw new ArgumentNullException(nameof(message));
byte[] privateKeyBytes = ConvertPkcs8PrivateKeyToByteArray(privateKey);
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] signBytes = SignWithSHA256(privateKeyBytes, msgBytes);
return Convert.ToBase64String(signBytes);
@ -109,8 +109,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
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, msgBytes, signBytes);
RsaKeyParameters rsaPublicKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
return VerifyWithSHA256(rsaPublicKeyParams, msgBytes, signBytes);
}
/// <summary>
@ -126,7 +126,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (message == null) throw new ArgumentNullException(nameof(message));
if (signature == null) throw new ArgumentNullException(nameof(signature));
byte[] publicKeyBytes = ConvertPkcs8PublicKeyToByteArray(publicKey);
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] signBytes = Convert.FromBase64String(signature);
return VerifyWithSHA256(publicKeyBytes, msgBytes, signBytes);
@ -144,8 +144,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (cipherBytes == null) throw new ArgumentNullException(nameof(cipherBytes));
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return DecryptWithECB(rsaKeyParams, cipherBytes, paddingAlgorithm);
RsaKeyParameters rsaPrivateKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return DecryptWithECB(rsaPrivateKeyParams, cipherBytes, paddingAlgorithm);
}
/// <summary>
@ -160,7 +160,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
byte[] privateKeyBytes = ConvertPkcs8PrivateKeyToByteArray(privateKey);
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
byte[] cipherBytes = Convert.FromBase64String(cipherText);
byte[] plainBytes = DecryptWithECB(privateKeyBytes, cipherBytes, paddingAlgorithm);
return Encoding.UTF8.GetString(plainBytes);
@ -178,8 +178,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (publicKeyBytes == null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
return EncryptWithECB(rsaKeyParams, plainBytes, paddingAlgorithm);
RsaKeyParameters rsaPublicKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
return EncryptWithECB(rsaPublicKeyParams, plainBytes, paddingAlgorithm);
}
/// <summary>
@ -194,7 +194,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
byte[] publicKeyBytes = ConvertPkcs8PublicKeyToByteArray(publicKey);
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
byte[] cipherBytes = EncryptWithECB(publicKeyBytes, plainBytes, paddingAlgorithm);
return Convert.ToBase64String(cipherBytes);

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Org.BouncyCastle.Crypto;
@ -15,13 +16,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
/// </summary>
public static class RSAUtility
{
// REF: https://github.com/bcgit/bc-csharp/blob/master/crypto/src/security/CipherUtilities.cs
private const string RSA_CIPHER_ALGORITHM_ECB = "RSA/ECB";
private const string RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1 = "OAEPWITHSHA1ANDMGF1PADDING";
// REF: https://github.com/bcgit/bc-csharp/blob/master/crypto/src/security/SignerUtilities.cs
private const string RSA_SIGNER_ALGORITHM_SHA256 = "SHA-256withRSA";
private static byte[] ConvertPkcs8PrivateKeyToByteArray(string privateKey)
private static byte[] ConvertPrivateKeyPkcs8PemToByteArray(string privateKey)
{
privateKey = privateKey
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
@ -30,7 +29,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
return Convert.FromBase64String(privateKey);
}
private static byte[] ConvertPkcs8PublicKeyToByteArray(string publicKey)
private static byte[] ConvertPublicKeyPkcs8PemToByteArray(string publicKey)
{
publicKey = publicKey
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
@ -39,7 +38,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
return Convert.FromBase64String(publicKey);
}
private static X509Certificate ParseX509Certificate(string certificate)
private static X509Certificate ParseCertificatePemToX509(string certificate)
{
using (TextReader sreader = new StringReader(certificate))
{
@ -48,10 +47,14 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
}
}
private static RsaKeyParameters ConvertCertificateToPublicKeyParams(string certificate)
private static RsaKeyParameters ParsePrivateKeyPemToPublicKeyParameters(byte[] privateKeyBytes)
{
X509Certificate cert = ParseX509Certificate(certificate);
return (RsaKeyParameters)cert.GetPublicKey();
return (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
}
private static RsaKeyParameters ParsePublicKeyPemToPublicKeyParameters(byte[] publicKeyBytes)
{
return (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
}
private static byte[] SignWithSHA256(RsaKeyParameters rsaPrivateKeyParams, byte[] msgBytes)
@ -95,8 +98,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return SignWithSHA256(rsaKeyParams, msgBytes);
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyPemToPublicKeyParameters(privateKeyBytes);
return SignWithSHA256(rsaPrivateKeyParams, msgBytes);
}
/// <summary>
@ -110,7 +113,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (message == null) throw new ArgumentNullException(nameof(message));
byte[] privateKeyBytes = ConvertPkcs8PrivateKeyToByteArray(privateKey);
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] signBytes = SignWithSHA256(privateKeyBytes, msgBytes);
return Convert.ToBase64String(signBytes);
@ -129,8 +132,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
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, msgBytes, signBytes);
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
return VerifyWithSHA256(rsaPublicKeyParams, msgBytes, signBytes);
}
/// <summary>
@ -146,7 +149,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
if (message == null) throw new ArgumentNullException(nameof(message));
if (signature == null) throw new ArgumentNullException(nameof(signature));
byte[] publicKeyBytes = ConvertPkcs8PublicKeyToByteArray(publicKey);
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] signBytes = Convert.FromBase64String(signature);
return VerifyWithSHA256(publicKeyBytes, msgBytes, signBytes);
@ -162,13 +165,43 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
public static bool VerifyWithSHA256ByCertificate(string certificate, string message, string signature)
{
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
if (message == null) throw new ArgumentNullException(nameof(message));
if (signature == null) throw new ArgumentNullException(nameof(signature));
RsaKeyParameters rsaKeyParams = ConvertCertificateToPublicKeyParams(certificate);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] signBytes = Convert.FromBase64String(signature);
return VerifyWithSHA256(rsaKeyParams, msgBytes, signBytes);
string publicKey = ExportPublicKeyFromCertificate(certificate);
return VerifyWithSHA256(publicKey, message, signature);
}
/// <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>
/// <returns>解密后的数据字节数组。</returns>
public static byte[] DecryptWithECB(byte[] privateKeyBytes, byte[] cipherBytes, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (cipherBytes == null) throw new ArgumentNullException(nameof(cipherBytes));
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyPemToPublicKeyParameters(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)
{
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
byte[] cipherBytes = Convert.FromBase64String(cipherText);
byte[] plainBytes = DecryptWithECB(privateKeyBytes, cipherBytes, paddingMode);
return Encoding.UTF8.GetString(plainBytes);
}
/// <summary>
@ -183,8 +216,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
if (publicKeyBytes == null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
return EncryptWithECB(rsaKeyParams, plainBytes, paddingMode);
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyPemToPublicKeyParameters(publicKeyBytes);
return EncryptWithECB(rsaPublicKeyParams, plainBytes, paddingMode);
}
/// <summary>
@ -199,7 +232,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
byte[] publicKeyBytes = ConvertPkcs8PublicKeyToByteArray(publicKey);
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
byte[] cipherBytes = EncryptWithECB(publicKeyBytes, plainBytes, paddingMode);
return Convert.ToBase64String(cipherBytes);
@ -215,46 +248,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
public static string EncryptWithECBByCertificate(string certificate, string plainText, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
RsaKeyParameters rsaKeyParams = ConvertCertificateToPublicKeyParams(certificate);
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
byte[] cipherBytes = EncryptWithECB(rsaKeyParams, plainBytes, paddingMode);
return Convert.ToBase64String(cipherBytes);
}
/// <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>
/// <returns>解密后的数据字节数组。</returns>
public static byte[] DecryptWithECB(byte[] privateKeyBytes, byte[] cipherBytes, string paddingMode = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (cipherBytes == null) throw new ArgumentNullException(nameof(cipherBytes));
RsaKeyParameters rsaKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return DecryptWithECB(rsaKeyParams, 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)
{
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
byte[] privateKeyBytes = ConvertPkcs8PrivateKeyToByteArray(privateKey);
byte[] cipherBytes = Convert.FromBase64String(cipherText);
byte[] plainBytes = DecryptWithECB(privateKeyBytes, cipherBytes, paddingMode);
return Encoding.UTF8.GetString(plainBytes);
string publicKey = ExportPublicKeyFromCertificate(certificate);
return EncryptWithECB(publicKey, plainText, paddingMode);
}
/// <summary>
@ -272,9 +268,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
using (TextWriter swriter = new StringWriter())
{
RsaKeyParameters rsaKeyParams = ConvertCertificateToPublicKeyParams(certificate);
X509Certificate x509cert = ParseCertificatePemToX509(certificate);
RsaKeyParameters rsaPublicKeyParams = (RsaKeyParameters)x509cert.GetPublicKey();
PemWriter pemWriter = new PemWriter(swriter);
pemWriter.WriteObject(rsaKeyParams);
pemWriter.WriteObject(rsaPublicKeyParams);
pemWriter.Writer.Flush();
return swriter.ToString()!;
}
@ -289,8 +286,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
X509Certificate cert = ParseX509Certificate(certificate);
return cert.SerialNumber.ToString(16);
X509Certificate x509cert = ParseCertificatePemToX509(certificate);
return x509cert.SerialNumber.ToString(16);
}
/// <summary>
@ -302,8 +299,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
X509Certificate cert = ParseX509Certificate(certificate);
return new DateTimeOffset(cert.NotBefore);
X509Certificate x509cert = ParseCertificatePemToX509(certificate);
return new DateTimeOffset(x509cert.NotBefore);
}
/// <summary>
@ -315,8 +312,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
X509Certificate cert = ParseX509Certificate(certificate);
return new DateTimeOffset(cert.NotAfter);
X509Certificate x509cert = ParseCertificatePemToX509(certificate);
return new DateTimeOffset(x509cert.NotAfter);
}
}
}

View File

@ -57,6 +57,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
string expectedSign = "EzeVEIoBhzOhXpwbXdJjIuGIGRc6ArKO7sVo2fuAdzYTDgorAEufEnw7lPPXV1GTfFcHOnsAJH9kGJmg7Orwlkh7x7TyOGrkMEhWGulA9SIdmou4iBsHpIZ/TERSgGt/VTmqmfpkzJqrvbQWIQENwo7Lr6uJSJBND0YT3nIBP8TzbO3cHnQb6chHIBHrDF5vOO7HHu+Cga2MZnAtRizhO8BhK0jOmyro32CgIML3EVX8yuPy0kOk6aN1R8xFblZUD4NU2M6zzQpydmxaHr9B1WNFoMwmpoAS5BuKJMYOHO5cc6DhB+0fAGxaWtKp6759KhKCf8M65zh3WKS4l262SGuWq4qG1+AKf2DOgCon769+A4z8flOmvl0iIwoH9FThGJoP156rpAJW7v/bWputSeC6WToUTBRmGWdwWySVwW5AZ26OAFFWs1CmrGp3jF5E2oUy1mQwgfM0QN6DW+wD769ggIYH9HLHqDHbF5UyF7eNh3s8Qy23xXEKZWNMAJ0IdtdMQ7FRRgLFSCai7HELLlBJSCz7P5WTeYZGQpbvnUShkRvzujjO6XlGiKKI0EwKb121z8N6KRpvs4SnRztWBGoXbzHZgnXKXU/BWWADemqB2cvaT3Bj0k3/N3sea0dAEtlNEklRWoyyNUUlscK9zg4LBlHrhbbFo66uuub8ySo=";
Assert.Equal(expectedSign, actualSign);
Assert.True(Utilities.RSAUtility.VerifyWithSHA256(RSA_PEM_PUBLIC_KEY, msgText, actualSign));
}
[Fact(DisplayName = "测试用例SHA256WithRSA 签名验证")]
@ -66,7 +67,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
string signText = "aHX+MrmZHDEraMKBEPV2Vnps1B9b25lGbv/rdppx/S7+oaXtjKJprzCq5H7RCpvrKS3xYIeTEPwQGC3Vots7dCdLi8v8ew1vvtXf8qNAnd7CTMHqu3wSohXzgyASTmNbXE2ml9LbWYPPYMvPJXROQbGVjoOrsErWBPPJYXuO3lIckIfwI05OTdl4H3+BvpD/ZoljRp8Qgo9+paGvarBc++TaAh0FXnQf0TGNFUIeHHiAKBee5oCBTuZZM9J5RPw0oIq/g7Wun+e/zWiwVBPHltOgZrV46uagSAE6nBDHk+hlNxDivCxkJdBVCSIYFFmBXIcnGZ/u4ZfBui/k1jGoKibyvPK4z2+6GSlj41Yo81kuSBfzLiSsx33EPR1eIJJkwDTsvap0ymL9pfIqMiLuiteH5kGmL/dyONy9oAJywLEeITfoVyElM/CY6Dc+xDhRnjN7Hu54meYyXRZrnCtQ3YhzEr1immNBn6npgA/qi9aHsuWFOw8b8aSwOHDHTDmjmvV+axI8CVMrR0MjB9QNCWrKLq2B9iQX9MtLgcUyDsQvzAsxUJm/OEfzUjs9SHvmgmyAvzNAuTdO7wLQ+ZmKg0yZne6nvcrJVvfh3lD5ZPt7NY57Y6OIJluqKUT5H+a3H6W9Q1Z+cBMnHGYaaK7Tv8IcDdEYqTIG8hc5BqjFOzE=";
Assert.True(Utilities.RSAUtility.VerifyWithSHA256(RSA_PEM_PUBLIC_KEY, msgText, signText));
Assert.False(Utilities.RSAUtility.VerifyWithSHA256(RSA_PEM_PUBLIC_KEY, msgText, "FAKE SIGN"));
Assert.True(Utilities.RSAUtility.VerifyWithSHA256ByCertificate(RSA_PEM_CERTIFICATE, msgText, signText));
Assert.False(Utilities.RSAUtility.VerifyWithSHA256ByCertificate(RSA_PEM_CERTIFICATE, msgText, "FAKE SIGN"));
}
[Fact(DisplayName = "测试用例:使用 RSA 公钥加密")]
@ -78,7 +81,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
Assert.NotNull(actualCipherByPublicKey);
Assert.NotNull(actualCipherByCertificate);
Assert.NotEqual(actualCipherByPublicKey, actualCipherByCertificate);
Assert.Equal(plainText, Utilities.RSAUtility.DecryptWithECB(RSA_PEM_PRIVATE_KEY, actualCipherByPublicKey));
Assert.Equal(plainText, Utilities.RSAUtility.DecryptWithECB(RSA_PEM_PRIVATE_KEY, actualCipherByCertificate));
}
[Fact(DisplayName = "测试用例:使用 RSA 私钥解密")]

View File

@ -0,0 +1,128 @@
using System;
using System.Text;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
{
public class TestCase_SM2UtilityTests
{
// 此处测试的 SM2 证书/公钥/私钥是自签名生成的,仅供执行 SM2 相关的单元测试,不能用于调用微信支付 API。
private const string SM2_CERT_SN = "e5a81b02429d8d08";
private const string SM2_CERT_START_DATE = "2022-11-09 21:12:20";
private const string SM2_CERT_END_DATE = "2023-11-09 21:12:20";
private const string SM2_PEM_CERTIFICATE = "-----BEGIN CERTIFICATE-----\nMIICNzCCAdygAwIBAgIJAOWoGwJCnY0IMAoGCCqBHM9VAYN1MGcxCzAJBgNVBAYT\nAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdIYWlEaWFuMRMwEQYDVQQK\nDApHTUNlcnQub3JnMR8wHQYDVQQDDBZHTUNlcnQgR00gUm9vdCBDQSAtIDAxMB4X\nDTIyMTEwOTEzMTIyMFoXDTIzMTEwOTEzMTIyMFowSzEtMCsGA1UEAwwkU0tJVC5G\nbHVybEh0dHBDbGllbnQuV2VjaGF0LlRlbnBheVYzMQ0wCwYDVQQKDARTS0lUMQsw\nCQYDVQQGEwJDTjBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABMXP1hZc2zBzreRN\nZgOR9hklE01tw10RDUfj176EXcVoVOvITMENJ3HREQtDPlOfz8i1SXCQEwclYyxI\n2KcTdKqjgYwwgYkwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCA/gwLAYJYIZIAYb4\nQgENBB8WHUdNQ2VydC5vcmcgU2lnbmVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRj\nIhoxmSgP84XT/scjkQNSWylMFTAfBgNVHSMEGDAWgBR/Wl47AIRZKg+YvqEObzmV\nQxBNBzAKBggqgRzPVQGDdQNJADBGAiEAnXykM0qDOWay2EMB6+c6YJ7h4n7Wbju7\nXuT5RkuM/3ICIQDAA3sLba/dQMhmKkCoJl31iZwYKz7NP+0aq6NhWDommQ==\n-----END CERTIFICATE-----";
private const string SM2_PEM_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAExc/WFlzbMHOt5E1mA5H2GSUTTW3D\nXRENR+PXvoRdxWhU68hMwQ0ncdERC0M+U5/PyLVJcJATByVjLEjYpxN0qg==\n-----END PUBLIC KEY-----";
private const string SM2_PEM_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg3WePog9R4UV/EVlk\nCw8YHu+rXC/imiB89jFmaAPeXz6gCgYIKoEcz1UBgi2hRANCAATFz9YWXNswc63k\nTWYDkfYZJRNNbcNdEQ1H49e+hF3FaFTryEzBDSdx0RELQz5Tn8/ItUlwkBMHJWMs\nSNinE3Sq\n-----END PRIVATE KEY-----";
private const string SM2_HEX_EC_PRIVATE_KEY = "dd678fa20f51e1457f1159640b0f181eefab5c2fe29a207cf631666803de5f3e";
private const string SM2_HEX_EC_PUBLIC_KEY = "04c5cfd6165cdb3073ade44d660391f61925134d6dc35d110d47e3d7be845dc56854ebc84cc10d2771d1110b433e539fcfc8b5497090130725632c48d8a71374aa";
[Fact(DisplayName = "测试用例:从 SM2 证书中导出公钥")]
public void TestSM2ExportPublicKeyFromCertificate()
{
string actualPublicKey = Utilities.SM2Utility.ExportPublicKeyFromCertificate(SM2_PEM_CERTIFICATE).Replace("\r", "").Replace("\n", "");
string expectedPublicKey = SM2_PEM_PUBLIC_KEY.Replace("\r", "").Replace("\n", "");
Assert.Equal(expectedPublicKey, actualPublicKey, ignoreLineEndingDifferences: true);
string actualECHexPublicKey = Utilities.SM2Utility.ExportECPublicKeyFromCertificate(SM2_PEM_CERTIFICATE);
string expectedECHexPublicKey = SM2_HEX_EC_PUBLIC_KEY;
Assert.Equal(expectedECHexPublicKey, actualECHexPublicKey, ignoreCase: true);
}
[Fact(DisplayName = "测试用例:从 SM2 证书中导出证书序列号")]
public void TestSM2ExportSerialNumberFromCertificate()
{
string actualSerialNumber = Utilities.SM2Utility.ExportSerialNumberFromCertificate(SM2_PEM_CERTIFICATE);
string expectedSerialNumber = SM2_CERT_SN;
Assert.Equal(expectedSerialNumber, actualSerialNumber, ignoreCase: true);
}
[Fact(DisplayName = "测试用例:从 SM2 证书中导出证书颁发时间")]
public void TestSM2ExportEffectiveTimeFromCertificate()
{
DateTimeOffset actualEffectiveTime = Utilities.SM2Utility.ExportEffectiveTimeFromCertificate(SM2_PEM_CERTIFICATE);
DateTimeOffset expectedEffectiveTime = DateTimeOffset.Parse(SM2_CERT_START_DATE);
Assert.Equal(expectedEffectiveTime, actualEffectiveTime);
}
[Fact(DisplayName = "测试用例:从 SM2 证书中导出证书过期时间")]
public void TestSM2ExportExpireTimeFromCertificate()
{
DateTimeOffset actualExpireTime = Utilities.SM2Utility.ExportExpireTimeFromCertificate(SM2_PEM_CERTIFICATE);
DateTimeOffset expectedExpireTime = DateTimeOffset.Parse(SM2_CERT_END_DATE);
Assert.Equal(expectedExpireTime, actualExpireTime);
}
[Fact(DisplayName = "测试用例:从 SM2 公钥中导出 EC 公钥")]
public void TestSM2ExportECPublicKeyFromPublicKey()
{
string actualECHexPublicKey = Utilities.SM2Utility.ExportECPublicKeyFromPublicKey(SM2_PEM_PUBLIC_KEY);
string expectedSM2HexPublicKey = SM2_HEX_EC_PUBLIC_KEY;
Assert.Equal(expectedSM2HexPublicKey, actualECHexPublicKey, ignoreCase: true);
}
[Fact(DisplayName = "测试用例:从 SM2 私钥中导出 EC 私钥")]
public void TestSM2ExportECPrivateKeyFromPrivateKey()
{
string actualSM2HexPrivateKey = Utilities.SM2Utility.ExportECPrivateKeyFromPrivateKey(SM2_PEM_PRIVATE_KEY);
string expectedSM2HexPrivateKey = SM2_HEX_EC_PRIVATE_KEY;
Assert.Equal(expectedSM2HexPrivateKey, actualSM2HexPrivateKey, ignoreCase: true);
}
[Fact(DisplayName = "测试用例SM2WithSM3 签名生成")]
public void TestSM2SignatureSM2WithSM3Sign()
{
string msgText = "SM2WithSM3SignTest";
string actualSignByPrivateKey = Utilities.SM2Utility.SignWithSM3(SM2_PEM_PRIVATE_KEY, msgText);
string actualSignByECPrivateKey = Convert.ToBase64String(Utilities.SM2Utility.SignWithSM3ByECPrivateKey(SM2_HEX_EC_PRIVATE_KEY, Encoding.UTF8.GetBytes(msgText)));
Assert.NotNull(actualSignByPrivateKey);
Assert.NotNull(actualSignByECPrivateKey);
Assert.True(Utilities.SM2Utility.VerifyWithSM3(SM2_PEM_PUBLIC_KEY, msgText, actualSignByPrivateKey));
}
[Fact(DisplayName = "测试用例SM2WithSM3 签名验证")]
public void TestSM2SignatureSM2WithSM3Verify()
{
string msgText = "SM2WithSM3SignTest";
string signText = "MEUCIQCDzgpF2Z//sbFzASVQnwme2phm4ho5cr8/1Pz0+MONTwIgeQvhoWOTk1rngYRSlHeqqwtNFVD/vf3qtgl9mecvERI=";
Assert.True(Utilities.SM2Utility.VerifyWithSM3(SM2_PEM_PUBLIC_KEY, msgText, signText));
Assert.False(Utilities.SM2Utility.VerifyWithSM3(SM2_PEM_PUBLIC_KEY, msgText, "FAKE SIGN"));
Assert.True(Utilities.SM2Utility.VerifyWithSM3ByCertificate(SM2_PEM_CERTIFICATE, msgText, signText));
Assert.False(Utilities.SM2Utility.VerifyWithSM3ByCertificate(SM2_PEM_CERTIFICATE, msgText, "FAKE SIGN"));
Assert.True(Utilities.SM2Utility.VerifyWithSM3ByECPublicKey(SM2_HEX_EC_PUBLIC_KEY, Encoding.UTF8.GetBytes(msgText), Convert.FromBase64String("MEUCIQCDzgpF2Z//sbFzASVQnwme2phm4ho5cr8/1Pz0+MONTwIgeQvhoWOTk1rngYRSlHeqqwtNFVD/vf3qtgl9mecvERI=")));
Assert.False(Utilities.SM2Utility.VerifyWithSM3ByECPublicKey(SM2_HEX_EC_PUBLIC_KEY, Encoding.UTF8.GetBytes(msgText), Encoding.UTF8.GetBytes("FAKE SIGN")));
}
[Fact(DisplayName = "测试用例:使用 SM2 公钥加密")]
public void TestSM2Encrypt()
{
string plainText = "SM2EncryptTest";
string actualCipherByPublicKey = Utilities.SM2Utility.Encrypt(SM2_PEM_PUBLIC_KEY, plainText);
string actualCipherByCertificate = Utilities.SM2Utility.EncryptByCertificate(SM2_PEM_CERTIFICATE, plainText);
string actualCipherByECPublicKey = Convert.ToBase64String(Utilities.SM2Utility.EncryptByECPublicKey(SM2_HEX_EC_PUBLIC_KEY, Encoding.UTF8.GetBytes(plainText)));
Assert.NotNull(actualCipherByPublicKey);
Assert.NotNull(actualCipherByCertificate);
Assert.NotNull(actualCipherByECPublicKey);
Assert.Equal(plainText, Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, actualCipherByPublicKey));
Assert.Equal(plainText, Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, actualCipherByCertificate));
Assert.Equal(plainText, Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, actualCipherByECPublicKey));
}
[Fact(DisplayName = "测试用例:使用 SM2 私钥解密")]
public void TestSM2Decrypt()
{
string cipherText = "MHYCIGJ7gjFjd6U7kOj63HLbRgPAn6cVf4eDF4emz9oCX5gKAiBAHmgAvH2WU/2+dyqMK7/Q8eD/Q9LhYFV2gqc+fv7EiAQgiHX2wr7GCnBbAsfR3stJ1i/Csc0Mq3RzVd+ZefVlr7gEDvfJIMlMcs4Q2HoMd8Jk";
string actualPlainByPrivateKey = Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, cipherText);
string actualPlainByECPrivateKey = Encoding.UTF8.GetString(Utilities.SM2Utility.DecryptByECPrivateKey(SM2_HEX_EC_PRIVATE_KEY, Convert.FromBase64String(cipherText)));
string expectedPlain = "SM2DecryptTest";
Assert.Equal(expectedPlain, actualPlainByPrivateKey);
Assert.Equal(expectedPlain, actualPlainByECPrivateKey);
}
}
}