AzureAD / azure-activedirectory-identitymodel-extensions-for-dotnet

IdentityModel extensions for .Net
MIT License
1.05k stars 396 forks source link

JwtSecurityTokenHandler Validate Token fails if not using Windows in 5.3.0 #1038

Closed RichardD012 closed 5 years ago

RichardD012 commented 5 years ago

When creating a new JwtSecurityTokenHandler() and passing in a RSACryptoServiceProvider in OSX, an exception is thrown when calling ValidateToken.

The error is: System.PlatformNotSupportedException: 'CspKeyContainerInfo' requires Windows Cryptographic API (CAPI), which is not available on this platform.

Exceptions caught:
 'System.PlatformNotSupportedException: 'CspKeyContainerInfo' requires Windows Cryptographic API (CAPI), which is not available on this platform.
   at System.Security.Cryptography.RSACryptoServiceProvider.get_CspKeyContainerInfo()
   at Microsoft.IdentityModel.Tokens.RSACryptoServiceProviderProxy..ctor(RSACryptoServiceProvider rsa) in C:\agent2\_work\56\s\src\Microsoft.IdentityModel.Tokens\RsaCryptoServiceProviderProxy.cs:line 74
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.Initialize(RSA rsa, String algorithm) in C:\agent2\_work\56\s\src\Microsoft.IdentityModel.Tokens\AsymmetricAdapter.cs:line 305
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter..ctor(SecurityKey key, String algorithm, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName, Boolean requirePrivateKey) in C:\agent2\_work\56\s\src\Microsoft.IdentityModel.Tokens\AsymmetricAdapter.cs:line 84

The stack-trace refers to RsaCryptoServiceProviderProxy.cs and looking at the commit history, it looks like it added Net Standard 2.0 Support. The parameter references the CspKeyContainerInfo in the constructor here.

This does not work in OSX as, as the exception states, CspKeyContainerInfo isn't supported. So when it tries to access the parameter, this exception is thrown. The code used to call this previously worked in 5.2.4.

RichardD012 commented 5 years ago

Not sure if this is correct but changing the constructor of RsaCryptoServiceProviderProxy.cs seems to work. Again, not familiar enough with this code base but it def works.

public RSACryptoServiceProviderProxy(RSACryptoServiceProvider rsa)
{
    if (rsa == null)
        throw LogHelper.LogArgumentNullException(nameof(rsa));

    // Level up the provider type only if:
    // 1. it is PROV_RSA_FULL or PROV_RSA_SCHANNEL which denote CSPs that only understand Sha1 algorithms
    // 2. it is not associated with a hardware key
    #if WINDOWS
    if ((rsa.CspKeyContainerInfo.ProviderType == PROV_RSA_FULL || rsa.CspKeyContainerInfo.ProviderType == PROV_RSA_SCHANNEL) && !rsa.CspKeyContainerInfo.HardwareDevice)
    {
        var csp = new CspParameters();
        csp.ProviderType = PROV_RSA_AES;
        csp.KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName;
        csp.KeyNumber = (int)rsa.CspKeyContainerInfo.KeyNumber;
        if (rsa.CspKeyContainerInfo.MachineKeyStore)
            csp.Flags = CspProviderFlags.UseMachineKeyStore;

        // If UseExistingKey is not specified, the CLR will generate a key for a non-existent group.
        // With this flag, a CryptographicException is thrown instead.
        csp.Flags |= CspProviderFlags.UseExistingKey;

        _rsa = new RSACryptoServiceProvider(csp);

        // since we created a new RsaCryptoServiceProvider we need to dispose it
        _disposeRsa = true;
    }
    else
    {
        // no work-around necessary
        _rsa = rsa;
    }
    #else
    _rsa = rsa;
    #endif
}

Another thing I noticed when building in OSX, JsonWebKey fails to build because once the statement is built you get unreachable code. It ultimately becomes:

internal ECDsa CreateECDsa(string algorithm, bool usePrivateKey)
{

    throw new NotImplementedException(LogMessages.IDX10676);

    if (Crv == null)
        throw LogHelper.LogArgumentNullException(nameof(Crv));

    if (X == null)
        throw LogHelper.LogArgumentNullException(nameof(X));

    if (Y == null)
        throw LogHelper.LogArgumentNullException(nameof(Y));
...
}
brentschmaltz commented 5 years ago

@RichardD012 RsaCryptoServiceProvider is available on .Net 2.0 and above. We'll look into ECDsa throwing on MAC.

