Ошибка несоответствия тегов при расшифровке ключа с помощью библиотеки Google Tink

Я новичок в криптографии. Я работаю над poc для шифрования и расшифровки строки. Когда я расшифровываю зашифрованную строку, она иногда работает, но иногда выдает ошибку несоответствия тегов. Я что-то упустил?

Вот мой код:

EncryptionServiceImpl.java

public class EncryptionServiceImpl {
    private static final Logger log = LoggerFactory.getLogger("EncryptionServiceImpl");

    private final KeysetHandle keysetHandle;
    private final Aead aead;

    public EncryptionServiceImpl() throws GeneralSecurityException {
        AeadConfig.register();
        this.keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_GCM);
        aead = AeadFactory.getPrimitive(keysetHandle);
    }

    public String encrypt(String text) throws GeneralSecurityException {

        log.info(String.format("Encrypting %s", text));
        byte[] plainText = text.getBytes();
        byte[] additionalData = "masterkey".getBytes();
        byte[] cipherText = aead.encrypt(plainText,additionalData);

        String output = new String(cipherText);

        log.info(String.format("The encrypted text: %s", output));
        return output;
    }

    public String decrypt(String text) throws GeneralSecurityException {

        log.info(String.format("Decrypting %s", text));

        byte[] cipherText = text.getBytes();
        byte[] additionalData = "masterkey".getBytes();
        byte[] decipheredData = aead.decrypt(cipherText,additionalData);

        String output = new String(decipheredData);

        log.info(String.format("The decrypted text: %s", output));
        return output;
    }

}

EncryptionServiceImplTest.java

public class EncryptionServiceImplTest {

    @Test
    public void encrypt() throws IOException, GeneralSecurityException {
        EncryptionServiceImpl encryptionService = new EncryptionServiceImpl();
        String encryptedText = encryptionService.encrypt("Hello World");
        assertThat(encryptedText, Matchers.notNullValue());
    }

    @Test
    public void decrypt() throws IOException, GeneralSecurityException {
        EncryptionServiceImpl encryptionService = new EncryptionServiceImpl();

        String encryptedText = encryptionService.encrypt("Hello World");
        String decrypedText = encryptionService.decrypt(encryptedText);

        assertThat(decrypedText, Matchers.is("Hello World"));
    }
}

Исключение: ИНФОРМАЦИЯ: префикс зашифрованного текста соответствует ключу, но не может расшифровать: javax.crypto.AEADBadTagException: Несоответствие тегов! com.encryption.api.service.EncryptionServiceImplTest> расшифровать НЕУДАЧНО

java.security.GeneralSecurityException at EncryptionServiceImplTest.java:25

расшифровка не удалась java.security.GeneralSecurityException: расшифровка не удалась на com.google.crypto.tink.aead.AeadFactory$1.decrypt(AeadFactory.java:109) в com.encryption.api.service.EncryptionServiceImpl.decrypt(EncryptionServiceImpl.java:53) в com.encryption.api.service.EncryptionServiceImplTest.decrypt(EncryptionServiceImplTest.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) в java.lang.reflect.Method.invoke(Method.java:498) в org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) в org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) в org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) в org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) в org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) в org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) в org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) на org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) в org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) в org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) на org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) в org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) на org.junit.runners.ParentRunner.run(ParentRunner.java:363) в org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114) в org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57) в org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66) в org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) at sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) в java.lang.reflect.Method.invoke(Method.java:498) в org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) в org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) в org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) в org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) в com.sun.proxy.$Proxy1.processTestClass (неизвестный источник) в org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:108) at sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) в java.lang.reflect.Method.invoke(Method.java:498) в org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) в org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) в org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146) в org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128) в org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404) в org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) в org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) в org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) на java.lang.Thread.run(Thread.java:748)

1 тест завершен, 1 не пройден

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
3 037
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Если последовательность байтов зашифрованного сообщения хранится в строке, необходимо использовать соответствующую кодировку. Подходящий означает, что кодирование должно разрешать все байты или комбинации байтов в последовательности. Если это не так, значения в последовательности байтов изменяются автоматически и незаметно во время хранения. Если массив байтов затем восстанавливается из строки во время дешифрования, исходный и восстановленный массивы байтов различаются, и расшифровка завершается неудачно. Это очень хорошо объяснено здесь.

Поскольку AES-GCM генерирует новый вектор инициализации для каждого шифрования, зашифрованное сообщение отличается для каждого шифрования, даже с одинаковым открытым текстом.

И то, и другое приводит к тому, что в вашем примере шифрование иногда работает, а иногда нет: всякий раз, когда последовательность байтов совместима с используемой вами кодировкой, дешифрование работает, в противном случае - нет.

Если вы хотите быть независимым от кодировки, просто используйте сам байт-массив, т.е. encrypt-метод возвращает байт-массив вместо строки и аналогично вместо строки байт-массив передается в decrypt-метод .

Спасибо за помощь. Это сработало после добавления кодировки ISO-8859-1, как это предлагается в ссылке, которой вы поделились.

Yahya 09.04.2019 15:55

