1

In most of the windows OS (Windows 2019 Server), below code works fine, but for some I am getting cryptographic exception: Invalid provider type specified while trying to access PrivateKey of self signed certificate.

What could be the issue?

enter image description here

Here is the code,

 var selfSignedCert = CreateSelfSignedCertificate("CN=localhost", "Agent Service");
        try
        {
            var rsaCsp = selfSignedCert.PrivateKey as RSACryptoServiceProvider;
            if (rsaCsp != null) Console.WriteLine(rsaCsp.KeySize);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex}");
        }

CreateSelfSignedCertificate Method :

  private static X509Certificate2 CreateSelfSignedCertificate(string subjectName, string friendlyName)
    {
        // create DN for subject and issuer
        var dn = new CX500DistinguishedName();
        dn.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);

        // create a new private key for the certificate
        CX509PrivateKey privateKey = new CX509PrivateKey();
        privateKey.ProviderName = "Microsoft Base Cryptographic Provider v1.0";
        privateKey.MachineContext = true;
        privateKey.Length = 2048;
        privateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; // use is not limited
        privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
        privateKey.Create();

        // Use SHA-2 SHA256 hashing algorithm
        var hashobj = new CObjectId();
        hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
            ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
            AlgorithmFlags.AlgorithmFlagsNone, "SHA256");

        var keyUsage = new CX509ExtensionKeyUsage();
        keyUsage.InitializeEncode(CertEnroll.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
            CertEnroll.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE);
        keyUsage.Critical = true;

        // add extended key usage if you want - look at MSDN for a list of possible OIDs
        // serverAuth (1.3.6.1.5.5.7.3.1) -- TLS Web server authentication
        // clientAuth(1.3.6.1.5.5.7.3.2)-- TLS Web client authentication
        var oidServerAuth = new CObjectId();
        oidServerAuth.InitializeFromValue("1.3.6.1.5.5.7.3.1");

        var oidClientAuth = new CObjectId();
        oidClientAuth.InitializeFromValue("1.3.6.1.5.5.7.3.2");

        var oidlist = new CObjectIds
        {
            oidServerAuth,
            oidClientAuth
        };

        var eku = new CX509ExtensionEnhancedKeyUsage();
        eku.InitializeEncode(oidlist);

        // Create the self signing request
        var cert = new CX509CertificateRequestCertificate();
        cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
        cert.Subject = dn;
        cert.Issuer = dn; // the issuer and the subject are the same
        var hoursAgo = TimeSpan.FromHours(12);
        cert.NotBefore = DateTime.UtcNow.Subtract(hoursAgo);
        // this cert expires in twenty years. Change to whatever makes sense for you
        cert.NotAfter = DateTime.UtcNow.AddYears(20);
        cert.X509Extensions.Add((CX509Extension)keyUsage);
        cert.X509Extensions.Add((CX509Extension)eku); // add the EKU

        cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
        cert.Encode(); // encode the certificate

        // Do the final enrollment process
        var enroll = new CX509Enrollment();
        enroll.InitializeFromRequest(cert); // load the certificate

        enroll.CertificateFriendlyName = friendlyName ?? subjectName;

        string csr = enroll.CreateRequest(); // Output the request in base64
                                             // and install it back as the response
        enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
            csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password
                                                            // output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
        var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption
            PFXExportOptions.PFXExportChainWithRoot);

        // instantiate the target class with the PKCS#12 data (and the empty password)
        return new X509Certificate2(
            System.Convert.FromBase64String(base64encoded), "",
            // mark the private key as exportable (this is usually what you want to do)
            // mark private key to go into the Machine store instead of the current users store
            // mark key as persistent https://support.microsoft.com/en-us/kb/950090
            X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
        );
    }
user584018
  • 10,186
  • 15
  • 74
  • 160

1 Answers1

1

Basically the exception is because the code is trying to access the private key of the self-signed certificate using the wrong type of provider, a problem that occurs on some operating systems. To resolve this exception, you can make a code modification to use the correct provider type when accessing the private key. Instead of using RSACryptoServiceProvider, you can use RSA from the System.Security.Cryptography namespace. Here is the modified code:

var privateKey = selfSignedCert.GetRSAPrivateKey();
if (privateKey != null)
{
     Console.WriteLine(privateKey.KeySize);
}

'GetRSAPrivateKey()' is used to get the private key of the self-signed certificate as an RSA object. you can verify that the private key is not null before accessing its KeySize property or performing other operations related to the private key.

  • 1
    Thanks for your answer @Felipe Gabriel Souza, but what is the reason `RSACryptoServiceProvider` is not working in some machine? Is there any registry crypto settings blocking it? Can I check something in machine? – user584018 Jul 06 '23 at 01:28
  • 1
    It's a question that I won't be able to give you a 100% right answer, however, it could be a missing cryptographic provider, some machines may not have the "Microsoft Base Cryptographic Provider v1.0" cryptographic provider available, if you are in a corporate environment group policies can restrict the use of certain cryptographic providers or algorithms, and it can also be something very simple like the application may not have proper permissions to access the certificate's private key. – Felipe Gabriel Souza Martins Jul 06 '23 at 01:39
  • Could you please suggest how to check "Microsoft Base Cryptographic Provider v1.0" cryptographic provider available in a machine or not? – user584018 Jul 06 '23 at 03:21