Skip to content

Instantly share code, notes, and snippets.

@pacodelacruz
Last active May 27, 2017 00:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pacodelacruz/366e841890b68151bb1d to your computer and use it in GitHub Desktop.
Save pacodelacruz/366e841890b68151bb1d to your computer and use it in GitHub Desktop.
using System;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace MutualAuthClient
{
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Starting...");
// Set the ServerCertificateValidationCallback property to a
// custom method.
ServicePointManager.ServerCertificateValidationCallback +=
CustomServiceCertificateValidation;
// We will call a service which expects a string and echoes it
// as a response.
var client = new EchoService.EchoServiceClient
("BasicHttpBinding_IEchoService");
// Load private key from PFX file.
// Reading from a PFX file requires specifying the password.
// You might want to consider adding encryption here.
Console.WriteLine("Loading Client Certificate (Private Key) from File: "
+ ConfigurationManager.AppSettings["ClientPFX"]);
client.ClientCredentials.ClientCertificate.Certificate =
new X509Certificate2(
ConfigurationManager.AppSettings["ClientPFX"],
ConfigurationManager.AppSettings["ClientPFXPassword"],
X509KeyStorageFlags.MachineKeySet);
// We are using a custom method for the Server Certificate Validation
client.ClientCredentials.ServiceCertificate.Authentication.
CertificateValidationMode =
X509CertificateValidationMode.None;
Console.WriteLine();
Console.WriteLine(String.Format("About to call client.Echo"));
string response = client.Echo("Test");
Console.WriteLine();
Console.WriteLine(String.Format("client.Echo Response: '{0}'", response));
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(
String.Format("Exception occurred{0}Message:{1}{2}Inner Exception: {3}"
, Environment.NewLine, ex.Message, Environment.NewLine,
ex.InnerException));
}
}
private static bool CustomServiceCertificateValidation(
object sender, X509Certificate cert, X509Chain chain,
SslPolicyErrors error)
{
Console.WriteLine();
Console.WriteLine("CustomServiceCertificateValidation has started");
// Load the authorised and expected service certificate (public key)
// from file.
Console.WriteLine("Loading Service Certificate (Public Key) from File: "
+ ConfigurationManager.AppSettings["ServicePublicKey"]);
X509Certificate2 authorisedServiceCertificate = new X509Certificate2
(ConfigurationManager.AppSettings["ServicePublicKey"]);
// Load the trusted CA (public key) from file.
Console.WriteLine("Loading the Trusted CA (Public Key) from File: "
+ ConfigurationManager.AppSettings["TrustedCAPublicKey"]);
X509Certificate2 trustedCertificateAuthority = new X509Certificate2
(ConfigurationManager.AppSettings["TrustedCAPublicKey"]);
// Load the received certificate from the service (input parameter) as
// an X509Certificate2
X509Certificate2 serviceCert = new X509Certificate2(cert);
// Compare the received service certificate against the configured
// authorised service certificate.
if (!authorisedServiceCertificate.Equals(serviceCert))
{
// If they are not the same, throw an exception.
throw new SecurityTokenValidationException(String.Format(
"Service certificate '{0}' does not match that authorised '{1}'"
, serviceCert.Thumbprint, authorisedServiceCertificate.Thumbprint));
}
else
{
Console.WriteLine(String.Format(
"Service certificate '{0}' matches the authorised certificate '{1}'."
, serviceCert.Thumbprint, authorisedServiceCertificate.Thumbprint));
}
// Create a new X509Chain to validate the received service certificate using
// the trusted CA
X509Chain chainToValidate = new X509Chain();
// When working with Self-Signed certificates,
// there is no need to check revocation.
// You might want to change this when working with
// a properly signed certificate.
chainToValidate.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chainToValidate.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chainToValidate.ChainPolicy.VerificationFlags =
X509VerificationFlags.AllowUnknownCertificateAuthority;
chainToValidate.ChainPolicy.VerificationTime = DateTime.Now;
chainToValidate.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
// Add the configured authorised Certificate Authority to the chain.
chainToValidate.ChainPolicy.ExtraStore.Add(trustedCertificateAuthority);
// Validate the received service certificate using the trusted CA
bool isChainValid = chainToValidate.Build(serviceCert);
if (!isChainValid)
{
// If the certificate chain is not valid, get all returned errors.
string[] errors = chainToValidate.ChainStatus
.Select(x => String.Format("{0} ({1})", x.StatusInformation.Trim(),
x.Status))
.ToArray();
string serviceCertChainErrors = "No detailed errors are available.";
if (errors != null && errors.Length > 0)
serviceCertChainErrors = String.Join(", ", errors);
throw new SecurityTokenValidationException(String.Format(
"The chain of service certificate '{0}' is not valid. Errors: {1}",
serviceCert.Thumbprint, serviceCertChainErrors));
}
// Validate that the Service Certificate Chain Root matches the Trusted CA.
if (!chainToValidate.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint ==
trustedCertificateAuthority.Thumbprint))
{
throw new SecurityTokenValidationException(String.Format(
"The chain of Service Certificate '{0}' is not valid. " +
" Service Certificate Authority Thumbprint does not match " +
"Trusted CA's Thumbprint '{1}'",
serviceCert.Thumbprint, trustedCertificateAuthority.Thumbprint));
}
else
{
Console.WriteLine(String.Format(
"Service Certificate Authority '{0}' matches the Trusted CA's '{1}'",
serviceCert.IssuerName.Name,
trustedCertificateAuthority.SubjectName.Name));
}
return true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment