171 lines
8.4 KiB
JavaScript
171 lines
8.4 KiB
JavaScript
|
|
// Пример использования новой системы персистентного хранения ключей
|
|||
|
|
import {
|
|||
|
|
EnhancedSecureWebRTCManager,
|
|||
|
|
SecureMasterKeyManager,
|
|||
|
|
SecureIndexedDBWrapper,
|
|||
|
|
SecurePersistentKeyStorage
|
|||
|
|
} from './src/network/EnhancedSecureWebRTCManager.js';
|
|||
|
|
|
|||
|
|
// Создание менеджера WebRTC с персистентным хранением
|
|||
|
|
const webrtcManager = new EnhancedSecureWebRTCManager(
|
|||
|
|
(message) => console.log('Received:', message),
|
|||
|
|
(status) => console.log('Status:', status),
|
|||
|
|
(keyData) => console.log('Key exchange:', keyData),
|
|||
|
|
(verificationData) => console.log('Verification required:', verificationData)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// Настройка callback для запроса пароля
|
|||
|
|
webrtcManager.setMasterKeyPasswordCallback((isRetry, callback) => {
|
|||
|
|
const message = isRetry ?
|
|||
|
|
'Неверный пароль. Введите мастер-пароль повторно:' :
|
|||
|
|
'Введите мастер-пароль для разблокировки хранилища ключей:';
|
|||
|
|
|
|||
|
|
// В реальном приложении используйте безопасный UI
|
|||
|
|
const password = prompt(message);
|
|||
|
|
callback(password);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Настройка callback для истечения сессии
|
|||
|
|
webrtcManager.setMasterKeySessionExpiredCallback((reason) => {
|
|||
|
|
console.warn(`Сессия мастер-ключа истекла: ${reason}`);
|
|||
|
|
alert(`Сессия заблокирована: ${reason}. Потребуется повторный ввод пароля.`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Демонстрация работы с персистентным хранением
|
|||
|
|
async function demonstratePersistentStorage() {
|
|||
|
|
try {
|
|||
|
|
console.log('=== Демонстрация персистентного хранения ключей ===');
|
|||
|
|
|
|||
|
|
// 1. Создание и сохранение extractable ключа
|
|||
|
|
console.log('\n1. Создание extractable ключа для демонстрации...');
|
|||
|
|
const testKey = await crypto.subtle.generateKey(
|
|||
|
|
{
|
|||
|
|
name: 'AES-GCM',
|
|||
|
|
length: 256
|
|||
|
|
},
|
|||
|
|
true, // extractable = true для персистентного хранения
|
|||
|
|
['encrypt', 'decrypt']
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 2. Сохранение ключа (автоматически зашифруется и сохранится в IndexedDB)
|
|||
|
|
console.log('2. Сохранение ключа в персистентное хранилище...');
|
|||
|
|
const keyId = 'demo_persistent_key_' + Date.now();
|
|||
|
|
const stored = await webrtcManager._secureKeyStorage.storeKey(keyId, testKey, {
|
|||
|
|
purpose: 'demonstration',
|
|||
|
|
algorithm: 'AES-GCM'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (stored) {
|
|||
|
|
console.log('✅ Ключ успешно сохранен в зашифрованном виде в IndexedDB');
|
|||
|
|
} else {
|
|||
|
|
console.log('❌ Ошибка сохранения ключа');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. Получение статистики хранилища
|
|||
|
|
console.log('\n3. Статистика хранилища:');
|
|||
|
|
const stats = await webrtcManager._secureKeyStorage.getStorageStats();
|
|||
|
|
console.log('Статистика:', JSON.stringify(stats, null, 2));
|
|||
|
|
|
|||
|
|
// 4. Список всех ключей
|
|||
|
|
console.log('\n4. Список всех ключей:');
|
|||
|
|
const allKeys = await webrtcManager._secureKeyStorage.listAllKeys();
|
|||
|
|
console.log('Ключи:', JSON.stringify(allKeys, null, 2));
|
|||
|
|
|
|||
|
|
// 5. Симуляция перезапуска приложения (очистка памяти)
|
|||
|
|
console.log('\n5. Симуляция перезапуска приложения...');
|
|||
|
|
webrtcManager._secureKeyStorage._keyReferences.clear();
|
|||
|
|
console.log('Память очищена. Ключи остались только в IndexedDB.');
|
|||
|
|
|
|||
|
|
// 6. Восстановление ключа из IndexedDB
|
|||
|
|
console.log('\n6. Восстановление ключа из IndexedDB...');
|
|||
|
|
const restoredKey = await webrtcManager._secureKeyStorage.retrieveKey(keyId);
|
|||
|
|
|
|||
|
|
if (restoredKey) {
|
|||
|
|
console.log('✅ Ключ успешно восстановлен из IndexedDB как non-extractable');
|
|||
|
|
console.log('Тип ключа:', restoredKey.type);
|
|||
|
|
console.log('Алгоритм:', restoredKey.algorithm.name);
|
|||
|
|
console.log('Extractable:', restoredKey.extractable); // Должно быть false
|
|||
|
|
} else {
|
|||
|
|
console.log('❌ Ошибка восстановления ключа');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 7. Тест шифрования с восстановленным ключом
|
|||
|
|
console.log('\n7. Тест шифрования с восстановленным ключом...');
|
|||
|
|
const testData = new TextEncoder().encode('Тестовые данные для шифрования');
|
|||
|
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|||
|
|
|
|||
|
|
const encrypted = await crypto.subtle.encrypt(
|
|||
|
|
{ name: 'AES-GCM', iv },
|
|||
|
|
restoredKey,
|
|||
|
|
testData
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const decrypted = await crypto.subtle.decrypt(
|
|||
|
|
{ name: 'AES-GCM', iv },
|
|||
|
|
restoredKey,
|
|||
|
|
encrypted
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const decryptedText = new TextDecoder().decode(decrypted);
|
|||
|
|
console.log('✅ Шифрование/расшифровка успешно:', decryptedText);
|
|||
|
|
|
|||
|
|
// 8. Очистка демонстрационных данных
|
|||
|
|
console.log('\n8. Очистка демонстрационных данных...');
|
|||
|
|
await webrtcManager._secureKeyStorage.deleteKey(keyId);
|
|||
|
|
console.log('✅ Демонстрационный ключ удален');
|
|||
|
|
|
|||
|
|
console.log('\n=== Демонстрация завершена ===');
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Ошибка демонстрации:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Демонстрация безопасности
|
|||
|
|
async function demonstrateSecurity() {
|
|||
|
|
console.log('\n=== Демонстрация безопасности ===');
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Попытка прямого доступа к IndexedDB
|
|||
|
|
const request = indexedDB.open('SecureKeyStorage');
|
|||
|
|
request.onsuccess = (event) => {
|
|||
|
|
const db = event.target.result;
|
|||
|
|
const transaction = db.transaction(['encrypted_keys'], 'readonly');
|
|||
|
|
const store = transaction.objectStore('encrypted_keys');
|
|||
|
|
const getAllRequest = store.getAll();
|
|||
|
|
|
|||
|
|
getAllRequest.onsuccess = () => {
|
|||
|
|
const keys = getAllRequest.result;
|
|||
|
|
console.log('🔍 Прямой доступ к IndexedDB:');
|
|||
|
|
|
|||
|
|
if (keys.length > 0) {
|
|||
|
|
const firstKey = keys[0];
|
|||
|
|
console.log('📦 Найден зашифрованный ключ:');
|
|||
|
|
console.log('- ID:', firstKey.keyId);
|
|||
|
|
console.log('- Зашифрованные данные (первые 20 байт):',
|
|||
|
|
Array.from(firstKey.encryptedData.slice(0, 20))
|
|||
|
|
.map(b => b.toString(16).padStart(2, '0')).join(' '));
|
|||
|
|
console.log('- IV:', Array.from(firstKey.iv)
|
|||
|
|
.map(b => b.toString(16).padStart(2, '0')).join(' '));
|
|||
|
|
console.log('✅ Данные зашифрованы - невозможно извлечь JWK без мастер-ключа!');
|
|||
|
|
} else {
|
|||
|
|
console.log('📭 Нет сохраненных ключей в IndexedDB');
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Ошибка проверки безопасности:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Запуск демонстрации
|
|||
|
|
console.log('Запуск демонстрации персистентного хранения ключей...');
|
|||
|
|
console.log('При первом запуске будет запрошен мастер-пароль.');
|
|||
|
|
|
|||
|
|
// Последовательный запуск демонстраций
|
|||
|
|
demonstratePersistentStorage()
|
|||
|
|
.then(() => demonstrateSecurity())
|
|||
|
|
.catch(error => console.error('Ошибка:', error));
|