I have a WCF Service that has Message security and I'm trying to connect a client to it.
The connection works wonders using the default Web.Config settings generated automatically by "Add Service Reference" in Visual Studio, with the following settings:
Endpoint:
<endpoint address="http://localhost:56017/services/MaternumPdfService.svc/soap"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IMaternumPdfService"
contract="MaternumPdfServiceReference.IMaternumPdfService" name="WSHttpBinding_IMaternumPdfService">
<identity>
<certificate encodedValue="BASE64 IS HERE" />
</identity>
</endpoint>
Binding:
<binding name="WSHttpBinding_IMaternumPdfService" maxReceivedMessageSize="2147483647">
<security>
<message clientCredentialType="UserName" negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
<readerQuotas maxStringContentLength="2147483647" maxDepth="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
</binding>
This works without a hitch:
using (MaternumPdfServiceClient pdfService = new MaternumPdfServiceClient("WSHttpBinding_IMaternumPdfService"))
{
pdfService.ClientCredentials.UserName.UserName = "sample";
pdfService.ClientCredentials.UserName.Password = "sample";
pdfService.Open();
if (!pdfService.State.Equals(CommunicationState.Opened))
{
return null;
}
MemoryStream ms = new MemoryStream();
pdfService.GetMaternumPdf(pdftype,params).CopyTo(ms);
}
However, trying to construct the connection without these settings fails. Here is how I build the connection:
X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, $"CN={CN}", false)[0];
store.Close();
Uri mpsUri = new Uri("http://localhost:56017/services/MaternumPdfService.svc/soap");
X509CertificateEndpointIdentity mpsIdentity = new X509CertificateEndpointIdentity(cert);
EndpointAddress mpsEndpoint = new EndpointAddress(mpsUri, mpsIdentity);
WSHttpBinding mpsBinding = new WSHttpBinding()
{
Name = "WSHttpBinding_IMaternumPdfService",
MaxReceivedMessageSize = Int32.MaxValue
};
//mpsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
mpsBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
mpsBinding.Security.Message.NegotiateServiceCredential = false;
mpsBinding.Security.Message.EstablishSecurityContext = false;
mpsBinding.ReaderQuotas.MaxDepth = Int32.MaxValue;
mpsBinding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
mpsBinding.ReaderQuotas.MaxArrayLength = Int32.MaxValue;
mpsBinding.ReaderQuotas.MaxBytesPerRead = Int32.MaxValue;
mpsBinding.ReaderQuotas.MaxNameTableCharCount = Int32.MaxValue;
using (MaternumPdfServiceClient pdfService = new MaternumPdfServiceClient(mpsBinding, mpsEndpoint))
{
pdfService.ClientCredentials.UserName.UserName = "sample";
pdfService.ClientCredentials.UserName.Password = "sample";
pdfService.ClientCredentials.ClientCertificate.Certificate = cert;
pdfService.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerTrust;
pdfService.ClientCredentials.ServiceCertificate.DefaultCertificate = GetCertificate("MaternumCertificateClient");
pdfService.Endpoint.Contract.Name = "MaternumPdfServiceReference.IMaternumPdfService";
pdfService.Endpoint.Name = "EndpointBehaviorWithAuthentication";
pdfService.Open();
if (!pdfService.State.Equals(CommunicationState.Opened))
{
return null;
}
MemoryStream ms = new MemoryStream();
//Next line throws the exception
pdfService.GetMaternumPdf(myPdf.PdfType, myPdf.JsonParameters).CopyTo(ms);
}
I thought I was constructing completely equivalent code between the C# version and the one that uses Web.Config settings, but connecting with the second one leads me to an exception:
'An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.'
With the following InnerException:
An error occurred when verifying security for the message.
Not particularly clear... It seems to be something about message security, but the connection with Web.Config works perfectly fine, which leads me to believe I'm not setting the certificate in the correct class.
I've looked into:
WCF gives an unsecured or incorrectly secured fault error
- Server and Client are both localhost, so it shouldn't be a time mismatch
- Credentials are correct, as the Web.Config version uses the same credentials and validates fine
- I'm using HTTP, not HTTPS
- Using IISExpress, not IIS
That's about it. I think I'm setting the client certificate in the wrong place, how should I set it if not via ClientCredentials.ClientCertificate.Certificate or the identity of the endpoint?