Sdkey 0.4.0

dotnet add package Sdkey --version 0.4.0
                    
NuGet\Install-Package Sdkey -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sdkey" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sdkey" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Sdkey" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sdkey --version 0.4.0
                    
#r "nuget: Sdkey, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sdkey@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sdkey&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Sdkey&version=0.4.0
                    
Install as a Cake Tool

Sdkey

Official C# client for SDKey license authentication.

Implements the sealed session protocol (Ed25519-verified handshake, HKDF session keys, AES-256-GCM) for validate and client auth (register / login / upgrade). See PROTOCOL.md.

Install

dotnet add package Sdkey

Requires .NET 8.0+.

Quick start

Embed these values from the SDKey dashboard when you ship your app. AppVersion must exactly match the application version configured in the dashboard (applications.version), or the server returns APP_OUTDATED.

using Sdkey;

var client = new SdkeyClient(new SdkeyClientOptions
{
    ApiBaseUrl = "https://api.sdkey.dev",
    AppId = "YOUR_APP_ID",
    AppVersion = "1.0.0",
    AppPublicKeyB64 = "YOUR_APP_PUBLIC_KEY_BASE64",
});

try
{
    // Desktop: bind the license to this machine. Web: omit hwid (JSON key is not sent).
    var result = await client.ValidateAsync("SDKY-XXXX-XXXX-XXXX-XXXX", HardwareId.GetHardwareId());
    if (result.Success)
    {
        Console.WriteLine($"licensed {result.Status} tier={result.SubscriptionTier} {result.ExpiresAt}");
        Console.WriteLine(result.Message); // e.g. "validated"
    }
    else
    {
        // Sealed validate failures use `message`, not `error`.
        Console.Error.WriteLine($"denied {result.Code} {result.Message}");
    }
}
catch (SdkeyError err)
{
    // Init / transport failures: Message is server `error`; ServerCode is server `code`.
    Console.Error.WriteLine($"{err.Code} {err.Message} serverCode={err.ServerCode}");
    throw;
}

ValidateAsync calls InitAsync automatically when no session exists. Sessions last ~15 minutes server-side; on SESSION_EXPIRED the client clears local state so the next call re-handshakes.

Hardware ID (HardwareId.GetHardwareId)

Opt-in helper for desktop apps. It reads a stable OS machine identifier, SHA-256-hashes the UTF-8 bytes, and returns lowercase hex (64 chars):

OS Source
Windows HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid
Linux /etc/machine-id, else /var/lib/dbus/machine-id
macOS IOPlatformUUID via ioreg
var hwid = HardwareId.GetHardwareId();
await client.ValidateAsync(licenseKey, hwid);

Do not call this for web clients — omit hwid so the server skips HWID lock / mismatch / HWID-ban checks. On unsupported platforms or when the ID is missing, the helper throws SdkeyError with code HWID_UNAVAILABLE (it never invents a random ID).

Client auth (register / login / upgrade)

Register / login / upgrade use the same sealed-session wire model as validate: auto-InitAsync when needed, then AES-256-GCM outer envelopes. Inner bodies omit appId / clientVersion (binding comes from the crypto session). Optional hwid follows the same omit-when-absent rules as validate.

Breaking change (0.4.0): plaintext client-auth bodies no longer work when the API has CRYPTO_ENFORCE=true (production).

// Reuses an active crypto session with validate when one already exists.
var auth = await client.RegisterAsync(new RegisterParams
{
    Username = "player1",
    Password = "••••••••",
    LicenseKey = "SDKY-XXXX-XXXX-XXXX-XXXX", // may be required by app settings
});

auth = await client.LoginAsync(new LoginParams
{
    Username = "player1",
    Password = "••••••••",
});

// Upgrade = username + license key only (no password).
auth = await client.UpgradeAsync(new UpgradeParams
{
    Username = "player1",
    LicenseKey = "SDKY-HIGHER-TIER-KEY",
});

Console.WriteLine(auth.SessionToken);

Auth failures throw SdkeyError with Code = AUTH_FAILED, Message = sealed plaintext message, and ServerCode = sealed plaintext code.

Where message vs error appears

Per-app responseMessages can customize many strings. The SDK surfaces whatever the server returns.

Surface Success text field Failure text field
Session init (none) errorSdkeyError.Message (ServerCode set)
Sealed validate message (ValidateResult.Message) message (ValidateResult.Message)
Sealed register/login/upgrade message (inside sealed body) messageSdkeyError.Message (ServerCode set)

Sealed validate success

{
  "success": true,
  "code": "OK",
  "message": "validated",
  "status": "active",
  "expiresAt": "2026-01-01T00:00:00.000Z",
  "subscriptionTier": 0,
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

Sealed validate failure

{
  "success": false,
  "code": "HWID_MISMATCH",
  "message": "Hardware ID mismatch",
  "status": null,
  "expiresAt": null,
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

Init failure (plaintext)

{
  "success": false,
  "error": "Client version outdated",
  "code": "APP_OUTDATED"
}

Sealed auth failure (after open + verify)

{
  "success": false,
  "code": "INVALID_CREDENTIALS",
  "message": "Invalid username or password",
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

API

new SdkeyClient(SdkeyClientOptions)

Option Type Description
ApiBaseUrl string API origin (no trailing slash)
AppId string Application UUID
AppVersion string Exact app version → sent as clientVersion
AppPublicKeyB64 string Raw Ed25519 public key (32 bytes), base64
HttpPost delegate Optional HTTP POST override (tests / custom transport)

Methods

  • InitAsync() — challenge handshake; verifies the signed hello; derives the AES session key
  • ValidateAsync(licenseKey, hwid?) — sealed validate; always decrypts then verifies the Ed25519 signature before trusting success
  • RegisterAsync(params) / LoginAsync(params) / UpgradeAsync(params) — sealed /api/v1/client/* (same outer envelope + verify order as validate)
  • GetSession() / ClearSession() — inspect or drop the local crypto session
  • HardwareId.GetHardwareId() — opt-in desktop HWID (SHA-256 hex); pass into validate / auth when binding to a machine

Errors

Protocol / transport failures throw SdkeyError with a Code:

INIT_FAILED · HELLO_SIGNATURE_INVALID · VALIDATE_RESPONSE_INVALID · RESPONSE_SIGNATURE_INVALID · SESSION_MISMATCH · CLOCK_SKEW · AUTH_FAILED · NETWORK · HWID_UNAVAILABLE

License denials on sealed validate (banned, HWID mismatch, etc.) return a normal ValidateResult with Success = false — they are not thrown.

This package does not include developer tooling / Bearer management APIs.

Security notes

  • Never ship app private keys in a client.
  • Do not skip signature verification — that is the anti-spoof binding.
  • This package is open source; the SDKey server remains a separate product.

Development

dotnet test
dotnet run --project examples/basic

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 117 7/31/2026
0.3.0 109 7/22/2026
0.2.0 106 7/18/2026
0.1.1 100 7/18/2026