I need to make a WCF service call to an external 3rd party using 2-way SSL from a class project. I have added the WSDL provided by the 3rd party to my project as a Service Reference. The problem is that all calls outside our domain (*.abc.com) pass through a proxy server
http://ironport:8080
This is what I have done in my code -
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
binding.BypassProxyOnLocal = false;
binding.UseDefaultWebProxy = true;
binding.AllowCookies = false;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
var endpoint = new EndpointAddress("https://blablabla.com/GetData.svc");
var client = new AccountClient(binding, endpoint);
X509Certificate2 certi = new X509Certificate2(#"path to pfx file", "password");
client.ClientCredentials.ClientCertificate.Certificate = certi;
I make the service call using -
var account = client.ExportAccounts(obj1, obj2, obj3);
It then gives me an error -
The remote server returned an Error (407): Proxy authentication required
That is but obvious because nowhere did I mention the proxy details the request needs to go through. What I need is a way to add the following info from a web.config file of a different project into my request above -
<system.net>
<defaultProxy useDefaultCredentials="true">
<proxy proxyaddress="http://ironport:8080" />
<bypasslist>
<add address="[\w]+\.abc\.com$" />
</bypasslist>
</defaultProxy>
</system.net>
Is there some way to achieve this in code? Or do I need to go about this in a different way altogether? Let me know if I need to post more information.
You could try using the WebProxy Class. Not tested, but something like this:
WebProxy proxy = new WebProxy("http://ironport:8080");
proxy.BypassList = new string[] { "[\w]+\.abc\.com$" };
Another alternative would be to move the relevant section of the config to the web/app.config of the application that is using your class library.
ADDED
Not 100% sure this will work, but you could try adding this line to your code:
WebRequest.DefaultProxy = proxy;
Taken from this answer
Another option might be to use the ProxyAddress property of WsHttpBinding (make sure in that case you set the UseDefaultProxy to false), but I don't see a way to add a bypass list with this one.
Related
I have an application that calls a web service... I get an error that I am just pulling the few hairs I have left on my head out maybe someone can help.
Here is the code:
Service_RetrieveIntervalDataserviceagent srv = new Service_RetrieveIntervalDataserviceagent();
srv.Credentials = new NetworkCredential(UserName, Password, Domain);
MDMIntervalDataInput dataInput = new MDMIntervalDataInput();
string Url = srv.Url;
DeviceList deviceList = new DeviceList();
deviceList.Type = DeviceListType.M;
deviceList.DeviceId = "2862,2876,2877".Split(',');
//Setup dataInput
dataInput.ApplicationName = "TestApp";
dataInput.StartDate = DateTime.Now.AddDays(-3).ToString("MM/dd/yyyy");
dataInput.EndDate = DateTime.Now.AddDays(-3).ToString("MM/dd/yyyy");
dataInput.OutputMode = MDMIntervalDataInputOutputMode.Wire;
dataInput.DeviceList = deviceList;
srv.RetrieveIntervalData_V10(dataInput);
I keep getting the error:
The server committed a protocol violation. Section=ResponseStatusLine
I did notice that the URL changes from HTTPS to HTTP could this be the problem?
I also tried adding the following to my config file and it still did not work:
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true" />
</settings>
</system.net>
How can I fix this?
Here is how I fixed it...
I noticed that the URL was changing from HTTPS to HTTP...The HTTP URL was the culprit so I updated it to manually set the URL and it worked.
I have a WCF service and a client that uses that service. They use WSHttpBinding with MTOM message encoding. The client doesn't use an app.config file, and does all of the endpoint configuration in code.
It all works rather nicely in a normal environment, however I now have some users who are attempting to connect to the service from behind an http proxy. Whenever they try to connect, they get a 407 (Proxy Authentication Required) warning.
I've managed to set up my own testing environment using a virtual machine with a private network that connects to a proxy, so I can simulate what they are seeing.
I've tried setting the system.net useDefaultCredentials property in app.config, but that doesn't seem to have any effect. I've examined the packets being sent, and they don't contain any Proxy-Authentication headers. Looking at web traffic through the proxy, they do use that header.
I've also tried hard coding the proxy server into the client, but that gives a "cannot connect to server" exception. Examining the packets, the client sends out 3 small ones to the proxy, none of which are an http request, and that's it. I'm not sure what it's doing there.
I even went so far as to add a message inspector to the client, and manually inject the required header into the requests, but that's not showing up in the header.
I'm hitting the end of my rope here, and I really need a solution. Any ideas on what I'm missing here, or a solution?
This ( WCF Service with wsHttpBinding - Manipulating HTTP request headers ) seems promising, but I'm still stuck.
EDIT:
Here's a portion of the code.
var binding = new WSHttpBinding();
binding.MaxReceivedMessageSize = 100000000;
binding.MessageEncoding = WSMessageEncoding.Mtom;
binding.ReaderQuotas.MaxArrayLength = 100000000;
binding.OpenTimeout = new TimeSpan(0, 2, 0);
binding.ReceiveTimeout = new TimeSpan(0, 2, 0);
binding.SendTimeout = new TimeSpan(0, 2, 0);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.UseDefaultWebProxy = true;
// I've also tried setting this to false, and manually specifying the binding.ProxyAddress
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
var endpointAddress = new EndpointAddress(new Uri(hostUrl));
clientProxy_ = new UpdaterServiceProxy(binding, endpointAddress);
// this behavior was the attempt to manually add the Proxy-Authentication header
//clientProxy_.Endpoint.Behaviors.Add(new MyEndpointBehavior());
clientProxy_.ClientCredentials.UserName.UserName = userName;
clientProxy_.ClientCredentials.UserName.Password = password;
clientProxy_.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
System.ServiceModel.Security.X509CertificateValidationMode.ChainTrust;
// do stuff...
After a lot of experimentation I've made some progress. It turns out that I can manually specify the proxy, and it will work.
WebProxy proxy = new WebProxy("http://x.x.x.x:3128", false);
proxy.Credentials = new NetworkCredential("user", "pass");
WebRequest.DefaultWebProxy = proxy;
That code appears just before I instantiate my clientProxy_. I had tried this previously, but it silently failed because I hadn't specified the port. I still cannot get it to pick up the proxy settings specified in the Windows Internet Settings. I tried setting proxy.Credentials = CredentialCache.DefaultNetworkCredentials; but that also seemed to have no effect.
I'm also running into a problem now with the client trying to validate the service's certificate using ChainTrust, but the requests for the chained certificates are not using the proxy settings. Since this is a more specific question, I wrote it up separately here: Certificate validation doesn't use proxy settings for chaintrust
So I'm still hoping for more help.
UPDATE:
I ended up just adding a network configuration dialog to my application so the user could specify their proxy settings. I've been unable to get the automatic proxy settings to work properly.
I have a self-hosted WCF service that is hosted by a desktop application.
I can successfully connect to the service locally on my PC, but I can't use the service remotely, at least without providing my windows/domain level credentials.
I use the following code to start the service in the app:
ServiceHost host = new ServiceHost(
typeof (SMService),
new Uri("net.tcp://localhost:" + SMGlobals._DEFAULTSERVICEPORT.ToString() + "/SMService"));
host.AddServiceEndpoint(
typeof(ISMService),
new NetTcpBinding(),
"");
System.ServiceModel.Channels.Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
var metadataBehavior =
new ServiceMetadataBehavior();
host.Description.Behaviors.Add(metadataBehavior);
host.AddServiceEndpoint(
typeof(IMetadataExchange),
mexBinding,
"net.tcp://localhost:" + SMGlobals._DEFAULTSERVICEPORT.ToString() + "/SMService/mex");
host.Open();
SMGlobals.SMServiceHost = host;
If I create a simple client to call the service using the following code:
var client = new SMServiceClient();
var uri = "net.tcp://192.168.11.10:8760/SMService";
client.Endpoint.Address = new EndpointAddress(uri);
var initiateResponse = client.InitiateAuthentication(new InitiateAuthenticationRequest());
MessageBox.Show("Success!");
I receive the following exception:
System.ServiceModel.Security.SecurityNegotiationException: The server has rejected the client credentials. ---> System.Security.Authentication.InvalidCredentialException: The server has rejected the client credentials. ---> System.ComponentModel.Win32Exception: The logon attempt failed
Now, from other research, I have discovered that I could provide my credentials with the client call using the following code:
var client = new SMServiceClient();
var uri = "net.tcp://192.168.11.10:8760/SMService";
client.Endpoint.Address = new EndpointAddress(uri);
client.ClientCredentials.Windows.ClientCredential.Domain = "domain";
client.ClientCredentials.Windows.ClientCredential.UserName = "my_user_name";
client.ClientCredentials.Windows.ClientCredential.Password = "my_password";
var initiateResponse = client.InitiateAuthentication(new InitiateAuthenticationRequest());
MessageBox.Show("Success!");
And now, the code successfully completes.
I'm having a hard time figuring out how to remove this requirement. I've tried messing around with the binding setup on the client without success.
Can anyone point me in the right direction?
A Tcp Binding has security enabled by default, so to get what you want, you need to explicitly turn it off. Add your endpoint like this. You might also explore the MSDN help for NetTcpBinding as you might want to user an alternate constructor to also switch off reliable messaging.
host.AddServiceEndpoint(
typeof(ISMService),
new NetTcpBinding(SecurityMode.None),
"");
Set the appropriate Authentication on the Binding.
ClientAuthenticationType="None"
I had a similar issue. Worked locally between two processes but the same code failed when the two processes were put on different machines (or locally using a public URL that resolved to the local machine, e.g. mylocalmachine.corp.com). I found that I needed to explicitly set the Anonymous binding's security to 'None':
<binding name="TcpAnonymousBinding" portSharingEnabled="true" receiveTimeout="24:00:00">
<security mode="None"/>
</binding>
Hi
The code below works fine to instruct the system not to use a proxy and to not auto detect one, which causes a delay without the code. However while on a network with a proxy I just get the underlying connection is closed!
So four questions:
Am I specifying the proxy correctly?
If so how do I tell it to use default proxy credentials?
Should the used want to specify credentials how are they set?
How do I set it back to the original state?
if (!Properties.Settings.Default.UseProxyServer){
//set the system not to use a proxy server
//saves the delay seen when browser set to auto detect proxy and not proxy
//is used. This works well!!
WebRequest.DefaultWebProxy = new WebProxy();
}
else{
WebRequest.DefaultWebProxy =
new WebProxy(proxyServerAddress, proxyServerPort);
//proxyServerPort is an int.
//How do I add default credentials??
}
WebClient client = new WebClient();
//specify an encoding for uploading.
client.Encoding = System.Text.Encoding.ASCII;
// Upload the data.
var myReply = client.UploadValues(addressURL, data);
I need to this in code not in the app.config.
Thanks
You can create a Web proxy object
var proxy = new WebProxy("http://server:8080");
proxy.credentials = new system.net.Credentials.DefaultCredenialCache;
proxy.Other properties
You can also create a config
<configuration>
<system.net>
<defaultProxy>
<proxy
usesystemdefaults="true"
proxyaddress="http://192.168.1.10:3128"
bypassonlocal="true"
/>
<bypasslist
<add address="[a-z]+\.contoso\.com" />
</bypasslist>
</defaultProxy>
</system.net>
</configuration>
Try this:
http://weblogs.asp.net/jan/archive/2004/01/28/63771.aspx
You may also want to check this out:
http://geekswithblogs.net/ranganh/archive/2005/08/29/51474.aspx
I've written a Windows Application to test a connection to a clients SAP web services. The web service call requires X509 certificate security.
After reading various articles on the internet I've come up with three ways to attach the X509 certificate to the web service call. Unfortunately all of these attempts return a '401 Unauthorised Access'. However, I can connect to the web service via the URL in IE.
Does anybody have any sugestions as to what I may be doing wrong? I am using WSE 3.0 and the three methods I am using to attach the certificate are as follows:-
Certificate
X509Certificate2 oCert = GetSecurityCertificate(oCertificate);
svc.ClientCertificates.Add(oCert);
Token
X509SecurityToken oToken = GetSecurityToken(oCertificate);
svc.RequestSoapContext.Security.Tokens.Add(oToken);
Policy
SAPX509Assertion sapX509Assertion = new SAPX509Assertion(oCertificate, oStoreLocation, oStoreName, oFindType);
svc.SetPolicy(sapX509Assertion.Policy());
GetSecurityToken() and GetSecuirtyCertificate both search the certificate store. The SAPX509Assertion does this:-
public SAPX509Assertion(String certSubject, StoreLocation oStoreLocation, StoreName oStoreName, X509FindType oFindType)
{
ClientX509TokenProvider = new X509TokenProvider(oStoreLocation,
oStoreName, certSubject, oFindType);
ServiceX509TokenProvider = new X509TokenProvider(oStoreLocation,
oStoreName, certSubject, oFindType);
Protection.Request.EncryptBody = false;
Protection.Response.EncryptBody = false;
}
Update
OK, I have a WCF call now in place. I couldn't use the BasicHttpBinding method shown by Eugarps as it complained that I was connecting to a https address and expected http...which made sense. The code I now have is:-
var binding = new WSHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
binding.Security.Mode = SecurityMode.Transport;
WCFConnection.CreateAbsenceWSlow.ZWSDHTM_GB_AMS_CREATEABS_lowClient client;
CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabsResponse response;
CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabs data;
//Assign address
var address = new EndpointAddress(sUrl);
//Create service client
client = new CreateAbsenceWSlow.ZWSDHTM_GB_AMS_CREATEABS_lowClient(binding, address);
//Assign credentials
client.ClientCredentials.UserName.UserName = sUserName;
client.ClientCredentials.UserName.Password = sPassword;
response = new CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabsResponse();
data = new WCFConnection.CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabs();
response = client.ZfhhrGbbapiZgeeamsCreateabs(data);
It's still failing to connect to the SAP web service. The error I am receiving is "The HTTP request is unauthorized with client authentication scheme 'Negotiate'". I've also tried using
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
which returned a similar error.
Does anybody have any further suggestions or ideas of where I am going wrong?
Now, this is all coming from my own experience so some of it may be wrong, but here's how I understand the process (I received no documentation and my company had no experience in calling SAP before I began doing it).
SAP WS calls are only supported by WCF BasicHttpBinding, and as far as I can tell, only using plain-text credentials. This means you will want to use IPSec or HTTPS if you need to make your communication private (outside intranet, or sensitive data within intranet). Our SAP server does not have HTTPS configured, but we use VPN with IPSec for external communication. Important to note is that, by default, SAP GUI also does not make communication private. In this situation, you are being no less secure by using the method detailed below than the business user down the hall who is looking up sensitive data in GUI 7.1. Here's how I connect to our SAP server internally:
//Create binding
//Note, this is not secure but it's not up to us to decide. This should only ever be run within
//the VPN or Intranet where IPSec is active. If SAP is ever directly from outside the network,
//credentials and messages will not be private.
var binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
//Assign address
var address = new EndpointAddress(Host);
//Create service client
var client = new SAP_RFC_READ_TABLE.RFC_READ_TABLEPortTypeClient(binding, address);
//Assign credentials
client.ClientCredentials.UserName.UserName = User;
client.ClientCredentials.UserName.Password = Password;
As far as I have been able to determine, message-level security is not supported, and bindings other than basicHttpBinding (SOAP 1.1) are not supported.
As I said, this is all from experience and not from training, so if anybody can add something through comments, please do so.
I've faced the same problem and it seems I've found the sollution here: http://ddkonline.blogspot.com/2009/08/calling-sap-pi-web-service-using-wcf.html.
CustomBinding binding = new CustomBinding();
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
HttpsTransportBindingElement transport = new HttpsTransportBindingElement();
transport.AuthenticationScheme = AuthenticationSchemes.Basic;
//transport.ProxyAuthenticationScheme = AuthenticationSchemes.Basic;
transport.Realm = "XISOAPApps";
binding.Elements.Add(transport);
var address = new EndpointAddress("https://foooo");
........ create client proxy class
service.ClientCredentials.UserName.UserName = "<login>";
service.ClientCredentials.UserName.Password = "<password>";
Unfortunatelly I'm not able to use WCF in my application, I have to stick with .NET 2.0 and WSE 3.0, and I wounder if anybody was able to find sollution to that?
After all this time, the client has finally obtained someone to deal with the issue from their SAP end of things. Turns out the WSDL files we were supplied were incorrect and the certification had been done wrong. I reran my code with the new WSDL files and it worked first time.
Does your certificate happen to be mapped to a valid user in your user store?