1

I need to create a Json Web Token and signing it with an asymetric RS256 algorithm. The key used for signing should be taken from a X509 certificate and be validated by the receiver using the public key.

Using HMAC and password works fine, but the JWT created with the code listed below, produces constantlyI an "Invalid signature" at https://jwt.io

I got through many postings here, found a lot of usefull hints but none of them solved my problem. Maybe I've still a problem in understanding.

I know, the package System.IdentityModel.Tokens.Jwt actually provides all I need, but I want to understand what I'm doing wrong.

public static string Create(string userName) {
        // Get certificate from local store
        X509Store store = new X509Store(StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);
        X509Certificate2 x509Cert = (from X509Certificate2 c in store.Certificates where c.SubjectName.Name == $"CN={userName}" select c).First();

        if (x509Cert == null)
        {
            return string.Empty;
        }

        // Setup header
        var header = new { typ = "JWT", alg = "RS256" };
        var headerEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header)));

        // Setup payload/claims
        var payload = new
        {
            unique_name = $"{userName}@myCompany.com",
            role = "User",
            iss = x509Cert.Issuer,
            aud = "https://dev.myCompany.com",
            nbf = (Int32)DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds,
            exp = (Int32)DateTime.Now.AddMinutes(5).Subtract(new DateTime(1970, 1, 1)).TotalSeconds,
            jti = Guid.NewGuid(),
            x5t = x509Cert.Thumbprint
        };
        var payloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload)));

        // ... and sign, using the RSACertificateExtensions
        var rsa = x509Cert.GetRSAPrivateKey();
        var signed = rsa.SignData(Encoding.UTF8.GetBytes($"{headerEncoded}.{payloadEncoded}"), 
                                     HashAlgorithmName.SHA256, 
                                     RSASignaturePadding.Pss);

        var signatureEncoded = Convert.ToBase64String(signed);

        return $"{headerEncoded}.{payloadEncoded}.{signatureEncoded}";
    }
Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
Christian
  • 21
  • 2

1 Answers1

3

You have some errors building the token:

  • For RS256 use RSASignaturePadding.Pkcs1 not RSASignaturePadding.Pss

  • It is needed base64url encoding, not base64. Change Convert.ToBase64String(bytes) with


 Convert.ToBase64String(bytes)
       .TrimEnd(padding).Replace('+', '-').Replace('/', '_');

With static readonly char[] padding = { '=' }; See this

pedrofb
  • 37,271
  • 5
  • 94
  • 142