I'm currently developing a wcf webservice(.NET 4.7.2) that accepts soap messages from different sources. One of the sources requires us to create a endpoint secured by a certificate using a custom authority(CA).
For this, I have created a new .svc file that initializes with the following binding and imports a certificate. This certificate is accessible in the azure web app:
public static void Configure(ServiceConfiguration config)
{
// Enable "Add Service Reference" support
config.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpsGetEnabled = true, HttpGetEnabled = false }); //mex
//setup support for certficate security
var binding = new WSHttpBinding
{
Name = "CertificateBinding",
ReaderQuotas = { MaxArrayLength = int.MaxValue },
MaxReceivedMessageSize = int.MaxValue,
Security = new WSHttpSecurity
{
Mode = SecurityMode.Transport,
Transport = new HttpTransportSecurity
{
ClientCredentialType = HttpClientCredentialType.Certificate
},
},
};
config.EnableProtocol(binding);
config.Credentials.ServiceCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindByThumbprint, "Thumbprint inserted here");
config.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
}
To enable certificate authentication for this endpoint I added the following to the web.config;
<location path="IntegrationWebService_CertificateSecurity.svc">
<system.webServer>
<httpErrors errorMode="Detailed"></httpErrors>
<security>
<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />
</security>
</system.webServer>
</location>
I tested this and this all works well locally, but when deploying to an azure web app it shows a 500 error. I can't seem to figure out the cause of this 500 error. Other .svc files keep working properly.
What I have tried;
Adding ELMAH; no exception is logged.
Multiple configurations for SslSettings, none worked though only adding "SslRequireCert" changes the error to "The SSL settings for the service 'SslRequireCert' does not match those of the IIS 'None'". I stopped persuing this because as far as I know the SslNegotiateCert part is required.
Tried, to no avail, adding an applicationHost.xdt file that transforms overrideModeDefault to allow override like this:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<configSections>
<sectionGroup name="system.webServer" xdt:Locator="Match(name)">
<sectionGroup name="security" xdt:Locator="Match(name)">
<section name="access" overrideModeDefault="Allow" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(overrideModeDefault)" />
</sectionGroup>
</sectionGroup>
</configSections>
</configuration>
Could it be that Azure does not allow a custom value in SslFlags?
Apparently, for Azure, you can't require client certificates using System.ServiceModel. So, first you can turn off the require client certificate part. Doing this using a CustomBinding looks like this:
var httpsBE = new HttpsTransportBindingElement();
httpsBE.RequireClientCertificate = false;
httpsBE.AuthenticationScheme = AuthenticationSchemes.Anonymous;
Go to your App service app settings and require client certificates from there;
Then, what Azure does, is move the certificate from the original request to a X-ARR-ClientCert header. This is accessible in a webservice like this;
var certHeader = WebOperationContext.Current.IncomingRequest.Headers["X-ARR-ClientCert"];
And to convert it to a X509Certificate2:
var clientCertBytes = Convert.FromBase64String(certHeader);
var x509Certificate2 = new X509Certificate2(clientCertBytes);
Then you'd have to run your own certificate validation.