How to create self-signed certificate programmatically for WCF service?
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.
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.
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);