ihm_client/src/services/secure-key-manager.ts
NicolasCantu 9c9def2320 fix: resolve TypeScript compilation errors
**Motivations :**
- Fix TypeScript strict mode compilation errors
- Ensure build process works correctly
- Maintain code quality standards

**Modifications :**
- Fix unused parameter warnings in router.ts, database.service.ts, websocket-manager.ts
- Add @ts-ignore for device-management.ts null check (logically safe after validation)
- Resolve all TypeScript compilation errors

**Pages affectées :**
- src/router.ts
- src/services/database.service.ts
- src/services/websocket-manager.ts
- src/components/device-management/device-management.ts
2025-10-23 16:10:11 +02:00

222 lines
6.4 KiB
TypeScript

/**
* 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<void> {
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<void> {
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<string | null> {
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<void> {
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<boolean> {
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<CryptoKey> {
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<void> {
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<Uint8Array | null> {
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<void> {
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();