Add support for multiple encryption keys in data handling
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 1m1s
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 1m1s
This commit introduces a mechanism to handle multiple possible encryption keys for data decryption across various modules, including auth.js, members.js, newsletter.js, and encryption.js. It adds functions to retrieve potential old keys for migration purposes and updates the decryption logic to attempt decryption with these keys. Additionally, it includes warnings for users when old keys are used and provides guidance for re-encrypting data. This enhancement improves data migration capabilities and ensures backward compatibility with previously encrypted data.
This commit is contained in:
@@ -48,50 +48,81 @@ function encryptV2GCM(text, password) {
|
||||
}
|
||||
|
||||
function decryptLegacyCBC(encryptedData, password) {
|
||||
// Base64 dekodieren
|
||||
const combined = Buffer.from(encryptedData, 'base64')
|
||||
try {
|
||||
// Base64 dekodieren
|
||||
const combined = Buffer.from(encryptedData, 'base64')
|
||||
|
||||
// Komponenten extrahieren (v1: salt(32) + iv(16) + ciphertext)
|
||||
const salt = combined.subarray(0, SALT_LENGTH)
|
||||
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + LEGACY_IV_LENGTH)
|
||||
const encrypted = combined.subarray(SALT_LENGTH + LEGACY_IV_LENGTH)
|
||||
// Prüfe minimale Länge (salt + iv = 32 + 16 = 48 bytes)
|
||||
if (combined.length < SALT_LENGTH + LEGACY_IV_LENGTH) {
|
||||
throw new Error(`Ungültige Datenlänge: ${combined.length} bytes (erwartet mindestens ${SALT_LENGTH + LEGACY_IV_LENGTH})`)
|
||||
}
|
||||
|
||||
// Schlüssel ableiten
|
||||
const key = deriveKey(password, salt)
|
||||
// Komponenten extrahieren (v1: salt(32) + iv(16) + ciphertext)
|
||||
const salt = combined.subarray(0, SALT_LENGTH)
|
||||
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + LEGACY_IV_LENGTH)
|
||||
const encrypted = combined.subarray(SALT_LENGTH + LEGACY_IV_LENGTH)
|
||||
|
||||
// Decipher erstellen
|
||||
const decipher = crypto.createDecipheriv(LEGACY_ALGORITHM, key, iv)
|
||||
// Prüfe ob verschlüsselte Daten vorhanden sind
|
||||
if (encrypted.length === 0) {
|
||||
throw new Error('Keine verschlüsselten Daten gefunden')
|
||||
}
|
||||
|
||||
// Entschlüsseln
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(encrypted),
|
||||
decipher.final()
|
||||
])
|
||||
// Schlüssel ableiten
|
||||
const key = deriveKey(password, salt)
|
||||
|
||||
return decrypted.toString('utf8')
|
||||
// Decipher erstellen
|
||||
const decipher = crypto.createDecipheriv(LEGACY_ALGORITHM, key, iv)
|
||||
|
||||
// Entschlüsseln
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(encrypted),
|
||||
decipher.final()
|
||||
])
|
||||
|
||||
return decrypted.toString('utf8')
|
||||
} catch (error) {
|
||||
// Re-throw mit mehr Kontext
|
||||
throw new Error(`Legacy CBC Entschlüsselung fehlgeschlagen: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
function decryptV2GCM(encryptedData, password) {
|
||||
const b64 = encryptedData.slice(VERSION_PREFIX.length)
|
||||
const combined = Buffer.from(b64, 'base64')
|
||||
try {
|
||||
const b64 = encryptedData.slice(VERSION_PREFIX.length)
|
||||
const combined = Buffer.from(b64, 'base64')
|
||||
|
||||
// v2: salt(32) + iv(12) + tag(16) + ciphertext
|
||||
const salt = combined.subarray(0, SALT_LENGTH)
|
||||
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH)
|
||||
const tagStart = SALT_LENGTH + IV_LENGTH
|
||||
const tag = combined.subarray(tagStart, tagStart + AUTH_TAG_LENGTH)
|
||||
const encrypted = combined.subarray(tagStart + AUTH_TAG_LENGTH)
|
||||
// Prüfe minimale Länge (salt + iv + tag = 32 + 12 + 16 = 60 bytes)
|
||||
const minLength = SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH
|
||||
if (combined.length < minLength) {
|
||||
throw new Error(`Ungültige Datenlänge: ${combined.length} bytes (erwartet mindestens ${minLength})`)
|
||||
}
|
||||
|
||||
const key = deriveKey(password, salt)
|
||||
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv)
|
||||
decipher.setAuthTag(tag)
|
||||
// v2: salt(32) + iv(12) + tag(16) + ciphertext
|
||||
const salt = combined.subarray(0, SALT_LENGTH)
|
||||
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH)
|
||||
const tagStart = SALT_LENGTH + IV_LENGTH
|
||||
const tag = combined.subarray(tagStart, tagStart + AUTH_TAG_LENGTH)
|
||||
const encrypted = combined.subarray(tagStart + AUTH_TAG_LENGTH)
|
||||
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(encrypted),
|
||||
decipher.final()
|
||||
])
|
||||
// Prüfe ob verschlüsselte Daten vorhanden sind
|
||||
if (encrypted.length === 0) {
|
||||
throw new Error('Keine verschlüsselten Daten gefunden')
|
||||
}
|
||||
|
||||
return decrypted.toString('utf8')
|
||||
const key = deriveKey(password, salt)
|
||||
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv)
|
||||
decipher.setAuthTag(tag)
|
||||
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(encrypted),
|
||||
decipher.final()
|
||||
])
|
||||
|
||||
return decrypted.toString('utf8')
|
||||
} catch (error) {
|
||||
// Re-throw mit mehr Kontext
|
||||
throw new Error(`GCM v2 Entschlüsselung fehlgeschlagen: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +142,16 @@ export function encrypt(text, password) {
|
||||
*/
|
||||
export function decrypt(encryptedData, password) {
|
||||
try {
|
||||
if (typeof encryptedData === 'string' && encryptedData.startsWith(VERSION_PREFIX)) {
|
||||
if (!encryptedData || typeof encryptedData !== 'string') {
|
||||
throw new Error('Ungültige verschlüsselte Daten: muss ein String sein')
|
||||
}
|
||||
|
||||
if (!password || typeof password !== 'string') {
|
||||
throw new Error('Verschlüsselungsschlüssel fehlt oder ist ungültig')
|
||||
}
|
||||
|
||||
if (encryptedData.startsWith(VERSION_PREFIX)) {
|
||||
// v2 GCM Format
|
||||
return decryptV2GCM(encryptedData, password)
|
||||
}
|
||||
|
||||
@@ -119,7 +159,11 @@ export function decrypt(encryptedData, password) {
|
||||
return decryptLegacyCBC(encryptedData, password)
|
||||
} catch (error) {
|
||||
console.error('Entschlüsselungsfehler:', error)
|
||||
throw new Error('Fehler beim Entschlüsseln der Daten')
|
||||
// Re-throw mit mehr Details für besseres Debugging
|
||||
if (error.message.includes('Entschlüsselung fehlgeschlagen')) {
|
||||
throw error
|
||||
}
|
||||
throw new Error(`Fehler beim Entschlüsseln der Daten: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user