Шифрование AES/GCM/NoPadding в JAVA и дешифрование в JavaScript

Я использую AES/GCM/NoPadding 128 бит. Я хочу зашифровать в Java и расшифровать его в Javascript.

Когда я шифрую в JS и пытаюсь расшифровать в Java, я получаю сообщение об ошибке «Несоответствие тегов! null».

Когда я шифрую в Java и пытаюсь расшифровать в JS, я получаю сообщение об ошибке

internal/crypto/cipher.js:164
  const ret = this._handle.final();
                           ^

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (internal/crypto/cipher.js:164:28)
    at decrypt (/tmp/HoErdq6TQ2.js:51:58)

Чего мне не хватает в моем JS Пожалуйста, предложите исправления в коде JS, код Java не может быть изменен, так как он используется в производстве.

Java-код:


import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import java.util.logging.Logger;
import java.util.Base64;

public class HelloWorld {

    private final static Logger LOGGER =  Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

    public static void main(String []args) {
        String masterKey = "2f12cb0f1d2e3d12345f1af2b123dce4";
        String encrypted = aesEncryptStringV2("Hello, World!", masterKey);
        System.out.println(encrypted);
        

        String decrypted = aesDecryptStringV2(encrypted, masterKey);
        System.out.println(decrypted);
    }


    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 16;

    private static SecretKeySpec setKeyV2(final String myKey) {
        try {
            byte[] newKey = myKey.getBytes(StandardCharsets.UTF_8);
            MessageDigest sha = MessageDigest.getInstance("SHA-512");

            newKey = sha.digest(newKey);
            newKey = Arrays.copyOf(newKey, 16);

            return new SecretKeySpec(newKey, "AES");
        } catch (Exception e) {
            System.out.println("Error in setKeyV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesEncryptStringV2(
        final String strToEncrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.ENCRYPT_MODE, newSecretKey, gcmParameterSpec);
            return Base64.getEncoder()
                   .encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8
                                                                       )));
        } catch (Exception e) {
            System.out.println("Error in aesEncryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesDecryptStringV2(
        final String strToDecrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.DECRYPT_MODE, newSecretKey, gcmParameterSpec);

            return new
                   String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error in aesDecryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }
}

Javascript-код:


const crypto = require('crypto');


const cryptoConfig = {
    cipherAlgorithm: 'aes-128-gcm',
    masterKey: '2f12cb0f1d2e3d12345f1af2b123dce4',
    ivLength: 12,
    tagLength: 16,
    digest: 'sha512'
}

const generateKey = () => {
    var h = crypto.createHash(cryptoConfig.digest);
    h.update(cryptoConfig.masterKey, 'utf8');
    
    var k = h.digest().slice(0, 16);
    return k;
  };

function encrypt(content) {
    const iv = crypto.randomBytes(cryptoConfig.ivLength);
    const key = generateKey();

    const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, key, iv, {authTagLength: cryptoConfig.tagLength});

    const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);

    const tag = cipher.getAuthTag();

    return Buffer.concat([iv, encrypted, tag]).toString('base64');
}


const decrypt = (encdata, masterkey) => {
    const bData = Buffer.from(encdata, 'base64');

    const iv = bData.slice(0, 12);
    const tag = bData.slice(bData.length - 16, bData.length);
    const text = bData.slice(12, bData.length - 16);

    const key = generateKey(masterkey);

    const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
    decipher.setAuthTag(tag);


    const decrypted =
      decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


    return decrypted;
  };

const encryptedData = encrypt('hello world'); 
console.info('encrypt data -> ', encryptedData);

const decryptedData = decrypt(encryptedData); 
console.info('decryptedData -> ', decryptedData);

Код шифрования Java не возвращает IV/одноразовый номер, используемый для шифрования, поэтому он не является частью разбиения кода дешифрования. Серьезное предупреждение о безопасности: НЕ используйте приведенный выше код в рабочей среде, поскольку он НЕЗАЩИЩЕН. Вам следует подумать об изменении производственного кода, поскольку шифрование в режиме AES GCM становится полностью небезопасным при использовании одноразового номера более одного раза с одним и тем же ключом (код Java использует статический IV/одноразовый номер с 8 0x).

Michael Fehr 26.12.2022 08:46

@MichaelFehr Теперь я могу расшифровать. Спасибо за ясность и предупреждение о безопасности, переключимся на более безопасный.

kashif-sk 26.12.2022 09:06
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
163
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Таким образом, код Java не прикрепляет IV/nonce к выходным данным. Он использует буфер из 0 заданной длины.

Обновленный код расшифровки JS:

const decrypt = (encdata, masterkey) => {
const bData = Buffer.from(encdata, 'base64');

const iv = Buffer.alloc(12);
const tag = bData.slice(bData.length - 16, bData.length);
const text = bData.slice(0, bData.length - 16);

const key = generateKey(masterkey);

const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
decipher.setAuthTag(tag);


const decrypted =
  decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


return decrypted;
};

Серьезное предупреждение о безопасности: НЕ используйте приведенный выше код в рабочей среде, поскольку он НЕЗАЩИЩЕН. Вам следует подумать об изменении производственного кода, поскольку шифрование в режиме AES GCM становится полностью небезопасным при использовании одноразового номера более одного раза с одним и тем же ключом (код Java использует статический IV/одноразовый номер)

Кредиты - комментарий Майкла Фера

Другие вопросы по теме