Certificates across servers and WCF - wcf

I have a website, wcf service, and a security token service (STS) running on one server. Everything works great. I am now trying to now seperate the peices across servers. When the website trys to login to get the token I get ssl cert errors.
This would be on Server 2008 and IIS 7.5 and my windows 7 IIS 7.5 while i debug.
An error occurred while making the HTTP request to https://x.x.x.x/STS/issue/wstrust/mixed/username. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by...
I generated a self signed cert on the STS server and exported it to the website server. I also exported the key and gave IIS access to the key on the website server. That got past a bunch of WIF errors, it would not run, but I'm not sure that its the right thing to do.
I also have tried [netsh http add sslcert ipport:0.0.0.0:44400 ect...] but im not sure what port to use, ive tried a half dozen different ones and none seem to work, and 443 wont work.
The website is using a WSTrustChannelFactory to create the connection. It bombs on the channel.issue command at the bottom.
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(signInEndpoint));
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = userName;
factory.Credentials.UserName.Password = password;
var channel = factory.CreateChannel();
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(realm),
KeyType = KeyTypes.Bearer
};
try
{
var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
** EDIT **
I've also set website servers iis default website https bindings port 443 to use the cert that i imported from the STS server and get the same error.
** End Edit **
I've been all over google and stackoverflow and while many questions seem to be close, none of the approved answers have worked.
Ideas? I'm a server/hardware noob so the "for dummies version" would be nice.
Thanks!

Since u are using a self signed certificate, have u made sure to turn off Certificate Chain Validation or else add it to the trusted store. It looks like u are using the url of IdentityServer, in there u can turn off strong endpoint requirements and on the client use a UserNameWSTrustBinding with only message security.

Related

WCF Request throwing error when connecting with the certificate

Am trying to connect to saop service developed by a third party they use certificate authentication. I have the certificate and the WSDL file. I tested the request in Soap UI and postman by adding the certificate, everything is working fine there.
Now I tired to implement the same in .net core using WCF connected service. This time am getting the following error even though i added the certificate from StoreLocation.
HL7SOAPEndPointSvcSoapClient hL7SOAPEndPointSvcSoapClient = new HL7SOAPEndPointSvcSoapClient(EndpointConfiguration.HL7SOAPEndPointSvcSoap, "endpointtoconnect");
hL7SOAPEndPointSvcSoapClient.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.Root, X509FindType.FindByIssuerName, "certificateiisuername");
submitMessageRequest submitMessageRequest = new submitMessageRequest() {
Body = "meesege to sent"
};
hL7SOAPEndPointSvcSoapClient.OpenAsync();
submitMessageResponse submitMessageResponse = await hL7SOAPEndPointSvcSoapClient.submitMessageAsync(submitMessageRequest);
The error am getting is
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was "".
Am not sure what is causing the issue because the same certificate is working fine in Soap UI and postman.
I find the solution for the issue, We need to add the binding settings as follow. once i added the below setting it started working
BasicHttpBinding b = new BasicHttpBinding();
b.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType=HttpClientCredentialType.Certificate;
It might help other, Thanks

Downloading page content via https from Azure web app protected by IP-SSL

