Connect to SSL SOAP Host via "Service Reference" and pass Security Header - c#

I am trying to connect to a SSL SOAP service host by C# using Service Reference.
This is my request message:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo/zwMmtdsVhFsAVDkQbiV/4AAAAA1zXtnc72UEm+4tlKzvCxsvN6OC2prvRIljIX4XzHKEYACQAA</VsDebuggerCausalityData>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2016-03-18T12:45:27.558Z</u:Created>
<u:Expires>2016-03-18T12:50:27.558Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-2c7986ba-eee5-4411-90a9-a02b625c55ff-1">
<o:Username>MyUserName</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">MyPlainPassword</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<generateId xmlns="http://com.vedaadvantage/dp3/Enterprise/StandardTradeCreditCommercial/IndividualCommercialService"/>
</s:Body>
</s:Envelope>
This is the message that my service sends to the host. But the host returns as below:
Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.
This is my config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="myBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="UserNameOverTransport"
messageSecurityVersion="WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10" >
</security>
<httpsTransport />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://{URL}"
binding="customBinding"
bindingConfiguration="myBinding"
contract="ServiceReference2.MyService"
name="IndividualCommercialService" />
</client>
</system.serviceModel>
</configuration>
Although when I send the same XML via SOAPUI or other HTTP Post methods it works fine.
I also extract and attached the certificate and user/pass as below:
private static X509Certificate2 DownloadSslCertificate(string strDNSEntry)
{
X509Certificate2 cert = null;
using (TcpClient client = new TcpClient())
{
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
client.Connect(strDNSEntry, 443);
SslStream ssl = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
ssl.AuthenticateAsClient(strDNSEntry);
}
catch (AuthenticationException e)
{
//log.Debug(e.Message);
ssl.Close();
client.Close();
return cert;
}
catch (Exception e)
{
//log.Debug(e.Message);
ssl.Close();
client.Close();
return cert;
}
cert = new X509Certificate2(ssl.RemoteCertificate);
ssl.Close();
client.Close();
return cert;
}
}
private static void Main(string[] args){
var proxy = new MyService();
var uri = proxy.Endpoint.Address.Uri;
var cer = DownloadSslCertificate(uri.DnsSafeHost);
EndpointIdentity identity = EndpointIdentity.CreateDnsIdentity(cer.Subject.Replace("CN=", ""));
EndpointAddress address = new EndpointAddress(proxy.Endpoint.Address.Uri, identity);
proxy.Endpoint.Address = address;
proxy.ClientCredentials.UserName.UserName = "MyUserName";
proxy.ClientCredentials.UserName.Password = "MyPlainPassword";
proxy.ClientCredentials.ServiceCertificate.DefaultCertificate = cer;
proxy.HellowWorld();
}
I am not sure whether the method that I am getting the certificate is correct or not and also why HTTP Post works but my Service Reference Call does not.
Thanks in advance for your help.
Cheers

Try to look inside WSDL (Service References) in order to see hidden files first select Show All Files in Solution explorer.
You`ll se inside service reference Reference.svcmap -> Reference.cs, and inside of this file add ProtectionLevel = System.Net.Security.ProtectionLevel.Sign
as shown below
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://www.your.url/Service/", ConfigurationName = "Service.Service", ProtectionLevel = System.Net.Security.ProtectionLevel.Sign)]
that should help you. Usually it`s really bad idea to modify autogenerated proxy, but seems like that is the only option.

Related

What is the .NET equivalent of this PHP web service call?

I am attempting to utilise an API, and the providers can only offer a PHP sample, which I have summarised as follows (with sensitive data removed):
<?php
ini_set('default_socket_timeout', 1600);
$options = array(
'login' => 'myusername',
'password' => 'mypassword',
'trace' => 1
);
$url = 'https://supplierurl/ws?wsdl';
$soapClient = new \SoapClient($url, $options);
$params = array('12345');
try{
$details = $soapClient->getData($params);
var_dump($details->paramdetails);
}
catch(\Exception $e){
echo "Last request headers:<br>\n".$soapClient->__getLastRequestHeaders()."<br><br>\n";
echo "Last request:<br>\n".$soapClient->__getLastRequest()."<br><br>\n";
echo "Last response headers:<br>\n".$soapClient->__getLastResponseHeaders()."<br><br>\n";
echo "Last response:<br>\n".$soapClient->__getLastResponse()."<br><br>\n";
}
?>
I have successfully run this on my development machine and get back the data as expected.
I have attempted to use this service in .NET by adding a service reference using the url provided, which generates the proxy code as expected, giving me a configuration as below:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="mybinding">
<security mode="Transport">
<transport clientCredentialType="Basic"/>
</security>
</binding>
<binding name="mybinding1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://supplierurl/ws"
binding="basicHttpBinding" bindingConfiguration="mybinding"
contract="API.portType" name="API.port" />
</client>
</system.serviceModel>
And test code as below:
API.portClient client = new API.portClient();
client.ClientCredentials.UserName.UserName = "myusername";
client.ClientCredentials.UserName.Password = "mypassword";
API.GetDataResponse response = client.getData(new string[] { "12345" });
The code executes with no exceptions thrown, but response is null. If I change the username or password to something invalid, I get an exception, indicating that the credentials side of things is working.
If anyone can point me in the right direction, it would be much appreciated!
Some further information, if I add this as a web reference it works, which gets me moving for now, although I'd still like to know how to make it work in the first instance. Code for using a web reference:
WebReference.customerV4Service svc = new WebReference.customerV4Service();
svc.Credentials = new System.Net.NetworkCredential("myusername", "mypassword");
WebReference.GetDataResponse resp = svc.getData(new string[] { "12345" });

