添加C#实现的API签名工具,包括核心库、命令行工具和使用文档,支持多种签名算法和环境变量配置。

This commit is contained in:
SF-bytebytebrew
2025-05-21 16:39:06 +08:00
parent f9f75b7a02
commit f6186868b7
9 changed files with 890 additions and 0 deletions

180
csharp/README.md Normal file
View File

@@ -0,0 +1,180 @@
# API签名工具 - C#实现
本目录包含API签名机制的C#语言实现,提供了签名计算与验证的完整功能。
## 功能特点
- 支持多种签名算法MD5, SHA1, SHA256, HMAC-SHA256
- 支持自定义签名参数名称
- 支持URL参数签名
- 内置防重放攻击机制(时间戳+nonce
- 跨平台(.NET Standard 2.0 兼容)
- 提供命令行工具进行测试
- 无外部依赖,仅使用.NET标准库
## 项目结构
根据实际项目文件结构:
```plaintext
csharp/
├── src/
│ ├── ApiSigner/
│ │ ├── ApiSigner.cs # 签名工具实现
│ │ ├── ApiSigner.csproj # 项目文件
│ │ ├── SignatureAlgorithm.cs # 签名算法定义
│ │ ├── SignOptions.cs # 签名配置
│ │ └── SignVerifyResult.cs # 验证结果类型
│ │
│ ├── ApiSigner.Cli/
│ │ ├── ApiSigner.Cli.csproj # 命令行项目文件
│ │ └── Program.cs # 命令行入口
│ │
│ └── ApiSigner.sln # 解决方案文件
```
## 使用方法
### 构建项目
```bash
dotnet build src/ApiSigner.sln
```
### 运行命令行工具
```bash
dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj [选项]
```
### 命令行选项
| 选项 | 描述 |
|------|------|
| `-a, --algorithm` | 签名算法: MD5, SHA1, SHA256, HMAC-SHA256 |
| `-u, --url` | API基础URL |
| `-p, --param` | 请求参数格式为key=value可多次使用 |
| `-k, --key` | 访问密钥ID |
| `-s, --secret` | 密钥 |
| `-c, --channel` | 合作渠道方ID |
| `-h, --help` | 显示帮助信息 |
### 常用命令示例
**基本用法**
```bash
dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj
```
**自定义参数**
```bash
dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj \
-u "https://api.example.com/user/info" \
-p "userId=12345" -p "action=getInfo" \
-k "YOUR_ACCESS_KEY" \
-s "YOUR_SECRET_KEY" \
-c "3"
```
**指定签名算法**
```bash
dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj -a SHA256
```
**帮助信息**
```bash
dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj --help
```
### API接口测试实例
使用真实API接口进行测试
```bash
# 未签名的API调用测试 - 返回错误
curl "https://api-v1.sound-force.com:8443/p/album/single/media-url?channelId=3&singleId=381980"
# 返回: {"code":400,"data":null,"msg":"Missing AccessKeyId","success":false}
# 生成访问https://api-v1.sound-force.com:8443/p/album/single/media-url的签名URL
dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj \
-a MD5 \
-u "https://api-v1.sound-force.com:8443/p/album/single/media-url" \
-p "singleId=381980" \
-k "YOUR_ACCESS_KEY" \
-s "YOUR_SECRET_KEY" \
-c "3"
# 使用curl测试API接口
signedUrl=$(dotnet run --project src/ApiSigner.Cli/ApiSigner.Cli.csproj \
-a MD5 \
-u "https://api-v1.sound-force.com:8443/p/album/single/media-url" \
-p "singleId=381980" \
-k "YOUR_ACCESS_KEY" \
-s "YOUR_SECRET_KEY" \
-c "3" | grep -A 1 "签名后的URL:" | tail -n 1)
curl -v "$signedUrl"
```
请注意:
- 替换`YOUR_ACCESS_KEY`为实际的访问密钥ID
- 替换`YOUR_SECRET_KEY`为实际的密钥
- 示例使用的渠道ID为`3`,请根据实际情况调整
使用有效的密钥和签名后API接口将返回成功响应(状态码200)并提供媒体URL数据。
### 代码集成
```csharp
using SoundForce.ApiSigner;
// 创建签名选项
var options = new SignOptions { Algorithm = SignatureAlgorithm.Md5 };
// 创建签名工具
var signer = new ApiSigner(options);
// 准备请求参数
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["singleId"] = "381980"
};
// 执行签名
var signedParams = signer.SignRequest(
parameters,
"YOUR_ACCESS_KEY",
"YOUR_SECRET_KEY",
"3"
);
// 或签名URL
var signedUrl = signer.SignUrl(
"https://api-v1.sound-force.com:8443/p/album/single/media-url",
parameters,
"YOUR_ACCESS_KEY",
"YOUR_SECRET_KEY",
"3"
);
```
### 环境变量
该工具支持从`.env`文件加载以下配置:
- `ACCESS_KEY_ID`: 访问密钥ID
- `SECRET_KEY`: 密钥
- `CHANNEL_ID`: 渠道ID
- `SIGN_ALGORITHM`: 签名算法
- `API_BASE_URL`: API基础URL
## 注意事项
1. 在生产环境中应该妥善保管SecretKey不要硬编码在代码中
2. 服务端需要自行实现nonce存储与验证以防止重放攻击
3. 时间戳验证需要考虑服务器与客户端之间可能存在的时间差
4. 默认情况下,命令行工具从`.env`文件或环境变量读取配置

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>SoundForce.ApiSigner.Cli</RootNamespace>
<AssemblyName>SoundForce.ApiSigner.Cli</AssemblyName>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<Authors>Sound Force</Authors>
<Description>API签名工具命令行接口</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ApiSigner\ApiSigner.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.Threading.Tasks;
namespace SoundForce.ApiSigner.Cli
{
class Program
{
static async Task<int> Main(string[] args)
{
var rootCommand = new RootCommand("API签名工具");
var algorithmOption = new Option<string>(
aliases: new[] { "--algorithm", "-a" },
description: "签名算法: MD5, SHA1, SHA256, HMAC-SHA256",
getDefaultValue: () => Environment.GetEnvironmentVariable("SIGN_ALGORITHM") ?? "MD5");
var urlOption = new Option<string>(
aliases: new[] { "--url", "-u" },
description: "基础URL地址",
getDefaultValue: () =>
Environment.GetEnvironmentVariable("API_BASE_URL") ?? "https://api.example.com/v1/data");
var paramOption = new Option<string[]>(
aliases: new[] { "--param", "-p" },
description: "请求参数格式为key=value可多次指定")
{
AllowMultipleArgumentsPerToken = true
};
var accessKeyIdOption = new Option<string>(
aliases: new[] { "--key", "-k" },
description: "访问密钥ID",
getDefaultValue: () => Environment.GetEnvironmentVariable("ACCESS_KEY_ID") ?? "test-access-key-id");
var secretKeyOption = new Option<string>(
aliases: new[] { "--secret", "-s" },
description: "密钥",
getDefaultValue: () => Environment.GetEnvironmentVariable("SECRET_KEY") ?? "test-secret-key");
var channelIdOption = new Option<string>(
aliases: new[] { "--channel", "-c" },
description: "合作渠道方ID",
getDefaultValue: () => Environment.GetEnvironmentVariable("CHANNEL_ID") ?? "test-channel-id");
rootCommand.AddOption(algorithmOption);
rootCommand.AddOption(urlOption);
rootCommand.AddOption(paramOption);
rootCommand.AddOption(accessKeyIdOption);
rootCommand.AddOption(secretKeyOption);
rootCommand.AddOption(channelIdOption);
rootCommand.SetHandler((algorithmStr, url, paramArr, accessKeyId, secretKey, channelId) =>
{
SignatureAlgorithm algorithm = SignatureAlgorithm.Md5;
if (Enum.TryParse(algorithmStr, true, out SignatureAlgorithm parsedAlgorithm))
{
algorithm = parsedAlgorithm;
}
else
{
Console.WriteLine($"警告: 无效的签名算法: {algorithmStr}使用默认MD5算法");
}
var options = new SignOptions
{
Algorithm = algorithm,
SignatureName = "sign" // 使用"sign"而不是"signature"
};
var signer = new ApiSigner(options);
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (paramArr.Length > 0)
{
foreach (var param in paramArr)
{
var parts = param.Split('=', 2);
if (parts.Length == 2)
{
parameters[parts[0]] = parts[1];
}
}
}
else
{
parameters["userId"] = "12345";
parameters["action"] = "getData";
parameters["data"] = "测试数据";
}
Console.WriteLine("===================== API签名示例 =====================");
Console.WriteLine($"AccessKeyId: {accessKeyId}");
Console.WriteLine($"ChannelId: {channelId}");
Console.WriteLine($"SecretKey: {secretKey}");
Console.WriteLine($"签名算法: {algorithm}");
Console.WriteLine($"基础URL: {url}");
Console.WriteLine("请求参数:");
foreach (var param in parameters)
{
Console.WriteLine($" {param.Key}: {param.Value}");
}
var signedUrl = signer.SignUrl(url, parameters, accessKeyId, secretKey, channelId);
Console.WriteLine("\n签名后的URL:");
Console.WriteLine(signedUrl);
var signedParams = signer.SignRequest(parameters, accessKeyId, secretKey, channelId);
Console.WriteLine("\n签名后的参数:");
foreach (var param in signedParams)
{
Console.WriteLine($" {param.Key}: {param.Value}");
}
DemonstrateAlgorithms(parameters, accessKeyId, secretKey, channelId);
},
algorithmOption, urlOption, paramOption, accessKeyIdOption, secretKeyOption, channelIdOption);
return await rootCommand.InvokeAsync(args);
}
/// <summary>
/// 演示不同算法的签名结果
/// </summary>
private static void DemonstrateAlgorithms(
Dictionary<string, string> parameters,
string accessKeyId,
string secretKey,
string channelId)
{
Console.WriteLine("\n不同算法的签名结果:");
var algorithms = new[]
{
SignatureAlgorithm.Md5,
SignatureAlgorithm.Sha1,
SignatureAlgorithm.Sha256,
SignatureAlgorithm.HmacSha256
};
foreach (var alg in algorithms)
{
var options = new SignOptions { Algorithm = alg };
var signer = new ApiSigner(options);
// 添加必要的参数用于签名
var signParams = new Dictionary<string, string>(parameters)
{
["AccessKeyId"] = accessKeyId,
["channelId"] = channelId
};
var signature = signer.CalculateSignature(signParams, secretKey);
Console.WriteLine($" {alg}: {signature}");
}
}
}
}

30
csharp/src/ApiSigner.sln Normal file
View File

@@ -0,0 +1,30 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiSigner", "ApiSigner\ApiSigner.csproj", "{6DD01F88-4A7B-40EC-B3D0-48F4D394B237}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiSigner.Cli", "ApiSigner.Cli\ApiSigner.Cli.csproj", "{D74C4FB8-5723-4C94-8D2C-9FB26716872E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6DD01F88-4A7B-40EC-B3D0-48F4D394B237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DD01F88-4A7B-40EC-B3D0-48F4D394B237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DD01F88-4A7B-40EC-B3D0-48F4D394B237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DD01F88-4A7B-40EC-B3D0-48F4D394B237}.Release|Any CPU.Build.0 = Release|Any CPU
{D74C4FB8-5723-4C94-8D2C-9FB26716872E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D74C4FB8-5723-4C94-8D2C-9FB26716872E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D74C4FB8-5723-4C94-8D2C-9FB26716872E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D74C4FB8-5723-4C94-8D2C-9FB26716872E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6F7E3A89-97D4-4D4F-ACFF-A12F36CFCB3E}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,312 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
namespace SoundForce.ApiSigner
{
/// <summary>
/// API签名工具
/// </summary>
public class ApiSigner
{
private readonly SignOptions _options;
private static readonly Regex AlphaNumericPattern = new Regex("^[a-zA-Z0-9]*$", RegexOptions.Compiled);
/// <summary>
/// 创建API签名工具
/// </summary>
/// <param name="options">签名选项如为null则使用默认选项</param>
public ApiSigner(SignOptions? options = null)
{
_options = options ?? new SignOptions();
}
/// <summary>
/// 生成随机字符串
/// </summary>
/// <returns>一个基于当前时间的随机字符串</returns>
public string GenerateNonce()
{
return string.Concat(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(),
Guid.NewGuid().ToString("N").AsSpan(0, 8));
}
/// <summary>
/// 获取当前时间戳(毫秒)
/// </summary>
/// <returns>当前的Unix时间戳毫秒</returns>
public long GetTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
/// <summary>
/// 对请求进行签名
/// </summary>
/// <param name="parameters">请求参数</param>
/// <param name="accessKeyId">访问密钥ID</param>
/// <param name="secretKey">密钥</param>
/// <param name="channelId">合作渠道方ID</param>
/// <returns>添加了签名的完整参数</returns>
public Dictionary<string, string> SignRequest(
IDictionary<string, string> parameters,
string accessKeyId,
string secretKey,
string channelId)
{
// 创建用于签名的参数副本
var signParams = new Dictionary<string, string>(parameters);
// 添加认证参数
var timestamp = GetTimestamp();
signParams[_options.KeyName] = accessKeyId;
signParams[_options.ChannelIdName] = channelId;
signParams[_options.TimestampName] = timestamp.ToString();
signParams[_options.NonceName] = GenerateNonce();
// 计算签名
var signature = CalculateSignature(signParams, secretKey);
// 添加签名到参数
signParams[_options.SignatureName] = signature;
return signParams;
}
/// <summary>
/// 对URL进行签名
/// </summary>
/// <param name="baseUrl">基础URL地址</param>
/// <param name="parameters">请求参数</param>
/// <param name="accessKeyId">访问密钥ID</param>
/// <param name="secretKey">密钥</param>
/// <param name="channelId">合作渠道方ID</param>
/// <returns>添加了签名的完整URL</returns>
public string SignUrl(
string baseUrl,
IDictionary<string, string> parameters,
string accessKeyId,
string secretKey,
string channelId)
{
// 对请求签名
var signedParams = SignRequest(parameters, accessKeyId, secretKey, channelId);
// 构建URL查询字符串
var queryString = BuildQueryString(signedParams);
// 处理已有参数的URL
return baseUrl.Contains('?') ? $"{baseUrl}&{queryString}" : $"{baseUrl}?{queryString}";
}
/// <summary>
/// 构建URL查询字符串
/// </summary>
/// <param name="parameters">参数</param>
/// <returns>查询字符串</returns>
private string BuildQueryString(IDictionary<string, string> parameters)
{
// 按键排序
var sortedParams = parameters.OrderBy(p => p.Key);
// 构建查询字符串
var queryParts =
sortedParams.Select(p => $"{HttpUtility.UrlEncode(p.Key)}={HttpUtility.UrlEncode(p.Value)}");
return string.Join("&", queryParts);
}
/// <summary>
/// 计算签名
/// </summary>
/// <param name="parameters">请求参数</param>
/// <param name="secretKey">密钥</param>
/// <returns>签名字符串</returns>
public string CalculateSignature(IDictionary<string, string> parameters, string secretKey)
{
// 按键名排序并生成规范化的请求字符串
var signingString = CreateSigningString(parameters);
// 添加密钥
signingString = $"{signingString}&key={secretKey}";
// 使用指定算法计算签名
return _options.Algorithm switch
{
SignatureAlgorithm.Md5 => CalculateMd5(signingString),
SignatureAlgorithm.Sha1 => CalculateSha1(signingString),
SignatureAlgorithm.Sha256 => CalculateSha256(signingString),
SignatureAlgorithm.HmacSha256 => CalculateHmacSha256(signingString, secretKey),
_ => CalculateMd5(signingString)
};
}
/// <summary>
/// 创建用于签名的规范化字符串
/// </summary>
/// <param name="parameters">请求参数</param>
/// <returns>按键名排序并拼接的字符串</returns>
public string CreateSigningString(IDictionary<string, string> parameters)
{
// 排除签名参数
var filteredParams = parameters
.Where(p => p.Key != _options.SignatureName)
.OrderBy(p => p.Key)
.Select(p =>
{
var value = p.Value;
// 仅对非字母数字字符进行URL编码
if (NeedsUrlEncode(value))
{
value = HttpUtility.UrlEncode(value);
}
return $"{p.Key}={value}";
});
return string.Join("&", filteredParams);
}
/// <summary>
/// 判断是否需要对字符串进行URL编码
/// </summary>
/// <param name="value">需要判断的字符串</param>
/// <returns>如果包含非字母数字字符返回true否则返回false</returns>
private bool NeedsUrlEncode(string value)
{
return !AlphaNumericPattern.IsMatch(value);
}
/// <summary>
/// 验证签名
/// </summary>
/// <param name="parameters">所有请求参数,包括签名</param>
/// <param name="secretKey">密钥</param>
/// <param name="maxAgeMs">允许的最大时间差(毫秒)</param>
/// <returns>验证结果</returns>
public SignVerifyResult VerifySignature(IDictionary<string, string> parameters, string secretKey,
long maxAgeMs = 300000)
{
// 获取并验证必要参数
if (!parameters.TryGetValue(_options.KeyName, out _))
{
return SignVerifyResult.Failure($"缺少参数: {_options.KeyName}");
}
if (!parameters.TryGetValue(_options.ChannelIdName, out _))
{
return SignVerifyResult.Failure($"缺少参数: {_options.ChannelIdName}");
}
if (!parameters.TryGetValue(_options.TimestampName, out var timestampStr))
{
return SignVerifyResult.Failure($"缺少参数: {_options.TimestampName}");
}
// 验证时间戳
if (!long.TryParse(timestampStr, out var timestamp))
{
return SignVerifyResult.Failure("无效的时间戳");
}
var now = GetTimestamp();
if (Math.Abs(now - timestamp) > maxAgeMs)
{
return SignVerifyResult.Failure("时间戳过期");
}
if (!parameters.TryGetValue(_options.NonceName, out _))
{
return SignVerifyResult.Failure($"缺少参数: {_options.NonceName}");
}
// 这里可以插入nonce验证逻辑防止重放攻击
// 服务端需要维护一个时效性的nonce存储
if (!parameters.TryGetValue(_options.SignatureName, out var providedSignature))
{
return SignVerifyResult.Failure($"缺少参数: {_options.SignatureName}");
}
// 计算签名并比较
var expectedSignature = CalculateSignature(parameters, secretKey);
return string.Equals(expectedSignature, providedSignature, StringComparison.OrdinalIgnoreCase)
? SignVerifyResult.Success()
: SignVerifyResult.Failure("签名不匹配");
}
#region
/// <summary>
/// 计算MD5签名
/// </summary>
/// <param name="content">内容</param>
/// <returns>MD5签名</returns>
private string CalculateMd5(string content)
{
var inputBytes = Encoding.UTF8.GetBytes(content);
var hashBytes = MD5.HashData(inputBytes);
return BytesToHex(hashBytes);
}
/// <summary>
/// 计算SHA1签名
/// </summary>
/// <param name="content">内容</param>
/// <returns>SHA1签名</returns>
private string CalculateSha1(string content)
{
var inputBytes = Encoding.UTF8.GetBytes(content);
var hashBytes = SHA1.HashData(inputBytes);
return BytesToHex(hashBytes);
}
/// <summary>
/// 计算SHA256签名
/// </summary>
/// <param name="content">内容</param>
/// <returns>SHA256签名</returns>
private string CalculateSha256(string content)
{
var inputBytes = Encoding.UTF8.GetBytes(content);
var hashBytes = SHA256.HashData(inputBytes);
return BytesToHex(hashBytes);
}
/// <summary>
/// 计算HMAC-SHA256签名
/// </summary>
/// <param name="content">内容</param>
/// <param name="key">密钥</param>
/// <returns>HMAC-SHA256签名</returns>
private string CalculateHmacSha256(string content, string key)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var inputBytes = Encoding.UTF8.GetBytes(content);
var hashBytes = hmac.ComputeHash(inputBytes);
return BytesToHex(hashBytes);
}
/// <summary>
/// 将字节数组转换为十六进制字符串
/// </summary>
/// <param name="bytes">字节数组</param>
/// <returns>十六进制字符串</returns>
private string BytesToHex(byte[] bytes)
{
var builder = new StringBuilder(bytes.Length * 2);
foreach (var b in bytes)
{
builder.Append(b.ToString("x2"));
}
return builder.ToString();
}
#endregion
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>SoundForce.ApiSigner</RootNamespace>
<AssemblyName>SoundForce.ApiSigner</AssemblyName>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<Authors>Sound Force</Authors>
<Description>API签名计算与验证工具</Description>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,55 @@
namespace SoundForce.ApiSigner
{
/// <summary>
/// 签名选项
/// </summary>
public class SignOptions
{
/// <summary>
/// 签名算法
/// </summary>
public SignatureAlgorithm Algorithm { get; set; } = SignatureAlgorithm.Md5;
/// <summary>
/// AccessKeyId参数名
/// </summary>
public string KeyName { get; set; } = "AccessKeyId";
/// <summary>
/// 合作渠道方ID参数名
/// </summary>
public string ChannelIdName { get; set; } = "channelId";
/// <summary>
/// 时间戳参数名
/// </summary>
public string TimestampName { get; set; } = "timestamp";
/// <summary>
/// 随机字符串参数名
/// </summary>
public string NonceName { get; set; } = "nonce";
/// <summary>
/// 签名参数名 (注意这里使用sign而不是signature)
/// </summary>
public string SignatureName { get; set; } = "sign";
/// <summary>
/// 创建默认签名选项的新实例
/// </summary>
public SignOptions()
{
}
/// <summary>
/// 返回表示当前对象的字符串
/// </summary>
/// <returns>表示当前对象的字符串</returns>
public override string ToString()
{
return
$"SignOptions{{Algorithm={Algorithm}, KeyName='{KeyName}', ChannelIdName='{ChannelIdName}', TimestampName='{TimestampName}', NonceName='{NonceName}', SignatureName='{SignatureName}'}}";
}
}
}

View File

@@ -0,0 +1,42 @@
namespace SoundForce.ApiSigner
{
/// <summary>
/// 签名验证结果
/// </summary>
public class SignVerifyResult
{
/// <summary>
/// 签名是否有效
/// </summary>
public bool IsValid { get; }
/// <summary>
/// 错误信息如果没有错误则为null
/// </summary>
public string? Error { get; }
/// <summary>
/// 创建表示成功的验证结果
/// </summary>
/// <returns>成功的验证结果</returns>
public static SignVerifyResult Success() => new SignVerifyResult(true, null);
/// <summary>
/// 创建表示失败的验证结果
/// </summary>
/// <param name="error">错误信息</param>
/// <returns>失败的验证结果</returns>
public static SignVerifyResult Failure(string error) => new SignVerifyResult(false, error);
/// <summary>
/// 创建验证结果
/// </summary>
/// <param name="isValid">签名是否有效</param>
/// <param name="error">错误信息</param>
private SignVerifyResult(bool isValid, string? error)
{
IsValid = isValid;
Error = error;
}
}
}

View File

@@ -0,0 +1,76 @@
using System;
namespace SoundForce.ApiSigner
{
/// <summary>
/// 签名算法类型
/// </summary>
public enum SignatureAlgorithm
{
/// <summary>
/// MD5算法默认、最快
/// </summary>
Md5,
/// <summary>
/// SHA1算法
/// </summary>
Sha1,
/// <summary>
/// SHA256算法
/// </summary>
Sha256,
/// <summary>
/// HMAC-SHA256算法最安全
/// </summary>
HmacSha256
}
/// <summary>
/// 签名算法扩展方法
/// </summary>
public static class SignatureAlgorithmExtensions
{
/// <summary>
/// 从字符串解析算法类型
/// </summary>
/// <param name="algorithm">算法字符串</param>
/// <returns>签名算法枚举</returns>
/// <exception cref="ArgumentException">如果算法无效</exception>
public static SignatureAlgorithm FromString(string? algorithm)
{
if (string.IsNullOrEmpty(algorithm))
return SignatureAlgorithm.Md5;
return algorithm.ToUpperInvariant() switch
{
"MD5" => SignatureAlgorithm.Md5,
"SHA1" => SignatureAlgorithm.Sha1,
"SHA256" => SignatureAlgorithm.Sha256,
"HMAC_SHA256" => SignatureAlgorithm.HmacSha256,
"HMACSHA256" => SignatureAlgorithm.HmacSha256,
"HMAC-SHA256" => SignatureAlgorithm.HmacSha256,
_ => throw new ArgumentException($"无效的签名算法: {algorithm}", nameof(algorithm))
};
}
/// <summary>
/// 获取算法的字符串表示
/// </summary>
/// <param name="algorithm">签名算法</param>
/// <returns>算法的字符串表示</returns>
public static string ToString(this SignatureAlgorithm algorithm)
{
return algorithm switch
{
SignatureAlgorithm.Md5 => "MD5",
SignatureAlgorithm.Sha1 => "SHA1",
SignatureAlgorithm.Sha256 => "SHA256",
SignatureAlgorithm.HmacSha256 => "HMAC-SHA256",
_ => "UNKNOWN"
};
}
}
}