9

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;

}
Spyral
  • 760
  • 1
  • 12
  • 33
  • X509KeyStorageFlags.Exportable https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509keystorageflags(v=vs.110).aspx – MMK Jan 13 '17 at 09:34
  • Yes I know it's there, you only use it if you want to explicitly set the priv key to exportable. Not the other way around. As you can see I have not used it. – Spyral Jan 13 '17 at 09:38
  • everything which relies only on software is easy extractable, even if its not exportable, you can just wait for your program to use it, and then memorydump it, or whatever. In my experience, if you want to be on the safe side, use something like codemeter plus codeencryption/obfuscation. Its not unhackable, but in almost every case not worth the effort. – Sebastian L Jan 16 '17 at 13:47
  • Yes I'm painfuly aware of that, the thing is our client is hard pressing the "make it non exportable" part. So we try to oblige and give them a feeling of security. – Spyral Jan 16 '17 at 15:02
  • I'm going to try and reimplement the method to create the self signed cert based on this post: http://stackoverflow.com/questions/13806299/how-to-create-a-self-signed-certificate-using-c - with this library I'm able to explicitally set "non exportable" – Spyral Jan 16 '17 at 15:03

2 Answers2

3

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.

Svekke
  • 1,470
  • 1
  • 12
  • 20
3

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.

Simmetric
  • 1,443
  • 2
  • 12
  • 20