0

I'm trying to create a self signed certificate in C# that has a non-exportable private, to be used to encrypt/decrypt a symmetrical key.

Now my initial try was to use bouncy castle, also see prev post. Now that isn't working out because I'm not finding any luck on explicitely putting the private key to non-exportable.

Moving on I found the following stack overflow post. Also posted the code I got from there at the bottom of this post.

For my purposes I'd drop the extended key usage part, change the X509PrivateKeyExportFlags to non exportable and change the X509CertificateEnrollmentContext tot CurrentUser Context. However when I do so I get:

CertEnroll::CX509CertificateRequestCertificate::InitializeFromPrivateKey: The parameter is incorrect. 0x80070057 (WIN32: 87 ERROR_INVALID_PARAMETER)

Hope someone cna help me out.

removed old code - see current version below

EDIT: current state

 public static X509Certificate2 CreateSelfSignedCertificate(string subjectName)
    {
        // create DN for subject and issuer
        var dn = new CX500DistinguishedName();
        dn.Encode("CN=" + 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 = false;
        privateKey.Length = 2048;
        privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; // use is not limited
        privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_NONE;
        privateKey.Create();

        // Use the stronger SHA512 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");

        // Create the self signing request
        var cert = new CX509CertificateRequestCertificate();
        cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, privateKey, "");
        cert.Subject = dn;
        cert.Issuer = dn; // the issuer and the subject are the same
        cert.NotBefore = DateTime.Now;
        // this cert expires immediately. Change to whatever makes sense for you
        cert.NotAfter = cert.NotBefore.AddYears(5);
        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 = subjectName; // Optional: add a friendly name
        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 System.Security.Cryptography.X509Certificates.X509Certificate2(
            System.Convert.FromBase64String(base64encoded), "",
            // mark the private key as exportable (this is usually what you want to do)
            System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable
        );
    }

After changing the KeySpec to "AT_KeyEchange" I now get:

Exception thrown: 'System.Runtime.InteropServices.COMException' in MyTool.exe Exception thrown: 'System.NullReferenceException' in MyTool.exe CertEnroll::CX509PrivateKey::Create: Invalid flags specified. 0x80090009 (-2146893815 NTE_BAD_FLAGS).

I changed it because encryption/decryption wasn't working anymore, figured it was because of the KeySet mode but now I need to change more. I'm assuming that the hashobject might be incorrect here? Shouldn't it be like SHA256withRSA or something like that? (in my bouncy castle implementation I used: "SHA256WithRSA".

Or am I missing something else?

Community
  • 1
  • 1
Spyral
  • 760
  • 1
  • 12
  • 33
  • Posted example should work. – Crypt32 Jan 16 '17 at 18:39
  • Yes but as said - I want to change a couple of things which makes it not work. "For my purposes I'd drop the extended key usage part, change the X509PrivateKeyExportFlags to non exportable and change the X509CertificateEnrollmentContext tot CurrentUser Context. However when I do so I get:" – Spyral Jan 16 '17 at 19:45
  • When changing to user context in `IX509CertificateRequestCertificate` interface call, just update this line: `privateKey.MachineContext = true;` change `true` to `false` and it will work. – Crypt32 Jan 16 '17 at 20:03
  • Thanks for the tip @Crypt32 ! Now it makes the cert. It gets stored in the usestore, as I wanted ! (which is great!) however, when I try to encrypt and then decrypt data using the pub/private key of the certificate I bounce upon the following error: https://i.imgur.com/GYcarFP.png (the decryption/encryption was tested before with another certificate I created with bouncy castle, but I couldn't put that one couldn't be set to non-exportable.) is it maybe because of X509KeySpec.XCN_AT_SIGNATURE; this flag? – Spyral Jan 16 '17 at 20:53
  • yes, most likely, the `KeySpec` is the issue. For encryption purposes you need to use `AT_KEYEXCHANGE` value. – Crypt32 Jan 16 '17 at 20:55
  • Should I change anything else? if I just change the flag to KEYEXCHANGE I get the following errors: Exception thrown: 'System.Runtime.InteropServices.COMException' in mytool.exe Exception thrown: 'System.NullReferenceException' in mytool.exe CertEnroll::CX509PrivateKey::Create: Invalid flags specified. 0x80090009 (-2146893815 NTE_BAD_FLAGS) (at the end don't forget to submit your answer so I can accept it!) – Spyral Jan 16 '17 at 21:41
  • @Crypt32 updated my post - could you please be so kind to have a look? I must be close! – Spyral Jan 16 '17 at 23:04
  • Trying this code (but for currentuser) fixed my problem. I honestly can't identify what I did wrong compared to this post.. but ah well, if it works.. http://stackoverflow.com/questions/18339706/how-to-create-self-signed-certificate-programmatically-for-wcf-service – Spyral Jan 17 '17 at 12:01

0 Answers0