2

I am using the token authentication (instead of cookie) with Azure Active Directory.

Based on this article: https://www.itunity.com/article/angular-2-openid-connect-azure-active-directory-3093

I was able to get it working on the client side.

   public validateSignature(token): Observable<boolean> {
        /* Retrieve from federated metadata endpoint.
        In this sample, the document was downloaded locally */
        return this.httpService.get("metadata/metadata.xml")
            .map((res: Response) => {
                let dom = (new DOMParser()).parseFromString(res.text(), "text/xml");
                let json = xml2json(dom, "");
                let cert = "-----BEGIN CERTIFICATE-----" + 
                JSON.parse(json).EntityDescriptor[0]["ds:Signature"]
                    ["KeyInfo"]["X509Data"]["X509Certificate"] + 
                 "-----END CERTIFICATE-----";
                 let key = KEYUTIL.getKey(cert);
                return KJUR.jws.JWS.verifyJWT(token, key, { alg: ['RS256'] });
            })
        } 

I was trying to re-implement the above method in the .NET Core 1.0.3.

Based on this article: how to sign and verify signature with net and a certificate

The following line won't compile on .NET Core:

RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;

I am not sure what is correct way to verify the token based on the certificate in .NET Core.

Nacimota
  • 22,395
  • 6
  • 42
  • 44
Derek Liang
  • 1,142
  • 1
  • 15
  • 22

2 Answers2

2

An easy way to verify the token issued by Azure AD is leverage the OWIN comment with web API. We just need to config the JwtBearerOptions and send the request to a controller which protected by Azure AD. If the token is not verified, you will get the 401 response. You can refer the code sample here.

And if you want to implement the code to verify the token manually, we can refer the code how the Microsoft verify the token in Microsoft.AspNetCore.Authentication.JwtBearer.

I also wrote a code sample for your reference:

public class JsonWebTokenValidator
{
    public void Validate(string token)
    {
        var stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
        var options = new JwtBearerOptions
        {
            ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever()),

            TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
            {
                ValidateIssuer = true,
                ValidIssuer = "https://sts.windows.net/{tenantId}/",

                ValidateAudience = true,
                ValidAudience = "{audience}",

                RequireExpirationTime = true,
                ValidateLifetime = true,

                ValidateIssuerSigningKey = true,

                ClockSkew = TimeSpan.Zero
            },
            Authority = "https://login.microsoftonline.com/{tenantId}",
        };

        SecurityToken validatedToken = null;
        ClaimsPrincipal result = null;
        var configuration = options.ConfigurationManager.GetConfigurationAsync(new CancellationToken()).Result;
        options.TokenValidationParameters.IssuerSigningKeys = configuration.SigningKeys;

        options.ConfigurationManager.RequestRefresh();
        foreach (var validators in options.SecurityTokenValidators)
        {
            result = validators.ValidateToken(token, options.TokenValidationParameters, out validatedToken);
        }

        foreach (var claim in result.Claims)
        {
            Console.WriteLine($"{claim.Subject}:{claim.Value}");
        }
    }

Project.json

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.9",
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.1"
    },

    "System.IdentityModel.Tokens.Jwt": {
      "version": "5.1.3"
    },
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
    "Microsoft.IdentityModel.Protocols": "2.1.3",
    "Microsoft.IdentityModel.Protocols.OpenIdConnect": "2.0.0"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}
Fei Xue
  • 14,369
  • 1
  • 19
  • 27
  • @DerekLiang If it is helpful for the issue, please mark it as the answer so that the community who have the same issue can recognize the helpful post easily:) – Fei Xue Jun 01 '17 at 01:20
  • I see the ValidateIssuerSigningKey set to true, but there is not signing key. When I add a SymmetricSecurityKey value to the IssuerSigningKey property is works no mater what value is given. I was assuming that it would only validate for the secret key that I have. – RyanOC Mar 06 '18 at 21:22
0

According to this QA: implement RSA in .NET core your cert object should have a GetRSAPublicKey() method which returns an RSA object - just be sure to wrap it in using as it's IDisposable.

static bool Verify(string text, byte[] signature, string certPath)
{
    X509Certificate2 cert = new X509Certificate2( certPath );

    using( RSA rsa = cert.GetRSAPublicKey() )
    using( SHA1Managed sha1 = new SHA1Managed() )
    {
        byte[] data = Encoding.Unicode.GetBytes( text );
        byte[] hash = sha1.ComputeHash( data );

        return rsa.VerifyHash( hash, CryptoConfig.MapNameToOID("SHA1"), signature );
    }
}

Apparently GetRSAPublicKey() is defined as an extension method: https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.rsacertificateextensions.getrsapublickey(v=vs.110).aspx

Dai
  • 141,631
  • 28
  • 261
  • 374
  • In the original question, what is the data to be verified? The id_token has 3 parts, header, claims and signature delimited by '.'. And also CryptoConfig.MapNameToOID("SHA1") is not available in .NET Core. – Derek Liang May 30 '17 at 23:15