I have been trying to send ping request to https://americas-uapi.copy-webservices.travelport.com/B2BGateway/connect/uAPI/SystemService but i am receiving following error:
Error making ping request: The provided URI scheme 'https' is invalid; expected 'http'.
The end point provided to me uses https. How can I correct this error?
Here is my web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ExternalCacheAccessBinding" />
<binding name="SystemPingBinding" />
<binding name="SystemInfoBinding" />
<binding name="SystemTimeBinding" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://americas-uapi.copy-webservices.travelport.com/B2BGateway/connect/uAPI/SystemService"
binding="basicHttpBinding" bindingConfiguration="ExternalCacheAccessBinding"
contract="WSDLService.ExternalCacheAccessPortType" name="ExternalCacheAccessPort" />
<endpoint address="https://americas-uapi.copy-webservices.travelport.com/B2BGateway/connect/uAPI/SystemService"
binding="basicHttpBinding" bindingConfiguration="SystemPingBinding"
contract="WSDLService.SystemPingPortType" name="SystemPingPort" />
<endpoint address="https://americas-uapi.copy-webservices.travelport.com/B2BGateway/connect/uAPI/SystemService"
binding="basicHttpBinding" bindingConfiguration="SystemInfoBinding"
contract="WSDLService.SystemInfoPortType" name="SystemInfoPort" />
<endpoint address="https://americas-uapi.copy-webservices.travelport.com/B2BGateway/connect/uAPI/SystemService"
binding="basicHttpBinding" bindingConfiguration="SystemTimeBinding"
contract="WSDLService.SystemTimePortType" name="SystemtimePort" />
</client>
</system.serviceModel>
This is the request I am sending following a tutorial:
// PING REQUEST
//
String payload= "this my payload; there are many like it but this one is mine";
String someTraceId = "doesntmatter-8176";
//set up the request parameters into a PingReq object
PingReq req = new PingReq();
PingRsp rsp = new PingRsp();
req.Payload=payload;
req.TraceId=someTraceId;
SystemPingPortTypeClient port = new SystemPingPortTypeClient();
try {
//run the ping request
UserNamePasswordClientCredential creds = port.ClientCredentials.UserName;
creds.UserName = "MyUserName";
creds.Password = "MyPassword";
rsp = port.service(req);
//print results.. payload and trace ID are echoed back in response
Label1.Text = rsp.Payload;
Label2.Text = rsp.TraceId;
Label3.Text = rsp.TransactionId;
}
catch (Exception ex) {
//usually only the error message is useful, not the full stack
//trace, since the stack trace in is your address space...
Label1.Text = "Error making ping request: " + ex.Message;
<security mode="Transport"> in Servicereference.ClientConfig file
Here
Check here too
UPDATE : As per your comments :
<bindings>
<basicHttpBinding>
<security mode="Transport">
Related
Since WCF routing doesn't support routing for REST services, I created a REST service that has one enpoint which accepts all incoming requests and than redirects those requests based on the query parameters.
I did this by following this article http://blog.tonysneed.com/2012/04/24/roll-your-own-rest-ful-wcf-router/.
This approach works for passing through requests and returning the results. The problem is whenever I get an error, like a 404, from the actual service the message that is returned to the client is a 400 (Bad Request).
What I would like to have is a routing proxy that actually just redirects the calls to the real service based on the query and returns all the errors to the client as they come from the real service.
Is this even the right approach to what I'm trying to accomplish, or are there easier or better solutions?
Any help is appreciated!
In the following I added what my code looks like.
app.config:
<!--
System.net
-->
<system.net>
<settings>
<servicePointManager expect100Continue="false" useNagleAlgorithm="false" />
</settings>
<connectionManagement>
<add address="*" maxconnection="24" />
</connectionManagement>
</system.net>
<!--
System.ServiceModel
-->
<system.serviceModel>
<!--
Services
-->
<services>
<service name="RoutingGateway.RoutingService">
<endpoint address="/api/routing" binding="webHttpBinding" bindingConfiguration="secureWebHttpBinding" contract="RoutingGateway.IRoutingService" behaviorConfiguration="RESTBehaviour" />
</service>
</services>
<client>
<endpoint binding="webHttpBinding" bindingConfiguration="secureWebHttpBinding" contract="RoutingGateway.IRoutingService" name="routingService" behaviorConfiguration="RESTBehaviour" />
</client>
<!--
Bindings
-->
<bindings>
<webHttpBinding>
<binding name="secureWebHttpBinding" hostNameComparisonMode="StrongWildcard" maxReceivedMessageSize="2147483647" transferMode="Streamed">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
</bindings>
<!--
Behaviors
-->
<behaviors>
<endpointBehaviors>
<behavior name="RESTBehaviour">
<dispatcherSynchronization asynchronousSendEnabled="true" />
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false before deployment -->
<serviceMetadata httpsGetEnabled="false" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
<!-- Enable Throttling -->
<serviceThrottling maxConcurrentCalls="100" maxConcurrentInstances="100" maxConcurrentSessions="100" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
IRoutingService.cs:
[ServiceContract(Namespace = "https://test/api/routing")]
public interface IRoutingService
{
[OperationContract(Action = "*", ReplyAction = "*")]
[WebInvoke(UriTemplate = "*", Method = "*")]
Message ProcessRequest(Message requestMessage);
}
RoutingService.cs:
public Message ProcessRequest(Message requestMessage)
{
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
Uri originalRequestUri = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri;
// Gets the URI depending on the query parameters
Uri uri = GetUriForRequest(requestMessage);
// Select rest client endpoint
string endpoint = "routingService";
// Create channel factory
var factory = new ChannelFactory<IRoutingService>(endpoint);
Uri requestUri = new Uri(uri, originalRequestUri.PathAndQuery);
factory.Endpoint.Address = new EndpointAddress(requestUri);
requestMessage.Headers.To = requestUri;
// Create client channel
_client = factory.CreateChannel();
// Begin request
Message result = _client.ProcessRequest(requestMessage);
return result;
}
I ended up catching all CommunicationExceptions and then rethrowing WebFaultExceptions with the appropriate messages and status codes.
Here is the code:
Message result = null;
try
{
result = _client.ProcessRequest(requestMessage);
}
catch (CommunicationException ex)
{
if (ex.InnerException == null ||
!(ex.InnerException is WebException))
{
throw new WebFaultException<string>("An unknown internal Server Error occurred.",
HttpStatusCode.InternalServerError);
}
else
{
var webException = ex.InnerException as WebException;
var webResponse = webException.Response as HttpWebResponse;
if (webResponse == null)
{
throw new WebFaultException<string>(webException.Message, HttpStatusCode.InternalServerError);
}
else
{
var responseStream = webResponse.GetResponseStream();
string message = string.Empty;
if (responseStream != null)
{
using (StreamReader sr = new StreamReader(responseStream))
{
message = sr.ReadToEnd();
}
throw new WebFaultException<string>(message, webResponse.StatusCode);
}
else
{
throw new WebFaultException<string>(webException.Message, webResponse.StatusCode);
}
}
}
}
UPDATE (8/7/2014) - The solution to this problem was that I needed to add a class that derived from "UserNamePasswordValidator" and register it in Web.Config.
I have created a simple test WCF service and test console client application (see below for code). I am using .NET 4.5.1. I have already searched for duplicates on StackOverflow (found similar posts here and here) - however I feel that the referenced posts are potentially outdated, and also feel that my post is more limited in scope.
OK now for the example:
The solution currently uses sessions (in ITestService.cs):
[ServiceContract(SessionMode = SessionMode.Required)]
... and uses wsHttpBinding (see below app.config and web.config).
When I deploy this to a server, I am successfully able to access it via a web browser using HTTPS like this: https://myserver.com/test/testservice.svc
However, when I change the endpoint in the client app.config from:
http://localhost:20616/TestService.svc/TestService.svc
to:
https://myserver.com/test/testservice.svc
and run the console application again, I receive the error: "The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via"
My question is, what is the minimum changes I need to make for this to work, without changing SessionMode.Required?
Here is the client console application code. Please be sure to change the App.Config value for "mycomputer\Matt" to the correct value for your machine.
Program.cs
using System;
namespace TestClient
{
class Program
{
static void Main(string[] args)
{
Console.Clear();
Console.WriteLine("Attempting to log in...");
try
{
TestServiceReference.TestServiceClient client = new TestServiceReference.TestServiceClient();
bool loginSuccess = client.LogIn("admin", "password");
if (loginSuccess)
{
Console.WriteLine("Successfully logged in.");
string secretMessage = client.GetSecretData();
Console.WriteLine("Retrieved secret message: " + secretMessage);
}
else
{
Console.WriteLine("Log in failed!");
}
}
catch (Exception exc)
{
Console.WriteLine("Exception occurred: " + exc.Message);
}
Console.WriteLine("Press ENTER to quit.");
Console.ReadLine();
}
}
}
App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ITestService"/>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://myserver.com/test/testservice.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ITestService" contract="TestServiceReference.ITestService" name="WSHttpBinding_ITestService">
<identity>
<userPrincipalName value="mycomputer\Matt"/>
</identity>
</endpoint>
<!--<endpoint address="http://localhost:20616/TestService.svc/TestService.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ITestService" contract="TestServiceReference.ITestService" name="WSHttpBinding_ITestService">
<identity>
<userPrincipalName value="mycomputer\Matt"/>
</identity>
</endpoint>-->
</client>
</system.serviceModel>
</configuration>
WCF Service code.
ITestService.cs:
using System.ServiceModel;
namespace WcfSessionsOverHttpsTest
{
[ServiceContract(SessionMode = SessionMode.Required)]
public interface ITestService
{
[OperationContract(IsInitiating = true)]
bool LogIn(string username, string password);
[OperationContract(IsInitiating = false, IsTerminating = true)]
bool LogOut();
[OperationContract(IsInitiating = false)]
string GetSecretData();
}
}
TestService.svc:
namespace WcfSessionsOverHttpsTest
{
public class TestService : ITestService
{
public bool IsAuthenticated { get; set; }
bool ITestService.LogIn(string username, string password)
{
if (username == "admin" && password == "password")
{
IsAuthenticated = true;
return true;
}
else
{
IsAuthenticated = false;
return false;
}
}
bool ITestService.LogOut()
{
IsAuthenticated = false;
return true;
}
string ITestService.GetSecretData()
{
if (!IsAuthenticated)
{
throw new System.Security.Authentication.AuthenticationException("User has not logged in.");
}
else
{
string secretMessage = "The Red Sox are going to win the World Series in 2016";
return secretMessage;
}
}
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.1"/>
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WcfSessionsOverHttpsTest.TestService">
<endpoint address="/TestService.svc" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" contract="WcfSessionsOverHttpsTest.ITestService"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="wsHttpBinding" scheme="http"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Thanks in advance for any help!
Matt
The solution to this problem was that I needed to add a class that derived from "UserNamePasswordValidator" and register it in Web.Config.
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
return;
}
}
Web.config:
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpsGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyProgram.CustomUserNameValidator,MyProgram" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
I'm trying to develop web service client with user authorization.
I've added service references to my project but now i've stuck on user authentication,
all i get is:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'.
This is how i call my webservice:
BasicHttpBinding a = new BasicHttpBinding("MyWebService1");
a.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
a.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
MyWebService1Client mySeviceClient= new MyWebService1Client();
mySeviceClient= new MyWebService1Client(a, mySeviceClient.Endpoint.Address);
mySeviceClient.ClientCredentials.UserName.UserName = "test2";
mySeviceClient.ClientCredentials.UserName.Password = "password";
try
{
mySeviceClient.Open();
MyWebService1 myWebService= mySeviceClient;
myRequest req = new myRequest(1234, "Test");
myResponse res = myWebService.getMyData(req);
mySeviceClient.Close();
}
catch (AddressAccessDeniedException adExc)
{
Console.WriteLine(adExc.Message);
Console.ReadLine();
}
catch (System.Exception exc)
{
Console.WriteLine(exc.Message);
Console.ReadLine();
}
And here is my app.config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="MyWebService1"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:7002/WSauth-WS_auth-context-root/MyWebService1Port"
binding="basicHttpBinding" bindingConfiguration="MyWebService1"
contract="TestWs.MyWebService1" name="MyWebService1Port" />
</client>
</system.serviceModel>
</configuration>
I have a situation where I would like to make a WCF call as another call is coming in.
Site1 request--> Site2
Site2 request --> Site3
Site2 <-- Site3 response
Site1 <-- Site2 response
The problem I am having is that when Site2 tries to send a message to Site3 while Site1 is sending to Site2; Site2 says it cannot find Site3.
The actual error message is:
Could not find endpoint element with name 'Endpoint_IEchoService' and contract
'FakeCompany.API.Services.Contract.IEchoService' in the ServiceModel client
configuration section. This might be because no configuration file was found
for your application, or because no endpoint element matching this name could be
found in the client element.
Each site is the same configuration and code base. The client, proxy and server are all in the same project. The apps are clones calling each other. It is one website with multiple address bindings. Other regular calls between the sites work fine until I try a call within a call.
As you can probably guess from the contact name, not much in the complex way is happening in my echo service. Single echo calls between the sites work. My problem is when i make a cascade call on the service side to another site.
I am wondering if this is not allowed or if a configuration setting change is required.
Some code and config.
Endpoint addresses are changed at runtime.
If you see something "funky", it is because the client, proxy and service inherit from generic base classes.
//-- ServiceModel Client
<endpoint address="http://FakeCompany.unittest/Services/EchoService.svc"
binding="basicHttpBinding" bindingConfiguration="SecureBinding"
contract="FakeCompany.API.Services.Contract.IEchoService" name="Endpoint_IEchoService">
<identity>
<servicePrincipalName value="host/mikev-ws" />
</identity>
</endpoint>
//-- Bindings
<bindings>
<basicHttpBinding>
<binding name="SecureBinding"
maxReceivedMessageSize="10000000"
sendTimeout="00:05:00">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
//-- Behaviours
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
//-- Services
<service name="FakeCompany.API.Services.Service.EchoService" behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint address="" binding="basicHttpBinding" contract="FakeCompany.API.Services.Contract.IEchoService" bindingConfiguration="SecureBinding" />
</service>
//-- TEST
[Test]
public void CascadeMessage()
{
//-- TEST: That a wcf call can occur within another wcf call.
//-- ARRANGE
DTO_Echo_Cascade_Request request = new DTO_Echo_Cascade_Request(unit1, unit2);
request.NextCall = string.Format("{0};{1};{2};", unit3, unit4, unit5);
//-- ACT
var response = EchoAgent.Cascade(request);
//-- ASSERT
Assert.AreEqual("TBA", response.Response);
}
//-- AGENT
internal static DTO_Echo_Response Cascade(DTO_Echo_Cascade_Request request)
{
DTO_Echo_Response response;
using (EchoServiceClient serviceClient = new EchoServiceClient(request))
{
response = serviceClient.Cascade(request);
}
return response;
}
//-- CLIENT
public DTO_Echo_Response Cascade(DTO_Echo_Cascade_Request request)
{
return Process(() => Proxy.Cascade(request));
}
CONTRACT, DTO, PROXY are omitted.
//-- SERVICE
public DTO_Echo_Response Cascade(DTO_Echo_Cascade_Request request)
{
DTO_Echo_Response response = new DTO_Echo_Response();
response.Response += string.Format("Hello from {0};", request.TargetAddress);
if (request.NextCall.NotNullOrEmpty())
{
var split = request.NextCall.Split(new [] {';'}, StringSplitOptions.RemoveEmptyEntries);
if (split.GetUpperBound(0) > 0)
{
DTO_Echo_Cascade_Request nextRequest = new DTO_Echo_Cascade_Request(request.TargetAddress, split[0]);
for (int i = 1; i < split.GetUpperBound(0); i++)
{
nextRequest.NextCall += split[i] + ";";
}
response.Response += EchoAgent.Cascade(nextRequest).Response;
}
}
return response;
}
The exception occurs on the following line
response.Response += EchoAgent.Cascade(nextRequest).Response;
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");
}
}
}
}