Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSACryptoServiceProviderProxy crashes on Mono #179

Closed
PinpointTownes opened this issue Jun 13, 2015 · 45 comments

Comments

@PinpointTownes
Copy link
Contributor

commented Jun 13, 2015

System.Security.Cryptography.CryptographicException: Keyset does not exist
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) [0x00000] in <filename unknown>:0 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) [0x00000] in <filename unknown>:0 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (System.Security.Cryptography.CspParameters parameters) [0x00000] in <filename unknown>:0 
  at System.IdentityModel.Tokens.RSACryptoServiceProviderProxy..ctor (System.Security.Cryptography.RSACryptoServiceProvider rsa) [0x00000] in <filename unknown>:0 
  at System.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor (System.IdentityModel.Tokens.AsymmetricSecurityKey key, System.String algorithm, Boolean willCreateSignatures) [0x00000] in <filename unknown>:0 
  at System.IdentityModel.Tokens.SignatureProviderFactory.CreateProvider (System.IdentityModel.Tokens.SecurityKey key, System.String algorithm, Boolean willCreateSignatures) [0x00000] in <filename unknown>:0 
  at System.IdentityModel.Tokens.SignatureProviderFactory.CreateForSigning (System.IdentityModel.Tokens.SecurityKey key, System.String algorithm) [0x00000] in <filename unknown>:0 
  at System.IdentityModel.Tokens.JwtSecurityTokenHandler.CreateSignature (System.String inputString, System.IdentityModel.Tokens.SecurityKey key, System.String algorithm, System.IdentityModel.Tokens.SignatureProvider signatureProvider) [0x00000] in <filename unknown>:0 
  at System.IdentityModel.Tokens.JwtSecurityTokenHandler.CreateToken (System.String issuer, System.String audience, System.Security.Claims.ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, System.IdentityModel.Tokens.SigningCredentials signingCredentials, System.IdentityModel.Tokens.SignatureProvider signatureProvider) [0x00000] in <filename unknown>:0 
  at AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerHandler+<CreateIdentityTokenAsync>d__21.MoveNext () [0x00000] in <filename unknown>:0 

Mono doesn't use CryptoAPI - which is Windows-specific - and always initializes CspKeyContainerInfo.ProviderType to 1, which causes RSACryptoServiceProviderProxy to create a proxy around the existing RSA provider. Sadly, it crashes on Mono.

The bug disappears when you remove csp.Flags |= CspProviderFlags.UseExistingKey; from RSACryptoServiceProviderProxy's constructor.

/cc @brentschmaltz @tushargupta51

@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Jun 15, 2015

@PinpointTownes I take it this is a runtime error?
the proxy was put in place to enable SHA2. I haven't been back to see what else can be done yet.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Jun 15, 2015

Yup, it's indeed a runtime error.

Removing csp.Flags |= CspProviderFlags.UseExistingKey seems to fix this issue, but I guess that's probably not the best solution.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Jun 15, 2015

FWIW, I opted for another temporary workaround: manually instantiating RSACryptoServiceProvider with CspParameters.ProviderType = 24 to bypass RSACryptoServiceProviderProxy.

Of course, it's terribly insecure, as you have to load the private key from an unprotected embedded resource (note that I used the same trick to "mimic" .pfx support on CoreCLR).

The more I add temporary (and ugly) workarounds for security bugs, the more I realize we're definitely on the bleeding edge with ASP.NET 5 on Core CLR and Mono: PinpointTownes/AspNet.Security.OpenIdConnect.Server@e405908#diff-dc37cbfe3a6d682094d1bc6bb6a22117R191 😄

@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Jun 15, 2015

bleeding edge, true enough.
The answer may lie in using a SignatureProvider that works on Mono. This was one of the reasons for the SignatureProviderFactory.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Jun 15, 2015

Out of curiosity, why forcing CspProviderFlags.UseExistingKey?

