Open quality-leftovers opened 3 years ago
Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @GrabYourPitchForks See info in area-owners.md if you want to be subscribed.
Author: | randomstring384540 |
---|---|
Assignees: | - |
Labels: | `area-System.Security`, `untriaged` |
Milestone: | - |
I am not sure I understand your request. There's a CopyWithPrivateKey
method. Similarly there are methods to get existing key.
CopyWithPrivateKey requires the crypto instance to support ExportParameters(true)
unless the key is provided by
In the past this was not needed, because it was possible to set X509Certificate2.PrivateKey
for "custom" RSA providers. It would be nice if this was still possible in the future.
RSACertificateExtensions.cs calls certificate.Pal.CopyWithPrivateKey(privateKey)
public static X509Certificate2 CopyWithPrivateKey(this X509Certificate2 certificate, RSA privateKey)
{
...
ICertificatePal pal = certificate.Pal.CopyWithPrivateKey(privateKey);
return new X509Certificate2(pal);
}
PAL on Windows using CertificatePal.PrivateKey.cs
public ICertificatePal CopyWithPrivateKey(RSA rsa)
{
RSACng? rsaCng = rsa as RSACng;
ICertificatePal? clone = null;
// Key backed by windows API #1
if (rsaCng != null)
{
clone = CopyWithPersistedCngKey(rsaCng.Key);
if (clone != null)
{
return clone;
}
}
// Key backed by windows API #2
RSACryptoServiceProvider? rsaCsp = rsa as RSACryptoServiceProvider;
if (rsaCsp != null)
{
clone = CopyWithPersistedCapiKey(rsaCsp.CspKeyContainerInfo);
if (clone != null)
{
return clone;
}
}
RSAParameters privateParameters = rsa.ExportParameters(true); // <-- export of private key to memory
using (PinAndClear.Track(privateParameters.D!))
using (PinAndClear.Track(privateParameters.P!))
using (PinAndClear.Track(privateParameters.Q!))
using (PinAndClear.Track(privateParameters.DP!))
using (PinAndClear.Track(privateParameters.DQ!))
using (PinAndClear.Track(privateParameters.InverseQ!))
using (RSACng clonedKey = new RSACng())
{
clonedKey.ImportParameters(privateParameters);
return CopyWithEphemeralKey(clonedKey.Key);
}
}
PAL on Unix using OpenSslX509CertificateReader
:
public ICertificatePal CopyWithPrivateKey(RSA privateKey)
{
RSAOpenSsl? typedKey = privateKey as RSAOpenSsl;
if (typedKey != null)
{
return CopyWithPrivateKey(typedKey.DuplicateKeyHandle());
}
RSAParameters rsaParameters = privateKey.ExportParameters(true);
using (PinAndClear.Track(rsaParameters.D!))
using (PinAndClear.Track(rsaParameters.P!))
using (PinAndClear.Track(rsaParameters.Q!))
using (PinAndClear.Track(rsaParameters.DP!))
using (PinAndClear.Track(rsaParameters.DQ!))
using (PinAndClear.Track(rsaParameters.InverseQ!))
using (typedKey = new RSAOpenSsl(rsaParameters))
{
return CopyWithPrivateKey(typedKey.DuplicateKeyHandle());
}
}
So on Windows
In the past this was not needed, because it was possible to set X509Certificate2.PrivateKey for "custom" RSA providers.
I am not sure that assertion is correct. The PrivateKey
setter never supported setting anything but CryptoAPI key: https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System/security/system/security/cryptography/x509/x509certificate2.cs#L819-L821. Even then it required the export to work.
It seems you are right and my memory was incorrect. I just stumbled on this when working with a linux container and thought it had been possible. Sorry for not double checking.
Would still like to see the ability to connect to a REST API service without platform dependent code one day.
sidecar REST service providing keys without implementing platform dependent libraries (OpenSSL) and passing around IntPtr.
What exactly is the sidecar doing and providing for you? How are you creating an instance of RSA
from what the sidecar provides?
Our components need to run on a properietary platform (maintained by a 3rd party) and we are working on having an REST based key service like https://azure.github.io/iot-identity-service/api/keys-service.html or https://github.com/GluuFederation/oxEleven accessible to our workloads. Ideally we just want to deploy one of the mentioned components and add an mediator that does some simple forwading and access/policy checks for API calls using functionality provided by the platform.
Using that since we are mostly a .NET shop we'd prefer not add additional native libraries unless they are "standardized" and we can be sure they will be maintained going forward (which OpenSSL surely is, but we are not so sure about the OpenSSL key service wrapper provided by iot-identity-service). So we'd prefer to derive from .NET RSA
base class and delegate the crypto API calls to the keys REST API (we will not be implementing any cryptographic operations). But this would still allow us to easier replace the keys REST service, if needed.
However it seems this is not possible, since when you derivce from RSA
you cannot use that derived class together with X509Certificate2 (unless the private key is exportable).
Some more background: Due to our platform we are limited in some ways and cannot just use an out of the box solution like a PKCS11 HSM that plugs nicely into OpenSSL. The most fitting solution for our problem (and our platform) we have stumbled upon so far is iot-identity-service which provides an OpenSSL library but the library has some hard-coded paths to unix domain sockets. But in case the .NET-only solution proves to be infeasible we'll probably try to get that up and running.
I changed the title to be reflective of what I think the request is (since, as already discussed, third party implementations have never worked).
At first blush, I don't think this'll ever be possible. e.g. we use X509Certificate2 with a paired key in SslStream/HttpClient, and they end up calling into OS libraries that need the OS-specific key handle. Binding third party implementations would cause those scenarios to fail in new and spectacular ways (but... maybe that's the only one, and we can accept it, just like Windows SslStream can't work with ephemeral private keys)
A second "how would we deal with this?" is that GetRSAPrivateKey and friends all return new instances, which means that we'd need to invent a key-clone mechanism as part of whatever "OK, I can be bound specially" system we used (and there's a lot of other related lifetime management).
Some higher level libraries, like SignedCms, allow passing an AsymmetricAlgorithm-derived instance along with a certificate, and then they do public key matching to ensure that they're compatible. That's the current best recommendation for how to enable these situations.
what about delegating non exportable certificate in Key Vault to X509Certificate2
Since X509Certificate2.PrivateKey has been "disabled" it is no longer possible to use X509Certificate2 with a sidecar REST service providing keys without implementing platform dependent libraries (OpenSSL) and passing around IntPtr.
Removing the ability to set the assymetric encryption in a ".NET only" way seems to contradict the goal of being platform independent. Please consider to allow setting
.PrivateKey
, until a ".NET-native" replacement for setting PrivateKey exists (e.g.ICertificatePal
being public would be great).Is there any workaround to the changes in X509Certificate2 ?