I´m looking for a way to add the following WSSE XML data into a SOAP request to a service:
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-D67150EFEFE71BA23416294396650191">
<wsse:Username>XXXXXXX</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXX</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">XXXXXXX</wsse:Nonce>
<wsu:Created>2021-08-20T06:07:45.015Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
I´ve added the service as a service reference in VS 2017 and I´ve seen suggestion that points to app.config like
<bindings>
<basicHttpBinding>
<binding name="myBinding">
<security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://...."
binding="basicHttpBinding" bindingConfiguration="myBinding"
contract="..." name="..." >
<headers>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>XXX</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>XXX</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</headers>
</endpoint>
</client>
And suggestion using BasicHttpBinding like
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
var endpoint = new EndpointAddress("https://....");
using (var client = new ServiceReference1.Client(binding, endpoint))
{
client.ClientCredentials.UserName.UserName = "XYZ";
client.ClientCredentials.UserName.Password = "XYZ";
ServiceReference1.Request request = new ServiceReference1.Request();
request.Request = new ServiceReference1.RequestType();
request.Request.Nr = "12345";
ServiceReference1.Response response = client.getData(request);
ServiceReference1.ResponseType[] responseMessages = response.Response;
}
But I´ve been unable to get anything working. The username and password have been tested via SoapUI.
Any suggestions?
Regards
Kaare
Related
I have an app.config to call this service from .Net framework but now I have to do the same from core app. Here's the WCF client config:
<bindings>
<ws2007FederationHttpBinding>
<binding name="WS2007FederationHttpBinding_certificate" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="128" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647"/>
<security mode="TransportWithMessageCredential">
<message establishSecurityContext="false" negotiateServiceCredential="true">
<claimTypeRequirements>
<add claimType="http://docs.oasis-open.org/wsfed/authorization/200706/claims/action" isOptional="true"/>
<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" isOptional="false"/>
<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"/>
</claimTypeRequirements>
<issuer address="https://some.url.com/STS/Issue.svc/trust/13/certificatemixed" binding="ws2007HttpBinding"
bindingConfiguration="certificateMixed"/>
<issuerMetadata address="https://some.url.com/STS/Issue.svc/mex"/>
<!-- THIS IS THE PROBLEM PART -->
<tokenRequestParameters>
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<EndpointReference xmlns="http://www.w3.org/2005/08/addressing">
<Address>URN:MY.TEST.SERVICE</Address>
</EndpointReference>
</wsp:AppliesTo>
</tokenRequestParameters>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
<ws2007HttpBinding>
<binding name="certificateMixed">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" establishSecurityContext="false"/>
</security>
</binding>
</ws2007HttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="behaviorSECertificate">
<clientCredentials>
<clientCertificate findValue="ab cd .. b4" storeLocation="LocalMachine" storeName="My"
x509FindType="FindByThumbprint"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
Pretty basic stuff. So now I do the same in .Net core code:
var issuerBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
issuerBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
issuerBinding.Security.Message.EstablishSecurityContext = false;
issuerBinding.Name = "certificateMixed";
var prm = new WsTrustTokenParameters
{
IssuerBinding = issuerBinding,
IssuerAddress = new EndpointAddress(_seOptions.STSEndpoint),
MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10,
EstablishSecurityContext = false,
};
prm.ClaimTypes.Add(new ClaimType() { Uri = "http://docs.oasis-open.org/wsfed/authorization/200706/claims/action", IsOptional = true });
prm.ClaimTypes.Add(new ClaimType() { Uri = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier", IsOptional = false });
prm.ClaimTypes.Add(new ClaimType() { Uri = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", IsOptional = true });
var binding = new WsFederationHttpBinding(prm);
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.NegotiateServiceCredential = true;
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
ChannelFactory<IMyService> factory = new ChannelFactory<IMyService>(binding, new EndpointAddress(_seOptions.MyServiceUrl));
var cert = GetClientCertificate(); //X509Certificate2
var x = factory.Endpoint.EndpointBehaviors[typeof(ClientCredentials)];
((ClientCredentials)x).ClientCertificate.Certificate = cert;
IMyService client = factory.CreateChannel(new EndpointAddress(_seOptions.MyServiceUrl));
So the main question is- how do I add the tokenRequestParameters/AppliesTo section in code? I assume it would be something like this:
RequestSecurityToken tk = new RequestSecurityToken() { AppliesTo = new EndpointAddress("URN:MY.TEST.SERVICE") };
But I have no idea what to assign this object to.
RequestSecurityToken class is not applicable to .net core, you can see its support on this page: RequestSecurityToken Class. So you need to find a replacement for it in .net core. You can refer to Overview of porting from .NET Framework to .NET Core to consider how to migrate from .net framework to .net core. Hope my answer will help you.
I have a WCF service reference which I use with the following client side app.config:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ManagerService">
<security>
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://myadress:8080/ManagerService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ManagerService"
contract="Service.ManagerService" name="WSHttpBinding_ManagerService">
<identity>
<certificate encodedValue="AwAAAAEAAAAUAAAAaJ8gJ+2BbDC//Nw76gp1Rx4Ii1AgAAAAAQAAACQDAAAwggMgMIICDKADAgECAhB4jlXUsVcUkE45Bq9sj6cdMAkGBSsOAwIdBQAwIzEhMB8GA1UEAxMYc2tpbGxjb25vbXkuY2xvdWRhcHAubmV0MB4XDTA2MTIzMTIyMDAwMFoXDTE5MTIzMTIyMDAwMFowIzEhMB8GA1UEAxMYc2tpbkjsahdjhdjsdhsjhdsjhdjhdsjhdFAAOCAQ8AMIIBCgKCAQEAwHjDPi/A7+4PfvYt40eySE2I6FgVO2Ewco8gJO21TUqHpKbinmsaNTO6wFJy+l3adMRB0dcmAvH938BPgdwbqbVaaG4mRCDpnekEserWmz5ii+ET1xhm0atIg6xW3sgnDOA+41Y0vB8m8AXTfHQYunILQjn/6xGM/RffK32vbR9WGJKEd/okOJ2/vV5dm2UsejlANwK2kCMe9wNRbjaKsH6PIqv26KeHAXxa0tSzoHfrn/lr46+54WzEXFHRzub1JbZk+IsdsdlakasjdksjdjdksjddjskdZZ0Oj0iG0GjvEVbmHWpBM/WhHrqIfGdqiMtXjOtwIDAQAsdsdsdsdsdsdsdaZtgIq+y6hm91EfPUToJ1ZUhWR8z/RG+IVZrs0O93FCMk6WU8OhYxubIgcVSTx0FDCakyOmfu1gnYeEZv53kVPZSmY4KUAUZz+MCQf/OXN2OGv9cRmsWg4iDlHjzDQwucO+rWkclvQo=" />
</identity>
</endpoint>
</client>
</system.serviceModel>
I now need to refactor the instantiation to work with a blank app.config:
Here is what I've tried:
var binding = new WSHttpBinding();
binding.Security = new WSHttpSecurity { Mode = SecurityMode.Message };
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cers = store.Certificates.Find(X509FindType.FindByThumbprint, "123THUMBPRINTOFCERT", false);
var cer = cers[0];
var identity = new X509CertificateEndpointIdentity(cer);
var endpoint = new EndpointAddress(new Uri("http://myservice:8080/ManagerService.svc"), identity);
var client = new ManagerServiceClient(binding, endpoint);
client.ClientCredentials.UserName.UserName = EMail;
client.ClientCredentials.UserName.Password = Password;
var resultBuilder = new StringBuilder();
var categories = client.Categorize(Text);
This throws an exception:
System.ServiceModel.Security.MessageSecurityException: Client cannot
determine the Service Principal Name based on the identity in the
target address 'http://myservice:8080/ManagerService.svc' for
the purpose of SspiNegotiation/Kerberos. The target address identity
must be a UPN identity (like acmedomain\alice) or SPN identity (like
host/bobs-machine).
I have checked, that the correct certificate is found in the store. I would be very glad to get a hint on what I am missing here and whether the way I'm going here is ok in general...?
you lost row clientCredentialType="UserName"
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
I have a WCF SOAP consumer that is implemented by Visual Studio 2012 from a WSDL. The WSDL was generated by PeopleTools. The base object is of type System.ServiceModel.ClientBase.
I need the SOAP request to resemble:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://xmlns.oracle.com/Enterprise/Tools/schemas">
<soapenv:Header>
<wsse:Security soap:mustUnderstand="1" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>[plain text username goes here]</wsse:Username>
<wsse:Password>[plain text password goes here]</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<sch:InputParameters>
<Last_Name>Aren</Last_Name>
<First_Name>Cambre</First_Name>
</sch:InputParameters>
</soapenv:Body>
</soapenv:Envelope>
Here's the closest we can get:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:3cc3f2ca-c647-466c-b38b-f2423462c837</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://[internal URL to soap listener]</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityToken Context="uuid-7db82975-2b22-4236-94a1-b3344a0bf04d-1" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeySize>256</t:KeySize>
<t:BinaryExchange ValueType=" http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FgMBAFoBAABWAwFQ9IhUFGUO6tCH+0baQ0n/3us//MMXzQA78Udm4xFj5gAAGAAvADUABQAKwBPAFMAJwAoAMgA4ABMABAEAABX/AQABAAAKAAYABAAXABgACwACAQA=</t:BinaryExchange>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
You'll notice two problems:
No plaintext WSSE credentials. Passes a binary form of the credentials that the service won't use.
Authentication is in Body, not Header.
The request omits InputParameters.
Here's the essential C# code:
var service = new ServiceWithBizarreNameFromPeoplesoft();
if (service.ClientCredentials == null)
throw new NullReferenceException();
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential) {Security = new WSHttpSecurity()};
service.Endpoint.Binding = binding;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);
There's no HTTP layer security, and transport is SSL-encrypted. Authentication is only based on the SOAP message.
That is request for WS-SecureConversation token. It is used by WSHttpSecurity by default unless you change its EstablishSecurityContext property to false. Use this binding instead:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
It will use SOAP 1.1 with UserName token and it will require HTTPS transport.
Edit:
For testing without HTTPS try to use this custom binding:
var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.AllowInsecureTransport = true;
var encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transportElement = new HttpTransportBindingElement();
var binding = new CustomBinding(securityElement, encodingElement, transportElement);
This looks to me like wsHttpBindings with Transport security using basic username password authentication.
These lines look wrong to me:
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;
Here's how I would expect to see this configured in your app or web.config
<bindings>
<wsHttpBinding>
<binding name="ws" >
<security mode="Transport">
<transport clientCredentialType="Basic" proxyCredentialType="Basic" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://www.bla.com/webservice" binding="basicHttpBinding" contract="bla.IService" name="ws" />
</client>
Then the code would look like this:
var service = new GeneratedProxyClient("basic");
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);
Might be better explained here --> http://msdn.microsoft.com/en-us/library/ms733775.aspx
I've this web.config file to configure client proxy from a external service this service required authentication on the messages header, I've the service well configured on my web.config but for now I'm try create the proxy with this configuration on code,with propose change dynamically the user and password.
<cliente>
<endpoint address="https://wwwww.hhhahdhadhs.askadadasda" binding="basicHttpBinding" bindingConfiguration="MyConfiguration" contract="PROXY_ACSS_SERVICES.RegistoPrescricaoMedicamentos">
<headers>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>MyUser</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">87726655434</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</headers>
</endpoint>
</cliente>
<basicHttpBinding>
<binding name="PrescricaoMedicamentos" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="Certificate" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
My problem is create the header where defined the username and password,
<headers>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>MyUser</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">87726655434</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</headers>
Thanks in advance
EDIT
Ladislav Mrnka after I create the proxy programmatically receive this message
'The request channel timed out attempting to send after
00:00:59.9829990. Increase the timeout value passed to the call to
Request or increase the SendTimeout value on the Binding. The time
allotted to this operation may have been a portion of a longer
timeout.'
This is my proxy configuration
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Name = "PrescricaoMedicamentos";
binding.CloseTimeout = new TimeSpan(0, 1, 0);
binding.OpenTimeout = new TimeSpan(0, 1, 0);
binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
binding.SendTimeout = new TimeSpan(0, 1, 0);
binding.AllowCookies = false;
binding.BypassProxyOnLocal = false;
binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
binding.MaxBufferSize = 65536;
binding.MaxBufferPoolSize = 524288;
binding.MaxReceivedMessageSize = 65536;
binding.MessageEncoding = WSMessageEncoding.Text;
binding.TextEncoding = System.Text.Encoding.UTF8;
binding.TransferMode = TransferMode.Buffered;
binding.UseDefaultWebProxy = true;
binding.ReaderQuotas = new XmlDictionaryReaderQuotas()
{
MaxDepth = 32,
MaxStringContentLength = 8192,
MaxArrayLength = 16384,
MaxBytesPerRead = 4096,
MaxNameTableCharCount = 16384
};
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
return binding;
}
I wondering if is possible add a static header to all messages in code.. Something like AddressHeader.CreateAddressHeader( 'authentication header') and apply this headers on my EndpointAddress configuration. This kind of approach substitute literal my first code
<headers>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>myusername</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">mypass</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</headers>
If the service correctly implements user name token profile change the security settings in your binding to:
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
And once you create a proxy set credentials by:
proxy.ClientCredentials.UserName.UserName = ...;
proxy.ClientCredentials.UserName.Password = ...;
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>
Try turning off the negotiateServiceCredential settings in your binding:
<wsHttpBinding>
<binding >
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="false" />
</security>
</binding>
</wsHttpBinding>
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");
}
}
}
}