We have a very basic C# code doing a download of a page content via https (no credentials required).
new WebClient().DownloadString(new Uri(url));
We are hosting this code on two Azure web apps : one web app with no IP-SSL and another one with IP-SSL. It was working fine until a few weeks ago.
Now, it is still working fine on the first web site (no IP SSL) but is failing on the second one with the error :
"Message":"Unable to connect to the remote server","Data":null,"InnerException":{"NativeErrorCode":10060,"ClassName":"System.Net.Sockets.SocketException","Message":"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond"
At first, we thought there could be an issue with TLS (with the changes on security) or with the certificate of the remote site so we added the lines :
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
but still it does not work...
Any ideas / ways of debugging ?
For the record : we changed the app service plan from premium v2 to a classic s3 which basically modified the outbound IPs addresses and made it work. Don't see why the previous Ip addresses would have been blacklisted...

Not able to connect from Windows Application to IdentityServer4 with SSL

I have a Windows application that is using the "password" grant type. It is able to authenticate to the Identityserver4 without SSL, but not with SSL. The problem is that it is giving an error:
The underlying connection was closed: An unexpected error occurred on a send
I tried it from postman, and it worked, but not from my Windows Application. Below is the code:
var tokenClient = new TokenClient($"{IdentityServer}/connect/token", Constants.ClientId, Constants.ClientSecret);
var tokenResponseTask = tokenClient.RequestResourceOwnerPasswordAsync(username, password, Constants.Scope);
tokenResponseTask.Wait();
return tokenResponseTask.Result;
Below also is another code the I tried, but it doesn't work:
TokenResponse tokenResponse;
string request = $"client_id={clientId}&client_secret={clientSecret}&grant_type={grantType}&scope={scope}&username={username}&password={password}";
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
byte[] result = client.UploadData(endpointUrl, "POST", Encoding.UTF8.GetBytes(request));
string resultJson = Encoding.UTF8.GetString(result, 0, result.Length);
tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(resultJson);
}
Finally, I was able to find the solution under the following link:
Authentication failed because remote party has closed the transport stream
'The underlying connection was closed' error is often seen when the SSL handshake fails.
SSL handshake failure usually has something to do with the relevant SSL certificate and whether or not the certificate is trusted.
Check ...
Whether IdentityServer is configured to run under HTTP and HTTPS.
Your Windows Application is correctly configured for SSL.
Test some of the IdentityServer endpoints using a browser with the https protocol.
Hope this gets you going on a helpful investigation path.

WCF - certificate not configured properly with HTTP.SYS in the HTTPS case. Works with Charles proxy running

This seems to be a common error (there are other posts with similar issues) - however, I have gone through all those posts and MSDN articles ( https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates ).
Scenario: Trying to access a service with an HTTPS end point.
Setting the client certificate in code (certificate is loading correctly).
As for the Server cert, I have tried both the options below:
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
I have imported the server certificate to Personal as well as machine store (Trusted Root certificate authorities / certificates).
The weird thing is the call is going through when I use Charles Proxy as the SSL proxy.
Other settings:
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) =>
{
//Console.WriteLine(cert.GetCertHashString());
if (cert.GetCertHashString() == "[actual hash here]")
return true;
else
return false;
};
The above Hash check works fine when Charles proxy is running. Without the proxy running, the callback does not even get called.
Any feedback is appreciated.
(It may be worthwhile to note that a Java client using Apache CXF library works fine - against the same service.)
Update:
For completeness, the original error also had this text:
This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.
OK, after days(& nights) of head banging, the following are my musings / findings (& of course the solution !):
There is "SSL" and then there is SSLv2, SSLv3, TLSv1.0, TLSv1.1, TLS1.2 & TLSv1.3 (draft as of now).
It is critical that the server and client are able to negotiate & pick one of these versions to successfully communicate.
The HTTP.SYS error seems to be a result of the client not being able to negotiate with the server on the appropriate version. When going through Charles proxy, it was clear that both Charles and the service we were trying to hit, were using TLSV1.1.
In my case, I was using wsHTTPBinding & though I tried setting the System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; and other combinations, I could never get the HTTP.SYS error to go away. It would seem that the server and the client could never pick a version that they could agree on.
I did try using other bindings such as basicHttpBinding (with TransportWithMessageCredential) as well as basicHttpsBinding, but to no avail. What's more with some minor tweaks in the binding elements (through config & code) in each case, I ended with exactly the same binding configuration in all 3 cases (basicHttp/basichHttps/wsHttp bindings)! In essence, while there are these out-of-the-box bindings, they probably work for the most simple of scenarios. What's more, there is probably no need for so many of these pre-packaged bindings, especially as they seem to be using mostly the same binding elements.
I did remember reading that using a custom binding is better in many cases - but I imagined that by customizing a wsHttpBinding I would be achieving the same thing. Looks not - as there are some hard-coded properties (e.g.: default SSL protocols) in this binding that seem difficult to get around. I did take a look at the source code of wsHttpBinding and its base class, but could not find the exact hard coded location (but there are references to "default" protocols in the System.ServiceModel code).
In the end a "CustomBinding" worked for me, configured like so:
Custom Binding configuration
- Sorry for including this as an image - as the formatting on SO was playing up.
The idea is to use httpsTransport with requireClientCertificate, security with authenticationMode="CertificateOverTransport" & includeTimestamp="true" (our service required Timestamp) and the relevant messageSecurityVersion - in our case it was:
WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10.
The above configurations automatically signed the Timestamp as well.
On top of this we had to include the username / password credentials. Simply setting the client.ClientCredentials.UserName.UserName & client.ClientCredentials.UserName.Password did not result in these credentials included in the Security header. The logic was to add the username "token" as well, like so:
//Get the current binding
System.ServiceModel.Channels.Binding binding = client.Endpoint.Binding;
//Get the binding elements
BindingElementCollection elements = binding.CreateBindingElements();
//Locate the Security binding element
SecurityBindingElement security = elements.Find<SecurityBindingElement>();
//This should not be null - as we are using Certificate authentication anyway
if (security != null)
{
UserNameSecurityTokenParameters uTokenParams = new UserNameSecurityTokenParameters();
uTokenParams.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
security.EndpointSupportingTokenParameters.SignedEncrypted.Add(uTokenParams);
}
client.Endpoint.Binding = new CustomBinding(elements.ToArray());
With all this setup, I was able to finally hit the Service and actually get the result - well, almost ! - as the result does not include a Timestamp, which WCF is throwing up as an exception. That is another problem to solve though.
Hopefully readers find this useful.
Update:
Now the Timestamp issue is also "sorted". The thing is the response lacked any security header, not just the timestamp. Thankfully there was a straightforward way to notify WCF to ignore unsecure responses, by simply marking an attribute on the security element: enableUnsecuredResponse="true". Obviously this is not desirable, but as we do not have any control on the service, this is the best we can do at the moment.

