Skip to content

Commit

Permalink
refactor: 在 .NET 5.0+ 目标框架下将 RSA 算法工具类的部分实现使用标准库原生方式重写
Browse files Browse the repository at this point in the history
  • Loading branch information
fudiwei committed May 21, 2024
1 parent 2df23a8 commit 680ddf2
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class AESUtility
/// 基于 CBC 模式解密数据。
/// </summary>
/// <param name="keyBytes">AES 密钥字节数组。</param>
/// <param name="ivBytes">加密使用的初始化向量字节数组。</param>
/// <param name="ivBytes">初始化向量字节数组。</param>
/// <param name="cipherBytes">待解密数据字节数组。</param>
/// <returns>解密后的数据字节数组。</returns>
public static byte[] DecryptWithCBC(byte[] keyBytes, byte[] ivBytes, byte[] cipherBytes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ public static ErroredResult VerifyResponseSignature<TResponse>(this DouyinMicroA
stringBuilder.Append(Encoding.UTF8.GetString(response.GetRawBytes()));
stringBuilder.Append("\n");

bool valid = Utilities.RSAUtility.Verify(
bool valid = Utilities.RSAUtility.VerifyWithSHA256(
publicKeyPem: client.Credentials.PlatformPublicKey,
messageData: stringBuilder.ToString(),
encodingSignature: new EncodedString(response.ByteSignature, EncodingKinds.Base64),
Utilities.RSAUtility.DIGEST_ALGORITHM_SHA256
encodingSignature: new EncodedString(response.ByteSignature, EncodingKinds.Base64)
);
if (valid)
result = ErroredResult.Ok();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -51,7 +50,7 @@ public override async Task BeforeCallAsync(HttpInterceptorContext context, Cance

try
{
sign = Utilities.RSAUtility.Sign(_pkPem, signData, Utilities.RSAUtility.DIGEST_ALGORITHM_SHA256).Value!;
sign = Utilities.RSAUtility.SignWithSHA256(_pkPem, signData).Value!;
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class AESUtility
/// 基于 CBC 模式解密数据。
/// </summary>
/// <param name="keyBytes">AES 密钥字节数组。</param>
/// <param name="ivBytes">加密使用的初始化向量字节数组。</param>
/// <param name="ivBytes">初始化向量字节数组。</param>
/// <param name="cipherBytes">待解密数据字节数组。</param>
/// <returns>解密后的数据字节数组。</returns>
public static byte[] DecryptWithCBC(byte[] keyBytes, byte[] ivBytes, byte[] cipherBytes)
Expand Down
64 changes: 43 additions & 21 deletions src/SKIT.FlurlHttpClient.ByteDance.MicroApp/Utilities/RSAUtility.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
Expand All @@ -18,11 +19,14 @@ public static class RSAUtility
/// <summary>
/// 签名算法:SHA-256withRSA。
/// </summary>
public const string DIGEST_ALGORITHM_SHA256 = "SHA-256withRSA";
private const string DIGEST_ALGORITHM_SHA256 = "SHA-256withRSA";

private static byte[] ConvertPrivateKeyPemToByteArray(string privateKeyPem)
{
if (!privateKeyPem.StartsWith("-----BEGIN PRIVATE KEY-----"))
const string PKCS8_HEADER = "-----BEGIN PRIVATE KEY-----";
const string PKCS8_FOOTER = "-----END PRIVATE KEY-----";

if (!privateKeyPem.StartsWith(PKCS8_HEADER))
{
using (TextReader textReader = new StringReader(privateKeyPem))
using (PemReader pemReader = new PemReader(textReader))
Expand Down Expand Up @@ -55,15 +59,18 @@ private static byte[] ConvertPrivateKeyPemToByteArray(string privateKeyPem)
}

privateKeyPem = privateKeyPem
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
.Replace("-----END PRIVATE KEY-----", string.Empty);
.Replace(PKCS8_HEADER, string.Empty)
.Replace(PKCS8_FOOTER, string.Empty);
privateKeyPem = Regex.Replace(privateKeyPem, "\\s+", string.Empty);
return Convert.FromBase64String(privateKeyPem);
}

private static byte[] ConvertPublicKeyPemToByteArray(string publicKeyPem)
{
if (!publicKeyPem.StartsWith("-----BEGIN PUBLIC KEY-----"))
const string PKCS8_HEADER = "-----BEGIN PUBLIC KEY-----";
const string PKCS8_FOOTER = "-----END PUBLIC KEY-----";

if (!publicKeyPem.StartsWith(PKCS8_HEADER))
{
using (TextReader textReader = new StringReader(publicKeyPem))
using (PemReader pemReader = new PemReader(textReader))
Expand All @@ -90,12 +97,14 @@ private static byte[] ConvertPublicKeyPemToByteArray(string publicKeyPem)
}

publicKeyPem = publicKeyPem
.Replace("-----BEGIN PUBLIC KEY-----", string.Empty)
.Replace("-----END PUBLIC KEY-----", string.Empty);
.Replace(PKCS8_HEADER, string.Empty)
.Replace(PKCS8_FOOTER, string.Empty);
publicKeyPem = Regex.Replace(publicKeyPem, "\\s+", string.Empty);
return Convert.FromBase64String(publicKeyPem);
}

#if NET5_0_OR_GREATER
#else
private static RsaKeyParameters ParsePrivateKeyToParameters(byte[] privateKeyBytes)
{
return (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
Expand All @@ -121,57 +130,71 @@ private static bool Verify(RsaKeyParameters rsaPublicKeyParams, byte[] messageBy
signer.BlockUpdate(messageBytes, 0, messageBytes.Length);
return signer.VerifySignature(signBytes);
}
#endif

/// <summary>
/// 使用私钥生成签名。
/// </summary>
/// <param name="privateKeyBytes">PKCS#1/PKCS#8 私钥字节数组。</param>
/// <param name="privateKeyBytes">PKCS#8 私钥字节数组。</param>
/// <param name="messageBytes">待签名的数据字节数组。</param>
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
/// <returns>签名字节数组。</returns>
public static byte[] Sign(byte[] privateKeyBytes, byte[] messageBytes, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
public static byte[] SignWithSHA256(byte[] privateKeyBytes, byte[] messageBytes)
{
if (privateKeyBytes is null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (messageBytes is null) throw new ArgumentNullException(nameof(messageBytes));

#if NET5_0_OR_GREATER
using (RSA rsa = RSA.Create())
{
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
return rsa.SignData(messageBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
#else
RsaKeyParameters rsaPrivateKeyParams = ParsePrivateKeyToParameters(privateKeyBytes);
return Sign(rsaPrivateKeyParams, messageBytes, digestAlgorithm);
return Sign(rsaPrivateKeyParams, messageBytes, DIGEST_ALGORITHM_SHA256);
#endif
}

/// <summary>
/// 使用私钥生成签名。
/// </summary>
/// <param name="privateKeyPem">PKCS#1/PKCS#8 私钥(PEM 格式)。</param>
/// <param name="messageData">待签名的数据。</param>
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
/// <returns>经过 Base64 编码的签名。</returns>
public static EncodedString Sign(string privateKeyPem, string messageData, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
public static EncodedString SignWithSHA256(string privateKeyPem, string messageData)
{
if (privateKeyPem is null) throw new ArgumentNullException(nameof(privateKeyPem));
if (messageData is null) throw new ArgumentNullException(nameof(messageData));

byte[] privateKeyBytes = ConvertPrivateKeyPemToByteArray(privateKeyPem);
byte[] messageBytes = EncodedString.FromLiteralString(messageData);
byte[] signBytes = Sign(privateKeyBytes, messageBytes, digestAlgorithm);
byte[] signBytes = SignWithSHA256(privateKeyBytes, messageBytes);
return EncodedString.ToBase64String(signBytes);
}

/// <summary>
/// 使用公钥验证签名。
/// </summary>
/// <param name="publicKeyBytes">PKCS#1/PKCS#8 公钥字节数组。</param>
/// <param name="publicKeyBytes">PKCS#8 公钥字节数组。</param>
/// <param name="messageBytes">待验证的数据字节数组。</param>
/// <param name="signBytes">签名字节数组。</param>
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
/// <returns>验证结果。</returns>
public static bool Verify(byte[] publicKeyBytes, byte[] messageBytes, byte[] signBytes, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
public static bool VerifyWithSHA256(byte[] publicKeyBytes, byte[] messageBytes, byte[] signBytes)
{
if (publicKeyBytes is null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (messageBytes is null) throw new ArgumentNullException(nameof(messageBytes));
if (signBytes is null) throw new ArgumentNullException(nameof(signBytes));

#if NET5_0_OR_GREATER
using (RSA rsa = RSA.Create())
{
rsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
return rsa.VerifyData(messageBytes, signBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
#else
RsaKeyParameters rsaPublicKeyParams = ParsePublicKeyToParameters(publicKeyBytes);
return Verify(rsaPublicKeyParams, messageBytes, signBytes, digestAlgorithm);
return Verify(rsaPublicKeyParams, messageBytes, signBytes, DIGEST_ALGORITHM_SHA256);
#endif
}

/// <summary>
Expand All @@ -180,9 +203,8 @@ public static bool Verify(byte[] publicKeyBytes, byte[] messageBytes, byte[] sig
/// <param name="publicKeyPem">PKCS#1/PKCS#8 公钥(PEM 格式)。</param>
/// <param name="messageData">待验证的数据。</param>
/// <param name="encodingSignature">经过编码后的(通常为 Base64)签名。</param>
/// <param name="digestAlgorithm">签名算法。(默认值:<see cref="DIGEST_ALGORITHM_SHA256"/>)</param>
/// <returns>验证结果。</returns>
public static bool Verify(string publicKeyPem, string messageData, EncodedString encodingSignature, string digestAlgorithm = DIGEST_ALGORITHM_SHA256)
public static bool VerifyWithSHA256(string publicKeyPem, string messageData, EncodedString encodingSignature)
{
if (publicKeyPem is null) throw new ArgumentNullException(nameof(publicKeyPem));
if (messageData is null) throw new ArgumentNullException(nameof(messageData));
Expand All @@ -191,7 +213,7 @@ public static bool Verify(string publicKeyPem, string messageData, EncodedString
byte[] publicKeyBytes = ConvertPublicKeyPemToByteArray(publicKeyPem);
byte[] messageBytes = EncodedString.FromLiteralString(messageData);
byte[] signBytes = EncodedString.FromString(encodingSignature, fallbackEncodingKind: EncodingKinds.Base64);
return Verify(publicKeyBytes, messageBytes, signBytes, digestAlgorithm);
return VerifyWithSHA256(publicKeyBytes, messageBytes, signBytes);
}
}
}

0 comments on commit 680ddf2

Please sign in to comment.