Декодирование Base64urlUInt-кодированного значения
Обычно я пытаюсь проверить значение id_token, полученное от поставщика OpenID Connect (например, Google). Маркер подписывается алгоритмом RSA, а открытый ключ считывается из документа Discovery (параметр jwks_uri). Например, ключи Google доступны здесь в формате JWK:
{
kty: "RSA",
alg: "RS256",
use: "sig",
kid: "38d516cbe31d4345819b786d4d227e3075df02fc",
n: "4fQxF6dFabDqsz9a9-XgVhDaadTBO4yBZkpUyUKrS98ZtpKIQRMLoph3bK9Cua828wwDZ9HHhUxOcbcUiNDUbubtsDz1AirWpCVRRauxRdRInejbGSqHMbg1bxWYfquKKQwF7WnrrSbgdInUZPv5xcHEjQ6q_Kbcsts1Nnc__8YRdmIGrtdTAcm1Ga8LfwroeyiF-2xn0mtWDnU7rblQI4qaXCwM8Zm-lUrpSUkO6E1RTJ1L0vRx8ieyLLOBzJNwxpIBNFolMK8-DYXDSX0SdR7gslInKCn8Ihd9mpI2QBuT-KFUi88t8TW4LsoWHAwlgXCRGP5cYB4r30NQ1wMiuQ",
e: "AQAB"
}
Я собираюсь использовать класс RSACryptoServiceProvider для декодирования подписи. Чтобы инициализировать его, я должен предоставить RSAParameters значения модуля и экспоненты. Эти значения считываются из вышеуказанного JWK как n и e соответственно. Согласно спецификации эти значения являются значениями в кодировке Base64urlUInt:
Представление положительного или нулевого целочисленного значения в качестве base64url-кодирования беззнакового представления с прямым порядком байтов значения в виде последовательности октетов. Последовательность октетов ДОЛЖНА использовать минимальное количество октетов, необходимое для представления значения. Ноль представлен как BASE64URL(один октет с нулевым значением), то есть "AA".
Итак, мой вопрос, как декодировать эти значения, чтобы поместить их в RSAParameters? Я попытался декодировать их как общую строку Base64url (Convert.FromBase64String(modulusRaw)
), но это, очевидно, не работает и генерирует эту ошибку:
Входные данные не являются допустимой строкой Base-64, так как содержат не базовый 64-символ, более двух символов заполнения или недопустимый символ среди символов заполнения.
1 ответ
RFC 7515 определяет кодировку base64url следующим образом:
Кодирование Base64 с использованием набора символов, безопасного для URL и имени файла, определенного в Разделе 5 RFC 4648, с опущенными всеми завершающими символами '=' (как разрешено Разделом 3.2) и без включения любых разрывов строк, пробелов или других дополнительных символов., Обратите внимание, что кодировка base64url пустой последовательности октетов является пустой строкой. (См. Приложение C для замечаний по реализации кодирования base64url без заполнения.)
RFC 4648 определяет "кодирование Base 64 с URL-адресом и безопасным алфавитом имени файла" как обычный base64, но:
- Заполнение может быть опущено (как здесь)
- С помощью
-
вместо+
а также_
вместо/
Так что использовать обычный Convert.FromBase64String
Вам просто нужно обратить этот процесс вспять:
static byte[] FromBase64Url(string base64Url)
{
string padded = base64Url.Length % 4 == 0
? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
string base64 = padded.Replace("_", "/")
.Replace("-", "+");
return Convert.FromBase64String(base64);
}
Возможно, что этот код уже существует где-то в рамках, но я не знаю об этом.
Кто хоть раз заходит сюда с Java: есть два метода в java.util.Base64
:
getDecoder()
getUrlDecoder()
Как вы, вероятно, предположили: взяв второй, все символы заменяются за вас.