HttpWebRequest, TLS and application pool service identity - c#

We use HttpWebRequest to make web service calls to a remote server. The call requires a certificate to be loaded from the local machine.
The problem we are running into is if the Application Pool identity is set to NetworkService then the call fails with a generic TLS/SSL error on the HttpWebResponse.GetResponse() call. If we change the identity of the app pool user to LocalSystem then it functions normally.
What I'm trying to figure out is why. What does LocalSystem have access to that NetworkService doesn't which is impacting a post call?
The code is pretty simple:
string URL = "https://mywebserver.net";
X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certResults = store.Certificates.Find(X509FindType.FindBySubjectName, "nameofthecertificate", false);
/* I have confirmed that the certificate is found regardless of
* how the application pool is configured.
*/
X509Certificate cert = certResults[0];
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
req.AllowAutoRedirect = true;
req.ClientCertificates.Add(cert);
req.Method = "POST";
req.ContentType = "application/soap+xml;charset=UTF-8";
StringBuilder postData = new StringBuilder();
postData.Append("some soap info");
byte[] postBytes = Encoding.UTF8.GetBytes(postData.ToString());
using (Stream postStream = req.GetRequestStream())
{
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
}
// dies here when running under NetworkService
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
String data = String.Empty;
using (StreamReader reader = new StreamReader(resp.GetResponseStream()))
{
data = reader.ReadToEnd();
reader.Close();
}

Generally speaking, an Application Pool needs an Identity to run under. You could configure the identity for it to run as you did, but bear in mind that the different identities have different levels of permission.
Your problem could be as simple as the application pool identity not having permissions
for using the code it's trying to execute. You can check that by viewing the deployed projects folder permissions to see.
Since you mentioned SSL/TLS errors, I would be thinking the identity does not have permission on the installed certificate. Open up mmc and check to see if have the certificate in the Local Computer/Personal/Certificates store. If you do, right click on the certificate and go to All Tasks -> Manage Private Keys.... Add in the Application pool identity by adding it from your computer location as:
IIS AppPool\YourNameOfAppPool.
You should not get another dialog saying it wasn't found it you added it correctly.
Read more about the Application pool identities here.

Related

401 Authentication error while running C# Application executing HttpWebRequest

I would like to know how can I fix this issue wherein a WebApp running on IIS 7/8 with Windows Authentication is throwing 401 error while executing HttpWebRequest to another site. This WebApp works fine if I run it locally i.e debug mode.
Here is the code snippet
HttpWebRequest webReq;
webReq = (HttpWebRequest)WebRequest.Create("http://sharepoint_site/_vti_bin/listdata.svc/mylist);
webReq.Accept = "application/json";
webReq.UseDefaultCredentials = true;
webReq.Credentials = CredentialCache.DefaultNetworkCredentials;
//webReq.Credentials = new NetworkCredential("user","password","domain");
webReq.Method = "GET";
webReq.KeepAlive = true;
Stream objStream = webReq.GetResponse().GetResponseStream();
StreamReader objReader = new StreamReader(objStream);
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();
I was also able to make it work by adding BackConnectionHostNames entry in the registry
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
but I need to pass in the credentials (commented above) which I don't like because I don't want to use my account or any service account.
I want the WebApp to use DefaultNetworkCredentials or DefaultCredentials. I enabled Windows Authentication and NTLM provider on the IIS of the machine hosting this WebApp.
Any help will be greatly appreciated, thanks and more power to this community.
CredentialCache.DefaultNetworkCredentials uses the network credentials that the process is running under. If it's running in IIS, it will be the application pool identity, which the web service won't accept.
You will either have to pass different credentials in code (what you said you didn't want to do) or update the application pool to run with network credentials (right-click the application pool in IIS -> Advanced Settings -> Identity)

'The request was aborted: Could not create SSL/TLS secure channel' after SSL setup

In a web service running on Windows Server 2008 R2, IIS 7.5. I have set up an api call with the following code
// ignore untrusted ssl connection
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
// create web request
var httpWebRequest = (HttpWebRequest)WebRequest.Create(REMOTE_URL);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(REQUEST_STRING);
streamWriter.Write(json);
streamWriter.Close();
}
// fetch response
httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
statusCode = (int)httpResponse.StatusCode;
It was working fine. After that I tried to install the SSL certificate in IIS server, get a new HTTPs web service running, and put the module with above code into the web service, the nightmare began and the following line started returning The request was aborted: Could not create SSL/TLS secure channel.
httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
I have tried to roll back by using the old web service with installed certificate removed but no luck.
Also tried the following methods but all failed.
1) Adding
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
2) Modify ApplicationPool Identitiy and set Load User Profile to True
3) Add the certificate in Personal Certificate and Intermediate Certifcation Authorities Certificate through Certificate MMC
A bit struggling so any hints and ideas would be appreciated. Thank you.