@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Jun 15, 2015

In case the key is tied to hardware, we want to use the same csp.

@groberts314

This comment has been minimized.

Copy link

commented Aug 13, 2015

Ran into this same issue, via System.IdentityModel.Tokens.JwtSecurityTokenHandler (System.IdentityModel.Tokens 5.0.0-beta6) trying to validate a JWT signed with RSA / SHA. I ended up rolling my own class that inherits JwtSecurityTokenHandler and overrides ValidateSignature() in which it makes use of a SHA Managed + System.Security.CryptographyRSAPKCS1SignatureDeformatter in order to perform the validation check, rather than wrestling with RSACryptoServiceProvider.

Any word on a resolution to this issue?

@tushargupta51 tushargupta51 modified the milestones: Beta8, Beta 7 Aug 20, 2015
@brentschmaltz brentschmaltz added P0 and removed P2 labels Sep 10, 2015
@PinpointTownes PinpointTownes referenced this issue Sep 25, 2015
0 of 8 tasks complete
@tushargupta51

This comment has been minimized.

Copy link
Contributor

commented Sep 29, 2015

@brentschmaltz Is this a P0?

@tushargupta51 tushargupta51 modified the milestones: Post Beta RC, Beta8 Oct 2, 2015
@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Oct 5, 2015

@groberts314 did you consider using a custom SignatureProvider by setting the SignatureProviderFactory on JwtSecurityTokenHandler? In this way you can control any crypto provider you want.

@groberts314

This comment has been minimized.

Copy link

commented Oct 5, 2015

@brentschmaltz: thanks for the suggestion. I had to solve this problem rather quickly in a a bit of a panic to avoid blocking myself and other developers working on the project. I was also not overly familiar with the space, and therefore had not considered that approach. We've actually since moved in a completely different direction security-wise, and as such we're not currently relying on any of this. But good to know the "right way" to handle this should it come up again. Thanks!

@brentschmaltz brentschmaltz added P1 and removed P0 labels Oct 9, 2015
@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Oct 9, 2015

@groberts314 let me know if we can help out.

@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Oct 9, 2015

@PinpointTownes I down graded this to P1 as OSX and Linux users are fine for dnxcore50 and there is a way to run scenarios.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Oct 9, 2015

@brentschmaltz the last time I tried dnxcore50 on Linux/OS X, there were many bugs and it was not really a viable option yet. I'll give it another try and get back to you 👍

@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Oct 9, 2015

@tushargupta51 @PinpointTownes Tushar was able to get: Osx, Linux and Windows nano working targeting dnxcore50.

@brentschmaltz

This comment has been minimized.

Copy link
Member

commented Nov 23, 2015

@PinpointTownes Tushar is out till Dec. This was only failing on MONO (full desktop) and worked on IOS and Linux using FxCore.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Nov 23, 2015

@brentschmaltz but I guess @tushargupta51 tested with a X509 RSA certificate, not directly with a RSA key. X509 security keys are not impacted, since we directly resolve the public/private keys from the certificate instead of manually instantiating a RSACng object.

@jamiehales

This comment has been minimized.

Copy link

commented Nov 23, 2015

We are having the issue with loading AsymmetricSignatureProvider as mentioned above. We're developing on OSX Mono using 1.0.0-rc1. We're building against the latest identitymodel packages available on Nuget. Beta 8 still works without a problem, as did beta 7 during development.

Unfortunately we have to stick with mono rather than coreclr for now. We require mysql support which isn't supported under coreclr yet.

Happy to provide any additional info needed to troubleshoot.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Dec 2, 2015

FYI, I moved the RSA issue to #324.

@tushargupta51

This comment has been minimized.

Copy link
Contributor

commented Jan 6, 2016

With #324 being fixed, this should work on coreclr on unix. Now that it is an issue on mono only, I will downgrade the priority to P2. Let me know if anyone has any concerns.

cc: @brentschmaltz

@tushargupta51 tushargupta51 added P2 and removed P1 labels Jan 6, 2016
@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Jan 6, 2016

No particular concern for me, as it wouldn't work even if you fixed this issue (since the ECDsa types are missing on Mono... we'd need a Mono TFM to fix that /cc @davidfowl)

@sunsided

This comment has been minimized.

Copy link

commented Jan 8, 2016

I just ran into this again. I had success with token validation on Mono using an X509 key (RSA-SHA256) and a custom AsymmetricSignatureProvider (and a matching factory for it) that removes all references to ECDSA (here). Now that I upgraded my authentication server to rc2 (16357) - using the same AsymmetricSignatureProvider - it fails again with "keyset does not exist" (Mono 4.3.2.221) at

 System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p)

I tried RSACryptoServiceProvider.UseMachineKeyStore = false; but that doesn't help and none of the fixes above seem to be applicable at the moment.

Edit:
I now created a custom CryptoServiceProviderProxy and removed UseExistingKey and, indeed, it works.

Edit 2:
Works on Windows that is; on Mono not using the proxy at all leads to success. (thanks @PinpointTownes for that one)

@polita

This comment has been minimized.

Copy link
Contributor

commented May 19, 2016

@tushargupta51 Please confirm that this is only an issue still for Mono. If so, let's close.

@tushargupta51

This comment has been minimized.

Copy link
Contributor

commented May 19, 2016

