Architecture Nitrokey Distribuée - Clés Physiques Distantes
Vue d'Ensemble de l'Architecture Corrigée
🎯 Contraintes Techniques Réelles
- Nitrokeys sur postes clients (non connectées au serveur PKIaaS)
- Accès PKCS#11 local via OpenSC sur chaque poste client
- Communication sécurisée entre clients et serveur pour parties Shamir
- Interface web pour orchestration et génération des parts
- Génération distribuée via interface web, déploiement sur sites distants
1. Architecture Système Distribuée
┌──────────────────────────────────┐
│ PKIaaS Server │
│ │
│ ┌─────────────────────────────┐ │
│ │ Shamir Generator Web UI │ │
│ │ - Génération parts CA │ │
│ │ - Attribution Nitrokeys │ │
│ │ - Packages déploiement │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Signing Orchestrator │ │
│ │ - Sessions de signature │ │
│ │ - Collecte parts Shamir │ │
│ │ - Reconstruction clé │ │
│ └─────────────────────────────┘ │
└──────────────┬───────────────────┘
│
HTTPS + WebSockets
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
│ │ │
┌───────▼────────┐ ┌────────▼────────┐ ┌───────▼────────┐
│ Client A │ │ Client B │ │ Client N │
│ │ │ │ │ │
│ ┌────────────┐ │ │ ┌─────────────┐ │ │ ┌────────────┐ │
│ │ Nitrokey │ │ │ │ Nitrokey │ │ │ │ Nitrokey │ │
│ │ HSM NK001 │ │ │ │ HSM NK002 │ │ │ │ HSM NK00N │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ Part 1/N │ │ │ │ Part 2/N │ │ │ │ Part N/N │ │
│ └────────────┘ │ │ └─────────────┘ │ │ └────────────┘ │
│ │ │ │ │ │
│ ┌────────────┐ │ │ ┌─────────────┐ │ │ ┌────────────┐ │
│ │Client Agent│ │ │ │Client Agent │ │ │ │Client Agent│ │
│ │PKCS#11 │ │ │ │PKCS#11 │ │ │ │PKCS#11 │ │
│ │OpenSC │ │ │ │OpenSC │ │ │ │OpenSC │ │
│ └────────────┘ │ │ └─────────────┘ │ │ └────────────┘ │
└────────────────┘ └─────────────────┘ └────────────────┘
Site Paris Site Lyon Site Remote
2. Composants de l'Architecture
🌐 2.1 Interface Web de Génération Shamir
Contrôleur Principal
<?php
namespace App\Http\Controllers;
use App\Services\ShamirSecretService;
use App\Services\NitrokeyDeploymentService;
use Illuminate\Http\Request;
class NitrokeyDistributedCAController extends Controller
{
private ShamirSecretService $shamirService;
private NitrokeyDeploymentService $deploymentService;
public function __construct(
ShamirSecretService $shamirService,
NitrokeyDeploymentService $deploymentService
) {
$this->shamirService = $shamirService;
$this->deploymentService = $deploymentService;
}
/**
* Interface de génération CA distribuée
*/
public function generateDistributedCA(Request $request)
{
$validated = $request->validate([
'ca_name' => 'required|string|max:255',
'organization' => 'required|string|max:255',
'country' => 'required|string|size:2',
'key_size' => 'required|integer|in:2048,3072,4096',
'validity_years' => 'required|integer|min:1|max:30',
'threshold' => 'required|integer|min:2|max:15',
'total_shares' => 'required|integer|min:3|max:20',
'nitrokey_assignments' => 'required|array',
'nitrokey_assignments.*.nitrokey_id' => 'required|string|regex:/^NK\d{3}$/',
'nitrokey_assignments.*.holder_name' => 'required|string|max:255',
'nitrokey_assignments.*.holder_email' => 'required|email',
'nitrokey_assignments.*.location' => 'required|string|max:255',
]);
// 1. Générer CA Master Private Key
$masterKeyPair = $this->generateCAKeyPair($validated['key_size']);
// 2. Créer le certificat CA auto-signé
$caCertificate = $this->createSelfSignedCACertificate(
$masterKeyPair,
$validated
);
// 3. Appliquer Shamir Secret Sharing sur la clé privée
$shamirShares = $this->shamirService->splitSecret(
$masterKeyPair['private_key'],
$validated['threshold'],
$validated['total_shares']
);
// 4. Créer les packages de déploiement
$deploymentPackages = [];
foreach ($shamirShares as $index => $share) {
$nitrokeyAssignment = $validated['nitrokey_assignments'][$index];
$deploymentPackages[] = $this->deploymentService->createDeploymentPackage([
'nitrokey_id' => $nitrokeyAssignment['nitrokey_id'],
'share_data' => $share,
'ca_metadata' => [
'ca_id' => $caCertificate['ca_id'],
'ca_name' => $validated['ca_name'],
'threshold' => $validated['threshold'],
'total_shares' => $validated['total_shares'],
],
'holder_info' => $nitrokeyAssignment
]);
}
// 5. Sauvegarder CA en base (SANS la clé privée maître)
$ca = $this->saveCertificateAuthority($caCertificate, $validated);
// 6. Sauvegarder métadonnées Shamir
$this->saveShamirMetadata($ca->id, $validated, $deploymentPackages);
// 7. EFFACEMENT SÉCURISÉ de la clé maître
sodium_memzero($masterKeyPair['private_key']);
unset($masterKeyPair);
return response()->json([
'success' => true,
'ca_id' => $ca->id,
'deployment_packages' => $deploymentPackages,
'next_steps' => [
'download_packages' => 'Télécharger les packages de déploiement',
'distribute_nitrokeys' => 'Distribuer physiquement les Nitrokeys',
'install_client_agents' => 'Installer agents clients sur postes',
'test_signing' => 'Tester une première signature distribuée'
]
]);
}
/**
* API pour télécharger package de déploiement spécifique
*/
public function downloadDeploymentPackage(string $caId, string $nitrokeyId)
{
$package = $this->deploymentService->getDeploymentPackage($caId, $nitrokeyId);
if (!$package) {
abort(404, 'Package de déploiement non trouvé');
}
// Générer archive sécurisée
$archive = $this->deploymentService->generateSecureArchive($package);
return response()->download($archive['path'], $archive['filename'])
->deleteFileAfterSend();
}
}
Service de Génération Shamir
<?php
namespace App\Services;
class ShamirSecretService
{
/**
* Divise un secret en N parts avec seuil M
*/
public function splitSecret(string $secret, int $threshold, int $totalShares): array
{
// Convertir la clé privée en nombre pour Shamir
$secretNumber = $this->convertSecretToNumber($secret);
// Générer polynôme aléatoire de degré (threshold - 1)
$polynomial = $this->generatePolynomial($secretNumber, $threshold);
// Générer les parts
$shares = [];
for ($x = 1; $x <= $totalShares; $x++) {
$y = $this->evaluatePolynomial($polynomial, $x);
$shares[] = [
'x' => $x,
'y' => $y,
'threshold' => $threshold,
'total_shares' => $totalShares,
'share_index' => $x - 1
];
}
return $shares;
}
/**
* Reconstruit le secret à partir de M parts
*/
public function reconstructSecret(array $shares, int $threshold): string
{
if (count($shares) < $threshold) {
throw new \InvalidArgumentException(
"Besoin d'au moins {$threshold} parts, seulement " . count($shares) . " fournies"
);
}
// Prendre seulement le nombre requis de parts
$selectedShares = array_slice($shares, 0, $threshold);
// Appliquer interpolation de Lagrange
$secretNumber = $this->lagrangeInterpolation($selectedShares);
// Reconvertir en clé privée
return $this->convertNumberToSecret($secretNumber);
}
private function convertSecretToNumber(string $secret): \GMP
{
// Convertir la clé privée PEM en nombre GMP
$binaryData = hash('sha256', $secret, true);
return gmp_import($binaryData);
}
private function convertNumberToSecret(\GMP $number): string
{
// Cette fonction nécessite une implémentation plus sophistiquée
// pour reconvertir correctement le nombre en clé privée
throw new \Exception("Implémentation complète requise pour production");
}
private function generatePolynomial(\GMP $secret, int $threshold): array
{
$polynomial = [$secret]; // Terme constant = secret
// Générer coefficients aléatoires pour les autres termes
for ($i = 1; $i < $threshold; $i++) {
$coefficient = gmp_random_bits(256);
$polynomial[] = $coefficient;
}
return $polynomial;
}
private function evaluatePolynomial(array $polynomial, int $x): \GMP
{
$result = gmp_init(0);
$xPower = gmp_init(1);
foreach ($polynomial as $coefficient) {
$term = gmp_mul($coefficient, $xPower);
$result = gmp_add($result, $term);
$xPower = gmp_mul($xPower, $x);
}
return $result;
}
private function lagrangeInterpolation(array $shares): \GMP
{
$result = gmp_init(0);
foreach ($shares as $i => $share) {
$numerator = gmp_init(1);
$denominator = gmp_init(1);
foreach ($shares as $j => $otherShare) {
if ($i !== $j) {
$numerator = gmp_mul($numerator, gmp_neg($otherShare['x']));
$denominator = gmp_mul($denominator, gmp_sub($share['x'], $otherShare['x']));
}
}
$lagrangeCoeff = gmp_div($numerator, $denominator);
$term = gmp_mul($share['y'], $lagrangeCoeff);
$result = gmp_add($result, $term);
}
return $result;
}
}
💾 2.2 Service de Déploiement Nitrokey
<?php
namespace App\Services;
use Illuminate\Support\Facades\Storage;
use ZipArchive;
class NitrokeyDeploymentService
{
/**
* Créer package de déploiement pour une Nitrokey
*/
public function createDeploymentPackage(array $config): array
{
$package = [
'nitrokey_id' => $config['nitrokey_id'],
'deployment_id' => $this->generateDeploymentId(),
'ca_metadata' => $config['ca_metadata'],
'holder_info' => $config['holder_info'],
'share_data' => $this->encryptShareForTransport($config['share_data']),
'deployment_scripts' => $this->generateDeploymentScripts($config),
'verification_data' => $this->generateVerificationData($config),
'created_at' => now()->toISOString()
];
return $package;
}
/**
* Générer archive sécurisée pour déploiement
*/
public function generateSecureArchive(array $package): array
{
$tempDir = storage_path('temp/deployments/' . $package['deployment_id']);
if (!is_dir($tempDir)) {
mkdir($tempDir, 0700, true);
}
// 1. Script de déploiement principal
$deploymentScript = $this->generateDeploymentScript($package);
file_put_contents($tempDir . '/deploy.sh', $deploymentScript);
chmod($tempDir . '/deploy.sh', 0700);
// 2. Agent client (binaire ou source)
$this->copyClientAgent($tempDir);
// 3. Configuration chiffrée
$configFile = $this->generateEncryptedConfig($package);
file_put_contents($tempDir . '/config.enc', $configFile);
// 4. Instructions de déploiement
$instructions = $this->generateDeploymentInstructions($package);
file_put_contents($tempDir . '/INSTRUCTIONS.md', $instructions);
// 5. Code QR pour vérification
$qrCode = $this->generateVerificationQR($package);
file_put_contents($tempDir . '/verification.png', $qrCode);
// 6. Créer archive ZIP protégée par mot de passe
$archivePath = $tempDir . '.zip';
$password = $this->generateSecurePassword();
$zip = new ZipArchive();
if ($zip->open($archivePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
$zip->setPassword($password);
$zip->setEncryptionName('deploy.sh', ZipArchive::EM_AES_256);
$zip->addFile($tempDir . '/deploy.sh', 'deploy.sh');
// Ajouter autres fichiers...
$files = ['config.enc', 'INSTRUCTIONS.md', 'verification.png'];
foreach ($files as $file) {
$zip->setEncryptionName($file, ZipArchive::EM_AES_256);
$zip->addFile($tempDir . '/' . $file, $file);
}
$zip->close();
}
// 7. Nettoyer répertoire temporaire
$this->cleanupTempDirectory($tempDir);
return [
'path' => $archivePath,
'filename' => "nitrokey-{$package['nitrokey_id']}-deployment.zip",
'password' => $password,
'holder_email' => $package['holder_info']['holder_email']
];
}
/**
* Générer script de déploiement spécifique à la Nitrokey
*/
private function generateDeploymentScript(array $package): string
{
return <<<BASH
#!/bin/bash
# Script de déploiement Nitrokey {$package['nitrokey_id']}
# CA: {$package['ca_metadata']['ca_name']}
# Responsable: {$package['holder_info']['holder_name']}
set -e
NITROKEY_ID="{$package['nitrokey_id']}"
CA_ID="{$package['ca_metadata']['ca_id']}"
echo "=== Déploiement Nitrokey HSM \$NITROKEY_ID ==="
# Vérifier prérequis
check_prerequisites() {
echo "Vérification des prérequis..."
# OpenSC installé
if ! command -v pkcs11-tool &> /dev/null; then
echo "ERREUR: OpenSC non installé. Installer avec:"
echo " Ubuntu/Debian: apt-get install opensc"
echo " RHEL/CentOS: yum install opensc"
exit 1
fi
# Nitrokey détectée
if ! pkcs11-tool --module /usr/lib/opensc-pkcs11.so --list-slots | grep -q "Nitrokey"; then
echo "ERREUR: Nitrokey HSM non détectée"
echo "Vérifier que la Nitrokey est connectée et initialisée"
exit 1
fi
echo "✓ Prérequis validés"
}
# Déployer la part Shamir
deploy_shamir_share() {
echo "Déploiement de la part Shamir..."
# Demander PIN utilisateur
echo -n "PIN utilisateur Nitrokey: "
read -s USER_PIN
echo
# Décrypter configuration
openssl enc -aes-256-cbc -d -in config.enc -out config.json -k "\$USER_PIN"
if [ \$? -ne 0 ]; then
echo "ERREUR: PIN incorrect ou configuration corrompue"
exit 1
fi
# Stocker données sur Nitrokey via PKCS#11
pkcs11-tool --module /usr/lib/opensc-pkcs11.so \\
--login --pin "\$USER_PIN" \\
--write-object config.json \\
--type data \\
--id "CA\$CA_ID" \\
--label "ShamirShare-\$NITROKEY_ID"
if [ \$? -eq 0 ]; then
echo "✓ Part Shamir déployée avec succès"
rm -f config.json # Nettoyage
else
echo "ERREUR: Échec du déploiement"
exit 1
fi
}
# Installation agent client
install_client_agent() {
echo "Installation de l'agent client..."
# Copier binaire agent
sudo cp nitrokey-client-agent /usr/local/bin/
sudo chmod +x /usr/local/bin/nitrokey-client-agent
# Créer service systemd
sudo tee /etc/systemd/system/nitrokey-agent.service > /dev/null <<EOF
[Unit]
Description=Nitrokey PKI Client Agent
After=network.target
[Service]
Type=simple
User=\$USER
ExecStart=/usr/local/bin/nitrokey-client-agent --config /home/\$USER/.nitrokey/config.json
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable nitrokey-agent
echo "✓ Agent client installé"
}
# Exécution principale
main() {
check_prerequisites
deploy_shamir_share
install_client_agent
echo
echo "=== Déploiement terminé ==="
echo "Nitrokey \$NITROKEY_ID prête pour signature distribuée"
echo "Agent client: sudo systemctl start nitrokey-agent"
}
main
BASH;
}
}
🖥️ 2.3 Agent Client Nitrokey
Application Client Standalone
<?php
// /usr/local/bin/nitrokey-client-agent (PHP CLI app)
namespace NitrokeyAgent;
class ClientAgent
{
private $config;
private $pkcs11Module = '/usr/lib/opensc-pkcs11.so';
private $serverEndpoint;
private $nitrokeyId;
public function __construct(string $configPath)
{
$this->config = json_decode(file_get_contents($configPath), true);
$this->serverEndpoint = $this->config['server_endpoint'];
$this->nitrokeyId = $this->config['nitrokey_id'];
}
/**
* Démarrer agent en mode daemon
*/
public function startDaemon(): void
{
echo "Démarrage Nitrokey Agent {$this->nitrokeyId}\n";
// Vérifier Nitrokey disponible
if (!$this->checkNitrokeyAvailable()) {
throw new \Exception("Nitrokey HSM non disponible");
}
// WebSocket ou polling pour écouter demandes serveur
$this->listenForSigningRequests();
}
/**
* Participer à une session de signature
*/
public function participateInSigning(array $signingSession): bool
{
echo "Participation à signature session: {$signingSession['session_id']}\n";
try {
// 1. Vérifier autorisation
if (!$this->isAuthorizedForSigning($signingSession)) {
throw new \Exception("Non autorisé pour cette session");
}
// 2. Demander PIN utilisateur
$pin = $this->promptUserForPIN($signingSession);
// 3. Connecter à Nitrokey avec PIN
$this->authenticateWithNitrokey($pin);
// 4. Récupérer part Shamir
$shamirShare = $this->retrieveShamirShare($signingSession['ca_id']);
// 5. Chiffrer et transmettre au serveur
return $this->transmitShamirShare($signingSession, $shamirShare);
} catch (\Exception $e) {
echo "Erreur participation signature: " . $e->getMessage() . "\n";
return false;
}
}
private function checkNitrokeyAvailable(): bool
{
$cmd = "pkcs11-tool --module {$this->pkcs11Module} --list-slots";
$output = shell_exec($cmd);
return strpos($output, 'Nitrokey') !== false;
}
private function promptUserForPIN(array $session): string
{
// Interface graphique ou console
echo "\n=== Demande de signature PKI ===\n";
echo "CA: {$session['ca_name']}\n";
echo "Demandeur: {$session['requester']}\n";
echo "Domaine: {$session['domain']}\n";
echo "===================================\n";
echo -n "PIN Nitrokey pour autoriser signature: ";
system('stty -echo');
$pin = trim(fgets(STDIN));
system('stty echo');
echo "\n";
return $pin;
}
private function authenticateWithNitrokey(string $pin): void
{
// Test authentification
$cmd = "pkcs11-tool --module {$this->pkcs11Module} --login --pin " . escapeshellarg($pin) . " --list-objects";
$output = shell_exec($cmd . ' 2>&1');
if (strpos($output, 'error') !== false || strpos($output, 'failed') !== false) {
throw new \Exception("Authentification Nitrokey échouée");
}
}
private function retrieveShamirShare(string $caId): array
{
$objectLabel = "ShamirShare-{$this->nitrokeyId}";
$objectId = "CA{$caId}";
$cmd = "pkcs11-tool --module {$this->pkcs11Module} " .
"--login --pin {$this->getStoredPin()} " .
"--read-object --id {$objectId} --type data";
$shamirData = shell_exec($cmd);
if (empty($shamirData)) {
throw new \Exception("Part Shamir non trouvée sur Nitrokey");
}
return json_decode($shamirData, true);
}
private function transmitShamirShare(array $session, array $shamirShare): bool
{
// Chiffrer avec clé session
$encryptedShare = $this->encryptShareForTransmission($shamirShare, $session['session_key']);
// HTTP POST vers serveur
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "{$this->serverEndpoint}/api/signing-sessions/{$session['session_id']}/contribute",
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'nitrokey_id' => $this->nitrokeyId,
'encrypted_share' => $encryptedShare,
'signature' => $this->signContribution($encryptedShare)
]),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->config['api_token']
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
return $httpCode === 200;
}
private function listenForSigningRequests(): void
{
// Implémentation WebSocket ou polling HTTP
while (true) {
$requests = $this->checkForSigningRequests();
foreach ($requests as $request) {
$this->participateInSigning($request);
}
sleep(5); // Polling every 5 seconds
}
}
}
// Point d'entrée CLI
if ($argc < 2) {
echo "Usage: nitrokey-client-agent --config /path/to/config.json\n";
exit(1);
}
$configPath = null;
for ($i = 1; $i < $argc; $i++) {
if ($argv[$i] === '--config' && isset($argv[$i + 1])) {
$configPath = $argv[$i + 1];
break;
}
}
if (!$configPath || !file_exists($configPath)) {
echo "Fichier de configuration non trouvé: $configPath\n";
exit(1);
}
try {
$agent = new ClientAgent($configPath);
$agent->startDaemon();
} catch (\Exception $e) {
echo "Erreur agent: " . $e->getMessage() . "\n";
exit(1);
}
3. Protocole de Signature Distribuée
🔄 Séquence Complète de Signature
sequenceDiagram
participant Admin as Administrateur PKI
participant Server as PKIaaS Server
participant Client1 as Client A (NK001)
participant Client2 as Client B (NK002)
participant Client3 as Client N (NK00N)
Admin->>Server: Demande signature CSR
Server->>Server: Créer session signature temporaire
Server->>Client1: Notification signature requise
Server->>Client2: Notification signature requise
Server->>Client3: Notification signature requise
Client1->>Client1: Demander PIN utilisateur
Client1->>Client1: Authentifier Nitrokey
Client1->>Client1: Récupérer part Shamir
Client1->>Server: Transmettre part chiffrée
Client2->>Client2: Demander PIN utilisateur
Client2->>Client2: Authentifier Nitrokey
Client2->>Client2: Récupérer part Shamir
Client2->>Server: Transmettre part chiffrée
Client3->>Client3: Demander PIN utilisateur
Client3->>Client3: Authentifier Nitrokey
Client3->>Client3: Récupérer part Shamir
Client3->>Server: Transmettre part chiffrée
Server->>Server: Vérifier seuil atteint (3/5)
Server->>Server: Reconstituer clé privée
Server->>Server: Signer certificat
Server->>Server: Effacer clé reconstruite
Server->>Admin: Retourner certificat signé
4. Sécurité de l'Architecture
🔐 Mesures de Sécurité Implémentées
4.1 Transport des Parts Shamir
class SecureShareTransport
{
public function encryptShareForTransmission(array $share, string $sessionKey): string
{
// Utilisation ChaCha20-Poly1305 pour transport
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES);
$additionalData = hash('sha256', json_encode([
'timestamp' => time(),
'nitrokey_id' => $this->nitrokeyId,
'session_id' => $this->currentSessionId
]));
$encrypted = sodium_crypto_aead_chacha20poly1305_encrypt(
json_encode($share),
$additionalData,
$nonce,
$sessionKey
);
return base64_encode($nonce . $encrypted);
}
public function validateShareContribution(string $encryptedShare, string $signature): bool
{
// Vérifier signature avec certificat Nitrokey
$nitrokeyCert = $this->getNitrokeyCertificate($this->nitrokeyId);
return openssl_verify($encryptedShare, base64_decode($signature), $nitrokeyCert, OPENSSL_ALGO_SHA256);
}
}
4.2 Gestion des Sessions Temporaires
class SigningSessionManager
{
private $sessionTTL = 300; // 5 minutes
public function createSigningSession(string $caId, string $csr): string
{
$sessionId = bin2hex(random_bytes(16));
$sessionKey = random_bytes(32);
// Stocker en Redis avec expiration automatique
Redis::setex("signing_session:{$sessionId}", $this->sessionTTL, json_encode([
'ca_id' => $caId,
'csr' => $csr,
'session_key' => base64_encode($sessionKey),
'required_nitrokeys' => $this->getRequiredNitrokeys($caId),
'received_shares' => [],
'status' => 'waiting_contributions',
'created_at' => time(),
'expires_at' => time() + $this->sessionTTL
]));
return $sessionId;
}
public function cleanupExpiredSessions(): void
{
// Automatic cleanup par expiration Redis
// + nettoyage explicite des données sensibles
$expiredSessions = Redis::keys('signing_session:*');
foreach ($expiredSessions as $sessionKey) {
$sessionData = Redis::get($sessionKey);
if ($sessionData) {
$session = json_decode($sessionData, true);
if ($session['expires_at'] < time()) {
// Effacement sécurisé des parts reçues
if (isset($session['received_shares'])) {
foreach ($session['received_shares'] as &$share) {
sodium_memzero($share);
}
}
Redis::del($sessionKey);
}
}
}
}
}
5. Configuration et Déploiement
⚙️ Configuration .env Mise à Jour
# Nitrokey HSM Configuration
NITROKEY_ENABLED=true
NITROKEY_THRESHOLD=3
NITROKEY_TOTAL_SHARES=5
NITROKEY_MODE=distributed
# PKCS#11 Library Paths
NITROKEY_PKCS11_MODULE=/usr/lib/opensc-pkcs11.so
NITROKEY_PKCS11_SLOTS_AUTO_DETECT=true
# Distributed Signing Configuration
SIGNING_SESSION_TTL=300
SIGNING_WEBSOCKET_ENABLED=true
SIGNING_WEBSOCKET_PORT=8080
# Client Agent Configuration
CLIENT_AGENT_ENDPOINT=wss://pkiaas.domain.com:8080/ws/nitrokey
CLIENT_AGENT_HEARTBEAT_INTERVAL=30
CLIENT_AGENT_RECONNECT_ATTEMPTS=5
Cette architecture corrigée respecte les contraintes réelles des Nitrokeys distantes et utilise OpenSC/PKCS#11 pour l'accès local aux HSM, tout en maintenant un niveau de sécurité élevé pour la communication distribuée.