How can you add a Certificate to WebClient (C#)?
You must subclass and override one or more functions.
class MyWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.ClientCertificates.Add(new X509Certificate());
return request;
}
}
Adding Certificate to WebClient
The answer is here: How can you add a Certificate to WebClient (C#)?
Converted to VB (untested):
Public Class MyWebClient
Inherits WebClient
Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest
Dim R = MyBase.GetWebRequest(address)
If TypeOf R Is HttpWebRequest Then
With DirectCast(R, HttpWebRequest)
.ClientCertificates.Add(new X509Certificate())
End With
End If
Return R
End Function
End Class
Webclient DownloadFile with ClientCertificate
I finally get a solution: overriding WebClient !
New webClient :
public class MyWebClient : WebClient
{
X509Certificate2 certificate;
public MyWebClient(X509Certificate2 certificate)
: base()
{
this.certificate = certificate;
}
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
request.ClientCertificates.Add(certificate);
request.Credentials = this.Credentials;
return request;
}
}
The way to use it :
using (var client = new MyWebClient(MyCertificate))
{
// optional login/password if website require both. If not, don't set the credentials
client.Credentials = new System.Net.NetworkCredential(MyLogin, MyPassword);
client.DownloadFile(MyUrl, MyFile);
}
Using System.Net.WebClient with HTTPS certificate
If you're not using client certificates and you can access your server using https:// then your code should look like:
private static WebClient client = new WebClient();
private static NameValueCollection nvc= new NameValueCollection();
nvc.Add(POST_ACTION, ACTION_CODE_LOGIN);
nvc.Add(POST_EMAIL, email);
nvc.Add(POST_PASSWORD, password);
sResponse = System.Text.Encoding.ASCII.GetString(client.UploadValues(BASE_URL + ACTION_PAGE, nvc));
As long as your BASE_URL uses https:// then all the data (to and from the server) will be encrypted.
IOW using SSL/TLS from a client to a server does not require the client to do anything special (with the certificate), besides using https:// as the scheme, since the operating system provides everything (e.g. trusted roots) you need to secure the data transmission.
Add client certificate to .NET Core HttpClient
Make all configuration in Main() like this:
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
string env="", sbj="", crtf = "";
try
{
var whb = WebHost.CreateDefaultBuilder(args).UseContentRoot(Directory.GetCurrentDirectory());
var environment = env = whb.GetSetting("environment");
var subjectName = sbj = CertificateHelper.GetCertificateSubjectNameBasedOnEnvironment(environment);
var certificate = CertificateHelper.GetServiceCertificate(subjectName);
crtf = certificate != null ? certificate.Subject : "It will after the certification";
if (certificate == null) // present apies even without server certificate but dont give permission on authorization
{
var host = whb
.ConfigureKestrel(_ => { })
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseConfiguration(configuration)
.UseSerilog((context, config) =>
{
config.ReadFrom.Configuration(context.Configuration);
})
.Build();
host.Run();
}
else
{
var host = whb
.ConfigureKestrel(options =>
{
options.Listen(new IPEndPoint(IPAddress.Loopback, 443), listenOptions =>
{
var httpsConnectionAdapterOptions = new HttpsConnectionAdapterOptions()
{
ClientCertificateMode = ClientCertificateMode.AllowCertificate,
SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
ServerCertificate = certificate
};
listenOptions.UseHttps(httpsConnectionAdapterOptions);
});
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls("https://*:443")
.UseStartup<Startup>()
.UseConfiguration(configuration)
.UseSerilog((context, config) =>
{
config.ReadFrom.Configuration(context.Configuration);
})
.Build();
host.Run();
}
Log.Logger.Information("Information: Environment = " + env +
" Subject = " + sbj +
" Certificate Subject = " + crtf);
}
catch(Exception ex)
{
Log.Logger.Error("Main handled an exception: Environment = " + env +
" Subject = " + sbj +
" Certificate Subject = " + crtf +
" Exception Detail = " + ex.Message);
}
}
Configure file startup.cs like this:
#region 2way SSL settings
services.AddMvc();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CertificateAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CertificateAuthenticationDefaults.AuthenticationScheme;
})
.AddCertificateAuthentication(certOptions =>
{
var certificateAndRoles = new List<CertficateAuthenticationOptions.CertificateAndRoles>();
Configuration.GetSection("AuthorizedCertficatesAndRoles:CertificateAndRoles").Bind(certificateAndRoles);
certOptions.CertificatesAndRoles = certificateAndRoles.ToArray();
});
services.AddAuthorization(options =>
{
options.AddPolicy("CanAccessAdminMethods", policy => policy.RequireRole("Admin"));
options.AddPolicy("CanAccessUserMethods", policy => policy.RequireRole("User"));
});
#endregion
The certificate helper
public class CertificateHelper
{
protected internal static X509Certificate2 GetServiceCertificate(string subjectName)
{
using (var certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
{
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(
X509FindType.FindBySubjectDistinguishedName, subjectName, true);
X509Certificate2 certificate = null;
if (certCollection.Count > 0)
{
certificate = certCollection[0];
}
return certificate;
}
}
protected internal static string GetCertificateSubjectNameBasedOnEnvironment(string environment)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile($"appsettings.{environment}.json", optional: false);
var configuration = builder.Build();
return configuration["ServerCertificateSubject"];
}
}
Accessing a cross-signed SSL service with a .net WebClient
If your computer trusts the Root CA, then you trust certificates issued by that CA. You need to add the Digicert Root CA certificate to the computer's Trusted Root CA group. This can be done using the Certificates MMC snap-in for the computer account.
To Export the Root CA Certificate
- View the site's certificate from the browser.
- On the Certification Path tab, select the Root CA cert (at the top of the chain).
- Click the View Certificate button
- On the Details tab of the Root CA cert click Copy to File. (DER encoded .CER is fine).
To trust the Root CA
- Open the Certificates MMC snap-in for the Computer (start > mmc > Add snap-in > certificates)
- Navigate to the "Trusted Root Certification Authorities" folder
- Right-click on the Certificates folder, choose All Tasks > Import.
- Browse to the .cer file and follow the wizard.
If WebClient adds a cert to request.ClientCertificates will the cert be found at context.Request.ClientCertificate in the web app?
The reason why the certificate is not showing up in the HttpContext is the certificate authentication hasn’t been established yet between the client-side and the server-side.
Simply speaking, when the web application is hosted in IIS, we disable other authentication modes in IIS and enable the IIS client certificate mapping authentication. the server requires a client certificate when the client tries to access the website/service.
Subsequently, the below function method will have a returned value.
public ActionResult About()
{
var result = System.Web.HttpContext.Current.Request.ClientCertificate;
ViewBag.Message = result.Subject+result.ServerSubject;
return View();
}
Please refer to the documentation for what is IIS Client Certificate Mapping Authentication and how to implement it in IIS.
https://learn.microsoft.com/en-us/iis/configuration/system.webserver/security/authentication/iisclientcertificatemappingauthentication/
https://learn.microsoft.com/en-us/troubleshoot/iis/configure-many-to-one-client-mappings
https://learn.microsoft.com/en-us/iis/manage/configuring-security/configuring-one-to-one-client-certificate-mappings
Feel free to let me know if there is anything I can help with.
Force HttpWebRequest to send client certificate
I resolved the problem, The point is that a P12 file (as a PFX) contains more then 1 certificate, so it must be loaded in this way:
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
and added to a HttpWebRequest in this way: request.ClientCertificates = certificates;
Thanks everybody for support.
COMPLETE SAMPLE CODE
string host = @"https://localhost/";
string certName = @"C:\temp\cert.pfx";
string password = @"password";
try
{
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.AllowAutoRedirect = true;
req.ClientCertificates = certificates;
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
}
stream.Close();
}
catch(Exception e)
{
Console.WriteLine(e);
}
Related Topics
Rotate - Transposing a List<List<String>> Using Linq C#
Could Not Find Installable Isam
Complex Models and Partial Views - Model Binding Issue in ASP.NET MVC 3
Entity Framework Core Using Multiple Dbcontexts
Cssrewriteurltransform with or Without Virtual Directory
How to Format Datetime to Web Utc Format
Long String Interpolation Lines in C#6
Convert Transparent Png to Jpg with Non-Black Background Color
Nsubstitute - Testing for a Specific Linq Expression
How to Create a Directory on Ftp Server Using C#
How to Create an Expression Tree Calling Ienumerable<Tsource>.Any(...)
Control Another Application Using C#
Wcf Gives an Unsecured or Incorrectly Secured Fault Error
Practical Usage of Virtual Functions in C#