Сообщение об ошибке
java.lang.IllegalArgumentException: Illegal base64 character 2d
Также выводится как
- java.lang.IllegalArgumentException: Illegal base64 character a
- java.lang.IllegalArgumentException: Illegal base64 character d
- java.lang.IllegalArgumentException: Illegal base64 character 5f
- java.lang.IllegalArgumentException: Illegal base64 character 20
Шестнадцатеричное значение указывает на недопустимый символ: `2d` — «-», `5f` — «_», `a` — LF, `d` — CR, `20` — пробел.
Java (JDK) — java.util.Base64.Decoder.decode
Что это означает
`Base64.getDecoder()` — строгий декодер по RFC 4648: он принимает только `A–Z a–z 0–9 + /` и дополнение `=` и останавливается на первом же символе не из этого набора. Шестнадцатеричное число — код этого символа: `2d` — это `-` (дефисы из `-----BEGIN …-----` или алфавит base64url), `a` — перевод строки, `d` — возврат каретки, `20` — пробел, `5f` — подчёркивание. С самими данными обычно всё в порядке: они просто в другой текстовой обёртке, чем ожидает декодер.
Почему это происходит
Не удалены заголовок и концевая строка PEM (2d)
частоПри декодировании всего PEM-файла декодер натыкается на первый `-` в `-----BEGIN`. В base64 закодировано только тело между строками обрамления.
Переводы строк внутри тела (a или d)
частоPEM переносит строки base64 через каждые 64 символа. Базовый декодер отвергает символ перевода строки, а в файлах, отредактированных в Windows, добавляется ещё и `\r`.
Данные base64url переданы базовому декодеру (2d или 5f)
частоВ сегментах JWT и координатах JWK используются `-` и `_` вместо `+` и `/`. Для них нужен `Base64.getUrlDecoder()`.
Пробелы или кавычки из конфигурации (20, 22)
иногдаКлючи, вставленные в YAML, файлы .properties или переменные окружения, могут обрасти пробелами, кавычками или экранированными последовательностями `\n`.
Как исправить
- 1.
Уберите обрамление и используйте декодер MIME
`Base64.getMimeDecoder()` пропускает разделители строк — именно это и нужно для тела PEM.
java String body = pem.replaceAll("-----(BEGIN|END) [A-Z ]+-----", ""); byte[] der = Base64.getMimeDecoder().decode(body); // ignores line separators and other non-alphabet characters - 2.
Используйте для данных JOSE декодер URL
Всё содержимое JWT и JWK закодировано в base64url; декодер URL к тому же принимает данные без дополнения.
java // JWT segments and JWK fields ("x", "y", "n", "e") use base64url without padding. byte[] header = Base64.getUrlDecoder().decode(jwt.split("\\.")[0]); byte[] x = Base64.getUrlDecoder().decode(jwk.get("x"));