BTW you seem to be our most advanced MAC user, no-one else has hit this.

brentschmaltz commented 5 years ago

@GeoK a new mac issue, can you look into it?

ghost commented 5 years ago

Hi I can add that I have just been sitting with a similar issue on Linux. It says the Kid can not be matched, but they are correct. A downgrade to 5.2.4 solves the issue.

CheloXL commented 5 years ago

Just to confirm, same issue here under linux. I'm getting the following stack trace

System.PlatformNotSupportedException: 'CspKeyContainerInfo' requires Windows Cryptographic API (CAPI), which is not available on this platform.
   at System.Security.Cryptography.RSACryptoServiceProvider.get_CspKeyContainerInfo()
   at Microsoft.IdentityModel.Tokens.RSACryptoServiceProviderProxy..ctor(RSACryptoServiceProvider rsa)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.Initialize(RSA rsa, String algorithm)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter..ctor(SecurityKey key, String algorithm, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName, Boolean requirePrivateKey)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.ResolveAsymmetricAdapter(SecurityKey key, String algorithm, Boolean requirePrivateKey)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityTokenPrivate(String issuer, String audience, ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, Nullable`1 issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityToken(SecurityTokenDescriptor tokenDescriptor)

while calling CreateJwtSecurityToken. 5.2.4 works just fine.

af-mst commented 5 years ago

Just to confirm, same issue here under linux. I'm getting the following stack trace

System.PlatformNotSupportedException: 'CspKeyContainerInfo' requires Windows Cryptographic API (CAPI), which is not available on this platform.
   at System.Security.Cryptography.RSACryptoServiceProvider.get_CspKeyContainerInfo()
   at Microsoft.IdentityModel.Tokens.RSACryptoServiceProviderProxy..ctor(RSACryptoServiceProvider rsa)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.Initialize(RSA rsa, String algorithm)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter..ctor(SecurityKey key, String algorithm, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName, Boolean requirePrivateKey)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.ResolveAsymmetricAdapter(SecurityKey key, String algorithm, Boolean requirePrivateKey)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityTokenPrivate(String issuer, String audience, ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, Nullable`1 issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityToken(SecurityTokenDescriptor tokenDescriptor)

while calling CreateJwtSecurityToken. 5.2.4 works just fine.

Same issue on my side in Linux in Azure, same solution is working (downgrade to 5.2.4)

brentschmaltz commented 5 years ago

@GeoK i think there is a clue in the #ifdef that @RichardD012 exposed. @af-mst hit the same issue.

there is no reason to use the RSACryptoServiceProviderProxy on non-windows machines. The only reason we do is for sha2 support.

GeoK commented 5 years ago

@RichardD012 @nenne @CheloXL @af-mst Thank you for reporting and confirming this issue. CspKeyContainerInfo is indeed a Windows-specific member.

We will fix this in 5.3.1.

GeoK commented 5 years ago

Marking this issue as Fixed for the upcoming release (5.3.1). Thank you again for reporting these issues.

mikesig commented 5 years ago

Is there a non-downgrade workaround for this? It's holding up a docusign integration for us and downgrading isn't really an option.

Alternatively, do we have an ETA on 5.4.0?

GeoK commented 5 years ago

Hi @mikesig, 5.4.0 will be released beginning next week.

brentschmaltz commented 5 years ago

@mikesig we just dropped 5.4.0

pauloofmeta commented 5 years ago

@GeoK i think your commit is not in 5.4.0, I updated in the project and still the error occurs, I looked in the repository and it's still the same way

GeoK commented 5 years ago

Hi @pauloofmeta, I'm sorry to hear that. Could you please open a new issue and specify which OS and Framework your app is using, provide stack trace, and possibly a small repro project?

pauloofmeta commented 5 years ago

@GeoK I'm sorry, I ran a test on a console application using only Microsoft.IdentityModel.Tokens in version 5.4.0, and it worked in linux and windows, I believe that this is not working in my webapi netcoreapp application because of the version of Microsoft.AspNetCore .App still does not contain this fix, correct?

GeoK commented 5 years ago

@pauloofmeta Correct. I believe that Microsoft.AspNetCore.App 2.2.0/2.2.1 will pull IdentityModel 5.3.0 by default. Can you upgrade Microsoft.IdentityModel.Protocols.OpenIdConnect in your AspNetCore app to version 5.4.0 and see if that resolves your issue?

<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="5.4.0" />
brentschmaltz commented 5 years ago

@pauloofmeta please reopen if this issue is still occurring.