Verifying JWT signed with the RS256 algorithm using public key in C#

后端 未结 6 602
遥遥无期
遥遥无期 2020-11-27 12:26

Ok, I understand that the question I am asking may be pretty obvious, but unfortunately I lack the knowledge on this subject and this task seems to be quite tricky for me.

6条回答
  •  伪装坚强ぢ
    2020-11-27 12:48

    .NET JWT Signature Verification using System.Security.Cryptography - No 3rd Party DLLs

    var errorMessage = string.Empty;
    
    // Google RSA well known Public Key data is available at https://accounts.google.com/.well-known/openid-configuration by navigating to the path described in the "jwks_uri" parameter.
    // {
    //     e: "AQAB",        // RSA Exponent
    //     n: "ya_7gV....",  // RSA Modulus aka Well Known Public Key
    //     alg: "RS256"      // RSA Algorithm
    // }
    
    var verified = VerifyJWT_RS256_Signature(
        jwt: "oicjwt....", 
        publicKey: "ya_7gV....", 
        exponent: "AQAB",
        errorMessage: out errorMessage);
    
    if (!verified)
    {
        // TODO: log error: 
        // TODO: Do something
    }
    

    NOTE: The following method verifies OpenID Connect JWT Signatures signed with Asymetric RS256 keys. OpenID Connect providers may opt to use other versions of Asymetric keys or even Symetric keys like HS256. This method does not directly support other key types.

    public static bool VerifyJWT_RS256_Signature(string jwt, string publicKey, string exponent, out string errorMessage)
    {
        if (string.IsNullOrEmpty(jwt))
        {
            errorMessage = "Error verifying JWT token signature: Javascript Web Token was null or empty.";
            return false;
        }
    
        var jwtArray = jwt.Split('.');
        if (jwtArray.Length != 3 && jwtArray.Length != 5)
        {
            errorMessage = "Error verifying JWT token signature: Javascript Web Token did not match expected format. Parts count was " + jwtArray.Length + " when it should have been 3 or 5.";
            return false;
        }
    
        if (string.IsNullOrEmpty(publicKey))
        {
            errorMessage = "Error verifying JWT token signature: Well known RSA Public Key modulus was null or empty.";
            return false;
        }
    
        if (string.IsNullOrEmpty(exponent))
        {
            errorMessage = "Error verifying JWT token signature: Well known RSA Public Key exponent was null or empty.";
            return false;
        }
    
        try
        {
            string publicKeyFixed = (publicKey.Length % 4 == 0 ? publicKey : publicKey + "====".Substring(publicKey.Length % 4)).Replace("_", "/").Replace("-", "+");
            var publicKeyBytes = Convert.FromBase64String(publicKeyFixed);
    
            var jwtSignatureFixed = (jwtArray[2].Length % 4 == 0 ? jwtArray[2] : jwtArray[2] + "====".Substring(jwtArray[2].Length % 4)).Replace("_", "/").Replace("-", "+");
            var jwtSignatureBytes = Convert.FromBase64String(jwtSignatureFixed);
    
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.ImportParameters(
                new RSAParameters()
                {
                    Modulus = publicKeyBytes,
                    Exponent = Convert.FromBase64String(exponent)
                }
            );
    
            SHA256 sha256 = SHA256.Create();
            byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(jwtArray[0] + '.' + jwtArray[1]));
    
            RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
            rsaDeformatter.SetHashAlgorithm("SHA256");
            if (!rsaDeformatter.VerifySignature(hash, jwtSignatureBytes))
            {
                errorMessage = "Error verifying JWT token signature: hash did not match expected value.";
                return false;
            }
        }
        catch (Exception ex)
        {
            errorMessage = "Error verifying JWT token signature: " + ex.Message;
            return false;
            //throw ex;
        }
    
        errorMessage = string.Empty;
        return true;
    }
    

    NOTE: Verifying the signature of an OpenID Connect JWT (Javascript Web Token) is only one necessary step of the JWT verification process. Make sure to set a NONCE value which your system can use to prevent Replay attacks. Make sure to validate each parameter of the JWT package for completeness and accuracy.

提交回复
热议问题