Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make priv key in certificate not exportable C#

So below you'll find my code that creates a self signed certificate with the private key in there. It's stored in the user store. Now when I use the mmc tool I can just export the private key from the certificate? I thought that was a flag you had to explicitly add when you create the cert?

So what I wanna know is, how do I change this code so that the private key is no longer exportable through mmc.

Code:

public static X509Certificate2 GenerateSelfSignedCertificateNoCA(string subjectName, string issuerName)
{
        const int keyStrength = 2048;

        // Generating Random Numbers
        CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
        SecureRandom random = new SecureRandom(randomGenerator);

        // The Certificate Generator
        X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();

        // Serial Number
        BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
        certificateGenerator.SetSerialNumber(serialNumber);

        // Signature Algorithm
        const string signatureAlgorithm = "SHA256WithRSA";
        certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);

        // Issuer and Subject Name
        X509Name subjectDN = new X509Name(subjectName);
        X509Name issuerDN = new X509Name(issuerName);
        certificateGenerator.SetIssuerDN(issuerDN);
        certificateGenerator.SetSubjectDN(subjectDN);

        // Valid For
        DateTime notBefore = DateTime.UtcNow.Date;
        DateTime notAfter = notBefore.AddYears(2);

        certificateGenerator.SetNotBefore(notBefore);
        certificateGenerator.SetNotAfter(notAfter);

        // Subject Public Key
        AsymmetricCipherKeyPair subjectKeyPair;
        var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
        var keyPairGenerator = new RsaKeyPairGenerator();
        keyPairGenerator.Init(keyGenerationParameters);
        subjectKeyPair = keyPairGenerator.GenerateKeyPair();

        certificateGenerator.SetPublicKey(subjectKeyPair.Public);

        // Generating the Certificate
        AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;

        // selfsign certificate
        Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(subjectKeyPair.Private, random);

        // correcponding private key
        PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);

        // merge into X509Certificate2
        X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());


        Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(info.PrivateKey.GetDerEncoded());
        if (seq.Count != 9)
        {
            //throw new PemException("malformed sequence in RSA private key");
        }

        RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq);
        RsaPrivateCrtKeyParameters rsaparams = new RsaPrivateCrtKeyParameters(
            rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient);

        x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
        // Console.Write("Private key: " + x509.PrivateKey);
        return x509;

}
like image 565
Spyral Avatar asked Dec 22 '16 10:12

Spyral


2 Answers

DotNetUtilities.ToRSA will always set the private key to Exportable. To prevent this, there is an alternate way to convert your BouncyCastle key to a .NET X509Certificate2 as taken from here

    public X509Certificate2 GenerateSelfSignedCertificateNoCA(string subjectName, string issuerName)
    {
        const int keyStrength = 2048;

        // Generating Random Numbers
        CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
        SecureRandom random = new SecureRandom(randomGenerator);

        // The Certificate Generator
        X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();

        // Serial Number
        BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
        certificateGenerator.SetSerialNumber(serialNumber);

        // Signature Algorithm
        const string signatureAlgorithm = "SHA256WithRSA";
        certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);

        // Issuer and Subject Name
        X509Name subjectDN = new X509Name(subjectName);
        X509Name issuerDN = new X509Name(issuerName);
        certificateGenerator.SetIssuerDN(issuerDN);
        certificateGenerator.SetSubjectDN(subjectDN);

        // Valid For
        DateTime notBefore = DateTime.UtcNow.Date;
        DateTime notAfter = notBefore.AddYears(2);

        certificateGenerator.SetNotBefore(notBefore);
        certificateGenerator.SetNotAfter(notAfter);

        // Subject Public Key
        AsymmetricCipherKeyPair subjectKeyPair;
        var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
        var keyPairGenerator = new RsaKeyPairGenerator();
        keyPairGenerator.Init(keyGenerationParameters);
        subjectKeyPair = keyPairGenerator.GenerateKeyPair();

        certificateGenerator.SetPublicKey(subjectKeyPair.Public);

        // Generating the Certificate
        AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;

        // selfsign certificate
        Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(subjectKeyPair.Private, random);

        //import into store
        var certificateEntry = new X509CertificateEntry(certificate);
        string friendlyName = certificate.SubjectDN.ToString();
        var store = new Pkcs12Store();
        store.SetCertificateEntry(friendlyName, certificateEntry);
        store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] {certificateEntry});

        //save to memorystream
        var password = "password";
        var stream = new MemoryStream();
        store.Save(stream, password.ToCharArray(), random);

        // convert into X509Certificate2
        X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(stream.ToArray(), password, X509KeyStorageFlags.UserKeySet);

        return x509;
    }

The private key is now marked as Exportable=false.

like image 178
Simmetric Avatar answered Oct 07 '22 00:10

Simmetric


This is not possible with PKCS12/PFX.

You should not percieve a non-exportable private key as a "unbreakable" or "hard-to-break" security precaution. There are utilities that will export a non-exportable private key as well.

If you need to protect private keys, I suggest you use a cryptographic token/smart-card or a hardware security module.

like image 33
Svekke Avatar answered Oct 07 '22 00:10

Svekke