8

我通常尝试做的是验证从 OpenID Connect 提供商(例如 Google)获得的id_token值。令牌使用 RSA 算法进行签名,公钥从Discovery文档(jwks_uri参数)中读取。例如,此处提供 JWK 格式的 Google 密钥:

{
  kty: "RSA",
  alg: "RS256",
  use: "sig",
  kid: "38d516cbe31d4345819b786d4d227e3075df02fc",
  n: "4fQxF6dFabDqsz9a9-XgVhDaadTBO4yBZkpUyUKrS98ZtpKIQRMLoph3bK9Cua828wwDZ9HHhUxOcbcUiNDUbubtsDz1AirWpCVRRauxRdRInejbGSqHMbg1bxWYfquKKQwF7WnrrSbgdInUZPv5xcHEjQ6q_Kbcsts1Nnc__8YRdmIGrtdTAcm1Ga8LfwroeyiF-2xn0mtWDnU7rblQI4qaXCwM8Zm-lUrpSUkO6E1RTJ1L0vRx8ieyLLOBzJNwxpIBNFolMK8-DYXDSX0SdR7gslInKCn8Ihd9mpI2QBuT-KFUi88t8TW4LsoWHAwlgXCRGP5cYB4r30NQ1wMiuQ",
  e: "AQAB"
}

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

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

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

输入不是有效的 Base-64 字符串,因为它包含非 base-64 字符、两个以上的填充字符或填充字符中的非法字符。

4

2 回答 2

9

RFC 7515定义 base64url 编码如下:

使用RFC 4648第 5 节中定义的 URL 和文件名安全字符集进行 Base64 编码,省略所有尾随 '=' 字符(如第 3.2 节允许的那样),并且不包含任何换行符、空格或其他附加字符. 注意空八位字节序列的base64url编码是空字符串。(有关在没有填充的情况下实现 base64url 编码的说明,请参见附录 C。)

RFC 4648 将“使用 URL 和文件名安全字母的 Base 64 编码”定义为常规 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);
}

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

于 2015-12-15T09:12:52.073 回答
1

谁曾经从 Java 来到这里:有两种方法java.util.Base64

  • getDecoder()
  • getUrlDecoder()

正如您可能假设的那样:使用第二个已经为您完成了所有字符替换。

于 2019-11-04T09:55:11.067 回答