feat: 新增百度云 SMS v3 签名算法与 HttpClient 直连实现

- BceSigner: BCE v1 签名算法(HMAC-SHA256),与 baidubce-sdk 输出 byte-for-byte 一致
- BceSmsClient: HttpClient 直连百度云 SMS v3 API,自写签名,无第三方 SDK
- BceSignerTests: 7 个单测,包括真实 AK/SK 对比百度 SDK debug 输出

暂未启用:SmsService 仍为开发桩,等签名运营商实名报备后接入真实发送
This commit is contained in:
MingNian
2026-07-23 18:58:33 +08:00
parent 28f704c98e
commit 32aae40b12
3 changed files with 224 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
using Health.Infrastructure.Services;
namespace Health.Tests;
/// <summary>
/// BCE v1 签名算法测试
/// Golden value 由 Python 标准库 hmac + hashlib 按 baidubce-sdk 算法手算,
/// 并与 baidubce-sdk 0.9.72 的实际 debug 输出对比验证一致
/// </summary>
public class BceSignerTests
{
private const string Ak = "test-ak";
private const string Sk = "test-sk";
private const string Host = "smsv3.bj.baidubce.com";
private const string Method = "POST";
private const string ApiPath = "/api/v3/sendSms";
private const string ContentType = "application/json;charset=utf-8";
private const int ContentLength = 199;
private static readonly DateTime Ts1 = new(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly DateTime Ts2 = new(2026, 7, 23, 8, 0, 0, DateTimeKind.Utc);
// Golden 1: 固定输入
private const string ExpectedSig1 = "7592321b147186724a0194344aa274b4875fbc3442b4a5a07db4fd00cf44e580";
private const string ExpectedAuth1 = "bce-auth-v1/test-ak/2026-01-01T00:00:00Z/1800/content-length;content-type;host;x-bce-date/" + ExpectedSig1;
// Golden 2: 不同 timestamp
private const string ExpectedSig2 = "5c058909c04e8a6964fc22f9ae00cd5aae9c9182cb21fca7a76eb834eaa47c0b";
// Golden 3: 不同 SK
private const string ExpectedSig3 = "b944339bad2635d0eb41344427c1328f9785335a64baaabced658fa487fb228a";
// Golden 4: 不同 content_length
private const string ExpectedSig4 = "7b2f8bfdda2255b2331992a4adb9fb7365aa83c62453b61380cc7be350e6105f";
[Fact]
public void BuildAuthorization_FixedInput_MatchesGoldenValue()
{
var auth = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, ContentLength, ContentType);
Assert.Equal(ExpectedAuth1, auth);
}
[Fact]
public void BuildAuthorization_DifferentTimestamp_ProducesDifferentSignature()
{
var auth1 = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, ContentLength, ContentType);
var auth2 = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts2, ContentLength, ContentType);
Assert.NotEqual(auth1, auth2);
Assert.EndsWith(ExpectedSig2, auth2);
}
[Fact]
public void BuildAuthorization_DifferentSecretKey_ProducesDifferentSignature()
{
var auth1 = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, ContentLength, ContentType);
var auth3 = BceSigner.BuildAuthorization(Ak, "test-sk-2", Method, ApiPath, Host, Ts1, ContentLength, ContentType);
Assert.NotEqual(auth1, auth3);
Assert.EndsWith(ExpectedSig3, auth3);
}
[Fact]
public void BuildAuthorization_DifferentContentLength_ProducesDifferentSignature()
{
var auth1 = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, ContentLength, ContentType);
var auth4 = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, 100, ContentType);
Assert.NotEqual(auth1, auth4);
Assert.EndsWith(ExpectedSig4, auth4);
}
[Fact]
public void BuildAuthorization_Signature_Is64CharLowercaseHex()
{
var auth = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, ContentLength, ContentType);
var sig = auth.Split('/')[^1];
Assert.Equal(64, sig.Length);
Assert.Matches("^[0-9a-f]{64}$", sig);
}
[Fact]
public void BuildAuthorization_Format_HasCorrectStructure()
{
var auth = BceSigner.BuildAuthorization(Ak, Sk, Method, ApiPath, Host, Ts1, ContentLength, ContentType);
var parts = auth.Split('/');
Assert.Equal(6, parts.Length);
Assert.Equal("bce-auth-v1", parts[0]);
Assert.Equal(Ak, parts[1]);
Assert.Equal("2026-01-01T00:00:00Z", parts[2]);
Assert.Equal("1800", parts[3]);
Assert.Equal("content-length;content-type;host;x-bce-date", parts[4]);
Assert.Equal(ExpectedSig1, parts[5]);
}
[Fact]
public void BuildAuthorization_RealCredentials_MatchesSdkDebugOutput()
{
// 用百度 SDK 0.9.72 实际 debug 日志里的真实输入,验证 C# 输出和 SDK 完全一致
var auth = BceSigner.BuildAuthorization(
"ALTAKnJn8wtdLtBPgl7bD1x3N3",
"c5ca05a967d04a69bbc0e12f08062dc8",
"POST",
"/api/v3/sendSms",
"smsv3.bj.baidubce.com",
new DateTime(2026, 7, 23, 9, 3, 42, DateTimeKind.Utc),
199,
"application/json;charset=utf-8");
Assert.Equal(
"bce-auth-v1/ALTAKnJn8wtdLtBPgl7bD1x3N3/2026-07-23T09:03:42Z/1800/content-length;content-type;host;x-bce-date/0fd388266f63e9036de09981d7312ce3b7e279c8789823cb80b6c1800005fe13",
auth);
}
}