A small, dependency-free client library for KamuSM's Zamane
timestamp service. ASN.1 work is done with the built-in
System.Formats.Asn1 API, so there are no external NuGet dependencies.
dotnet add package Zamane| Type | Responsibility |
|---|---|
IdentityGenerator |
customerNo + password + hash → DER-encoded hex identity |
TimeStampRequestParser |
TimeStampReq → messageImprint.hashedMessage |
TimeStampClient |
HTTP POST + identity header → Zamane TimeStampResp or remaining credit |
using Zamane;
using var http = new HttpClient();
var client = new TimeStampClient(
TimeStampClient.TestBaseUrl, // or .ProductionBaseUrl
http);
byte[] tsr = await client.RequestTimestampAsync(
customerNo: "12345",
password: "secret",
timeStampReq: tsReqBytes);
await File.WriteAllBytesAsync("token.tsr", tsr);tsReqBytes must be a DER-encoded RFC 3161 TimeStampReq. Build one
with BouncyCastle (or any other ASN.1 toolkit) before calling the
client.
int credit = await client.RequestRemainingCreditAsync(
customerNo: "12345",
password: "secret");
Console.WriteLine($"Remaining credit: {credit}");Credit spend is applied asynchronously on the server, so the balance may lag briefly after timestamping. Authentication uses the system clock; keep the machine synchronized (for example via NTP). KamuSM allows about 10 minutes of clock drift.
Pass a TimeStampClientOptions to override defaults. Set
RegenerateNonce = true to have the client rewrite the nonce field of
the supplied TimeStampReq with a fresh cryptographically random value
on every request (useful when the same DER-encoded request is reused):
var client = new TimeStampClient(
TimeStampClient.TestBaseUrl,
http,
new TimeStampClientOptions { RegenerateNonce = true });RegenerateNonce defaults to false, in which case the request is sent
verbatim.
using System.Numerics;
using Zamane;
string identity = IdentityGenerator.CreateIdentity(
customerNo: BigInteger.Parse("12345"),
password: "secret",
hashValue: hashBytes);
// Override iteration count, salt, IV:
string identity2 = IdentityGenerator.CreateIdentity(
customerNo: 42,
password: "secret",
hashValue: hashBytes,
iterations: 10_000,
salt: saltBytes, // 8 bytes
iv: ivBytes); // 16 bytesThe hex string decodes to:
SEQUENCE {
INTEGER customerNo,
OCTET STRING salt (8 bytes),
INTEGER iterations,
OCTET STRING iv (16 bytes),
OCTET STRING ciphertext
}
using Zamane;
byte[] hashedMessage = TimeStampRequestParser.GetHashedMessage(tsReqBytes);
// or from a hex string:
byte[] hashedMessage2 = TimeStampRequestParser.GetHashedMessage(tsReqHex);services.AddHttpClient<TimeStampClient>(c =>
{
c.BaseAddress = new Uri(TimeStampClient.ProductionBaseUrl);
c.Timeout = TimeSpan.FromSeconds(30);
});TimeStampClient accepts a base URL and an optional HttpClient. In
DI scenarios, prefer passing a shared HttpClient instance.