-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Proposal:
namespace System.Security.Cryptography {
public partial class AsymmetricAlgorithm {
public virtual void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<char> password);
public virtual void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<byte> passwordBytes);
public virtual void ImportFromPem(ReadOnlySpan<char> input);
}
public partial class RSA : AsymmetricAlgorithm {
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<char> password);
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<byte> passwordBytes);
public override void ImportFromPem(ReadOnlySpan<char> input);
}
public partial class DSA : AsymmetricAlgorithm {
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<char> password);
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<byte> passwordBytes);
public override void ImportFromPem(ReadOnlySpan<char> input);
}
public partial class ECDsa : AsymmetricAlgorithm {
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<char> password);
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<byte> passwordBytes);
public override void ImportFromPem(ReadOnlySpan<char> input);
}
public partial class ECDiffieHellman : AsymmetricAlgorithm {
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<char> password);
public override void ImportFromEncryptedPem(ReadOnlySpan<char> input, ReadOnlySpan<byte> passwordBytes);
public override void ImportFromPem(ReadOnlySpan<char> input);
}
}The expected usage of this is to be able to import RFC 7468 textually encoded public and private keys with an API that "does the right thing". The intent of this API would be to allow importing keys that are encoded with a label indicating what kind of key it is.
Usage example:
using var rsa = RSA.Create();
rsa.ImportFromPem(@"
-----BEGIN RSA PRIVATE KEY-----
MII....
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MII....
-----END CERTIFICATE-----
");Rationale:
There are a few different ways that a key can be encoded, when PEM encoded, such keys may look like "BEGIN PRIVATE KEY", "BEGIN RSA PRIVATE KEY", "BEGIN PUBLIC KEY", "BEGIN RSA PUBLIC KEY", etc. .NET Core 3 introduced APIs for importing these keys. However, this leaves developers the wrangle the key out of the file themselves, and know which API to use once they do. This is often not the experience with other software that when presented with a PEM textually encoded key, it often gets it right and just works.
Behaviors:
If the API can unambiguously determine the key that should be imported, it will import it, regardless of the rest of the contents such as a PEM aggregate. An example may be a PEM aggregate that contains a BEGIN CERTIFICATE and a BEGIN RSA PRIVATE KEY. In this circumstance, the BEGIN CERTIFICATE would be ignored because it is not a key.
If the contents contains multiple keys of the same algorithm, or a PKCS#8 key, an error is raised. This allows raising an error early without being forced to decode all PKCS#8 keys to determine what algorithm they are for.
For algorithms that support textually encoded parameters like DSA and ECDsa, if present, they will be imported as well and validated with the key. For example:
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIA1yny8h/fosik9s5aK19sIeGCxw1bjyBDD64G7LSkXoAoGCCqGSM49
AwEHoUQDQgAEwAWFpzlNs/YtqL5RxEeA66oqLDtrF5Et7mbyfhKoRYzjx/ItNUy/
UTyAoMKfzkEwM2PUsXZ23sVIjWD1KtOvMw==
-----END EC PRIVATE KEY-----
This private key is nistP256, as well as the EC parameters. Both explicit and named parameters should be supported. The parameters are not required, and appear at most once.
The PEM label and the key algorithm must match.
When ImportFromEncryptedPem is used, the PEM must be an encrypted private key. Even if contents contain exactly one key that could be imported because it is not encrypted, an error will still be raised.
The implementation of this is dependent on something like #29588 being implemented first.
Considerations:
- I considered
ReadOnlySpan<byte>overloads, but that depends on encoding which complicates things, either by requiring one specific encoding (which?) or overloads to support all.