dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.12k stars 4.7k forks source link

CLR IsEphemeral hinders use of some CNG Providers #71310

Open vcsjones opened 2 years ago

vcsjones commented 2 years ago

Consider this:

CngKey.Create(
    CngAlgorithm.Rsa,
    null,
    new CngKeyCreationParameters { Provider = CngProvider.MicrosoftPlatformCryptoProvider });

This will fail with:

Unhandled exception. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Invalid flags specified.
at System.Security.Cryptography.CngKey.set_IsEphemeral(Boolean value)
at System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm, String keyName, CngKeyCreationParameters creationParameters)

The Microsoft PCP / TPM provider, and likely other 3rd party CNG Providers, may not support custom properties.

As the get for IsEphemeral correctly calls out:

https://github.com/dotnet/runtime/blob/5877e8b713742b6d80bd1aa9819094be029e3e1f/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CngKey.StandardProperties.cs#L100-L102

However, generating a key always tries to call the set, and if it fails, throws an exception:

https://github.com/dotnet/runtime/blob/5877e8b713742b6d80bd1aa9819094be029e3e1f/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CngKey.StandardProperties.cs#L122-L123

This makes using CngKey.Create or CngKey.Import with some providers difficult to work with.

ghost commented 2 years ago

Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones See info in area-owners.md if you want to be subscribed.

Issue Details
Consider this: ```C# CngKey.Create( CngAlgorithm.Rsa, null, new CngKeyCreationParameters { Provider = CngProvider.MicrosoftPlatformCryptoProvider }); ``` This will fail with:
Unhandled exception. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Invalid flags specified.
at System.Security.Cryptography.CngKey.set_IsEphemeral(Boolean value)
at System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm, String keyName, CngKeyCreationParameters creationParameters)
The Microsoft PCP / TPM provider, and likely other 3rd party CNG Providers, may not support custom properties. As the `get` for `IsEphemeral` correctly calls out: https://github.com/dotnet/runtime/blob/5877e8b713742b6d80bd1aa9819094be029e3e1f/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CngKey.StandardProperties.cs#L100-L102 However, generating a key always tries to call the `set`, and if it fails, throws an exception: https://github.com/dotnet/runtime/blob/5877e8b713742b6d80bd1aa9819094be029e3e1f/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CngKey.StandardProperties.cs#L122-L123 This makes using `CngKey.Create` or `CngKey.Import` with some providers difficult to work with.
Author: vcsjones
Assignees: -
Labels: `area-System.Security`
Milestone: -
vcsjones commented 2 years ago

I think the set should continue to throw, however the places where we use it in Create and Import should consider ignoring the exception. Since the expectation is the get will fail anyway, it doesn't matter if the set succeeded in this circumstance or not.

bartonjs commented 2 years ago

I think the set should continue to throw, however the places where we use it in Create and Import should consider ignoring the exception.

Since the set is private, the only differences between the set ignoring the error and the callers eating the exception is the latter allows break on exception to work, and has more code.

A more reasonable change would probably be:

samintz commented 1 year ago

Yeah, unfortunately there is no easy work-around either. The CngKey() constructor that takes the provider and key handles is private. So I cannot implement my own Import method. And DeriveKeyMaterial() needs to be passed a CngKey. This simple .Net code becomes messy because everything has to be done using p/invoke calls to NCrypt.dll.

CngKey ephKey = CngKey.Import(ephblob, CngKeyBlobFormat.EccPublicBlob, KspKeyController.KspProvider);
priv.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hmac;  
priv.HmacKey = null;  
byte[] prk = priv.DeriveKeyMaterial(ephKey);  

The really strange thing for me anyway, is that it works on Server 2012 R2. But fails in Win10, Server 2016, and Server 2019. This is with the "SafeNet Key Storage Provider".