The original issue where creating a proxy does not work on mono is still an issue but only on mono. The other issues reported about RSACng not working on CoreClr has been fixed for both RsaSecurityKey and ECDsaSecurityKey, since we use Rsa.Create() method and for ECDsa, we provide the extensiblity to hook up custom providers (which will work on unix/mac.

Since this is an issue only for mono, which 5.x do not support, I am closing this. Let me know if anyone has any concerns.

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented May 19, 2016

Since this is an issue only for mono, which 5.x do not support, I am closing this. Let me know if anyone has any concerns.

Really unfortunate, but not surprising.

@in10se

This comment has been minimized.

Copy link

commented May 29, 2016

@sunsided , I'm facing the same issue while creating a JWT token:

Couldn't generate token. System.TypeLoadException: Could not load type 'System.IdentityModel.Tokens.AsymmetricSignatureProvider' from assembly 'System.IdentityModel.Tokens, Version=5.0.0.112, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
at System.IdentityModel.Tokens.SignatureProviderFactory.CreateForSigning (System.IdentityModel.Tokens.SecurityKey key, System.String algorithm) [0x00000] in :0
at System.IdentityModel.Tokens.RsaSecurityKey.GetSignatureProvider (System.String algorithm, Boolean verifyOnly) [0x00000] in :0
at System.IdentityModel.Tokens.SecurityKey.GetSignatureProviderForSigning (System.String algorithm) [0x00000] in :0
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwt (System.String issuer, System.String audience, IEnumerable1 claims, Nullable1 notBefore, Nullable1 expires, Nullable1 issuedAt, System.IdentityModel.Tokens.SigningCredentials signingCredentials) [0x00000] in :0
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateToken (System.String issuer, System.String audience, System.Security.Claims.ClaimsIdentity subject, Nullable1 notBefore, Nullable1 expires, Nullable`1 issuedAt, System.IdentityModel.Tokens.SigningCredentials signingCredentials) [0x00000] in :0

You wrote the following:

Works on Windows that is; on Mono not using the proxy at all leads to success. (thanks @PinpointTownes for that one)

How does one not use a proxy when creating a JWT token?

@kcd83

This comment has been minimized.

Copy link

commented Jun 14, 2016

@in10se regarding using this with JWT I posted a simple workaround for symmetric keys here: http://stackoverflow.com/a/37802139/516748

@frodestorhaug

This comment has been minimized.

Copy link

commented Jul 31, 2016

@kcd83 Is it possible to ask for a more complete example of your workaround ? Thanks

@kcd83

This comment has been minimized.

Copy link

commented Jul 31, 2016

@frodestorhaug what are you missing? do you have something like this running http://stackoverflow.com/questions/30546542/token-based-authentication-in-asp-net-core-refreshed?

I'm no longer at the employer where I implemented that

@frodestorhaug

This comment has been minimized.

Copy link

commented Aug 1, 2016

Thanks for your answer. Yes, we have something like that running. And everything works great on windows. But we also want this to work on mono. Our setup still uses .net core rc1 and we want this to work on linux/mono for a demo and do not have the time to upgrade to rc2 for now. But as you wrote the AsymmetricSignatureProvider throws an exception on mono. So I wanted to try your workaround, but can not find where I should implement your MonoFriendlyCryptoProviderFactory. Any help would be greatly appreciated.

@sunsided

This comment has been minimized.

Copy link

commented Aug 1, 2016

@in10se Sorry it took so long. My solution is actually pretty wildly stitched together. The idea is that the SignatureProvider actually creates (or, back then, created) an RsaCryptoServiceProviderProxy to manage the signing and verification of RSA keys, which failed on mono. So I first wrote a CustomRsaCryptoServiceProviderProxy which would be instantiated by a CustomAsymmetricSignatureProvider instead, but in the end just skipped that part altogether by having that class' constructor call

var x509Key = key as X509SecurityKey;
if (x509Key != null)
{
    RSACryptoServiceProvider csp;
    if (willCreateSignatures)
    {
        csp = x509Key.PrivateKey as RSACryptoServiceProvider;
    }
    else
    {
        csp = x509Key.PublicKey as RSACryptoServiceProvider;
    }

    if (Type.GetType("Mono.Runtime") != null)
    {
        _rsaCryptoServiceProvider = csp;
    }
    else
    {
        _rsaCryptoServiceProviderProxy = new CustomRsaCryptoServiceProviderProxy(csp);
    }

    return;
}

The CustomAsymmetricSignatureProvider would be implictly used in the JwtSecurityTokenHandler's CreateJwtSecurityToken method as

var cpf = signingCredentials?.Key;
// ...
cpf.CryptoProviderFactory = new CustomSignatureProviderFactory(...);

and in the Startup as

CryptoProviderFactory.Default = new CustomSignatureProviderFactory(...);
// ...
branch.UseJwtBearerAuthentication(
    new JwtBearerOptions
    {
        TokenValidationParameters = new TokenValidationParameters
        {
            CryptoProviderFactory = new CustomSignatureProviderFactory(...)
        }
    });

Note that CryptoProviderFactory.Default currently doesn't seem to have any effect.

@andycmaj

This comment has been minimized.

Copy link

commented Sep 1, 2016

@sunsided, thanks so much for working on this! question though: this seems to work great with logging in via OpenIdConnect Middleware, but when i log out, i am unable to log back in due to an ObjectDisposedException.

reference app here: https://github.com/auth0-samples/auth0-aspnetcore-sample/blob/master/01-Login/SampleMvcApp/Startup.cs#L93 and i'm using your workaround like so in UseOpenIdConnectAuthentication.

...
                // Configure the Claims Issuer to be Auth0
                ClaimsIssuer = "Auth0",

                // https://gist.github.com/sunsided/099d375031813e2eed2c5430303af277
                // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/179#issuecomment-236543627
                TokenValidationParameters = new TokenValidationParameters {
                    CryptoProviderFactory = new CustomSignatureProviderFactory(loggerFactory)
                }

here's what i see:

Exceptions caught:
 'System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Keypair was disposed'.
  at Mono.Security.Cryptography.RSAManaged.get_KeySize () <0x111d78350 + 0x0007a> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider.get_KeySize () <0x111ea9660 + 0x00031> in <filename unknown>:0
  at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_KeySize () <0x1177386d0 + 0x00020> in <filename unknown>:0
  at TheChunnel.FrontEnd.CustomAsymmetricSignatureProvider.ValidateAsymmetricSecurityKeySize (Microsoft.IdentityModel.Tokens.SecurityKey key, System.String algorithm, Boolean willCreateSignatures) <0x1177383f0 + 0x000d8> in <filename unknown>:0
  at TheChunnel.FrontEnd.CustomAsymmetricSignatureProvider..ctor (ILoggerFactory loggerFactory, Microsoft.IdentityModel.Tokens.AsymmetricSecurityKey key, System.String algorithm, Boolean willCreateSignatures) <0x117737a50 + 0x001e4> in <filename unknown>:0
  at TheChunnel.FrontEnd.CustomSignatureProviderFactory.CreateForVerifying (Microsoft.IdentityModel.Tokens.SecurityKey key, System.String algorithm) <0x117737390 + 0x00330> in <filename unknown>:0
  at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature (System.Byte[] encodedBytes, System.Byte[] signature, Microsoft.IdentityModel.Tokens.SecurityKey key, System.String algorithm, Microsoft.IdentityModel.Tokens.TokenValidationParameters validationParameters) <0x1177371e0 + 0x00068> in <filename unknown>:0
  at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature (System.String token, Microsoft.IdentityModel.Tokens.TokenValidationParameters validationParameters) <0x117730ce0 + 0x004c8> in <filename unknown>:0

wonder if it has something to do with the fact that i'm using the provider via TokenValidationParameters instead of on the signingKey:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/master/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs#L729

@sunsided

This comment has been minimized.

Copy link

commented Sep 1, 2016

@andycmaj I think it was the JwtSecurityTokenHandler hack that fixed this; that is, set OpenIdConnectServerOptions's AccessTokenHandler to a custom class and then force that one's CreateJwtSecurityToken method to use your signature provider factory. I saw it was missing in my gist; just updated it.

For some reason, nothing I did to the keys (i.e. to get key.CryptoProviderFactory to work) helped to maintain a stable solution - it started to fail after about a week or two of working - but I was assuming that this was due to the fact that I actually serialize the encryption keys via a custom DataProtection IXmlRepository in order to keep my dockerized OpenID servers in sync, where then loading the keys (and/or rolling them) by the data protection stack would drop the configuration.

@andycmaj

This comment has been minimized.

Copy link

commented Sep 2, 2016

hmm added that piece in and now i have


                SecurityTokenValidator = new CustomJwtSecurityTokenHandler(loggerFactory),

                // https://gist.github.com/sunsided/099d375031813e2eed2c5430303af277
                // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/179#issuecomment-236543627
                TokenValidationParameters = new TokenValidationParameters {
                    CryptoProviderFactory = new CustomSignatureProviderFactory(loggerFactory)
                }

and i'm still seeing the

Exceptions caught:
 'System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Keypair was disposed'.
  at Mono.Security.Cryptography.RSAManaged.get_KeySize () <0x10cd78350 + 0x0007a> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider.get_KeySize () <0x10cea9660 + 0x00031> in <filename unknown>:0
  at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_KeySize () <0x112672100 + 0x00020> in <filename unknown>:0
  at TheChunnel.FrontEnd.CustomAsymmetricSignatureProvider.ValidateAsymmetricSecurityKeySize (Microsoft.IdentityModel.To

exception after i log out and try logging back in.
i'll have to hook up a debugger on OSX and see if i can step through to see why this disposed key is trying to be re-used...

@PinpointTownes

This comment has been minimized.

Copy link
Contributor Author

commented Sep 2, 2016

@andycmaj that's another bug: #477

@andycmaj

This comment has been minimized.

Copy link

commented Sep 6, 2016

thanks @PinpointTownes, @sunsided! after looking at #477 i was able to get this working.
@sunsided, i commented on your gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.