Search code examples
c#.netcryptographycertificatebouncycastle

BouncyCastle PrivateKey To X509Certificate2 PrivateKey (ECC)


Using .NET Core 3.1 and BouncyCastle

I have a Private ECC key from Pkcs12. How can I store this in X509Certificate2 Private Key please? The reason I am trying it this way is because when I load the Pkcs12 as X509Certificate2, the X509Certificate2.PrivateKey method throws a "not implemented / algorithm not supported exception".

This is what I have so far:

        using var stream = new MemoryStream(myPkcs12);

        Pkcs12Store pstore = new Pkcs12Store(stream, password.ToCharArray());
        
        var name = "";
        foreach (string alias in store.Aliases)
        {
            if (pstore.IsKeyEntry(alias))
            {
                name = alias;
            }
        }

        var key = pstore.GetKey(name);

            var cert = new X509Certificate2(myPkcs12, "mypassword", X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable);
          
                cert.PrivateKey = // key? I imagine it is incorrect to use  DotNetUtilities.ToRSA()?

Thank you!

UPDATE:

The reason for this post is due to this problem:

private const string EccTestCert = "MIINbQIBAzCCDSkGCSqGSIb3DQEHAaCCDRoEgg0WMIIN.... 9wQUpQgYbgB7yknIW7Oaz3hogAVihJoCAgfQ";
var cert = new X509Certificate2(Convert.FromBase64String(EccTestCert), "1");

//  If you inspect it, the PrivateKey throws an exception.  Whilst with an RSA cert, it will not.

Solution

  • The source shows that, depending on the platform you run on the exception is thrown.

                        switch (GetKeyAlgorithm())
                        {
                            case Oids.Rsa:
                                _lazyPrivateKey = Pal.GetRSAPrivateKey();
                                break;
                            case Oids.Dsa:
                                _lazyPrivateKey = Pal.GetDSAPrivateKey();
                                break;
                            default:
                                // This includes ECDSA, because an Oids.EcPublicKey key can be
                                // many different algorithm kinds, not necessarily with mutual exclusion.
                                //
                                // Plus, .NET Framework only supports RSA and DSA in this property.
                                throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
                        }
    

    The private key is of type AsymmetricAlgorithm witch needs to be casted anyway to RSA or ECDsa. I remember @bartonjs saying that one should use the GetXXXPrivateKey() methods. So you can do that on your own:

    string EccTestCert = "{base64-pkcs-12-here}";
    var cert = new X509Certificate2(Convert.FromBase64String(EccTestCert), "1");
    
    if (cert.HasPrivateKey) {
      var key =
        (AsymmetricAlgorithm) cert.GetRSAPrivateKey()
          ?? cert.GetECDsaPrivateKey()
            ?? throw new NotSupportedException("Who still uses DSA?");
    
      if (key is ECDsa ecdsa) {
        var ecdsaSignature = ecdsa.SignData(new byte[]{ 0x00}, HashAlgorithmName.SHA256);
      } else if (key is RSA rsa) {
        var rsaSignature = rsa.SignData(new byte[]{ 0x00}, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
      } else {
        throw new NotSupportedException("Who still uses DSA?");
      }
    }