Archive completed sprint documentation and deliverables: ## SPRINT_3500 - Proof of Exposure (PoE) Implementation (COMPLETE ✅) - Windows filesystem hash sanitization (colon → underscore) - Namespace conflict resolution (Subgraph → PoESubgraph) - Mock test improvements with It.IsAny<>() - Direct orchestrator unit tests - 8/8 PoE tests passing (100% success) - Archived to: docs/implplan/archived/2025-12-23-sprint-3500-poe/ ## SPRINT_7100.0001 - Proof-Driven Moats Core (COMPLETE ✅) - Four-tier backport detection system - 9 production modules (4,044 LOC) - Binary fingerprinting (TLSH + instruction hashing) - VEX integration with proof-carrying verdicts - 42+ unit tests passing (100% success) - Archived to: docs/implplan/archived/2025-12-23-sprint-7100-proof-moats/ ## SPRINT_7100.0002 - Proof Moats Storage Layer (COMPLETE ✅) - PostgreSQL repository implementations - Database migrations (4 evidence tables + audit) - Test data seed scripts (12 evidence records, 3 CVEs) - Integration tests with Testcontainers - <100ms proof generation performance - Archived to: docs/implplan/archived/2025-12-23-sprint-7100-proof-moats/ ## SPRINT_3000_0200 - Authority Admin & Branding (COMPLETE ✅) - Console admin RBAC UI components - Branding editor with tenant isolation - Authority backend endpoints - Archived to: docs/implplan/archived/ ## Additional Documentation - CLI command reference and compliance guides - Module architecture docs (26 modules documented) - Data schemas and contracts - Operations runbooks - Security risk models - Product roadmap All archived sprints achieved 100% completion of planned deliverables. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
27 KiB
27 KiB
stella CLI - Crypto Plugin Development Guide
Sprint: SPRINT_4100_0006_0006 - CLI Documentation Overhaul
Overview
This guide explains how to develop custom cryptographic plugins for the stella CLI. Plugins enable support for regional cryptographic algorithms (GOST, eIDAS, SM) and custom signing infrastructure (HSMs, KMS, remote signers).
Prerequisites:
- .NET 10 SDK
- Understanding of cryptographic concepts (signing, verification, key management)
- Familiarity with Dependency Injection patterns
Plugin Interface Specification
ICryptoProvider
All crypto providers must implement the ICryptoProvider interface:
namespace StellaOps.Cli.Crypto;
/// <summary>
/// Core interface for all cryptographic providers
/// </summary>
public interface ICryptoProvider
{
/// <summary>
/// Unique provider name (e.g., "gost", "eidas", "sm", "default")
/// Used for --provider flag in CLI
/// </summary>
string Name { get; }
/// <summary>
/// Supported cryptographic algorithms
/// (e.g., "GOST12-256", "ECDSA-P256", "SM2")
/// </summary>
string[] SupportedAlgorithms { get; }
/// <summary>
/// Sign data with specified algorithm and key
/// </summary>
/// <param name="data">Data to sign</param>
/// <param name="algorithm">Algorithm to use</param>
/// <param name="keyRef">Key reference</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Signature bytes</returns>
Task<byte[]> SignAsync(
byte[] data,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken = default);
/// <summary>
/// Verify signature
/// </summary>
/// <param name="data">Original data</param>
/// <param name="signature">Signature to verify</param>
/// <param name="algorithm">Algorithm used</param>
/// <param name="keyRef">Key reference (public key)</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if valid, false otherwise</returns>
Task<bool> VerifyAsync(
byte[] data,
byte[] signature,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken = default);
/// <summary>
/// List available keys in this provider
/// </summary>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of available keys</returns>
Task<IReadOnlyList<CryptoKeyInfo>> ListKeysAsync(
CancellationToken cancellationToken = default);
}
ICryptoProviderDiagnostics (Optional)
For advanced diagnostics and health checks:
namespace StellaOps.Cli.Crypto;
/// <summary>
/// Optional interface for provider diagnostics and health checks
/// </summary>
public interface ICryptoProviderDiagnostics
{
/// <summary>
/// Run provider self-test
/// </summary>
Task<ProviderHealthCheck> HealthCheckAsync(
CancellationToken cancellationToken = default);
/// <summary>
/// Get provider version and capabilities
/// </summary>
ProviderInfo GetInfo();
}
/// <summary>
/// Health check result
/// </summary>
public sealed record ProviderHealthCheck
{
public required string ProviderName { get; init; }
public required bool IsHealthy { get; init; }
public required string[] Checks { get; init; }
public string? ErrorMessage { get; init; }
}
/// <summary>
/// Provider metadata
/// </summary>
public sealed record ProviderInfo
{
public required string Name { get; init; }
public required string Version { get; init; }
public required string[] Capabilities { get; init; }
public required string[] SupportedAlgorithms { get; init; }
}
Supporting Types
CryptoKeyReference
Represents a reference to a cryptographic key:
namespace StellaOps.Cli.Crypto;
/// <summary>
/// Reference to a cryptographic key
/// </summary>
public sealed record CryptoKeyReference
{
/// <summary>
/// Key identifier (e.g., "prod-key-2024", file path, HSM slot)
/// </summary>
public required string KeyId { get; init; }
/// <summary>
/// Key source type: "file", "hsm", "kms", "csp", "pkcs11"
/// </summary>
public required string Source { get; init; }
/// <summary>
/// Additional parameters (e.g., HSM PIN, KMS region, CSP container)
/// </summary>
public IReadOnlyDictionary<string, string>? Parameters { get; init; }
}
CryptoKeyInfo
Metadata about an available key:
namespace StellaOps.Cli.Crypto;
/// <summary>
/// Information about an available cryptographic key
/// </summary>
public sealed record CryptoKeyInfo
{
public required string KeyId { get; init; }
public required string Algorithm { get; init; }
public required string Source { get; init; }
public string? FriendlyName { get; init; }
public DateTimeOffset? ExpiresAt { get; init; }
public bool CanSign { get; init; }
public bool CanVerify { get; init; }
}
Implementation Guide
Step 1: Create Plugin Project
# Create new library project
dotnet new classlib -n StellaOps.Cli.Crypto.MyProvider
cd StellaOps.Cli.Crypto.MyProvider
# Add reference to interface project
dotnet add reference ../StellaOps.Cli.Crypto/StellaOps.Cli.Crypto.csproj
# Add required packages
dotnet add package Microsoft.Extensions.Options
dotnet add package Microsoft.Extensions.Logging
Project structure:
StellaOps.Cli.Crypto.MyProvider/
├── MyProviderCryptoProvider.cs # ICryptoProvider implementation
├── MyProviderOptions.cs # Configuration options
├── ServiceCollectionExtensions.cs # DI registration
├── Adapters/
│ ├── LibraryAdapter.cs # Native library adapter
│ └── RemoteSignerAdapter.cs # Remote signer client
└── StellaOps.Cli.Crypto.MyProvider.csproj
Step 2: Implement ICryptoProvider
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cli.Crypto;
namespace StellaOps.Cli.Crypto.MyProvider;
/// <summary>
/// Crypto provider for MyProvider algorithm
/// </summary>
public class MyProviderCryptoProvider : ICryptoProvider, ICryptoProviderDiagnostics
{
private readonly MyProviderOptions _options;
private readonly ILogger<MyProviderCryptoProvider> _logger;
public MyProviderCryptoProvider(
IOptions<MyProviderOptions> options,
ILogger<MyProviderCryptoProvider> logger)
{
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_logger.LogInformation("MyProvider crypto provider initialized");
}
public string Name => "myprovider";
public string[] SupportedAlgorithms => new[]
{
"MYPROVIDER-ALG1",
"MYPROVIDER-ALG2"
};
public async Task<byte[]> SignAsync(
byte[] data,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(data);
ArgumentNullException.ThrowIfNull(algorithm);
ArgumentNullException.ThrowIfNull(keyRef);
_logger.LogDebug("Signing {DataLength} bytes with {Algorithm}", data.Length, algorithm);
if (!SupportedAlgorithms.Contains(algorithm))
{
throw new NotSupportedException($"Algorithm '{algorithm}' is not supported by this provider");
}
// Implementation: Call native library, HSM, or remote signer
// Example: Use native library
if (_options.UseNativeLibrary)
{
return await SignWithNativeLibraryAsync(data, algorithm, keyRef, cancellationToken);
}
// Example: Use remote signer
if (_options.UseRemoteSigner)
{
return await SignWithRemoteSignerAsync(data, algorithm, keyRef, cancellationToken);
}
throw new InvalidOperationException("No signing method configured");
}
public async Task<bool> VerifyAsync(
byte[] data,
byte[] signature,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(data);
ArgumentNullException.ThrowIfNull(signature);
ArgumentNullException.ThrowIfNull(algorithm);
ArgumentNullException.ThrowIfNull(keyRef);
_logger.LogDebug("Verifying signature for {DataLength} bytes with {Algorithm}", data.Length, algorithm);
if (!SupportedAlgorithms.Contains(algorithm))
{
throw new NotSupportedException($"Algorithm '{algorithm}' is not supported by this provider");
}
// Implementation: Verify signature
if (_options.UseNativeLibrary)
{
return await VerifyWithNativeLibraryAsync(data, signature, algorithm, keyRef, cancellationToken);
}
throw new InvalidOperationException("No verification method configured");
}
public async Task<IReadOnlyList<CryptoKeyInfo>> ListKeysAsync(
CancellationToken cancellationToken = default)
{
_logger.LogDebug("Listing available keys");
var keys = new List<CryptoKeyInfo>();
// Example: List keys from configuration
if (_options.Keys != null)
{
foreach (var keyConfig in _options.Keys)
{
keys.Add(new CryptoKeyInfo
{
KeyId = keyConfig.KeyId,
Algorithm = keyConfig.Algorithm,
Source = keyConfig.Source,
FriendlyName = keyConfig.FriendlyName,
CanSign = true,
CanVerify = true
});
}
}
return keys.AsReadOnly();
}
public async Task<ProviderHealthCheck> HealthCheckAsync(
CancellationToken cancellationToken = default)
{
var checks = new List<string>();
var isHealthy = true;
string? errorMessage = null;
try
{
// Check 1: Native library loaded
if (_options.UseNativeLibrary)
{
if (IsNativeLibraryLoaded())
{
checks.Add("✅ Native library loaded");
}
else
{
checks.Add("❌ Native library not loaded");
isHealthy = false;
errorMessage = "Native library not found or failed to load";
}
}
// Check 2: Remote signer reachable
if (_options.UseRemoteSigner)
{
if (await IsRemoteSignerReachableAsync(cancellationToken))
{
checks.Add("✅ Remote signer reachable");
}
else
{
checks.Add("❌ Remote signer unreachable");
isHealthy = false;
errorMessage = "Remote signer not reachable";
}
}
// Check 3: Keys accessible
var keyList = await ListKeysAsync(cancellationToken);
if (keyList.Count > 0)
{
checks.Add($"✅ {keyList.Count} keys accessible");
}
else
{
checks.Add("⚠️ No keys configured");
}
}
catch (Exception ex)
{
checks.Add($"❌ Health check failed: {ex.Message}");
isHealthy = false;
errorMessage = ex.Message;
}
return new ProviderHealthCheck
{
ProviderName = Name,
IsHealthy = isHealthy,
Checks = checks.ToArray(),
ErrorMessage = errorMessage
};
}
public ProviderInfo GetInfo()
{
return new ProviderInfo
{
Name = Name,
Version = "1.0.0",
Capabilities = new[] { "sign", "verify", "list-keys" },
SupportedAlgorithms = SupportedAlgorithms
};
}
// Private helper methods
private async Task<byte[]> SignWithNativeLibraryAsync(
byte[] data,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken)
{
// Example: Call native library via P/Invoke or wrapper
// This is a placeholder - actual implementation depends on your crypto library
throw new NotImplementedException("Native library signing not implemented");
}
private async Task<bool> VerifyWithNativeLibraryAsync(
byte[] data,
byte[] signature,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken)
{
throw new NotImplementedException("Native library verification not implemented");
}
private async Task<byte[]> SignWithRemoteSignerAsync(
byte[] data,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken)
{
// Example: Call remote signer API
throw new NotImplementedException("Remote signer not implemented");
}
private bool IsNativeLibraryLoaded()
{
// Check if native library is loaded
return true; // Placeholder
}
private async Task<bool> IsRemoteSignerReachableAsync(CancellationToken cancellationToken)
{
// Ping remote signer
return true; // Placeholder
}
}
Step 3: Configuration Options
namespace StellaOps.Cli.Crypto.MyProvider;
/// <summary>
/// Configuration options for MyProvider crypto provider
/// </summary>
public sealed class MyProviderOptions
{
/// <summary>
/// Use native library for crypto operations
/// </summary>
public bool UseNativeLibrary { get; set; } = true;
/// <summary>
/// Path to native library (if not in standard location)
/// </summary>
public string? NativeLibraryPath { get; set; }
/// <summary>
/// Use remote signer API
/// </summary>
public bool UseRemoteSigner { get; set; } = false;
/// <summary>
/// Remote signer API URL
/// </summary>
public string? RemoteSignerUrl { get; set; }
/// <summary>
/// Remote signer API key
/// </summary>
public string? RemoteSignerApiKey { get; set; }
/// <summary>
/// Configured keys
/// </summary>
public List<KeyConfiguration>? Keys { get; set; }
public sealed class KeyConfiguration
{
public required string KeyId { get; init; }
public required string Algorithm { get; init; }
public required string Source { get; init; }
public string? FriendlyName { get; init; }
public string? FilePath { get; init; }
public string? HsmSlot { get; init; }
}
}
Step 4: DI Registration
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace StellaOps.Cli.Crypto.MyProvider;
/// <summary>
/// Service collection extensions for MyProvider crypto provider
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Add MyProvider crypto providers to DI container
/// </summary>
public static IServiceCollection AddMyProviderCryptoProviders(
this IServiceCollection services,
IConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
// Register provider as ICryptoProvider
services.AddSingleton<ICryptoProvider, MyProviderCryptoProvider>();
// Bind configuration
services.Configure<MyProviderOptions>(
configuration.GetSection("StellaOps:Crypto:Providers:MyProvider"));
return services;
}
}
Step 5: Configuration Example
# appsettings.myprovider.yaml
StellaOps:
Crypto:
Providers:
MyProvider:
UseNativeLibrary: true
NativeLibraryPath: "/usr/lib/libmyprovider.so"
UseRemoteSigner: false
RemoteSignerUrl: "https://signer.example.com/api/v1/sign"
RemoteSignerApiKey: "${MYPROVIDER_API_KEY}"
Keys:
- KeyId: "prod-key-2024"
Algorithm: "MYPROVIDER-ALG1"
Source: "file"
FilePath: "/etc/stellaops/keys/prod-key.pem"
FriendlyName: "Production Signing Key 2024"
- KeyId: "hsm-key-001"
Algorithm: "MYPROVIDER-ALG2"
Source: "hsm"
HsmSlot: "0"
FriendlyName: "HSM Key Slot 0"
Step 6: Update CLI Project
Update StellaOps.Cli.csproj
<Project Sdk="Microsoft.NET.Sdk">
<!-- ... -->
<!-- MyProvider plugin (custom distribution) -->
<ItemGroup Condition="'$(StellaOpsEnableMyProvider)' == 'true'">
<ProjectReference Include="..\StellaOps.Cli.Crypto.MyProvider\StellaOps.Cli.Crypto.MyProvider.csproj" />
<DefineConstants>$(DefineConstants);STELLAOPS_ENABLE_MYPROVIDER</DefineConstants>
</ItemGroup>
</Project>
Update Program.cs
using StellaOps.Cli.Crypto.Default;
#if STELLAOPS_ENABLE_GOST
using StellaOps.Cli.Crypto.Gost;
#endif
#if STELLAOPS_ENABLE_MYPROVIDER
using StellaOps.Cli.Crypto.MyProvider;
#endif
namespace StellaOps.Cli;
public class Program
{
public static async Task<int> Main(string[] args)
{
// ... configuration setup ...
// Register default crypto providers (always available)
services.AddDefaultCryptoProviders(configuration);
#if STELLAOPS_ENABLE_GOST
services.AddGostCryptoProviders(configuration);
#endif
#if STELLAOPS_ENABLE_MYPROVIDER
services.AddMyProviderCryptoProviders(configuration);
#endif
// ... rest of setup ...
}
}
Testing
Unit Tests
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Cli.Crypto;
using StellaOps.Cli.Crypto.MyProvider;
using Xunit;
namespace StellaOps.Cli.Crypto.MyProvider.Tests;
public class MyProviderCryptoProviderTests
{
[Fact]
public void Name_ReturnsExpectedName()
{
var provider = CreateProvider();
Assert.Equal("myprovider", provider.Name);
}
[Fact]
public void SupportedAlgorithms_ContainsExpectedAlgorithms()
{
var provider = CreateProvider();
Assert.Contains("MYPROVIDER-ALG1", provider.SupportedAlgorithms);
Assert.Contains("MYPROVIDER-ALG2", provider.SupportedAlgorithms);
}
[Fact]
public async Task SignAsync_WithUnsupportedAlgorithm_ThrowsNotSupportedException()
{
var provider = CreateProvider();
var data = "test"u8.ToArray();
var keyRef = new CryptoKeyReference { KeyId = "test-key", Source = "file" };
await Assert.ThrowsAsync<NotSupportedException>(async () =>
{
await provider.SignAsync(data, "UNSUPPORTED-ALG", keyRef);
});
}
[Fact]
public async Task ListKeysAsync_ReturnsConfiguredKeys()
{
var options = new MyProviderOptions
{
Keys = new List<MyProviderOptions.KeyConfiguration>
{
new()
{
KeyId = "key1",
Algorithm = "MYPROVIDER-ALG1",
Source = "file",
FriendlyName = "Test Key 1"
}
}
};
var provider = new MyProviderCryptoProvider(
Options.Create(options),
NullLogger<MyProviderCryptoProvider>.Instance);
var keys = await provider.ListKeysAsync();
Assert.Single(keys);
Assert.Equal("key1", keys[0].KeyId);
Assert.Equal("MYPROVIDER-ALG1", keys[0].Algorithm);
}
[Fact]
public async Task HealthCheckAsync_ReturnsHealthStatus()
{
var provider = CreateProvider();
var healthCheck = await provider.HealthCheckAsync();
Assert.NotNull(healthCheck);
Assert.Equal("myprovider", healthCheck.ProviderName);
}
[Fact]
public void GetInfo_ReturnsProviderInfo()
{
var provider = CreateProvider();
var info = provider.GetInfo();
Assert.Equal("myprovider", info.Name);
Assert.Equal("1.0.0", info.Version);
Assert.Contains("sign", info.Capabilities);
Assert.Contains("verify", info.Capabilities);
}
private static MyProviderCryptoProvider CreateProvider()
{
var options = Options.Create(new MyProviderOptions
{
UseNativeLibrary = true
});
return new MyProviderCryptoProvider(options, NullLogger<MyProviderCryptoProvider>.Instance);
}
}
Integration Tests
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Cli.Crypto;
using Xunit;
namespace StellaOps.Cli.Crypto.MyProvider.Tests;
public class MyProviderIntegrationTests
{
[Fact]
public void ServiceProvider_ResolvesMyProvider()
{
var services = new ServiceCollection();
var configuration = new ConfigurationBuilder().Build();
services.AddLogging();
services.AddMyProviderCryptoProviders(configuration);
var serviceProvider = services.BuildServiceProvider();
var providers = serviceProvider.GetServices<ICryptoProvider>().ToList();
var myProvider = providers.FirstOrDefault(p => p.Name == "myprovider");
Assert.NotNull(myProvider);
}
[Fact]
public async Task EndToEnd_SignAndVerify()
{
// This test requires actual crypto library or mocking
// Example structure:
var provider = CreateProvider();
var data = "test data"u8.ToArray();
var keyRef = new CryptoKeyReference
{
KeyId = "test-key",
Source = "file",
Parameters = new Dictionary<string, string>
{
["FilePath"] = "/path/to/key.pem"
}
};
// Sign
var signature = await provider.SignAsync(data, "MYPROVIDER-ALG1", keyRef);
Assert.NotNull(signature);
Assert.NotEmpty(signature);
// Verify
var isValid = await provider.VerifyAsync(data, signature, "MYPROVIDER-ALG1", keyRef);
Assert.True(isValid);
}
private MyProviderCryptoProvider CreateProvider()
{
// Setup provider with test configuration
throw new NotImplementedException();
}
}
Build and Distribution
Build Plugin
# Build plugin
dotnet build src/Cli/StellaOps.Cli.Crypto.MyProvider
# Run tests
dotnet test src/Cli/StellaOps.Cli.Crypto.MyProvider.Tests
Build CLI with Plugin
# Build CLI with MyProvider plugin
dotnet publish src/Cli/StellaOps.Cli \
--configuration Release \
--runtime linux-x64 \
-p:StellaOpsEnableMyProvider=true
Verify Plugin Inclusion
# Check available providers
./stella crypto providers
# Expected output:
# Available Crypto Providers:
# - default (.NET Crypto, BouncyCastle)
# - myprovider (MYPROVIDER-ALG1, MYPROVIDER-ALG2)
Best Practices
1. Error Handling
public async Task<byte[]> SignAsync(...)
{
try
{
// Signing logic
}
catch (FileNotFoundException ex)
{
throw new CryptoException($"Key file not found: {keyRef.KeyId}", ex);
}
catch (UnauthorizedAccessException ex)
{
throw new CryptoException($"Access denied to key: {keyRef.KeyId}", ex);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to sign data with {Algorithm}", algorithm);
throw;
}
}
2. Logging
_logger.LogDebug("Signing {DataLength} bytes with {Algorithm}", data.Length, algorithm);
_logger.LogInformation("Successfully signed data with {Algorithm}", algorithm);
_logger.LogWarning("Key {KeyId} expires soon: {ExpiresAt}", keyRef.KeyId, expiresAt);
_logger.LogError(ex, "Failed to sign data with {Algorithm}", algorithm);
3. Configuration Validation
public MyProviderCryptoProvider(IOptions<MyProviderOptions> options, ILogger<MyProviderCryptoProvider> logger)
{
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
// Validate configuration
if (_options.UseNativeLibrary && string.IsNullOrEmpty(_options.NativeLibraryPath))
{
throw new InvalidOperationException("NativeLibraryPath must be set when UseNativeLibrary is true");
}
if (_options.UseRemoteSigner && string.IsNullOrEmpty(_options.RemoteSignerUrl))
{
throw new InvalidOperationException("RemoteSignerUrl must be set when UseRemoteSigner is true");
}
}
4. Thread Safety
private readonly SemaphoreSlim _hsmLock = new(1, 1);
public async Task<byte[]> SignAsync(...)
{
// Protect HSM access with semaphore
await _hsmLock.WaitAsync(cancellationToken);
try
{
// HSM signing
}
finally
{
_hsmLock.Release();
}
}
Advanced Topics
HSM Integration
// Example: PKCS#11 HSM integration
private async Task<byte[]> SignWithHsmAsync(
byte[] data,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken)
{
var hsmSlot = keyRef.Parameters?["HsmSlot"];
var pin = keyRef.Parameters?["Pin"];
// Initialize PKCS#11 library
using var pkcs11 = new Pkcs11(_options.Pkcs11LibraryPath, AppType.MultiThreaded);
// Get slot
var slot = pkcs11.GetSlotList(SlotsType.WithTokenPresent)[int.Parse(hsmSlot!)];
// Open session
using var session = slot.OpenSession(SessionType.ReadOnly);
// Login
session.Login(CKU.User, pin);
// Find private key
var template = new List<IObjectAttribute>
{
session.Factories.ObjectAttributeFactory.Create(CKA.Label, keyRef.KeyId)
};
var foundObjects = session.FindAllObjects(template);
// Sign
var mechanism = session.Factories.MechanismFactory.Create(CKM.ECDSA);
var signature = session.Sign(mechanism, foundObjects[0], data);
return signature;
}
Remote Signer Integration
private async Task<byte[]> SignWithRemoteSignerAsync(
byte[] data,
string algorithm,
CryptoKeyReference keyRef,
CancellationToken cancellationToken)
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_options.RemoteSignerUrl!);
httpClient.DefaultRequestHeaders.Add("X-API-Key", _options.RemoteSignerApiKey);
var request = new
{
keyId = keyRef.KeyId,
algorithm = algorithm,
data = Convert.ToBase64String(data)
};
var response = await httpClient.PostAsJsonAsync("/api/v1/sign", request, cancellationToken);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<SignResponse>(cancellationToken);
return Convert.FromBase64String(result!.Signature);
}
private sealed record SignResponse(string Signature);
See Also
- CLI Architecture - Plugin architecture overview
- Command Reference - Crypto command usage
- Compliance Guide - Regional crypto requirements
- Distribution Matrix - Build and distribution guide
- Troubleshooting - Common plugin issues