Reputation: 41
I have a self hosted Wcf service running on Windows XP and am attempting to use Certificates for message security. This is being done via the service and client config files. Both service and client are running on the same machine and I have created certificates for both using makecert.exe. This worked fine when I had clientCredentialType="Windows" but when I modified the configuration files to use certificates it no longer works. The problem is that when I attempt to connect to the service from the client I am getting the following exception:
Exception Type: System.ServiceModel.Security.SecurityNegotiationException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message: Incoming binary negotiation has invalid ValueType http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego.
My configuration settings are:
Service config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding0" closeTimeout="00:10:00" sendTimeout="00:10:00">
<security>
<!-- <transport clientCredentialType="Certificate"/> -->
<message clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CommMgr.ServiceBehavior">
<serviceMetadata httpGetEnabled="true" policyVersion="Policy15" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<!--
<authentication certificateValidationMode="PeerTrust"/>
-->
<authentication certificateValidationMode="None"/>
</clientCertificate>
<serviceCertificate findValue="WcfServer" storeLocation="CurrentUser"
storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="CommMgr.Service" behaviorConfiguration="CommMgr.ServiceBehavior">
<endpoint address="http://localhost:8002/Service"
binding="wsHttpBinding"
name="DataService"
bindingNamespace="CommMgr"
contract="CommMgr.Service"
bindingConfiguration="wsHttpBinding0">
<!--
<identity>
<dns value="localhost"/>
</identity>
-->
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/Service/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
<connectionStrings>
</configuration>
Client config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_Service" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="16384" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<!-- <transport clientCredentialType="Certificate"/> -->
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Certificate" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ClientCertificateBehavior">
<clientCredentials>
<clientCertificate findValue="WcfClient" storeLocation="CurrentUser"
storeName="My" x509FindType="FindBySubjectName" />
<serviceCertificate>
<!--
<authentication certificateValidationMode="PeerTrust"/>
-->
<authentication certificateValidationMode="None"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://localhost:8080/Service" behaviorConfiguration="ClientCertificateBehavior"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_Service"
contract="ServiceReference.Service" name="WSHttpBinding_Service">
<identity>
<!-- <dns value="WcfServer" /> -->
<certificate encodedValue="MIIBuTCCAWOgAwIBAgIQD6mW56bjgapOill7ECgRMzANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0xMDA3MjAxODMwMThaFw0zOTEyMzEyMzU5NTlaMBQxEjAQBgNVBAMTCVdjZkNsaWVudDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAv2p/0NDo4iZU35gN+k7nGXe0LZWdnP9i4MHYD3IsFcZGIamMyXwRT8//3jx+1fs1xEb+8+QbZuj8TXt/7aX6x2kz2O5tynuholP35iObDqOd7nYSXN+70QDrZ/uktPOkLrw/nfrA8sK0aZCZjfiINHCRt/izJIzESOGzDOh1if0CAwEAAaNLMEkwRwYDVR0BBEAwPoAQEuQJLQYdHU8AjWEh3BZkY6EYMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5ghAGN2wAqgBkihHPuNSqXDX0MA0GCSqGSIb3DQEBBAUAA0EALA+gVZDyjk4+qL7zAEV8esMX38X5QKGXHxBdd6K1+xApnSU79bRCWI9xU+HZ4rRhRJgtOdGQ1qfc9/WfvWXcYw=="/>
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
Upvotes: 4
Views: 1884
Reputation: 101
After one week of hard work, this works fine. o:)
Server:
using Demo.Auth;
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;
namespace Demo.Services
{
public class TcpHostService
{
public const string CertificateName = "MyCertificateName";
public static ServiceHost GetServiceHost()
{
string tcpHost = GetTcpHost();
var portsharingBinding = new NetTcpBinding();
portsharingBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
portsharingBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
portsharingBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
var serviceHost = new ServiceHost(typeof(RemotingService), new Uri(tcpHost));
serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
serviceHost.AddServiceEndpoint(typeof(IRemote), portsharingBinding, tcpHost);
if (!File.Exists("Certificate.pfx"))
{
MakeCert();
}
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateName, false);
if (certificates == null || certificates.Count == 0)
{
InstallCert();
}
}
serviceHost.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.CurrentUser, StoreName.My,
X509FindType.FindBySubjectName, CertificateName);
Console.WriteLine("Server escutando " + tcpHost);
return serviceHost;
}
private static void MakeCert()
{
var rsa = RSA.Create(2048);
var req = new CertificateRequest($"cn={CertificateName},OU=UserAccounts,DC=corp,DC=contoso,DC=com",
rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Parse("127.0.0.1"));
req.CertificateExtensions.Add(sanBuilder.Build());
var oidCollection = new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.2")
};
req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(oidCollection, true));
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, false));
using (X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now.AddDays(-10), DateTimeOffset.Now.AddYears(5)))
{
cert.FriendlyName = "JJConsulting Integration Certificate";
// Create PFX (PKCS #12) with private key
File.WriteAllBytes("Certificate.pfx", cert.Export(X509ContentType.Pfx, "pwd123"));
// Create Base 64 encoded CER (public key only)
File.WriteAllText("Certificate.cer",
"-----BEGIN CERTIFICATE-----\r\n"
+ Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
+ "\r\n-----END CERTIFICATE-----");
}
}
public static void InstallCert()
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
var cert = new X509Certificate2("Certificate.pfx", "pwd123", X509KeyStorageFlags.PersistKeySet);
store.Open(OpenFlags.ReadWrite);
store.Add(cert); //where cert is an X509Certificate object
}
}
private static string GetTcpHost()
{
return "net.tcp://localhost:5050/myservice1";
}
}
}
Client:
private ChannelFactory<IRemote> GetChannelFactory()
{
var sTcp = "net.tcp://localhost:5050/myservice1"
var myBinding = new NetTcpBinding();
myBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
myBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
myBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
var endpointIdentity = EndpointIdentity.CreateDnsIdentity("MyCertificateName");
var myEndpoint = new EndpointAddress(new Uri(sTcp), endpointIdentity);
var factory = new ChannelFactory<IRemote>(myBinding, myEndpoint);
factory.Credentials.UserName.UserName = User;
factory.Credentials.UserName.Password = Password;
factory.Credentials.ServiceCertificate.SslCertificateAuthentication =
new X509ServiceCertificateAuthentication()
{
CertificateValidationMode = X509CertificateValidationMode.None,
RevocationMode = X509RevocationMode.NoCheck
};
return factory;
}
User Validator:
using System;
using System.IdentityModel.Selectors;
using System.ServiceModel;
namespace Demo.Auth
{
public class CustomUserNameValidator : UserNamePasswordValidator
{
// This method validates users. It allows in two users, test1 and test2
// with passwords 1tset and 2tset respectively.
// This code is for illustration purposes only and
// must not be used in a production environment because it is not secure.
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!"user1".Equals(userName) || !"pwd".Equals(password))
{
throw new FaultException("Usuário ou senha inválido");
// When you do not want to throw an infomative fault to the client,
// throw the following exception.
// throw new SecurityTokenException("Unknown Username or Incorrect Password");
}
}
}
}
Upvotes: 1
Reputation: 9500
Try turning off the negotiateServiceCredential settings in your binding:
<wsHttpBinding>
<binding >
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="false" />
</security>
</binding>
</wsHttpBinding>
Upvotes: 1