Calling webservice in C# using SSL - '(401) Unauthorized' - c#

I'm calling a method on a web service from behind a proxy server using the following code:
myWebService.TestWebService webservice = new myWebService.TestWebService();
webservice.Url = "http://test.com/webservice?wsdl";
WebProxy proxy = new WebProxy("1.2.3.4", 8080);
proxy.Credentials = new NetworkCredential("username", "password");
webservice.Proxy = proxy;
string response = webservice.TestWebMethod();
This works fine when using HTTP, I get the response I'm expecting in the 'response' string.
However - if I change the URL to HTTPS then I get a (401) Unauthorized response.
If I put the URL into my browser it works fine using HTTP or HTTPS.
I've added code to handle the SSL certificate validation by creating a System.Net.ServicePointManager.ServerCertificateValidationCallback delegate but the code never gets this far. The request is rejected before it validates the certificate or so it seems.
Any help is really appreciated...

Do you need credentials to navigate to the SSL url?
If so you need the web service credentials set.
Have you tried adding a web reference in Visual Studio using the SSL url?
If you can't add web reference through Visual Studio then the code is not going to work either.
Can you make sure that the last thing that you set is the proxy (e.g. change the url before you set the proxy)?
There is a small chance that the proxy could be lost, this should be the very last thing to try

Here is an example using a client cert (which i'm sure you don't need) but might provide some insight & using credentials to a web service.
WebService.ManageOutboundDelivery oWS = new WebService.ManageOutboundDelivery();
if (My.Settings.HasClientCert == true) {
X509Certificate2 signedCert = new X509Certificate2(HttpContext.Current.Server.MapPath(My.Settings.ClientCertName), My.Settings.ClientCertPW);
oWS.ClientCertificates.Add(signedCert);
}
System.Net.CredentialCache oCred = new System.Net.CredentialCache();
Net.NetworkCredential netCred = new Net.NetworkCredential(My.Settings.WebServiceUID, My.Settings.WebServicePW);
oCred.Add(new Uri(oWS.Url), "Basic", netCred);
oWS.Credentials = oCred;
Have you also checked the SSL cert is valid - i'm guessing you would see this when you hit it through the browser but it could be causing a problem trying to hit it programmatically.

Related

Could not create SSL/TLS secure channel while calling pages within the site

I am working on a web call that is supposed to do some checking on WSDL pages within the same site. I thought this would be simple. It works fine until we add in the certificate call and then the SSL breaks
var httpClientHandler = new HttpClientHandler
{
SslProtocols = SslProtocols.Tls12,
ServerCertificateCustomValidationCallback = (message, certificate2, chain, errors) => { return true; }
};
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "fakename.com", false);
httpClientHandler.ClientCertificates.AddRange(collection);
HttpClient client = new HttpClient(httpClientHandler);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpResponseMessage resp = client.GetAsync(uri, HttpCompletionOption.ResponseContentRead, token).Result;
the user clicks the healthcheck link
I construct a full uri for the WSDL.
the code above runs and I read the respnse.
Unit testing shows this working fine, the difference is that I am making the call outside of the site, from that I can deduce that the certificate does get added, and a call is possible.
Has anyone else seen this happen? Is there some rule I need to observe in order to call a page from within my own site behind SSL?
I am answering my own question...
when I was running the unit tests the call was coming from outside the site and simply used the public .cer file for the client certificate.
when creating the same call from within the site I had to grab the private key file and apply the password.
var clientCertificate = new X509Certificate2(_509FilePath, "my password");
I think I would have expected a 403 error rather than the SSL issue, but hey... at least it's working now... thanks #CodeCaster for the hint in the comments

C# signing Soap Header in windows phone 8.1

We have the need to call a server with Soap with signed timestamp header on a Windows 8.1 phone. We are using VS Community 2015 as our dev environment. The webservice site has provided us with certificate to sign with. No username/password is to be used.
We have tried to use the code generated from adding a Service Reference from the wsdl file but have not found how to sign the header. As we try to run the call we get an "InvalidOperationException" with text saying we need to supply username. If we then supply a random username/password in the ClientCredentials we get MessageSecurityException with text "An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail."
Has anyone managed to use a certificate to sign a time stamped soap header on Windows 8.1 phone? Noticed that most pages on MSDN for soap clients are page not found.
After this we looked around for examples of signing soap request header in c#. In following what code examples do we have not found that the System.Security.Cryptography.XML package is not available. Does anyone know where XmlDsigC14NTransform was moved or what a replacement would be for getting the DigestValue for the timestamp in the soap header?
Bottom line question on this would be does anyone have example of how to perform a soap request with a timestamped soap header that works on windows 8.1 phone?
Following is what we have tried. Names and addresses etc changed
SoapWS.myRequest req = new SoapWS.myRequest();
// setup the request fields
CustomBinding binding = new CustomBinding();
binding.Namespace = "urn:<the name space>";
binding.Name = "<name of binding>";
TransportSecurityBindingElement securityElement = new TransportSecurityBindingElement();
securityElement.IncludeTimestamp = true;
X509Certificate cert = new X509Certificate(<from loaded bytes>);
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, System.Text.Encoding.UTF8));
binding.Elements.Add(securityElement);
binding.Elements.Add(new HttpsTransportBindingElement());
SoapWS.myRequestClient client = new SoapWS.myRequestClient(
binding,
addr);
//client.ClientCredentials.UserName.UserName = "admin";
//client.ClientCredentials.UserName.Password = "admin";
using (new OperationContextScope(client.InnerChannel))
{
// Add a SOAP Header to an outgoing request
MessageHeader aMessageHeader = MessageHeader.CreateHeader("Security", binding.Namespace, cert);
OperationContext.Current.OutgoingMessageHeaders.Add(aMessageHeader);
}
client.myRequestCompleted += Client_completed;
client.myRequestSessionAsync(req);

WebView Source - automatic authentication

I am looking for a way to automatically authenticate for a web site.
I've got a WebView in my c# Windows Store App and I want to access a site that is password protected.
WebView.Source= new URI("http://UserId:Password#foo.com/");
This is not working as I get a Security exception:
A security problem occurred. (Exception from HRESULT: 0x800C000E);
The method below is also not working as I only get the html of a site, but no css or JavaScript:
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("UserId", "Password");
HttpClient client = new HttpClient(handler);
string body = await client.GetStringAsync("http://foo.com");
webview.NavigateToString(body);
Is there any other way?
I have came across same problem, but luckily found an answer :)
Main problem in here is that Windows store applications contain 2 different HttpClient's
One of them is "classic" one we know from c# apps (used automatically) and the other one is "new" HttpClient - which is connected with WebView :)
Bellow are both types :
System.Net.Http.HttpClient ( classic one )
Windows.Web.Http.HttpClient ( new one )
So remember to declare the new One and do something like the code bellow
var filter = new HttpBaseProtocolFilter();
filter.ServerCredential = new Windows.Security.Credentials.PasswordCredential("http://website","login", "password");
Windows.Web.Http.HttpClient client2 = new Windows.Web.Http.HttpClient(filter);
var response = await client2.GetAsync(new Uri("http://website"));
WebView.Source = new Uri("http://website");
Now remember to change login and password to credentials you want to use, and a website is a site you want to authenticate to.
It is important to get the response from server - this will make user authenticated # server so next time you go with webView to that site you will be authenticated
It works fine with basic authentication and NTLM authentication
Hope it will help people searching for solution of this problem :)

Getting WSHttpBinding working through an http proxy

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.

Connecting to SAP Web Service from C# .NET application

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?

Categories

Resources