-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Add an easier way of opening named keys via OpenSSL #55356
Comments
Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @GrabYourPitchforks Issue DetailsWhen a key is available via an ENGINE (or the new OSSL3 provider model) by name, it currently requires a couple of fussy P/Invokes. The easiest option, is to load just the key handle, a la namespace System.Security.Cryptography
{
partial class SafeEvpPKeyHandle
{
public static SafeEvpPKeyHandle LoadKey(string providerName, string keyId) => throw null;
}
} That needs to be reconciled with OSSL3 providers to make sure that two strings is sufficient. Loading just the EVP_PKEY better enables the scenario of loading a key from an HSM (or other ENGINE-based system), but does still require a bit of a dance with a certificate (when applicable). using (X509Certificate2 pubCert = ...)
using (SafeEvpPKeyHandle keyHandle = SafeEvpPKeyHandle.LoadKey(...))
{
switch (pubCert.GetKeyAlgorithm())
{
case Oids.Rsa:
using (RSAOpenSsl rsa = new RSAOpenSsl(keyHandle))
{
return pubCert.CopyWithPrivateKey(rsa);
}
case Oids.Dsa:
...
case Oids.Ecc:
case Oids.Ecdh:
{
using (ECDiffieHellmanOpenSsl ecdh = ...)
...
}
default:
throw something;
}
} So perhaps we can find a better way of doing that. Especially since the CopyWithPrivateKey implementation eventually throws away the wrapper type and just saves a copy of the key handle. That helper operation should fail on macOS (assuming it's tied to the OpenSSL library), since macOS can't bind OpenSSL keys without exporting them.
|
Explictly out of scope: Putting the key into the ENGINE/provider in the first place. |
Openssl has two APIs I assume this will wrap |
Yeah, that's probably reasonable. I'm way less clear on the use case for LoadPublicKey (since a software key is probably fine and faster (or fast enough) for public ops); but it's a low enough incremental cost that it doesn't hurt. |
Is doing this currently documented anywhere? I'd like to pull an RSA private key out of a TPM2 |
Having proven to myself that |
As far as I know, the C code to open a named key from a specific OSSL_PROVIDER looks like https://github.com/bartonjs/runtime/blob/294131ec9e02bc8604c0a8cba8423b82032539c0/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c#L321-L421. I only used it to load keys named like For the If you have an ENGINE instead of provider, then that'd be the code from the top post: ENGINE* e = ENGINE_by_id(providerName);
EVP_PKEY* key = NULL;
if (e != NULL)
{
if (ENGINE_init(e))
{
key = ENGINE_load_private_key(e, keyId, NULL, NULL);
ENGINE_finish(e);
}
}
return key; Either way, you'd currently need to either provide your own C wrapper-target for those, or convert them all to P/Invokes. |
namespace System.Security.Cryptography;
public partial class SafeEvpPKeyHandle
{
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenPrivateKeyFromEngine(string engineName, string keyId);
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenPublicKeyFromEngine(string engineName, string keyId);
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenKeyFromProvider(string providerName, string keyUri);
} |
Due to testing I've made I'll cut Provider part from this proposal. Some providers can work with API designed as is with just simple Interop to OpenSSL but they're mostly not that useful. Some more useful providers like tpm require (well, they load but keys don't work) that you always create them in separate library context which would require quite a bit of refactoring to support (or lookup which would make everyone suffer lookup penalty). It might be simpler to implement RSA/ECDsa directly using providers than trying to get SafeEvpPKeyHandle to work with them since that implementation could pass in library context. My attempts to get that to work can be found here: https://github.com/krwq/runtime/blob/open_named_keys/src/libraries/System.Security.Cryptography/tests/osslplugins/testprovider.c where I experimented with similar setup to what we do in .NET. but my finding is that this currently doesn't work in the global context (not sure if this is openssl bug, provider bug or by design) but can be made to work when loaded in separate library context. |
When a key is available via an ENGINE (or the new OSSL3 provider model) by name, it currently requires a couple of fussy P/Invokes.
Proposal
Points worth mentioning
load_public("some key")
and `load_private("some key") are related.tpm2
provider supports key URIs like "handle:0x81000000" and "object:".Usage examples
RSA Public key from ENGINE
RSA Private key from ENGINE
RSA Private key from Provider
Original speculation
The easiest option, is to load just the key handle, a la
which turns into something like
That needs to be reconciled with OSSL3 providers to make sure that two strings is sufficient.
Loading just the EVP_PKEY better enables the scenario of loading a key from an HSM (or other ENGINE-based system), but does still require a bit of a dance with a certificate (when applicable).
So perhaps we can find a better way of doing that. Especially since the CopyWithPrivateKey implementation eventually throws away the wrapper type and just saves a copy of the key handle.
That helper operation should fail on macOS (assuming it's tied to the OpenSSL library), since macOS can't bind OpenSSL keys without exporting them.
The text was updated successfully, but these errors were encountered: