У меня есть быстрая функция для шифрования строки.
Когда я даю ему «abc», он создает зашифрованный текст без заполнения.
Когда я даю ему «a», он создает зашифрованный текст с дополнением «==».
Такое поведение понятно. Проблема в том, что мне нужно расшифровать строку в java.
Код Java отлично расшифровывает зашифрованный текст «abc», но не «a».
Выдает ошибку "Входной массив байтов имеет неверный конечный байт в...".
Очевидно, что он не может расшифровать байты заполнения.
Как решить эту проблему в java-коде? Я попытался создать экземпляр Cipher с помощью AES/GCM/PKCS5Padding, но он говорит, что не может найти провайдера, поддерживающего это дополнение.
БЫСТРЫЙ ШИФРОВАТЕЛЬ
static func encrypt() {
let plain = "a" // another string "abc"
static let secret = "my-xxx-bit-secret-my-secret-my-s"
static let nonceString = "fv1nixTVoYpSvpdA"
static let nonce = try! AES.GCM.Nonce(data: Data(base64Encoded: nonceString)!)
static let symKey = SymmetricKey(data: secret.data(using: .utf8)!)
let sealedBox = try! AES.GCM.seal(plain.data(using: .utf8)!, using: symKey, nonce: nonce)
let ciphertext = sealedBox.ciphertext.base64EncodedString()
let tag = sealedBox.tag
print("ciphertext: .\(ciphertext).")
print("tag: \(tag.base64EncodedString())")
}
ДЕКРИПТОР ЯВА
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
public class StringDecryptor {
public static void main(String[] args) throws Exception {
String actualText1 = "abc";
String cipherText1 = "UoRs";
String tag1 = "7VhlWAPpKka0CkmpshyOjw= = ";
decryptSimpleString(cipherText1, tag1);
String actualText2 = "a";
String cipherText2 = "Ug= = ";
String tag2 = "hkjeGS301OgQyGqdGDuHAA= = ";
decryptSimpleString(cipherText2, tag2);
}
public static void decryptSimpleString(String cipherText, String tag) throws Exception {
String secret = "my-xxx-bit-secret-my-secret-my-s";
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
String nonce = "fv1nixTVoYpSvpdA";
byte[] nonceBytes = Base64.getDecoder().decode(nonce);
byte[] tagBytes = Base64.getDecoder().decode(tag);
String ciphterTextWithTag = cipherText + tag;
byte[] ciphertextBytes = Base64.getDecoder().decode(ciphterTextWithTag);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonceBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
byte[] plaintextBytes = cipher.doFinal(ciphertextBytes);
String plaintext = new String(plaintextBytes, StandardCharsets.UTF_8);
System.out.println("plain text was "+plaintext);
}
}
Вывод Java-кода
plain text was abc
Exception in thread "main" java.lang.IllegalArgumentException: Input byte array has incorrect ending byte at 4
at java.base/java.util.Base64$Decoder.decode0(Base64.java:875)
at java.base/java.util.Base64$Decoder.decode(Base64.java:566)
at java.base/java.util.Base64$Decoder.decode(Base64.java:589)
at com.mydomain.crypto.StringDecryptor.decryptSimpleString(StringDecryptor.java:34)
at com.mydomain.crypto.StringDecryptor.main(StringDecryptor.java:21)
Если вы сомневаетесь, проверьте документацию.
Документация для Base64 начинается словами:
Этот класс состоит исключительно из статических методов для получения кодировщиков и декодеров для схемы кодирования Base64. Реализация этого класса поддерживает следующие типы Base64, указанные в RFC 4648 и RFC 2045.
Если мы перейдем по этой первой ссылке, мы увидим интернет-стандарт для кодировки Base64 (и некоторых других кодировок). Раздел 4 говорит:
Используется 65-символьный подмножество US-ASCII, что позволяет использовать 6 битов. представлено на печатный символ. (Дополнительный 65-й символ, "=", используется для обозначения специальной функции обработки.)
А чуть ниже написано:
Заполнение в конце данных выполняется с помощью символа '='. Поскольку все входные данные с основанием 64 представляют собой целое число октетов, могут возникнуть только следующие случаи:
- Конечный квант ввода кодирования является целым числом, кратным 24 битам; здесь конечная единица закодированного вывода будет целым числом, кратным 4 символам, без заполнения "=".
- Конечный квант ввода кодирования составляет ровно 8 бит; здесь последней единицей закодированного вывода будут два символа, за которыми следуют два символа заполнения "=".
- Окончательный квант ввода кодирования составляет ровно 16 бит; здесь последней единицей закодированного вывода будут три символа, за которыми следует один символ заполнения "=".
Надеюсь, это проясняет, что символ =
может появляться только в конце байтов, закодированных в Base64.
В вашем коде есть это:
String cipherText2 = "Ug= = ";
String tag2 = "hkjeGS301OgQyGqdGDuHAA= = ";
decryptSimpleString(cipherText2, tag2);
что означает, что первым аргументом decryptSimpleString, cipherText
, является «Ug==", а вторым аргументом decyptSimpleString, tag
, является более длинная последовательность байтов в кодировке Base64.
Метод decryptSimpleString содержит следующее:
String ciphterTextWithTag = cipherText + tag;
byte[] ciphertextBytes = Base64.getDecoder().decode(ciphterTextWithTag);
Упс. cipherText
содержит символы =
, а поскольку в спецификации Base64 четко указано, что =
может появляться только в конце содержимого, закодированного в Base64, cipherText + tag
не является допустимой последовательностью Base64.
Таким образом, вы не можете просто соединить две последовательности Base64 и предположить, что результат сам по себе является допустимой последовательностью Base64.
Если у вас есть две последовательности Base64 и вы хотите создать из них одну последовательность байтов, декодируйте каждую из них отдельно:
Base64.Decoder decoder = Base64.getDecoder();
byte[] decodedCipherText = decoder.decode(cipherText);
byte[] decodedTag = decoder.decode(tag);
byte[] ciphertextBytes =
new byte[decodedCipherText.length + decodedTag.length];
ByteBuffer.wrap(ciphertextBytes).put(decodedCipherText).put(decodedTag);
Огромное спасибо! Я новичок в шифровании, и вы очень хорошо это объяснили. Действительно, большое спасибо!