1
votes

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.

        // 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}";
    }
1
Sorry, skipped the first code lineChristian
public static string Create(string userName) {Christian
you can click on edit (just below your question) to edit your question an add the missing line. And afterwards you can also delete your comments.jps

1 Answers

2
votes

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