Для полноты: если зашифрованное сообщение уже сохранено в виде строки, то также следует учитывать Base64- и шестнадцатеричное кодирование. Оба сопоставляют значение каждый с читаемыми символами, чего не делает ISO-8859-1 (например, среди прочего, значения от 0x80 до 0x9f не присваиваются никакому символу в ISO-8859-1). Иногда это имеет значение. Но несмотря на это ISO-8859-1 конечно решает текущую проблему. Напомню, что вместо строки можно использовать массив байтов напрямую, чтобы вообще не было зависимости от кодировки.

Topaco 09.04.2019 19:25

Я избегал строк и имел дело непосредственно с байтовым массивом и шифрованием base64. Я все еще вижу эту проблему несколько раз... Вот мой измененный код:

Yahya 12.04.2019 18:29

Для всех, кто борется с этим при использовании TestNG: TestNG преобразует ваши массивы byte[] в строки при вызове assertEquals(byte[], byte[]), и это приведет к описанной здесь проблеме.

Lennart Koopmann 07.05.2020 02:46

Я пересмотрел код, но все же вижу, что расшифровка иногда не работает для одного и того же запроса.

public class Utils {
private static final Logger log = LoggerFactory.getLogger(Utils.class);
private Aead aead;
private static Utils utils;

private Utils() {
    try {
        AeadConfig.register();
        KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_GCM);
        aead = AeadFactory.getPrimitive(keysetHandle);
    } catch (GeneralSecurityException e) {
        log.error(String.format("Error occured: %s",e.getMessage())).log();
    }
}

public static Utils getInstance() {
    if (null == utils) {
        utils = new Utils();
    }

    return utils;
}

public String encrypt(String text) throws GeneralSecurityException, UnsupportedEncodingException {
    byte[] plainText = text.getBytes("ISO-8859-1");
    byte[] additionalData = null;
    byte[] cipherText = aead.encrypt(plainText,additionalData);

    String output = Base64.getEncoder().encodeToString(cipherText);
    return output;
}

public String decrypt(String text) throws GeneralSecurityException, UnsupportedEncodingException {
    byte[] cipherText = Base64.getDecoder().decode(text);
    byte[] additionalData = null;
    byte[] decipheredData = aead.decrypt(cipherText,additionalData);

    String output = new String(decipheredData,"ISO-8859-1");
    return output;
}

}

Я не могу найти ошибку в текущем коде. Я адаптировал старый тест JUnit и протестировал новый код. За 100 запусков ошибок не было. Для сравнения: процент ошибок старого кода составлял около 50%. Поэтому я не могу воспроизвести проблему. Возможно, вы используете неправильный тест? Но также не хватает некоторой информации для более детального анализа, т.е. как выглядит тест JUnit для нового кода и какой обычный текст используется? Сообщение об ошибке такое же? Как часто возникает ошибка?

Topaco 12.04.2019 20:42

У меня тоже не было ошибок в коде, когда я запускал на локальной машине. Но когда я развернулся в облако (облако Google) Я начал видеть ошибку.

Вот мой код Junit:

public class UtilsTest {

private static final Utils cryptographicUtils = Utils.getInstance();

@Test
public void encrypt() throws IOException, GeneralSecurityException {
    String encryptedText = cryptographicUtils.encrypt("Hello World");
    assertThat(encryptedText, Matchers.notNullValue());
}

@Test
public void decrypt() throws IOException, GeneralSecurityException {
    String encryptedText = cryptographicUtils.encrypt("Hello 123456");
    String decrypedText = cryptographicUtils.decrypt(encryptedText);

    assertThat(decrypedText, Matchers.is("Hello 123456"));
}

}

Соединение с облаком Google было бы важной информацией, о которой вы могли бы упомянуть ранее. Вероятно, текущая проблема не связана с шифрованием, а зависит от выбранной тестовой среды. Попробуйте следующую модификацию: Изменить модификатор доступа Utils-конструктора с private на public и использовать отдельный локальный Utils-экземпляр в encrypt-методе JUnit-теста и аналогичный, отдельный локальный Utils-экземпляр в decrypt-методе из JUnit-теста каждый с Utils cryptographicUtils = new Utils(). Проверьте еще раз, если проблема не устранена.

Topaco 13.04.2019 10:02

Я также рекомендую не отправлять ответы с дополнительными вопросами. Вместо этого вы можете отредактировать свой старый вопрос, добавить комментарий или задать новый вопрос. Иначе будет путаница.

Topaco 13.04.2019 11:17

Похоже, я не смогу расшифровать одну и ту же строку из другого экземпляра aead. Я изменил код, сделал общедоступными утилиты и создал новый экземпляр для шифрования и новый экземпляр для расшифровки. Сбой расшифровки с ошибкой сбоя расшифровки. То же, что я вижу в облаке. Вот мой тестовый код: @Test public void decrypt() выдает IOException, GeneralSecurityException { StringcryptedText = new Utils2().encrypt("Hello 123456"); Строка decryptedText = new Utils2().decrypt(encryptedText); assertThat(decryptedText, Matchers.is("Привет 123456")); }

Yahya 15.04.2019 16:16

Вероятно, это то, что происходит в облаке. У меня есть два экземпляра в облаке, каждый из которых имеет собственный экземпляр Aead. Зашифрованный текст одного экземпляра не может быть расшифрован в другом экземпляре

Yahya 15.04.2019 16:20

Я создал новый пост, чтобы внести ясность в свой вопрос - stackoverflow.com/questions/55692186/…

Yahya 15.04.2019 17:10

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