i am looking to get the data from any given domain names SSL certificate. For example I want to put in any website address e.g. "http://stackoverflow.com" and my code would firstly check if an SSL certificate exists. If it does then I want it to pull out the expiry date of the certificate. [ i am reading Domainnames from DB ]
Example :http://www.digicert.com/help/
i need to create a web service to check expiry date. how can i implement it?? - I have looked up loads of different things such as RequestCertificateValidationCallback and ClientCertificates etc.
I could be completely wrong (hence why I need help) but would I create a HTTPWebRequest and then somehow request the client certificate and specific elements that way?
i tried the example provided #SSL certificate pre-fetch .NET , but i am getting forbitten 403 error.
For this to work your project will need a reference to System.Security:
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
//Do webrequest to get info on secure site
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://mail.google.com");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
response.Close();
//retrieve the ssl cert and assign it to an X509Certificate object
X509Certificate cert = request.ServicePoint.Certificate;
//convert the X509Certificate to an X509Certificate2 object by passing it into the constructor
X509Certificate2 cert2 = new X509Certificate2(cert);
string cn = cert2.GetIssuerName();
string cedate = cert2.GetExpirationDateString();
string cpub = cert2.GetPublicKeyString();
//display the cert dialog box
X509Certificate2UI.DisplayCertificate(cert2);
.NET Core 2.1 - .NET 5
You can use HttpClientHandler and ServerCertificateCustomValidationCallback Property. (This class is available in .net 4.7.1 and above also).
var handler = new HttpClientHandler
{
UseDefaultCredentials = true,
ServerCertificateCustomValidationCallback = (sender, cert, chain, error) =>
{
/// Access cert object.
return true;
}
};
using (HttpClient client = new HttpClient(handler))
{
using (HttpResponseMessage response = await client.GetAsync("https://mail.google.com"))
{
using (HttpContent content = response.Content)
{
}
}
}
#cdev's solution didn't work for me on .NET Core 2.1. It seems HttpWebRequest is not completely supported on .NET Core.
Here is the function I'm using on .NET Core to get any server's X509 certificate:
// using System;
// using System.Net.Http;
// using System.Security.Cryptography.X509Certificates;
// using System.Threading.Tasks;
static async Task<X509Certificate2> GetServerCertificateAsync(string url)
{
X509Certificate2 certificate = null;
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_, cert, __, ___) =>
{
certificate = new X509Certificate2(cert.GetRawCertData());
return true;
}
};
var httpClient = new HttpClient(httpClientHandler);
await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
return certificate ?? throw new NullReferenceException();
}
One thing to note is that you might need to set request.AllowAutoRedirect = False. Otherwise, if the server redirects HTTPS to HTTP, you won't be able to get the certificate from the HttpWebRequest object.
Recreating the HttpClient each time you want to make a request is very ineffective and may cause performance issues. Better to create a single readonly client for all the methods. More informations can be found here.
private readonly HttpClientHandler _handler;
private readonly HttpClient _client;
And this is my solution to getting certificate info:
Code inside constructor:
_handler = new HttpClientHandler {
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
sender.Properties.Add("Valid", sslPolicyErrors == System.Net.Security.SslPolicyErrors.None);
sender.Properties.Add("Errors", sslPolicyErrors);
return true;
}
};
_client = new HttpClient(_handler);
Then you can read all the variables by:
using var request = new HttpRequestMessage(HttpMethod.Get, "https://www.google.com/");
var response = await _client.SendAsync(request);
var isCertificateValid = (bool)request.Properties["Valid"];
Related
I know that this question was asked many times on Stackoverflow, however I looked at many of the answers, yet I still couldn't find a solution that worked for me.
I have this block of code, in which I am trying to send a request to a server using WebClient
using (var client = new CertificateWebClient())
{
var postData = $"<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:v3='http://xmldefs.volkswagenag.com/PP/QM/GroupProblemManagementService/V3'><soapenv:Header><To xmlns='http://www.w3.org/2005/08/addressing'>ws://volkswagenag.com/PP/QM/GroupProblemManagementService/V3</To><Action xmlns='http://www.w3.org/2005/08/addressing'>http://xmldefs.volkswagenag.com/PP/QM/GroupProblemManagementService/V3/KpmService/GetMultipleProblemDataRequest</Action><MessageID xmlns='http://www.w3.org/2005/08/addressing'>${{= \"urn:uuid:\" + UUID.randomUUID()}}</MessageID></soapenv:Header><soapenv:Body><v3:GetMultipleProblemData><UserAuthentification><UserId>{Username}</UserId></UserAuthentification><OverviewAddress><AddressTimestamp/><ContactPerson/><Description/><OrganisationalUnit>KPMEE-05</OrganisationalUnit><Group/><Plant>Z$</Plant></OverviewAddress><ActiveOverview>true</ActiveOverview><PassiveOverview>false</PassiveOverview></v3:GetMultipleProblemData></soapenv:Body></soapenv:Envelope>";
var postArray = Encoding.UTF8.GetBytes(postData);
try
{
var resposneData = Encoding.UTF8
.GetString(client.UploadData("https://ws-gateway-cert.volkswagenag.com/services", postArray));
Debug.WriteLine(resposneData);
}
catch (WebException we)
{
var webResponse = (HttpWebResponse)we.Response;
if (webResponse != null)
{
Debug.WriteLine(GetErrorText(webResponse.StatusCode.ToString()));
}
else
{
Debug.WriteLine(we);
}
}
}
Now since the server requires a certificate for authentication, I created a sub class that inherits from WebClient and uses HttpWebRequest to add the certificate.
internal class CertificateWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
string filePath = "../../Resources/Systemuser PAG R_ PAG_KPM_PREH_MFL VWPKI A145CCD2D02592DB.p12";
X509Certificate2 certificate = new X509Certificate2(filePath, ""); //password is not shown in here
request.ClientCertificates.Add(certificate);
return request;
}
}
When starting the program in debug mode, I get the following error:
The request was aborted: Could not create SSL/TLS secure channel.
I tried fixing this problem by setting the SecurityProtocol to Tls1.2, since this is what the server is using, setting Expect100Continue to true and setting the ServerCertificateValidationCallback, as it was suggeested in many responses.
internal class CertificateWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslError) => true;
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
string filePath = "../../Resources/Systemuser PAG R_ PAG_KPM_PREH_MFL VWPKI A145CCD2D02592DB.p12";
X509Certificate2 certificate = new X509Certificate2(filePath, ""); //password is not shown in here
request.ClientCertificates.Add(certificate);
return request;
}
}
That unfortunately couldn't fix the problem either. I know that the certificate is working tho, since I set up a test environment via Soap UI, using the same certificate, the same username and the same URI. And it worked perfectly. I use .NET Framework version 4.8
What am I missing out on?
I was able to solve the problem by using another Constructor for the X509Certificate2 class. Instead of using X509Certificate2(string, string) I used X509Certificate(string, string, X509KeyStorageFlags)
internal class CertificateWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
string filePath = "../../Resources/Systemuser PAG R_ PAG_KPM_PREH_MFL VWPKI A145CCD2D02592DB.p12";
X509Certificate2 certificate = new X509Certificate2(filePath, "", X509KeyStorageFlags.UserKeySet); //password is not shown here
request.ClientCertificates.Add(certificate);
return request;
}
}
By doing so, the server accepted the certificate and the request was successful.
I have an endpoint that I need to hit and validate.
I have a generated .p12 file and a chain of certificates (Root and Intermediate) that I can use to validate.
Here is the code I currently am using which gives me this error:
System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
var handler = new HttpClientHandler();
var p12 = new X509Certificate2(
Path.Combine(Environment.CurrentDirectory, "Assets/Certs/p12filename.p12"),
"TheRealPassword");
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(p12);
handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, chain, sslPolicyErrors) =>
{
var root = new X509Certificate2(
Path.Combine(Environment.CurrentDirectory, "Assets/Certs/Root.cer"));
X509Chain CertificateChain = chain;
CertificateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
return CertificateChain.Build( root );
};
var client = new HttpClient(handler);
var url = "https://urlHere"
var request = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = HttpMethod.Get,
};
var response = await client.SendAsync(request);
I'm not super well versed in using certs so it is perhaps something quite elementary. Of note if inside the ServerCertificateCustomValidationCallback I return true I have no issues.
Update:
To clarify, how can I validate the end point I am hitting using my .p12 and the corresponding root and intermediate certificates. My assumption based on returning true in the call back giving me what I want is that I need to do these checks here.
I'm trying to get cookies on the Spotify login page with C# and the HttpClient class. However, the CookieContainer is always empty when I know cookies are being set. I'm not sending any headers, but it should still give me the cookie(s) because when I send a GET request without any headers with python (requests module) I get the csrf token. Here's my code:
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections;
using System.Web;
class Program
{
static void Main()
{
Task t = new Task(MakeRequest);
t.Start();
Console.WriteLine("Getting cookies!");
Console.ReadLine();
}
static async void MakeRequest()
{
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
Uri uri = new Uri("https://accounts.spotify.com/en/login/?_locale=en-US&continue=https:%2F%2Fwww.spotify.com%2Fus%2Faccount%2Foverview%2F");
HttpClient client = new HttpClient(handler);
var response = await client.GetAsync(uri);
string res = await response.Content.ReadAsStringAsync();
Console.WriteLine(cookies.Count);
foreach (var cookie in cookies.GetCookies(uri)) {
Console.WriteLine(cookie.ToString());
}
}
}
It seems pretty simple to me, but the program always says there's 0 cookies. Anyone know what's going on?
You need to enable the use of cookies using HttpClientHandler.UseCookies Property
public bool UseCookies { get; set; }
Gets or sets a value that indicates whether the handler uses the CookieContainer property to store server cookies and uses these cookies when sending requests.
//...
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
handler.UseCookies = true; //<-- Enable the use of cookies.
//...
I tried to write the response headers to the console with Console.WriteLine(response.Headers) and a Set-Cookie header with the csrf token was printed to the console. So it seems that HttpClient doesn’t count cookies in this header as actual cookies, thus not adding these said cookies to the CookieContainer.
Make sure you are decompression the response automatically. See my answer here:
https://stackoverflow.com/a/74750572/1158313
An example of what I use:
var clientHandler = new HttpClientHandler {
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = cookieContainer,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, // <--
};
I have been trying to add a client certificate to the HttpClient in a .NET Core 1.1 WebAPI for the past 20 minutes and can not figure out what the issue is. When I debug the program and check to see if the certificate was attached with the handler, I am able to see the certificate. I still receive a 401 though. I know for certain that the certificate is properly installed on the other API as I am able to make calls using it from another program. The other program is using .NET Framework 4.5.2, but I doubt that would be the issue the code is almost identical. The only difference is I need to use HttpClientHandlerfor the .NET Core instead of WebRequestHandler.
.NET Core 1.1
string uri = "https://other-api-url.com/something";
try
{
X509Certificate2 clientCert = GetClientCertificate();
HttpClientHandler client = new HttpClientHandler();
requestHandler.ClientCertificates.Add(clientCert);
HttpClient client = new HttpClient(requestHandler)
{
BaseAddress = new Uri(uri)
};
HttpResponseMessage response = client.GetAsync("").Result;
response.EnsureSuccessStatusCode();
string responseContent = response.Content.ReadAsStringAsync().Result;
return Ok(responseContent);
}
catch (Exception ex)
{
return BadRequest(ex.Message + uri);
}
.NET Framework 4.5.2 Code:
string uri = "https://other-api-url.com/something";
try
{
X509Certificate2 clientCert = GetClientCertificate();
WebRequestHandler requestHandler = new WebRequestHandler();
requestHandler.ClientCertificates.Add(clientCert);
HttpClient client = new HttpClient(requestHandler)
{
BaseAddress = new Uri(uri)
};
HttpResponseMessage response = client.GetAsync("").Result;
response.EnsureSuccessStatusCode();
string responseContent = response.Content.ReadAsStringAsync().Result;
return Ok(responseContent);
}
catch (Exception ex)
{
return BadRequest(ex.Message + uri);
}
It is an older post, but I had the same scenario using certificates.
Make sure the UseDefaultCredentials property on the HttpClientHandler is set to true. Because it is false by default.
See quote below from the HttpClientHandler class:
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.usedefaultcredentials?view=net-5.0#System_Net_Http_HttpClientHandler_UseDefaultCredentials
Set this property to true when requests made by the HttpClientHandler
object should, if requested by the server, be authenticated using the
credentials of the currently logged on user. For client applications,
this is the desired behavior in most scenarios. For middle-tier
applications, such as ASP.NET applications, instead of using this
property, you would typically set the Credentials property to the
credentials of the client on whose behalf the request is made.
It is the first time I have to use certificate authentication.
A commercial partner expose two services, a XML Web Service and a HTTP service. I have to access both of them with .NET clients.
What I have tried
0. Setting up the environment
I have installed the SSLCACertificates (on root and two intermediate) and the client certificate in my local machine (win 7 professional) using certmgr.exe.
1. For the web service
I have the client certificate (der).
The service will be consumed via a .NET proxy.
Here's the code:
OrderWSService proxy = new OrderWSService();
string CertFile = "ClientCert_DER.cer";
proxy.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(CertFile));
orderTrackingTO ot = new orderTrackingTO() { order_id = "80", tracking_id = "82", status = stateOrderType.IN_PREPARATION };
resultResponseTO res = proxy.insertOrderTracking(ot);
Exception reported at last statement: The request failed with an empty response.
2. For the HTTP interface
it is a HTTPS interface I have to call through POST method.
The HTTPS request will be send from a .NET client using HTTPWebRequest.
Here's the code:
string PostData = "MyPostData";
//setting the request
HttpWebRequest req;
req = (HttpWebRequest)HttpWebRequest.Create(url);
req.UserAgent = "MyUserAgent";
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(CertFile, "MyPassword"));
//setting the request content
byte[] byteArray = Encoding.UTF8.GetBytes(PostData);
Stream dataStream = req.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
//obtaining the response
WebResponse res = req.GetResponse();
r = new StreamReader(res.GetResponseStream());
Exception reported at last statement: The request was aborted: Could not create SSL/TLS secure channel.
3. Last try: using the browser
In Chrome, after installing the certificates, if I try to access both urls I get a 107 error:
Error 107 (net::ERR_SSL_PROTOCOL_ERROR)
I am stuck.
The following should help you identify the issue, here are two methods to test SSL connectivity one tests the site whilst the other is a callback method to identify why SSL failed. If nothing else it should give you a better idea why it is failing.
When the method is called it will pop up with the select certificate dialog box, obviously when you do this for real you'll want to read from the cert store automatically. The reason I have put this in is because if no valid certificate is found then you will know your problem is with the way the certificate is installed.
The best thing to do is put this code in a simple console app:
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;
private static void CheckSite(string url, string method)
{
X509Certificate2 cert = null;
ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection certcollection = (X509Certificate2Collection)store.Certificates;
// pick a certificate from the store
cert = X509Certificate2UI.SelectFromCollection(certcollection,
"Caption",
"Message", X509SelectionFlag.SingleSelection)[0];
store.Close();
HttpWebRequest ws = (HttpWebRequest)WebRequest.Create(url);
ws.Credentials = CredentialCache.DefaultCredentials;
ws.Method = method;
if (cert != null)
ws.ClientCertificates.Add(cert);
using (HttpWebResponse webResponse = (HttpWebResponse)ws.GetResponse())
{
using (Stream responseStream = webResponse.GetResponseStream())
{
using (StreamReader responseStreamReader = new StreamReader(responseStream, true))
{
string response = responseStreamReader.ReadToEnd();
Console.WriteLine(response);
responseStreamReader.Close();
}
responseStream.Close();
}
webResponse.Close();
}
}
/// <summary>
/// Certificate validation callback.
/// </summary>
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
// If the certificate is a valid, signed certificate, return true.
if (error == System.Net.Security.SslPolicyErrors.None)
{
return true;
}
Console.WriteLine("X509Certificate [{0}] Policy Error: '{1}'",
cert.Subject,
error.ToString());
return false;
}