Open vcsjones opened 3 years ago
Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq See info in area-owners.md if you want to be subscribed.
Unhandled exception. System.ArgumentException: Keys used with the RSACng algorithm must have an algorithm group of RSA. (Parameter 'key') at System.Security.Cryptography.RSACng..ctor(CngKey key) at Internal.Cryptography.Pal.CertificatePal.<>c.b__67_1(CngKey cngKey) at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng) at Internal.Cryptography.Pal.CertificatePal.GetRSAPrivateKey() at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints) at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)
Author: | vcsjones |
---|---|
Assignees: | - |
Labels: | `area-System.Security`, `untriaged` |
Milestone: | - |
Found this while working on #44535. Seems like behavior we don't want to replicate in Linux / macOS.
This was tested in Windows and assumed to not work, but that is because the test was coded to use ephemeral key imports, which was preventing the import, not this specific scenario.
Hm, well, the former case of RSA-and-RSA seems to be tested here:
So it appears to be a known, albeit odd, behavior. The latter case however seems odd. I'm not sure it is expected that GetRSAPrivateKey
will throw. Granted this is also a pretty specific case of a bad PKCS12 file, so perhaps all of this is acceptable behavior.
Importing a PKCS12 ("PFX") file on Windows seems very permissive as to what kind of keys and certificate matching is done if the certificate and key contains a LocalKeyId.
For algorithm-to-algorithm, it will work and associate the key when in fact the key does not match the certificate's public key. So
GetRSA{Public,Private}Key
will return a key that does not match the certificate.Repro case for matching algorithms
```c# using System; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; using RSA rsa = RSA.Create(); using RSA rsa2 = RSA.Create(); Pkcs9LocalKeyId kid = new Pkcs9LocalKeyId(new byte[] { 1 }); CertificateRequest req = new CertificateRequest("CN=blah", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); using X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now); Pkcs12Builder builder = new Pkcs12Builder(); Pkcs12SafeContents keyContents = new Pkcs12SafeContents(); Pkcs12SafeContents certContents = new Pkcs12SafeContents(); string password = "YabbaDabbaDoo"; PbeParameters pbeParameters = new PbeParameters(PbeEncryptionAlgorithm.TripleDes3KeyPkcs12, HashAlgorithmName.SHA1, 2000); Pkcs12SafeBag keyBag = keyContents.AddShroudedKey(rsa2, password, pbeParameters); Pkcs12SafeBag certBag = certContents.AddCertificate(cert); keyBag.Attributes.Add(kid); certBag.Attributes.Add(kid); builder.AddSafeContentsEncrypted(keyContents, password, pbeParameters); builder.AddSafeContentsUnencrypted(certContents); builder.SealWithMac(password, HashAlgorithmName.SHA1, 2000); byte[] pfxBytes = builder.Encode(); using var imported = new X509Certificate2(pfxBytes, password, X509KeyStorageFlags.Exportable); using var importedKey = imported.GetRSAPrivateKey(); // Certificate public key does not match the RSA key. Console.WriteLine(imported.GetPublicKey().AsSpan().SequenceEqual(importedKey.ExportRSAPublicKey())); ```It gets a little weirder when you have two completely unrelated key algorithms. For example, the certificate is RSA, and the key is ECDSA, and a LocalKeyId associate the two, the import will still succeed.
The certificate will have
HasPrivateKey
astrue
. If the certificate has an RSA SPKI, callingGetRSAPrivateKey()
will throw:Repro case for mismatched algorithms
```c# using System; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; using RSA rsa = RSA.Create(); using ECDsa ecdsa = ECDsa.Create(); Pkcs9LocalKeyId kid = new Pkcs9LocalKeyId(new byte[] { 1 }); CertificateRequest req = new CertificateRequest("CN=blah", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); using X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now); Pkcs12Builder builder = new Pkcs12Builder(); Pkcs12SafeContents keyContents = new Pkcs12SafeContents(); Pkcs12SafeContents certContents = new Pkcs12SafeContents(); string password = "YabbaDabbaDoo"; PbeParameters pbeParameters = new PbeParameters(PbeEncryptionAlgorithm.TripleDes3KeyPkcs12, HashAlgorithmName.SHA1, 2000); Pkcs12SafeBag keyBag = keyContents.AddShroudedKey(ecdsa, password, pbeParameters); Pkcs12SafeBag certBag = certContents.AddCertificate(cert); keyBag.Attributes.Add(kid); certBag.Attributes.Add(kid); builder.AddSafeContentsEncrypted(keyContents, password, pbeParameters); builder.AddSafeContentsUnencrypted(certContents); builder.SealWithMac(password, HashAlgorithmName.SHA1, 2000); byte[] pfxBytes = builder.Encode(); using var imported = new X509Certificate2(pfxBytes, password, X509KeyStorageFlags.Exportable); _ = imported.GetRSAPrivateKey(); // BOOM ```