Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decoding Base64urlUInt-encoded value

What I am generally trying to do, is to validate an id_token value obtained from an OpenID Connect provider (e.g. Google). The token is signed with the RSA algorithm and the public key is read from the Discovery document (the jwks_uri parameter). For example, Google keys are available here in the JWK format:

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

I am going to use the RSACryptoServiceProvider class for decoding the signature. To initialize it, I have to provide RSAParameters with the Modulus and Exponent values. These values are read from the above JWK as n and e correspondingly. According to the specification, these values are Base64urlUInt-encoded values:

The representation of a positive or zero integer value as the base64url encoding of the value's unsigned big-endian representation as an octet sequence. The octet sequence MUST utilize the minimum number of octets needed to represent the value. Zero is represented as BASE64URL(single zero-valued octet), which is "AA".

So, my question is how to decode these values to put them to RSAParameters? I tried decoding them as a common Base64url string (Convert.FromBase64String(modulusRaw)), but this obviously does not work and generates this error:

The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.

like image 367
Dmitry Nikolaev Avatar asked Dec 15 '15 08:12

Dmitry Nikolaev


2 Answers

RFC 7515 defines base64url encoding like this:

Base64 encoding using the URL- and filename-safe character set defined in Section 5 of RFC 4648, with all trailing '=' characters omitted (as permitted by Section 3.2) and without the inclusion of any line breaks, whitespace, or other additional characters. Note that the base64url encoding of the empty octet sequence is the empty string. (See Appendix C for notes on implementing base64url encoding without padding.)

RFC 4648 defines "Base 64 Encoding with URL and Filename Safe Alphabet" as regular base64, but:

  • The padding may be omitted (as it is here)
  • Using - instead of + and _ instead of /

So to use regular Convert.FromBase64String, you just need to reverse that process:

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);
}

It's possible that this code already exists somewhere in the framework, but I'm not aware of it.

like image 192
Jon Skeet Avatar answered Sep 20 '22 07:09

Jon Skeet


Who ever comes here from Java: there are two methods in java.util.Base64:

  • getDecoder()
  • getUrlDecoder()

As you probably assumed: taking the second one does all the chars replacements for you already.

like image 26
Ice09 Avatar answered Sep 18 '22 07:09

Ice09