How to create self-signed certificate programmatically for WCF service?

前端 未结 3 1142
慢半拍i
慢半拍i 2020-12-10 03:34

I have a self-hosted WCF server running as a Windows service under the Local System account. I am trying to create a self-signed certificate programmatically in c# for use w

相关标签:
3条回答
  • 2020-12-10 04:11

    You can also use the CLR Security library on CodePlex (https://clrsecurity.codeplex.com/). Here is sample code which creates a self signed certificate, and tests it with SSLStream.

            var machineName = Environment.MachineName;
            var keyCreationParameters = new CngKeyCreationParameters();
            keyCreationParameters.KeyUsage = CngKeyUsages.AllUsages;
            keyCreationParameters.KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey;
            keyCreationParameters.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(4096), CngPropertyOptions.None));
            var cngKey = CngKey.Create(CngAlgorithm2.Rsa, "Test", keyCreationParameters);
    
            var x500DistinguishedName = new X500DistinguishedName("CN=" + machineName);
            x500DistinguishedName.Oid.Value = "1.3.6.1.5.5.7.3.1";
            var certificateCreationParameters = new X509CertificateCreationParameters(x500DistinguishedName);
            certificateCreationParameters.SignatureAlgorithm = X509CertificateSignatureAlgorithm.RsaSha512;
            certificateCreationParameters.TakeOwnershipOfKey = true;
            certificateCreationParameters.CertificateCreationOptions = X509CertificateCreationOptions.None;
            certificateCreationParameters.EndTime = new DateTime(9999, 12,31, 23, 59, 59, 999, DateTimeKind.Utc);
            var certificate = cngKey.CreateSelfSignedCertificate(certificateCreationParameters);
    
            var certificateStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
            certificateStore.Open(OpenFlags.ReadWrite);
            certificateStore.Add(certificate);
            certificateStore.Close();
    
    
            var tcpListener = TcpListener.Create(6666);
            tcpListener.Start();
            var client = new TcpClient("localhost", 6666);
            var acceptedClient = tcpListener.AcceptTcpClient();
            var acceptedClinetSslStream = new SslStream(
                acceptedClient.GetStream(), false);
            var serverAuthTask = acceptedClinetSslStream.AuthenticateAsServerAsync(certificate,
                                false, SslProtocols.Tls, true);
    
            SslStream clientSslStream = new SslStream(
                client.GetStream(),
                false,
                delegate(object o, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors errors)
                    {
                        if (errors == SslPolicyErrors.None)
                            return true;
    
                        Console.WriteLine("Certificate error: {0}", errors);
    
                        // Do not allow this client to communicate with unauthenticated servers. 
                        return false;
                    },
                null);
            var clientAuthTask = clientSslStream.AuthenticateAsClientAsync(machineName);
    
            Task.WaitAll(serverAuthTask, clientAuthTask);
    
    0 讨论(0)
  • 2020-12-10 04:23

    I could not make this work, but I found an alternate solution. (Update December 2014: I have now gotten it to work using the accepted answer.)

    I was able to use the PluralSight.Crypto library to achieve what I need. I had to modify the source code slightly to get the private key to store in the LocalMachine store. The changes I made were to the file CryptContext.cs. I changed the CreateSelfSignedCertificate method. Following is a snippet of code including the change that I made. In essence, I set the Flags member of the CryptKeyProviderInformation structure to set it to 0x20 (CRYPT_MACHINE_KEYSET) if the CryptContext object contains this value in its Flags.

            byte[] asnName = properties.Name.RawData;
            GCHandle asnNameHandle = GCHandle.Alloc(asnName, GCHandleType.Pinned);
    
            int flags = 0;                    // New code
            if ((this.Flags & 0x20) == 0x20)  // New code
                flags = 0x20;                 // New code
    
            var kpi = new Win32Native.CryptKeyProviderInformation
            {
                ContainerName = this.ContainerName,
                KeySpec = (int)KeyType.Exchange,
                ProviderType = 1, // default RSA Full provider
                Flags = flags                 // New code
            };
    

    Then I use the function in my own code like this:

            using (Pluralsight.Crypto.CryptContext ctx = new Pluralsight.Crypto.CryptContext()) {
    
                ctx.Flags = 0x8 | 0x20;
                ctx.Open();
    
                X509Certificate2 cert = ctx.CreateSelfSignedCertificate(
                    new Pluralsight.Crypto.SelfSignedCertProperties
                    {
                        IsPrivateKeyExportable = true,
                        KeyBitLength = 4096,
                        Name = new X500DistinguishedName("CN=" + subjectName),
                        ValidFrom = DateTime.Today,
                        ValidTo = DateTime.Today + expirationLength,
                    });
    
                return cert;
            }
    

    Notice that I set the Flags for the CryptContext object to be 0x8 | 0x20 (CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET).

    I wish I could figure out what was wrong with my original solution. But I need something to work and in my testing this solution does what I need. I hope it helps someone else along the way.

    0 讨论(0)
  • 2020-12-10 04:24

    I had the same issue using the equivalent code in PowerShell. It appears that sometime the private key just disappears. I used Process Monitor and you can see the key file being deleted.

    The way I solved this was to add X509KeyStorageFlags.PersistKeySet to the X509Certificate2 constructor.

    0 讨论(0)
提交回复
热议问题