95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
import { createCipheriv, createDecipheriv, scryptSync, randomBytes } from "crypto";
|
|
import { ValueTransformer } from "typeorm";
|
|
|
|
export abstract class CodingHelper {
|
|
private static readonly algorithm = "aes-256-gcm";
|
|
private static readonly ivLength = 16;
|
|
private static readonly authTagLength = 16;
|
|
|
|
static entityBaseCoding(key: string = "", fallback: string = ""): ValueTransformer {
|
|
return {
|
|
from(val: string | null | undefined): string {
|
|
if (!val || val == "") return fallback;
|
|
try {
|
|
return CodingHelper.decrypt(key, val, true);
|
|
} catch (error) {
|
|
console.error("Decryption error:", error);
|
|
if (fallback == "<self>") return val;
|
|
else return fallback;
|
|
}
|
|
},
|
|
to(val: string | null | undefined): string {
|
|
const valueToEncrypt = val || fallback;
|
|
if (valueToEncrypt === "") return "";
|
|
|
|
try {
|
|
return CodingHelper.encrypt(key, valueToEncrypt, true);
|
|
} catch (error) {
|
|
console.error("Encryption error:", error);
|
|
if (fallback == "<self>") return val;
|
|
return "";
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
public static encrypt(phrase: string, content: string, passError = false): string {
|
|
if (!content) return "";
|
|
|
|
try {
|
|
// Generiere zufälligen IV für jede Verschlüsselung (sicherer als statischer IV)
|
|
const iv = randomBytes(this.ivLength);
|
|
const key = scryptSync(phrase, "salt", 32);
|
|
|
|
const cipher = createCipheriv(this.algorithm, Uint8Array.from(key), Uint8Array.from(iv));
|
|
|
|
// Verschlüssele den Inhalt
|
|
let encrypted = cipher.update(content, "utf8", "hex");
|
|
encrypted += cipher.final("hex");
|
|
|
|
// Speichere das Auth-Tag für GCM (wichtig für die Entschlüsselung)
|
|
const authTag = cipher.getAuthTag();
|
|
|
|
// Gib das Format: iv:verschlüsselter_text:authTag zurück
|
|
return Buffer.concat([
|
|
Uint8Array.from(iv),
|
|
Uint8Array.from(Buffer.from(encrypted, "hex")),
|
|
Uint8Array.from(authTag),
|
|
]).toString("base64");
|
|
} catch (error) {
|
|
if (passError) throw error;
|
|
console.error("Encryption failed:", error);
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public static decrypt(phrase: string, content: string, passError = false): string {
|
|
if (!content) return "";
|
|
|
|
try {
|
|
// Dekodiere den Base64-String
|
|
const buffer = Buffer.from(content, "base64");
|
|
|
|
// Extrahiere IV, verschlüsselten Text und Auth-Tag
|
|
const iv = buffer.subarray(0, this.ivLength);
|
|
const authTag = buffer.subarray(buffer.length - this.authTagLength);
|
|
const encryptedText = buffer.subarray(this.ivLength, buffer.length - this.authTagLength).toString("hex");
|
|
|
|
const key = scryptSync(phrase, "salt", 32);
|
|
|
|
// Erstelle Decipher und setze Auth-Tag
|
|
const decipher = createDecipheriv(this.algorithm, Uint8Array.from(key), Uint8Array.from(iv));
|
|
decipher.setAuthTag(Uint8Array.from(authTag));
|
|
|
|
// Entschlüssele den Text
|
|
let decrypted = decipher.update(encryptedText, "hex", "utf8");
|
|
decrypted += decipher.final("utf8");
|
|
|
|
return decrypted;
|
|
} catch (error) {
|
|
if (passError) throw error;
|
|
console.error("Decryption failed:", error);
|
|
return "";
|
|
}
|
|
}
|
|
}
|