Skip to content

Commit

Permalink
Fixes OuterLoop test failure on Server 2012 machines
Browse files Browse the repository at this point in the history
The certificates needed to be regenerated with slightly
different steps to ensure the exported files were written
in ASCII.

The CertificateManager also avoids loading certificates already
loaded.
  • Loading branch information
roncain committed Aug 28, 2015
1 parent 85ba4eb commit 56ece9d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,24 @@ private static string CreateCertificateFilePath(BridgeConfiguration configuratio
return path;
}

// Install the certificate in the given file path into the Root store and returns its thumbprint
private static bool TryFindCertificate(X509Store store, string subjectName, out string thumbprint)
{
thumbprint = null;
foreach (var c in store.Certificates)
{
if (String.Equals(c.SubjectName.Name, subjectName, StringComparison.OrdinalIgnoreCase))
{
thumbprint = c.Thumbprint;
return true;
}
}

return false;
}

// Install the certificate in the given file path into the Root store and returns its thumbprint.
// It will not install the certificate if there is already one with the same full SubjectName present.
// It returns the thumbprint of the certificate, regardless whether it was added or found.
public static string InstallRootCertificate(BridgeConfiguration configuration, string certificateName)
{
string certificateFilePath = CreateCertificateFilePath(configuration, certificateName);
Expand All @@ -88,15 +105,28 @@ public static string InstallRootCertificate(BridgeConfiguration configuration, s
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
X509Certificate2 cert = new X509Certificate2(certificateFilePath);
store.Add(cert);
string thumbprint = null;
if (!TryFindCertificate(store, cert.SubjectName.Name, out thumbprint))
{
store.Add(cert);
thumbprint = cert.Thumbprint;
s_rootCertificates[cert.Thumbprint] = certificateName;
Console.WriteLine("Added to root store certificate '{0}' : '{1}'", certificateName, cert.SubjectName.Name);
}
else
{
Console.WriteLine("Reusing existing root store certificate '{0}' : '{1}'", certificateName, cert.SubjectName.Name);
}

store.Close();

s_rootCertificates[cert.Thumbprint] = certificateName;
return cert.Thumbprint;
return thumbprint;
}
}

// Install the certificate in the given file path into the My store
// Install the certificate in the given file path into the My store.
// It will not install the certificate if there is already one with the same full SubjectName present.
// It returns the thumbprint of the certificate, regardless whether it was added or found.
public static string InstallMyCertificate(BridgeConfiguration configuration, string certificateName)
{
// Installing any certificate guarantees the certificate authority is loaded first
Expand All @@ -118,12 +148,23 @@ public static string InstallMyCertificate(BridgeConfiguration configuration, str
store.Open(OpenFlags.ReadWrite);

X509Certificate2 cert = new X509Certificate2();
cert.Import(certificateFilePath, String.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
store.Add(cert);
store.Close();
// "test" is currently the required password to allow exportable private keys
cert.Import(certificateFilePath, "test", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
string thumbprint = null;
if (!TryFindCertificate(store, cert.SubjectName.Name, out thumbprint))
{
store.Add(cert);
thumbprint = cert.Thumbprint;
s_myCertificates[cert.Thumbprint] = certificateName;
Console.WriteLine("Added to my store certificate '{0}' : '{1}'", certificateName, cert.SubjectName.Name);
}
else
{
Console.WriteLine("Reusing existing my store certificate '{0}' : '{1}'", certificateName, cert.SubjectName.Name);
}

s_myCertificates[cert.Thumbprint] = certificateName;
return cert.Thumbprint;
store.Close();
return thumbprint;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
=======================

This file describes how the certificates were created
and how they are used.
and how they are used. Basic steps are at:
https://msdn.microsoft.com/en-US/library/Ff648498.aspx


The root certificate authority stored in the "Root" store
----------------------------------------------------------
This is a certificate authority used to create the self-signed certificate.
It was created with these commands:

Template:
makecert -n "CN=RootCATest" -r -sv RootCATest.pvk RootCATest.cer
Actual:
makecert -n "CN=DO_NOT_TRUST_RootCAWcfBridge, O=DO_NOT_TRUST, OU=Created by https://github.com/dotnet/wcf" -e "11/20/2015" -r -sv RootCAWcfBridge.pvk RootCAWcfBridge.cer

The password option "None" was selected.
The password option "test" was selected when one was requested for the private key.
This password appears necessary or the private key cannot be exported.

At runtime, the Bridge will load this certificate and place it into the Root store.


Expand All @@ -20,7 +27,20 @@ The self-signed certificate for SSL stored in the "My" store
This is the self-signed certificate used for SSL testing.
It was created with this command:

pvk2pfx -pvk RootCAWcfBridge.pvk -spc RootCAWcfBridge.cer -pfx RootCAWcfBridge.pfx
Template:
makecert -sk <<UniqueKeyName>> -iv RootCATest.pvk -n "CN=<<MachineName>>" -ic RootCATest.cer -sr localmachine -ss my -sky exchange -pe
Actual:
Run this to create the certificate *and put it into the store* (critical step):
makecert -sk "RootCAWcfBridge" -iv RootCAWcfBridge.pvk -n "CN=localhost" -ic RootCAWcfBridge.cer -sr localmachine -ss my -sky exchange -pe
Then start the certificate manager and do these steps:
1. Under the "Personal"folder find the "localhost" certificate whose issuer is the "DO_NOT_TRUST_RootCAWcfBridge" above
2. Right-click | All Tasks | Export
3. Check "Export private keys" | NEXT
4. NEXT
5. When asked for a name to save, choose RootCAWcfBridge.pfx in the same folder as RootCAWcfBridge.cer

The reason for this use of the certificate manager UI is that makecert writes the .pfx file as UNICODE,
but certificate manager writews it as ASCII. If written as UNICODE it fails to load properly.

At runtime, the Bridge will import this .pfx file into the "My" store.
It will also configure Http to use this certificate for SSL.
Expand Down
Binary file not shown.
Binary file not shown.

0 comments on commit 56ece9d

Please sign in to comment.