Using a X509 Certificate in .Net Compact Framework for Client Authentication HTTPRequest

I'm working in Windows Mobile 6 and would like to have client authentication when talking to a Apache webserver. I have a certificate in my local certificate store and it should be rather straightforward:
X509Store myStore = new X509Store("MY", StoreLocation.CurrentUser);
myStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = myStore.Certificates;
X509Certificate2 clientcertificate;
foreach (X509Certificate 2certificate in certificates) {
clientcertificate = certificate; //omitted code to validate certificate
}
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(webPage);
req.AllowWriteStreamBuffering = true;
req.AllowAutoRedirect = false;
req.Method = "POST";
req.ContentType = "text/xml";
req.Accept = "text/xml";
req.ClientCertificates.Add(clientcertificate);
Stream stream = req.GetRequestStream();
stream.Write(buffer, 0, buffer.Length);
stream.Close();
This code segment works as long as I remove the "req.ClientCertificates.Add(clientcertificate)" line.
Once that's inserted, I get a "Could not establish secure channel for SSL/TLS". Maddeningly enough, when I use this exact code in the regular .Net Framework it transmits the certificate perfectly.
Does anyone know if this is possible in Compact Framework? If I can't present the X509Certificate for Client Authentication, what other ways should I pursue to ensure that authentication is proper (I should have access to CAPI or other Microsoft Cryptographic modules)
Thanks.
It could be that your Apache Server does not support secure connections.
For example, I've got a few websites on hosted domains that cost little or nothing. I use these websites to test code on all the time.
But, to get SSL capabilities, I've got to shell out like $50/month. So, I can't test those on my sites.
To test: If the Apache Server supports SSL, you should be able to replace the URL with the SSL equivalent: http://www.stackoverflow.com with https://www.stackoverflow.com
Good news: I solved it. It turned out not to have to do with the .Net Compact Framework. In 3.5 CF, HTTPWebRequest.ClientCertificates is supported as long as the X509 certificate can be accessed.
The reason why the SSL Handshake failed was because of a trust issue with the server side certificate. Our server certificates were self-signed and we used certificates that were signed for the wrong URL, so the application rightly wouldn't trust the provided server certificate. For testing purposes, we put in place a Trust All Certificates policy, which will be removed for the production.
sealed class AcceptAllCertificatePolicy : ICertificatePolicy
{
private const uint CERT_E_UNTRUSTEDROOT = 0x800B0109;
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate
certificate, WebRequest request, int certificateProblem)
{
// Just accept.
return true;
}
/*public bool CheckValidationResult(ServicePoint sp,
X509Certificate cert, WebRequest req, int problem)
{
return true;
}*/
}
Referenced right before the HTTPWebRequest
System.Net.ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();
And this solves our problem with the SSL/TLS secure channel.

WinForm Security Context

I have written a WinForms app that uploads addresses from a spreadsheet, and geocodes them using an external geocoding service. This all works fine on my local machine, but the time has come for it to be installed on other peoples computers for testing. The app no longer works now though, generating the below error:
System.Net.WebException: The remote server returned an error: (407) Proxy Authentication Required.
Having read a lot and chatted breifly to our network guys, it seems i need to establish the Security Context for the users account and work with this to correct the error.
Has anyone got any pointers about how I should be going about this?
Thanks in advance!
C
It depends on how your uploading the data. If your using a http request (as it looks like you are) it will look something like;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("https://test.example.com/");
req.Method = "POST";
req.ContentType = "text/xml";
req.Credentials = new NetworkCredential("TESTACCOUNT", "P#ssword");
StreamWriter writer = new StreamWriter(req.GetRequestStream());
writer.Write(input);
writer.Close();
var rsp = req.GetResponse().GetResponseStream();

