/** * SecureKeyManager - Gestion sécurisée des clés privées * Chiffre les clés privées avant stockage et les déchiffre à la demande */ export class SecureKeyManager { private keyStore: CryptoKey | null = null; private salt: Uint8Array; private isInitialized: boolean = false; constructor() { this.salt = crypto.getRandomValues(new Uint8Array(16)); } /** * Initialise le gestionnaire de clés avec un mot de passe */ async initialize(password: string): Promise { try { const derivedKey = await this.deriveKey(password); this.keyStore = derivedKey; this.isInitialized = true; console.log('🔐 SecureKeyManager initialized'); } catch (error) { console.error('❌ Failed to initialize SecureKeyManager:', error); throw new Error('Failed to initialize secure key manager'); } } /** * Stocke une clé privée de manière chiffrée */ async storePrivateKey(key: string, password: string): Promise { if (!this.isInitialized) { await this.initialize(password); } try { const derivedKey = await this.deriveKey(password); const iv = crypto.getRandomValues(new Uint8Array(12)); const encryptedKey = await crypto.subtle.encrypt( { name: 'AES-GCM', iv }, derivedKey, new TextEncoder().encode(key) ); // Stocker la clé chiffrée avec l'IV const encryptedData = new Uint8Array(iv.length + encryptedKey.byteLength); encryptedData.set(iv); encryptedData.set(new Uint8Array(encryptedKey), iv.length); // Stocker dans IndexedDB de manière sécurisée await this.storeEncryptedData(encryptedData); console.log('🔐 Private key stored securely'); } catch (error) { console.error('❌ Failed to store private key:', error); throw new Error('Failed to store private key securely'); } } /** * Récupère et déchiffre une clé privée */ async getPrivateKey(password: string): Promise { try { const encryptedData = await this.getEncryptedData(); if (!encryptedData) {return null;} const derivedKey = await this.deriveKey(password); const iv = encryptedData.slice(0, 12); const encryptedKey = encryptedData.slice(12); const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv }, derivedKey, encryptedKey ); return new TextDecoder().decode(decrypted); } catch (error) { console.error('❌ Failed to retrieve private key:', error); return null; } } /** * Supprime toutes les clés stockées */ async clearKeys(): Promise { try { await this.clearEncryptedData(); this.keyStore = null; this.isInitialized = false; console.log('🔐 All keys cleared'); } catch (error) { console.error('❌ Failed to clear keys:', error); } } /** * Vérifie si une clé est stockée */ async hasStoredKey(): Promise { try { const encryptedData = await this.getEncryptedData(); return encryptedData !== null; } catch { return false; } } /** * Dérive une clé de chiffrement à partir du mot de passe */ private async deriveKey(password: string): Promise { const keyMaterial = await crypto.subtle.importKey( 'raw', new TextEncoder().encode(password), 'PBKDF2', false, ['deriveKey'] ); return crypto.subtle.deriveKey( { name: 'PBKDF2', salt: new Uint8Array(this.salt), iterations: 100000, hash: 'SHA-256' }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'] ); } /** * Stocke les données chiffrées dans IndexedDB */ private async storeEncryptedData(data: Uint8Array): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open('SecureKeyStore', 1); request.onerror = () => reject(new Error('Failed to open IndexedDB')); request.onsuccess = () => { const db = request.result; const transaction = db.transaction(['keys'], 'readwrite'); const store = transaction.objectStore('keys'); const putRequest = store.put(data, 'encryptedKey'); putRequest.onsuccess = () => resolve(); putRequest.onerror = () => reject(new Error('Failed to store encrypted data')); }; request.onupgradeneeded = () => { const db = request.result; if (!db.objectStoreNames.contains('keys')) { db.createObjectStore('keys'); } }; }); } /** * Récupère les données chiffrées depuis IndexedDB */ private async getEncryptedData(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open('SecureKeyStore', 1); request.onerror = () => reject(new Error('Failed to open IndexedDB')); request.onsuccess = () => { const db = request.result; const transaction = db.transaction(['keys'], 'readonly'); const store = transaction.objectStore('keys'); const getRequest = store.get('encryptedKey'); getRequest.onsuccess = () => { const result = getRequest.result; resolve(result ? new Uint8Array(result) : null); }; getRequest.onerror = () => reject(new Error('Failed to retrieve encrypted data')); }; request.onupgradeneeded = () => { const db = request.result; if (!db.objectStoreNames.contains('keys')) { db.createObjectStore('keys'); } }; }); } /** * Supprime les données chiffrées */ private async clearEncryptedData(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open('SecureKeyStore', 1); request.onerror = () => reject(new Error('Failed to open IndexedDB')); request.onsuccess = () => { const db = request.result; const transaction = db.transaction(['keys'], 'readwrite'); const store = transaction.objectStore('keys'); const deleteRequest = store.delete('encryptedKey'); deleteRequest.onsuccess = () => resolve(); deleteRequest.onerror = () => reject(new Error('Failed to clear encrypted data')); }; }); } } // Instance singleton pour l'application export const secureKeyManager = new SecureKeyManager();