Web Service user authentication

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>

With C#, WCF SOAP consumer that uses WSSE plain text authentication?

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

SSL client/server mutual authentication

Hello I am trying to do in C# an ssl client/server communication with mutual authentication using server and client certificate. A managed to do the ssl communication only using server certificate, where on the client side I use sth like that:
TcpClient client = new TcpClient(machineName, port);
//Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
try
{
// The server name must match the name on the server certificate.
sslStream.AuthenticateAsClient(serverName);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
I assume I would need to use
AuthenticateAsClient(string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
method, am I corrent? Could anyone please show me how to use it with all things around?Even on the server side, or point me to a basic example?
Thank you a lot.
static void HTTPSClient()
{
try
{
string message = "GET / HTTP/1.0\r\nHost: host.com\r\n\r\n";
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
string server = "host.com";
int nPort = 443;
TcpClient client = new TcpClient(server, nPort);
X509Certificate2Collection cCollection = new X509Certificate2Collection();
cCollection.Add(new X509Certificate2("cert.pfx", "password"));
using (SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null))
{
// Add a client certificate to the ssl connection
sslStream.AuthenticateAsClient(server, cCollection, System.Security.Authentication.SslProtocols.Default, true);
sslStream.Write(data, 0, data.Length);
data = new Byte[8192];
int bytes = 0;
string responseData = "";
do
{
bytes = sslStream.Read(data, 0, data.Length);
if (bytes > 0)
{
responseData += System.Text.Encoding.ASCII.GetString(data, 0, bytes);
}
}
while (bytes > 0);
Console.WriteLine("Response: " + responseData);
}
// Disconnect and close the client
client.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.ToString());
}
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
You need a x509 self certificate, to create it simple, download pluralsight self cert
Generate certificate as in image
Create new web site, there choose wcf service.
Add in solution new console application, to test our service.
In web.config of service put configuration:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceCredentialsBehavior">
<serviceCredentials>
<serviceCertificate findValue="cn=cool" storeName="TrustedPeople" storeLocation="CurrentUser" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ServiceCredentialsBehavior" name="Service">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="MessageAndUserName" name="SecuredByTransportEndpoint" contract="IService"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="MessageAndUserName">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client/>
In Service class, delete existing methods and add:
public string TestAccess()
{
return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;
}
in IService delete Data Contract, delete operation contracts and add new operation contract:
[OperationContract]
public string TestAccess();
Run service and add service reference in client application to our service
Client config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="LocalCertValidation">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="CurrentUser" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService" >
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="your service addresss"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService"
contract="ServiceReference1.IService"
name="WSHttpBinding_IService" behaviorConfiguration="LocalCertValidation">
<identity>
<dns value ="cool" />
</identity>
</endpoint>
</client>
Client code:
ServiceClient client = new ServiceClient();
client.ClientCredentials.UserName.UserName = "Your windows user";
client.ClientCredentials.UserName.Password = "Your windows user password";
Console.WriteLine(client.TestAccess());
Console.ReadLine();
if you dont want to use windows login/password you have to create a custom user/passwd validator ->msdn:
Regards,
Sergiu.

Problem with authentication on java ws

I have application in C# that consume Java WS. Everything worked fine until WS was configured to use authentication. Now I should user login i password to execute methods from WS but I'm not sure how to do it.
I've try
var client = new MyBeanClient();
client.ClientCredentials.UserName.UserName = "admin";
client.ClientCredentials.UserName.Password = "";
client.addConsumer("whatever", "", "", "");
But I get
SecurityMessageException-{"The HTTP request is unauthorized with client
authentication scheme 'Anonymous'. The authentication header received from
the server was 'Negotiate,NTLM'."}
InnerException - (WebException) - {"The remote server returned an error:
(401) Unauthorized."}.
What's wrong?
Thanks
Try this:
var credentialCache = new CredentialCache();
var credentials = new NetworkCredential("username", "password", "domain");
credentialCache.Add(new Uri(client.Url), "NTLM", credentials);
client.Credentials = credentialCache;
client.addConsumer("whatever", "", "", "");
UPDATE:
Sorry in my first post I thought you were using wsdl.exe to generate the client proxy. For a WCF client you need to configure the endpoint:
var basicHttpBinding = new BasicHttpBinding();
basicHttpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var endpoint = new EndpointAddress("http://example.com/myWindowsAuthN");
var client = new MyBeanClient(basicHttpBinding, endpoint);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ChannelFactory.Credentials.Windows.ClientCredential.Domain = "domain";
client.ChannelFactory.Credentials.Windows.ClientCredential.UserName = "username";
client.ChannelFactory.Credentials.Windows.ClientCredential.Password = "password";
UPDATE2:
I've used the following configuration to invoke web services protected with NTLM authentication. In app.config of the client put the following:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="NtlmBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://example.com/SomeWindowsAuthenticatedService"
binding="basicHttpBinding"
bindingConfiguration="NtlmBinding"
contract="IOperationContractOfTheService"
name="WSTestSoap" />
</client>
</system.serviceModel>
and then you could set the corresponding credentials before invoking the method:
using (var client = new MyBeanClient())
{
client.ChannelFactory.Credentials.Windows.ClientCredential =
new NetworkCredential("username", "password", "DOMAIN");
client.addConsumer("whatever", "", "", "");
}

Categories

Resources