HTTP 407 proxy authentication error when calling a web service

I'm working on a .NET app that calls 3rd party web services over the internet. The services do not use SOAP, so we manually construct an XML request document, send it to the service via HTTP, and retrieve an XML response.
Our code is a Windows service that is run in the context of a normal Windows domain account, and sits behind a proxy server (Microsoft ISA Server) configured to require NTLM authentication. The account running our service has permission to access the internet through the proxy server.
The code looks like this:
// Create the request object.
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "POST";
// Configure for authenticating proxy server requiring Windows domain credentials.
request.Proxy = New WebProxy(proxyAddress) { UseDefaultCredentials = true };
// Set other required headers.
request.Accept = acceptableMimeType;
request.Headers.Add(HttpRequestHeader.AcceptCharset, acceptableCharset);
request.Headers.Add(HttpRequestHeader.AcceptEncoding, "none");
request.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-gb");
request.Headers.Add(HttpRequestHeader.CacheControl, "no-store");
request.Headers.Add(HttpRequestHeader.ContentEncoding, "none");
request.Headers.Add(HttpRequestHeader.ContentLanguage, "en-gb");
request.ContentType = requestMimeType;
request.ContentLength = requestBytes.Length;
// Make the method call.
using(Stream stream = request.GetRequestStream()) {
stream.Write(requestBytes, 0, requestBytes.Length);
}
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
// Extract the data from the response without relying on the HTTP Content-Length header
// (we cannot trust all providers to set it correctly).
const int bufferSize = 1024 * 64;
List<byte> responseBytes = new List<byte>();
using(Stream stream = new BufferedStream(response.GetResponseStream(), bufferSize)) {
int value;
while((value = stream.ReadByte()) != -1) {
responseBytes.Add((byte) value);
}
}
This works fine if the proxy server is turned off, or the URL has been whitelisted as not requiring authentication, but as soon as authentication is active, it always fails with an HTTP 407 error.
I put the above code in a test harness, and tried every method I could think of for configuring the request.Proxy property, without success.
I then noticed that all the 3rd party web services that we have to call are HTTPS. When I tried accessing them as HTTP instead, the proxy authentication started working. Is there some extra hoop I have to jump through to get proxy authentication and HTTPS to play nicely?
PS: The same problems occur with the open source SmoothWall proxy server, so I can't just write it off as a bug in ISA Server.
PPS: I'm aware that you can configure proxy settings in app.config, but (a) doing it in code shouldn't make any difference, and (b) the application design requires that we read the proxy settings from a database at runtime.
Have you tried setting the proxy in the app.config ?
To disable the proxy, in the App.config file add the following configuration
<system.net>
<defaultProxy enabled="false" useDefaultCredentials="false">
<proxy/>
<bypasslist/>
<module/>
</defaultProxy>
</system.net>
To enable the proxy and to use the default proxy settings(specified in IE) add this configuration in your App.config
<system.net>
<defaultProxy enabled="true" useDefaultCredentials="true">
<proxy/>
<bypasslist/>
<module/>
</defaultProxy>
</system.net>
I did have a similar situation
Did you noticed it worked when you accessed the internet before you ran the code? and if you had not accessed the internet for ages (20mins for me) you got the error.
have you tried to set the proxy credentials directly?
//setup the proxy
request.Proxy = new WebProxy("proxyIp", 8080);
request.Proxy.Credentials = CredentialCache.DefaultCredentials;
I hope this fixes your issue too
I think I will have to write off this question. My original posted code does appear to work sometimes. Our proxy server is extremely unreliable; one minute it will block an internet connection from any software, and the next it will allow it. The IT guys seem powerless to do anything about it, and we (everyone outside the IT department) have no authority to make changes to the network infrastructure.
If anyone has any ideas on how to "harden" my code to compensate for an unreliable proxy server, then I'd be interested to hear them. :-)
Is there something wrong with your proxy server's certificate? If your service can't establish HTTPS then it will throw an error.

Categories

Resources