SSL HttpWebRequest Issues - c#

I am attempting to access an api which they had me install a local cert via mmc I have done so on my local windows 7 machine so I could test out the service. I'm using a simple winforms app to test. I'm assuming this is a self signed cert server side so I'm hoping to bypass the exception for dev purposes.
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(postData);
req.Method = "POST";
req.ContentType = "application/x-www-form-uriencoded";
req.KeepAlive = true;
req.AllowAutoRedirect = false;
X509Store store = new X509Store(StoreName.Root,StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate cert = new X509Certificate();
for (int i = 0; i < store.Certificates.Count; i++)
{
if (store.Certificates[i].SerialNumber == "XXXX")
{
cert = store.Certificates[i];
}
}
req.ClientCertificates.Add(cert);
req.PreAuthenticate = true;
try
{
using (WebResponse resp = req.GetResponse())
{
Stream responseStream = resp.GetResponseStream();
using (StreamReader responseStreamReader = new
StreamReader(responseStream, new ASCIIEncoding()))
{
respString = responseStreamReader.ReadToEnd();
}
}
}
catch (Exception ex)
{
// there is a problem
}
I'm getting the following exception in the try catch:
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
I do have a certificate object in the variable "cert"
When I go to the api url in my browser I get the caution warning but then can proceed and get data.
I have tried the other fixes recommended on stackoverflow such as suppressing the exception.
ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback
(
delegate { return true; }
);
I realize there are quite a few of these questions similar to this and trust me I have read through them all. Thanks for any suggestions they are appreciated.

Hey the following finally fixed it for me. I went to the api server address in my browser and exported their certificate to a file on my desktop. I then opened mmc and installed the certificate like in the following link:
http://support.esri.com/en/knowledgebase/techarticles/detail/38408
After I installed I had no issues calling the webservice and the exception went away.

Related

Cannot use client certificate from certificate store, only by loading from file

I am using WebRequest to connect to a webpage that uses https.
If I try to use a client certificate that is in the personal store in LocalMachine by using
var myStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
myStore.Open(OpenFlags.ReadOnly);
var clientCertificates = myStore.Certificates.Find(X509FindType.FindBySubjectName, "foobar",
validOnly: false);
and then I create a connection with
var req = (HttpWebRequest)WebRequest.Create("https://theserver.com/the/url");
req.Method = "POST";
req.ContentType = "application/json";
req.ProtocolVersion = HttpVersion.Version11;
req.ClientCertificates = clientCertificates;
req.ServerCertificateValidationCallback += (o, certificate, chain, errors) =>
{
return true;
};
using (var stream = req.GetRequestStream())
{
stream.Write(data, 0, data.Length);
stream.Flush();
}
var response = req.GetResponse();
I fail on the req.GetResponse line with
System.Net.WebException: 'The request was aborted: Could not create
SSL/TLS secure channel.'
But if I instead load the client certificate from file, (the same file that I had previously installed in the store) with
var certificatePath = #"C:\temp\file.p12";
var clientCertificate = new X509Certificate2(certificatePath, "pwd",
X509KeyStorageFlags.Exportable);
and
req.ClientCertificates.Add(clientCertificate);
I will be able to run the https query successfully.
This requires both that I keep a file in the file system and that I put a password in the source code.
How do I use the store instead of the file system?
Why do I have to use validOnly: false to get the client certificates?
edit
The preferrable way of getting access to those certificates (client and server certificate) would be to have them in web.config as endpoints, but I have not succeeded doing that.
To solve the symptoms of the problem/many problems, I had to add the user Everyone to the ACL of the private key of the certificate.
Instead of changing the ACL of the private key, I found that the ClientCertificates property worked perfectly well if I only gave it public keys. Yes, the authentication won't work if you don't have the private key in the store, but converting to a public-key-only certificate appears to work for me every time.
var publicKeyOnly = new X509Certificate2(authenticationCert.Export((X509ContentType.Cert));

Recover from a NameResolutionFailure error during a HTTPWebRequest

In our Outlook COM add-in, we're making an API call to our server using the .NET HTTPWebRequest method. One of our customers is running into a System.Net.WebException with the message The remote name could not be resolved: 'mydomain.com' and WebExceptionStatus.NameResolutionFailure as the status. All the users from this particular customer's company are using outlook/the addin from behind a VPN so we are piggy-backing proxy configuration from IE in order to make the request.
Our API calls work for a period of time but then it randomly disconnects and then does not allow future requests to go through either. Once the users close and restart Outlook though, it seems to work just fine again without changing any network configuration or reconnecting to wifi etc.
Other posts like this suggested retrying with a sleep in between. We have added a retry mechanism with 3 attempts, each with a sleep in between but that has not resolved the intermitent issue.
Our domain is hooked up to an AWS Classic Load Balancer so mydomain.com actually resolves a CNAME record to an AWS static domain, pointing to the ELB. I'm not sure if that would have any impact on the request or routing it.
The strange part is we also have a web browser component that loads a web page in a sidebar from the exact same domain as the API calls. It works perfectly and loads a URL from the same domain. The users can also load the URL in their browsers without any issues. It just appears that the HTTPWebRequest is running into the domain resolution issue. We've checked that it's not just a matter of a weak wifi signal. Since they are able to use IE which has the same proxy config to access the site just fine, I don't think it's that.
We're at a loss for how to gracefully recover and have the request try again. I've looked into suggestions from this answer and this other answer, we'll be trying those next. Unfortunately, we are not able to make the requests use direct IP addresses as some of the other answers suggest. That also eliminates the ability to edit the hosts file to point straight to it. The reason is we can't assign a static IP on a classic ELB.
We're considering trying to set the host to use the CNAME record from AWS directly but this is going to cause SSL errors as it doesn't have a valid cert for the CNAME entry. Is there a way to get around that by masking it via a header, similar to the IP approach?
Feel free to ask for more information, I will do my best to provide it.
Any suggestions on what to try / troubleshoot are welcome!
Update: We’re targeting .NET v4.5
Here's the code
var result = string.Empty;
bool retrying = false;
int retries = 0;
HttpWebRequest webRequest = null;
try
{
ServicePointManager.ServerCertificateValidationCallback =
CertificateCheck;
ServicePointManager.MaxServicePoints = 4;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
retry:
webRequest = (HttpWebRequest)WebRequest.Create(uriParam);
webRequest.Timeout = 120000;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Accept = acceptParam;
webRequest.Headers.Add("Cookie", cookieParam);
webRequest.UseDefaultCredentials = true;
webRequest.Proxy = null;
webRequest.KeepAlive = true; //default
webRequest.ServicePoint.ConnectionLeaseTimeout = webRequest.Timeout;
webRequest.ServicePoint.MaxIdleTime = webRequest.Timeout;
webRequest.ContentLength = dataParam.Length;
using (var reqStream = webRequest.GetRequestStream())
{
reqStream.Write(dataParam, 0, dataParam.Length);
reqStream.Flush();
reqStream.Close();
}
try
{
using (WebResponse webResponse = webRequest.GetResponse())
{
using (var responseStream = webResponse.GetResponseStream())
{
if (responseStream != null)
{
using (var reader = new StreamReader(responseStream))
{
result = reader.ReadToEnd();
}
}
}
webResponse.Close();
}
}
catch (WebException)
{
if (retrying && retries == 3)
{
//don't retry any more
return string.Empty;
}
retrying = true;
retries++;
webRequest.Abort();
System.Threading.Thread.Sleep(2000);
goto retry;
}
}
catch (Exception ex)
{
Log.Error(ex);
result = string.Empty;
}
finally
{
webRequest?.Abort();
}
return result;

Adding .pfx certificate to HttpWebRequest fails from Web application

I have created a Asp.net web application-C#, where I need to send a HttpWebRequest with a .pfx certificate.
I have installed the certificate to the personal certificates folder(added rights for user NETWORK SERVICE) and able to see it in the X509Certificate2Collection and no issues while adding the certificate to the HttpWebRequest.
But it throws cannot connect to server error while fetching the response (HttpWebResponse)req.GetResponse()
I am facing this issue only from web application, the same code works fine(got the correct response) when i tried using a C# console application.
X509Store oLocalMachineStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
X509Certificate2Collection oCertColl;
X509Certificate oCert = new X509Certificate();
oLocalMachineStore.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
oCertColl = oLocalMachineStore.Certificates.Find(System.Security.Cryptography.X509Certificates.X509FindType.FindByIssuerName, "mycertificate issuer", false);
oLocalMachineStore.Close();
foreach (X509Certificate oCertFromStore in oCertColl)
{
if (oCertFromStore.Issuer.Contains("mycertificate issuer"))
{
oCert = oCertFromStore;
oCert.Import(#"certlocation\mycertificate.pfx", "pwd", X509KeyStorageFlags.MachineKeySet);
break;
}
}
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("destination URL");
req.ClientCertificates.Add(oCert);
req.UserAgent = "LOL API Client";
req.Accept = "application/json";
req.Method = WebRequestMethods.Http.Get;
string result = null;
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
result = reader.ReadToEnd();
}
The issue got resolved after adding proxy to the http request.
req.Proxy = New Net.WebProxy("ProxyURL", False);
But still i have no idea, how it worked when i tried with the same code from a console application with out adding proxy.
If anyone has any idea on this please share.

How to invoke a Web Service which requires a certificate in C#?

I need to communicate with a third party which has a .asmx web service. This web service is using https. I have the required certificate (.pfx).
When first trying to add this service using Add Service Reference in Visual Studio, I got an error. I got passed this error by importing the certificate into the Personal store. After I did that, I tried to add the Service Reference again and it works. So now I can create an instance of the web service. Nice.
But now I want to invoke the service. And when I do that I get this error:
302 Notification: Digital Certificate Missing
So how can I tell my service to use the right certificate?
I finally managed to fix my problem as follows:
var service = new Service1SoapClient();
service.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.TrustedPublisher, X509FindType.FindByIssuerName, "name_of_issuer");
((BasicHttpBinding)service.Endpoint.Binding).Security.Mode = BasicHttpSecurityMode.Transport;
((BasicHttpBinding)service.Endpoint.Binding).Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
Please use Certificate.pfx and install it with password.
Try adding this before getting the request stream:
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
request.ProtocolVersion = HttpVersion.Version10;
request.ClientCertificates.Add(new X509Certificate2("YourPfxFile(full path).pfx", "password for your pfx file");
Depending on your security requirements and environment, you may need to use a different SecurityProrocolType value.
I was also facing "The request was aborted: Could not create SSL/TLS secure channel" while requesting a web service.
Fixed issue with below code, hope this will help someone and save time.
HttpWebRequest web_request = (HttpWebRequest)WebRequest.Create("https://yourservices/test.asmx");
web_request.ContentType = "text/xml;charset=\"utf-8\"";
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
web_request.ProtocolVersion = HttpVersion.Version10;
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
web_request.ClientCertificates = certificates;
web_request.Accept = "text/xml";
web_request.Method = "POST";
using (Stream stm = web_request.GetRequestStream())
{
using (StreamWriter stmw = new StreamWriter(stm))
{
stmw.Write(soap);
}
}
WebResponse web_response = null;
StreamReader reader = null;
try
{
web_response = web_request.GetResponse();
Stream responseStream = web_response.GetResponseStream();
XmlDocument xml = new XmlDocument();
reader = new StreamReader(responseStream);
xml.LoadXml(reader.ReadToEnd());
}
catch (Exception webExp) {
string exMessage = webExp.Message;
}

Accessing ssl server (web service) from .net client

I’m tryng to access a ssl server (web service) from a .net client (c#). I have access to a java client code that can connect to the server. Included in the source code are 3 files (saPubKey.jks, WebServices.pfx and trustStore) and I think that only 2 of the 3 are used in the java example.
private void setSSLConnection(WSBindingProvider bp){
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(this.getClass().getClassLoader().getResourceAsStream("WebServices.pfx"), "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "password".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(this.getClass().getClassLoader().getResourceAsStream("trustStore"), "pass".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
bp.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, sslContext.getSocketFactory());
}
The code I have for the c# client is this
var clientCertificate = new X509Certificate2(#"C:\WebServices.pfx", "password");
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://blablabla.org:700/fws/ftrs");
req.ClientCertificates.Add(clientCertificate);
req.Headers.Add("SOAPAction", "\"https://blablabla.org:700/fws/ftrs/Rgdvice\"");
req.ContentType = "text/xml;charset=\"utf-8\"";
req.Accept = "text/xml";
req.Method = "POST";
using (Stream stm = req.GetRequestStream())
{
using (StreamWriter stmw = new StreamWriter(stm))
{
stmw.Write(soap);
}
}
WebResponse response = req.GetResponse(); //(500) Internal Server Error.
Stream responseStream = response.GetResponseStream();
Method req.GetResponse() throws "(500) Internal Server Error."
With no client certificates added I get: "An unexpected error occurred on a send."
I also tried this line of code with no luck:
ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
I'm new to ssl and certificates, and really need help accessing the ssl server from c#.
This could be unrelated but make sure web service is not in the app_code folder.

Categories

Resources