Remote service call through proxy

I'm trying to make a very simple service call from VS2012.
The service is on a domain outside a proxy and requires logon credentials.
I have set a refrence to the service in visuals studio. At that point i entered in the remote domian username and password and VS created all the proxy classes for me.
I then added this line to appconf file.
<system.net>
<defaultProxy enabled="true" useDefaultCredentials="true">
</defaultProxy>
</system.net>
Which i believe will allow me to get through our proxy using my own credentails
I then wronte this simple piece of code
private void GetData()
{
OASIS.OasisServiceSoapClient o = new OASIS.OasisServiceSoapClient();
o.ClientCredentials.UserName.UserName = #"OtherDimain\UserName";
o.ClientCredentials.UserName.Password = "Password";
var d = o.SelectOfficersAll();
}
and of course it didn't work and i got all the errors that everyone has posted on.
So first question is
do i need to add this
o.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
because i did and still get that same stupid error
"The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate,NTLM'."
and inner exception
"{"The remote server returned an error: (401) Unauthorized."}"
so am i getting through the proxy ?
Am i using my own credentials ?
Am i passing the right paramaters in to the Service Model ?
Some examples show the username and password properties in the code above are to impersonate the current job.
But i read these on the MSDN page as being the credentials you want to use on the remote serve. The Help topic is ambigious. And if i don't enter them here then how ?
I'm trying to do something so simple , yet can't seem to get past this point.
Ok thanks to my Colleague Sean. It seems that depending on wether you are calling a web service or a WCF services determines what you need to do.
So as a web service this works
OASISWeb.OasisService s = new OASISWeb.OasisService();
s.Credentials = new System.Net.NetworkCredential("Username", "Password", "Domain");
var d = s.SelectOfficersAll();
DataSet x = (DataSet)d;
if it's a WCF service then you need this
var service = new OasisTest2.ServiceReference1.OasisServiceSoapClient();
System.Net.WebRequest.DefaultWebProxy.Credentials = system.Net.CredentialCache.DefaultNetworkCredentials;
service.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("Username", "Password", "Domain");
var result = service.SelectOfficersAll();
It seems that WebRequest is a global object and you need to set the DefaultWebProxy.Credentails on it.
How you are suppose to know that ? I never found any reference to it when i searched on how to connect to a WCF service on MSDN. Must be a secret. So keep it under your hat.