(NODEJS) AES-256-GCM разрывает кодировку pdf, gzip, png после расшифровки

#javascript #node.js #aes-gcm

#javascript #node.js #aes-gcm

Вопрос:

Жаль, что мне не помогли, потому что я не знаю, почему моя реализация AES-GCM нарушает кодировку файлов.
У меня есть API, который использует 1 функцию для шифрования / дешифрования с помощью AES-256-GCM. (С КЛЮЧОМ = буфер из 32 случайных байтов)

Вот функция:

 const aes256gcm = (key) => {
    const ALGO = 'aes-256-gcm';

    const encrypt = (str) => {
        try {
            const salt = crypto.randomBytes(64);
            const iv =  crypto.randomBytes(32);
            let derivedkey = crypto.pbkdf2Sync(key, salt, 55000, 32, 'sha512');
            const cipher = crypto.createCipheriv(ALGO, derivedkey, iv);
            let encrypted = Buffer.concat([cipher.update(str), cipher.final()]);
            const tag = cipher.getAuthTag();
            let buffer = Buffer.concat([salt, iv, encrypted]);
            encrypted = {
                tag: tag,
                buffer: buffer
            }
            return encrypted;
        } catch (e) {
            console.log(e);
        }
    };

    const decrypt = (data, authTag) => {
        try {
            const salt = data.slice(0, 64);
            const iv = data.slice(64, 96);
            const text = data.slice(96, data.length);
            authTag = new Buffer.from(authTag, 'base64');
            let derivedkey = crypto.pbkdf2Sync(key, salt, 55000, 32, 'sha512');
            let decipher = crypto.createDecipheriv(ALGO, derivedkey, iv);
            decipher.setAuthTag(authTag);
            let decrypted = decipher.update(text, 'binary')   decipher.final();
            return decrypted;
        } catch (e) {
            console.log(e);
        }
    };

    return {
        encrypt,
        decrypt
    };
};
  

С помощью этого кода я шифрую и записываю в файл результат:

 const aesCipher = aes.aes256gcm(aes.loadKey(path.resolve(__dirname, `key`)));
            const encrypted = aesCipher.encrypt(file.data);
            if (encrypted !== undefined) {
                fs.writeFile(`${file.name}.enc`, encrypted.buffer, function (err) {
                    if (err) return console.log(err);
                    console.log(`${file.name}.enc successfully created`);
                });
            }
  

И, наконец, с этим я расшифровываю и записываю содержимое в файл:

 const aesCipher = aes.aes256gcm(aes.loadKey(path.resolve(__dirname, `key`)));
        let filename = 'test1.gz';
        let authTag = 'puI0FfV4Btiy7iPiZFbwew==';
        let encrypted = fs.readFileSync(path.resolve(__dirname, `test1.gz.enc`));
        const decrypted = aesCipher.decrypt(encrypted, authTag);
        if (decrypted !== undefined) {
            const file = fs.createWriteStream(filename);
            file.write(new Buffer.from(decrypted, 'ascii'), function (err) {
                if (err) return console.log(err);
                console.log(`Successfully decrypted`);
                file.close();
            });
            res.send({
                status: true,
                message: 'File is decrypted',
            });
        }
  

Разница в моих файлах ввода / вывода :
Разница

Итак, что я делаю не так? Хорош ли мой процесс шифрования? Почему это хорошо работает только с файлами .txt?

Спасибо вам!

Комментарии:

1. Тот факт, что он явно в основном правильный, означает, что шифрование / дешифрование почти наверняка в порядке. Я с подозрением Buffer.from(decrypted, 'ascii') отношусь к; произвольные двоичные форматы не являются ASCII (если он работает с простым текстом ASCII, это подтвердило бы мои подозрения). Я бы надеялся, что JS сможет читать и записывать необработанные двоичные данные или, в противном случае, иметь некоторую кодировку, которую вы можете использовать для «эквивалента необработанных байтов», например latin-1 (в Python; технически он не использует все 256 байт, но Python позволяет ему соответствовать первым 256 порядковым номерам Unicode дажекогда это технически не определено для них).

2. Спасибо за ваш быстрый ответ @ShadowRanger! Я уже тестировал это с кодировкой «binary», которая является псевдонимом для latin-1, поэтому я думаю, что это не проблема. Есть другие идеи? (Он отлично работает с длинными файлами .txt)

Ответ №1:

Я думаю, что довольно небольшое изменение в вашей функции дешифрования должно устранить проблему, если вы просто обновите ее до:

 const decrypt = (data, authTag) => {
    try {
        const salt = data.slice(0, 64);
        const iv = data.slice(64, 96);
        const text = data.slice(96, data.length);
        authTag = new Buffer.from(authTag, 'base64');
        let derivedkey = crypto.pbkdf2Sync(key, salt, 55000, 32, 'sha512');
        let decipher = crypto.createDecipheriv(ALGO, derivedkey, iv);
        decipher.setAuthTag(authTag);
        let decrypted = Buffer.concat([decipher.update(text), decipher.final()]);
        return decrypted;
    } catch (e) {
        console.log(e);
    }
};
  

Я думаю, что предыдущая реализация неправильно объединяла результат для нетекстовых файлов.

Комментарии:

1. Это то же самое, если бы я хотел защитить PDF-файл паролем, а затем, когда я его открыл, он спросил у меня ключ?

2. Я не верю, что это одно и то же. Это, скорее всего, встроенный механизм паролей PDF.