BNN.KeyKet
1.0.2
dotnet add package BNN.KeyKet --version 1.0.2
NuGet\Install-Package BNN.KeyKet -Version 1.0.2
<PackageReference Include="BNN.KeyKet" Version="1.0.2" />
<PackageVersion Include="BNN.KeyKet" Version="1.0.2" />
<PackageReference Include="BNN.KeyKet" />
paket add BNN.KeyKet --version 1.0.2
#r "nuget: BNN.KeyKet, 1.0.2"
#:package BNN.KeyKet@1.0.2
#addin nuget:?package=BNN.KeyKet&version=1.0.2
#tool nuget:?package=BNN.KeyKet&version=1.0.2
KeyKet
Class library for hardware-backed cryptographic operations — TPM 2.0 and USB Token (PKCS#11) behind a single interface
Quick start
IKeyKetManager manager = KeyKetFactory.Create(new KeyKetOptions
{
Provider = KeyKetProviderType.Tpm,
RootPath = "/var/myapp", // optional — defaults to AppData
RsaKeyAccount = "device-001"
});
// or USB Token
IKeyKetManager manager = KeyKetFactory.Create(new KeyKetOptions
{
Provider = KeyKetProviderType.UsbToken,
Pkcs11LibPath = "/usr/lib/softhsm/libsofthsm2.so",
TokenSerial = "ABC123",
Pin = "1234"
});
manager.Dispose(); // always dispose when done
Dependency Injection
Fixed provider (most common)
builder.Services.AddSingleton<IKeyKetManager>(_ =>
KeyKetFactory.Create(new KeyKetOptions
{
Provider = KeyKetProviderType.Tpm,
RsaKeyAccount = "device-001"
}));
Runtime-switchable provider
When you need to swap hardware without restarting, use SwitchableKeyKetManager.
Register under both names — both resolve to the same singleton instance.
// Line 1: create the instance, register under concrete type
// so any controller/service that needs SwitchTo() can inject this directly
builder.Services.AddSingleton<SwitchableKeyKetManager>(_ =>
new SwitchableKeyKetManager(new KeyKetOptions
{
Provider = KeyKetProviderType.Tpm,
RsaKeyAccount = "device-001"
}));
// Line 2: alias IKeyKetManager → same instance (no second object created)
// ordinary services inject IKeyKetManager and never need to know about switching
builder.Services.AddSingleton<IKeyKetManager>(sp =>
sp.GetRequiredService<SwitchableKeyKetManager>());
// Regular service — inject by interface, unaware of hardware details
public class SigningService(IKeyKetManager manager) { ... }
// Admin endpoint — inject concrete type to access SwitchTo()
[HttpPost("admin/switch")]
public IActionResult Switch(
[FromServices] SwitchableKeyKetManager manager,
[FromBody] KeyKetOptions options)
{
manager.SwitchTo(options); // atomic swap, old instance auto-disposed
return Ok(manager.CurrentProvider.ToString());
}
SwitchTo()usesInterlocked.Exchange— pointer swap is atomic.
Requests in flight at the moment of switch may receive an error — this is accepted behaviour.
Both providers simultaneously (.NET 8 keyed services)
builder.Services.AddKeyedSingleton<IKeyKetManager>("tpm",
(_, _) => KeyKetFactory.Create(tpmOptions));
builder.Services.AddKeyedSingleton<IKeyKetManager>("usb",
(_, _) => KeyKetFactory.Create(usbOptions));
public class MyService(
[FromKeyedServices("tpm")] IKeyKetManager tpm,
[FromKeyedServices("usb")] IKeyKetManager usb) { }
Always register as
Singleton. TPM handles and PKCS#11 sessions are stateful hardware resources — never create per-request.
Return type
All methods return KeyKetCommonResult. Always check Success before using Data.
public class KeyKetCommonResult
{
public string Data { get; set; } // result value when successful
public string Error { get; set; } // error message when failed
public bool Success => Error == null;
}
var result = manager.GenerateAndGetPublicKey();
if (!result.Success)
throw new Exception(result.Error);
string publicKey = result.Data;
API reference
GetDeviceUserSerial()
Returns a stable unique identifier for the current machine.
| Returns | Data = machine ID string |
GenerateAndGetPublicKey()
Generates an RSA 2048 key pair on the hardware device if one does not exist, then returns the public key. Safe to call multiple times — only generates once.
| Returns | Data = Base64 DER encoded RSA public key |
serial |
Send
Datato your server so the server can encrypt SAD keys for this device.
CreateJWT(Dictionary<string, string> payload)
Creates a signed RS256 JWT using the device's own key pair.
payload |
Claims to embed — e.g. { "sub": "device-001", "exp": "..." } |
| Returns | Data = full JWT string header.payload.signature |
| Algorithm | RS256 — signed directly on hardware (TPM) or via PKCS#11 (USB Token) |
SignSAD(byte[] encryptedKey, byte[] iv, byte[] blob, string sad)
Decrypts the SAD private key using the hardware device, then uses it to sign sad. The decrypted key is disposed immediately after signing.
| Parameter | Description |
|---|---|
encryptedKey |
AES-256 key encrypted with this device's RSA public key (server-generated) |
iv |
AES-CBC initialization vector (16 bytes) |
blob |
SAD private key (PKCS#8) encrypted with the AES key above |
sad |
The data to sign — auto-detected as JWT, XML (Base64), or binary (Base64) |
sad format |
Detection | Data returned |
|---|---|---|
| JWT | Splits into ≥ 2 parts, header/payload are valid Base64url JSON | Completed JWT header.payload.signature |
| XML | Decodes as Base64 → valid XML | Original XML with <Signature> node appended, re-encoded as Base64 |
| Binary | Everything else | Base64 signature bytes |
CreateCSR(byte[] encryptedKey, byte[] iv, byte[] blob, string subjectName)
Decrypts the SAD private key (same mechanism as SignSAD) and creates a PKCS#10 Certificate Signing Request.
| Parameter | Description |
|---|---|
encryptedKey |
AES key encrypted with device RSA public key |
iv |
AES-CBC IV (16 bytes) |
blob |
SAD private key (PKCS#8) encrypted with AES key |
subjectName |
X.500 subject — e.g. "CN=device-001, O=MyCompany, C=VN" |
| Returns | Data = Base64 DER encoded PKCS#10 CSR |
Submit
Datato your CA to receive a signed certificate.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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. |
-
net5.0
- Microsoft.TSS (>= 2.1.1)
- Pkcs11Interop (>= 5.3.0)
- System.Management (>= 8.0.0)
-
net6.0
- Microsoft.TSS (>= 2.1.1)
- Pkcs11Interop (>= 5.3.0)
- System.Management (>= 8.0.0)
-
net7.0
- Microsoft.TSS (>= 2.1.1)
- Pkcs11Interop (>= 5.3.0)
- System.Management (>= 8.0.0)
-
net8.0
- Microsoft.TSS (>= 2.1.1)
- Pkcs11Interop (>= 5.3.0)
- System.Management (>= 8.0.0)
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 |
|---|