解码Base64urlUInt编码的值

9
我通常要做的是验证从OpenID Connect提供程序(例如Google)获取的id_token值。令牌使用RSA算法签名,公钥从Discovery文档(参数为jwks_uri)中读取。例如,Google密钥以JWK格式here可用:
{
  kty: "RSA",
  alg: "RS256",
  use: "sig",
  kid: "38d516cbe31d4345819b786d4d227e3075df02fc",
  n: "4fQxF6dFabDqsz9a9-XgVhDaadTBO4yBZkpUyUKrS98ZtpKIQRMLoph3bK9Cua828wwDZ9HHhUxOcbcUiNDUbubtsDz1AirWpCVRRauxRdRInejbGSqHMbg1bxWYfquKKQwF7WnrrSbgdInUZPv5xcHEjQ6q_Kbcsts1Nnc__8YRdmIGrtdTAcm1Ga8LfwroeyiF-2xn0mtWDnU7rblQI4qaXCwM8Zm-lUrpSUkO6E1RTJ1L0vRx8ieyLLOBzJNwxpIBNFolMK8-DYXDSX0SdR7gslInKCn8Ihd9mpI2QBuT-KFUi88t8TW4LsoWHAwlgXCRGP5cYB4r30NQ1wMiuQ",
  e: "AQAB"
}

我将使用 RSACryptoServiceProvider 类来解码签名。为了初始化它,我必须提供 RSAParameters 的模数和指数值。这些值从上面的 JWK 中读取,分别对应 ne。根据 规范,这些值是 Base64urlUInt 编码值:

将正整数或零值表示为其无符号大端表示形式的八位序列的base64url编码。八位序列必须使用最少的八位字节来表示该值。零表示为 BASE64URL(单个零值八位字节),即“AA”。

所以,我的问题是如何解码这些值以将它们放入RSAParameters中?我尝试将它们解码为常规的Base64url字符串(Convert.FromBase64String(modulusRaw)),但这显然行不通,并生成以下错误:

输入不是有效的Base-64字符串,因为它包含一个非Base64字符,超过两个填充字符或在填充字符中有一个非法字符。


HttpServerUtility.UrlTokenDecode 在您的情况下不起作用吗? - Baldrick
@Baldrick 谢谢!但是,在这种情况下,这并不起作用。 - Dmitry Nikolaev
2个回答

10

RFC 7515 定义了如下 base64url 编码方式:

使用在RFC 4648 第5节中定义的URL-和文件名安全字符集进行 Base64 编码,去掉所有尾随的 '=' 字符(正如第3.2节所允许的),不包括任何换行符、空白或其他附加字符。请注意,空字节序列的 base64url 编码是空字符串。(有关不带填充实现base64url编码的说明,请参见附录C。)

RFC 4648 定义了“Base64编码与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);
}

有可能这段代码已经存在于框架中的某个地方,但我不知道。


1

如果你从Java来,这里有java.util.Base64中的两种方法:

  • getDecoder()
  • getUrlDecoder()

你可能已经猜到了:选择第二个方法会自动为你替换所有字符。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接