|
|
|
|
using System;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Org.BouncyCastle.Asn1.X9;
|
|
|
|
|
using Org.BouncyCastle.Crypto;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
|
|
|
using Org.BouncyCastle.Math;
|
|
|
|
|
using Org.BouncyCastle.Security;
|
|
|
|
|
using Org.BouncyCastle.Crypto.Signers;
|
|
|
|
|
|
|
|
|
|
public class TokenGenerator
|
|
|
|
|
{
|
|
|
|
|
// 1. 获取当前时间的毫秒级时间戳 A
|
|
|
|
|
public static long GetCurrentTimestamp()
|
|
|
|
|
{
|
|
|
|
|
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 拼接 B + A 得到 C
|
|
|
|
|
public static string ConcatenateCodeAndTimestamp(string codeB, long timestampA)
|
|
|
|
|
{
|
|
|
|
|
return $"{codeB}{timestampA}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 获取 C 的 UTF-8 字节数组 D
|
|
|
|
|
public static byte[] GetUtf8Bytes(string input)
|
|
|
|
|
{
|
|
|
|
|
return Encoding.UTF8.GetBytes(input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 将 Hex 或 Base64 字符串私钥转换为 ECPrivateKeyParameters
|
|
|
|
|
public static ECPrivateKeyParameters ParsePrivateKey(string privateKeyStr)
|
|
|
|
|
{
|
|
|
|
|
// 假设私钥是 Hex 字符串(如 "ABCD1234...")
|
|
|
|
|
byte[] privateKeyBytes = HexToBytes(privateKeyStr); // 如果是 Base64,用 Convert.FromBase64String
|
|
|
|
|
// SM2 曲线参数
|
|
|
|
|
var curve = ECNamedCurveTable.GetByName("sm2p256v1");
|
|
|
|
|
var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
|
|
|
|
|
|
|
|
|
|
// 创建私钥
|
|
|
|
|
var privateKey = new ECPrivateKeyParameters(new BigInteger(1, privateKeyBytes), domainParams);
|
|
|
|
|
return privateKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 使用 SM2 私钥签名 D 得到 E
|
|
|
|
|
public static byte[] SignWithSm2(byte[] data, ECPrivateKeyParameters privateKey)
|
|
|
|
|
{
|
|
|
|
|
var signer = new SM2Signer();
|
|
|
|
|
signer.Init(true, privateKey);
|
|
|
|
|
signer.BlockUpdate(data, 0, data.Length);
|
|
|
|
|
return signer.GenerateSignature();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. 对 E 进行 Base64 编码得到 F
|
|
|
|
|
public static string Base64Encode(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
return Convert.ToBase64String(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7. 组装 Token: {B}.{A}.{F}
|
|
|
|
|
public static string GenerateToken(string codeB, string privateKeyStr)
|
|
|
|
|
{
|
|
|
|
|
// 1. 获取时间戳 A
|
|
|
|
|
long timestampA = GetCurrentTimestamp();
|
|
|
|
|
|
|
|
|
|
// 2. 拼接 B + A 得到 C
|
|
|
|
|
string c = ConcatenateCodeAndTimestamp(codeB, timestampA);
|
|
|
|
|
|
|
|
|
|
// 3. 获取 UTF-8 字节数组 D
|
|
|
|
|
byte[] d = GetUtf8Bytes(c);
|
|
|
|
|
|
|
|
|
|
// 4. 解析私钥字符串
|
|
|
|
|
ECPrivateKeyParameters privateKey = ParsePrivateKey(privateKeyStr);
|
|
|
|
|
|
|
|
|
|
// 5. SM2 签名得到 E
|
|
|
|
|
byte[] e = SignWithSm2(d, privateKey);
|
|
|
|
|
|
|
|
|
|
// 6. Base64 编码得到 F
|
|
|
|
|
string f = Base64Encode(e);
|
|
|
|
|
|
|
|
|
|
// 7. 组装 Token
|
|
|
|
|
return $"{codeB}.{timestampA}.{f}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 辅助方法:Hex 字符串转字节数组
|
|
|
|
|
private static byte[] HexToBytes(string hex)
|
|
|
|
|
{
|
|
|
|
|
if (hex.Length % 2 != 0)
|
|
|
|
|
throw new ArgumentException("Hex 字符串长度必须是偶数");
|
|
|
|
|
|
|
|
|
|
byte[] bytes = new byte[hex.Length / 2];
|
|
|
|
|
for (int i = 0; i < bytes.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
|
|
|
|
|
}
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
|
|
|
|
}
|