Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
- SQLInsight.Licensing: ECDSA P-256 signed documents (SQLI1.payload.sig), licenses with plan, expiry, instance count and key id for rotation; 30-day grace; invalid or missing licenses are states, never errors, and never stop monitoring (a license only adds hub sync and support) - Trusted public keys are embedded; the source ships with none, so a published key can never mint licenses. The vendor adds its own. - tools/SQLInsight.LicenseTool: keygen, issue, verify - *.private.pem ignored by git Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
72 lines
2.7 KiB
C#
72 lines
2.7 KiB
C#
// Vendor tool: generate a signing key pair, issue licenses, verify them. Runs on the vendor's machine only.
|
|
using System.Globalization;
|
|
using System.Security.Cryptography;
|
|
using SQLInsight.Licensing;
|
|
|
|
return args.FirstOrDefault() switch
|
|
{
|
|
"keygen" => KeyGen(Option("--out") ?? "."),
|
|
"issue" => Issue(),
|
|
"verify" => Verify(),
|
|
_ => Usage(),
|
|
};
|
|
|
|
int Usage()
|
|
{
|
|
Console.Error.WriteLine("""
|
|
SQLInsight.LicenseTool
|
|
keygen --out <mappe> Opret nøglepar (privat PEM + offentlig nøgle)
|
|
issue --key <privat.pem> --kid <id> --customer <navn> --plan <Professional|Enterprise>
|
|
--expires <yyyy-mm-dd> [--instances <antal>] Udsted licens
|
|
verify --public <base64> --kid <id> <licens> Verificér licens
|
|
""");
|
|
return 1;
|
|
}
|
|
|
|
int KeyGen(string folder)
|
|
{
|
|
using var key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
|
var kid = "k" + DateTime.UtcNow.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
|
|
Directory.CreateDirectory(folder);
|
|
var privatePath = Path.Combine(folder, $"sqlinsight-{kid}.private.pem");
|
|
File.WriteAllText(privatePath, key.ExportPkcs8PrivateKeyPem());
|
|
var publicKey = Convert.ToBase64String(key.ExportSubjectPublicKeyInfo());
|
|
Console.WriteLine($"Privat nøgle: {privatePath} (opbevar sikkert, aldrig i git)");
|
|
Console.WriteLine("Tilføj til src/SQLInsight.Licensing/trusted-keys.json:");
|
|
Console.WriteLine($$"""{ "id": "{{kid}}", "publicKey": "{{publicKey}}" }""");
|
|
return 0;
|
|
}
|
|
|
|
int Issue()
|
|
{
|
|
using var key = ECDsa.Create();
|
|
key.ImportFromPem(File.ReadAllText(Required("--key")));
|
|
var license = new License
|
|
{
|
|
LicenseId = Guid.NewGuid().ToString("N"),
|
|
Customer = Required("--customer"),
|
|
Plan = Enum.Parse<LicensePlan>(Required("--plan"), ignoreCase: true),
|
|
IssuedAt = DateTimeOffset.UtcNow,
|
|
ExpiresAt = DateTimeOffset.Parse(Required("--expires") + "T23:59:59Z", CultureInfo.InvariantCulture),
|
|
MaxInstances = int.Parse(Option("--instances") ?? "0", CultureInfo.InvariantCulture),
|
|
KeyId = Required("--kid"),
|
|
};
|
|
Console.WriteLine(SignedDocument.Sign(license, key));
|
|
return 0;
|
|
}
|
|
|
|
int Verify()
|
|
{
|
|
var keys = new TrustedKeys([KeyValuePair.Create(Required("--kid"), Required("--public"))]);
|
|
var status = new LicenseVerifier(keys, TimeProvider.System).Check(args[^1]);
|
|
Console.WriteLine($"{status.State}: {status.Message}");
|
|
return status.State is LicenseState.Valid ? 0 : 2;
|
|
}
|
|
|
|
string? Option(string name)
|
|
{
|
|
var i = Array.IndexOf(args, name);
|
|
return i >= 0 && i + 1 < args.Length ? args[i + 1] : null;
|
|
}
|
|
|
|
string Required(string name) => Option(name) ?? throw new ArgumentException($"{name} mangler.");
|