How to operate with X509 certificates in .NET code - asp.net

I have some code that needs to work with X509 Certificate information. I have downloaded a sample that does this:
const string CertWithoutPrivateKey = "MII....";
const string CertWithPrivateKey = "MII...";
public static SecurityToken GetSigningToken(bool includePrivateKey)
{
X509Certificate2 cert = null;
if (includePrivateKey)
{
cert = new X509Certificate2(
Convert.FromBase64String(CertWithPrivateKey),
"pw", X509KeyStorageFlags.PersistKeySet);
}
else
{
cert = new X509Certificate2(
Convert.FromBase64String(CertWithoutPrivateKey));
}
return cert;
}
The code needs to be able to get the cert with the private key. The Saml2AuthenticationModule (from the WIF Extension for the SAML 2.0 Protocol) relies on this private key to decrypt information sent from a SAML Identity Provider.
I don't know much about certificates or encryption, but it seems to me that hard-coding the certificate into a class is not secure.
So, how should my code go about retrieving the cert with private key? Afaik, this code is only run one time at app startup (so probably also after an app pool recycle).
I could:
Store the cert as an appSetting in config file. As long as appSettings are config encrypted, is this secure?
Store the cert in a database.
Store the cert as a file in bin/App_Data. Afaik this means it could not be read over the web, but would be in plain view to anyone who can access the host server. Imo if someone can get into my server, letting them read this cert is probably the least of my worries.
Are there any other options? What is most appropriate in this case?

A certificate with or without private key can be save to X509 store of a user or the computer. This already has build-in Windows security that should be sufficient. You can use mmc with Certificates snap-in to add certificates to the store and manage them.
A reference to the certificate, for example, its name or thumbnail, can saved to the config file and used to retrieve the certificate. The retrieval may look like the following:
public static X509Certificate2 GetCertificate(string name)
{
try
{
X509Store store = new X509Store (StoreLocation.LocalMachine);
X509Certificate2Collection collection = store.Certificates;
foreach (X509Certificate2 x509 in collection)
{
if (x509.FriendlyName.Equals(name))
{
return x509;
}
}
}
finally
{
store.Close();
}
return null;
}
Using Find on the collection is another (and cleaner) way to search certificates.

I'm not sure how WIF does it (you could probably use Reflector to see the internals of how it interacts with the certificate store), but it sounds like you are using WIF in an application hosted in IIS. If that's the case, WIF should take care of all the certificate interactions for you. You'll just have to make sure you have the following things set up:
The identity model configuration section set up with references to the thumbprint of the certificate you are using to either encrypt or verify the digital signature of the token.
The certificate needs to be registered in IIS
The application pool's hosting identity needs to have permission to "read" the certificate to extract the private key information (see the accepted answer here)

Related

.Net Core Web API with Client Certificate Authentication

I've developed a simple WEB API service in .Net Core 2.1
I'm trying to implement a client certificate authentication, so I can give access to the APIs only to the clients that have a specific certificate installed on their machine.
The clients access the API using a browser (Chrome, Edge, IE11 or Firefox).
I've added in the API method the request for the certificate:
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
X509Certificate2 cert = Request.HttpContext.Connection.ClientCertificate;
if (cert!=null && cert.Verify())
{
//more verification here...
return Content("....", "application/json");
}
else
{
return Content("....", "application/json");
}
}
then I've installed a self-signed certificate and added to the Trusted Root, enabling the Client Authentication purpose.
but the variable cert is always null and the browser didn't even prompt me to use a certificate when I request the page.
I suppose because I have to set somewhere that the web server must ask for the client certificate as it is possible to set in IIS, but in my development environment, I'm using IIS Express.
How can I force IIS express to request a client certificate?
For proper certificate authentication using the ASP.NET Core authentication stack, you can also check out idunno.Authentication.Certificate by Barry Dorrans himself. It allows you to enable certificate authentication for your application and handles it like any other authentication scheme, so you can keep actual certificate-based logic out of your business logic.
This project sort of contains an implementation of Certificate Authentication for ASP.NET Core. Certificate authentication happens at the TLS level, long before it ever gets to ASP.NET Core, so, more accurately this is an authentication handler that validates the certificate and then gives you an event where you can resolve that certificate to a ClaimsPrincipal.
You must configure your host for certificate authentication, be it IIS, Kestrel, Azure Web Applications or whatever else you're using.
Make sure to also check out the “documentation” on how to set this up properly, since it requires configuration of the host to work properly, just like you did with IIS Express. Instructions for other servers like raw Kestrel, IIS, Azure or general reverse proxies are included.
In order to enable IIS Express to start requesting client certificates and therefore pass them to the server side, the configuration file must be edited:
The whole configuration is inside the solution folder in the .vs\config\applicationhost.config
Ensure the following values are set:
<security>
<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />
and
<iisClientCertificateMappingAuthentication enabled="true"></iisClientCertificateMappingAuthentication>
For local testing, you can enable SSL in IIS Express from Visual Studio. In the Properties window, set SSL Enabled to True. Note the value of SSL URL; use this URL for testing HTTPS connections.
For Who needs
Details here
For .NET 3.1+ there is now official package supporting this feature:
builder.Services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices
.GetRequiredService<ICertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(
ClaimTypes.NameIdentifier,
context.ClientCertificate.Subject,
ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(
ClaimTypes.Name,
context.ClientCertificate.Subject,
ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(
new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
return Task.CompletedTask;
}
};
});
There is also configuration required on the server side, see:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-6.0

Identifying https encryption type used in ASP.NET Web API DelegatingHandler

I'm currently expanding the logging part of my ASP.NET Web API application but I'm having trouble identifying what encryption scheme is being used. As this logging is done through a DelegatingHandler I only have access to the HttpRequestMessage and HttpResponseMessage given to me through the middleware chain.
How do I Identify the https encryption type (SSL 3.0, TLS 1.0, TLS 1.3 etc..) used in a ASP.NET Web API DelegatingHandler?
I'm not sure if you're asking about whether the request is https or just http. If that's what you're looking for you just need httpRequestMessage.RequestUri.Scheme.
However it sounds like you're looking for more information about the certificate itself. Information about the SSL certificate isn't really a property of the individual request and can't be accessed from HttpRequestMessage or HttpResponseMessage as far as I'm aware. It's more a property of the application and machine configuration.
If you wanted to get information about the SSL certificate you could do something like this:
var store = new X509Store(StoreLocation.LocalMachine);
var certificateCollection = store.Certificates.Find(X509FindType.FindByThumbprint, "thumbprint", true);
var certificate = certificateCollection[0];
var version = certificate.Version;
var signatureAlgorithm = certificate.SignatureAlgorithm;
Obviously that's just one way to do it and there's more error handling you would want to do. The general idea is you probably have access to the certificate thumbprint used by the application through the app.config. If you use that you can look up the certificate information from X509Store as I did above. Then you'll have access to all kinds of properties.
Hope that helps!

Sccess some folder on file share based on user authenticated

I have an asp.net web application with forms authentication and users (credentials) are checked against active directory, username is actually samAccountName attribute from AD.
Now I need to enable users to get access to some files which are located on file share, where each user has his own folder.
First proof of concept works like this:
appPool in IIS is configured to run under some domain user, and this user was given R/W access to file share and all user folders
when the user logs into web app only content of the folder on the path "\\myFileServer\username" is visible to him. And same when uploading files they get stored to "\\myFileServer\username".
While this works, doesn't seem to be secure at all. First issue is that user under which application pool runs has access to folders from all users. And even bigger concern is that only username determines to which folder you have access.
So my question is what is the correct/better way to doing this ? I was reading about impersonating the user, but this is not advised anymore if I understood correctly ? And I don't have Windows authentications since the web application must be accessible from internet.
I recommend not running the application under a user account, but creating an application specific account under which it runs with the proper R/W rights, and separate the person who gives these rights from the development team.
Within the application's authentication: after you receive a GET/POST request, you can verify the path to which the current user would read/write data, and cross-reference this with the path the user is authorized to read/write from. If these are incorrect, return a 401 NOT AUTHORIZED response, else, carry on the operation as you do now.
If your endpoints are protected properly, and the application runs under its own account, I don't see any harm in the setup itself. This still however gives the developers a way, through the application, to indirectly access other user's files. Based on how tight these checks must be, you could add additional controls, (like only allowing the application to connect from the production server, and only allowing server transport in a controlled way).
From the Description of your Problem i think Custom HttpHandlers are the right choice for you. You didn't mention what type of files will be present in your Folder , for brevity i will answer by assuming it will be having PDF files.
As you were mentioning that your application will be having different users so for this you need to use .NET built-in authentication manager and role provider. With a simple security framework setup, we'll place a PDF file in the web application, behind a web.config protected folder.then create a custom HTTP handler to restrict access on the static document to only those users who should be allowed to view it.
A sample HTTP Handler:
public class FileProtectionHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
switch (context.Request.HttpMethod)
{
case "GET":
{
// Is the user logged-in?
if (!context.User.Identity.IsAuthenticated)
{
FormsAuthentication.RedirectToLoginPage();
return;
}
string requestedFile =
context.Server.MapPath(context.Request.FilePath);
// Verify the user has access to the User role.
if (context.User.IsInRole("User"))
{
SendContentTypeAndFile(context, requestedFile);
}
else
{
// Deny access, redirect to error page or back to login
//page.
context.Response.Redirect("~/User/AccessDenied.aspx");
}
break;
}
}
}
Method SendContentTypeAndFile :
private HttpContext SendContentTypeAndFile(HttpContext context, String strFile)
{
context.Response.ContentType = GetContentType(strFile);
context.Response.TransmitFile(strFile);
context.Response.End();
return context;
}
private string GetContentType(string filename)
{
// used to set the encoding for the reponse stream
string res = null;
FileInfo fileinfo = new FileInfo(filename);
if (fileinfo.Exists)
{
switch (fileinfo.Extension.Remove(0, 1).ToLower())
{
case "pdf":
{
res = "application/pdf";
break;
}
}
return res;
}
return null;
}
Last step is that you need to configure this HTTP Handler in the webconfig ,
and You can see the more info here
Here is the complete Source Code
You're architecture (and assumptions) seem good for a low/mid security level, but if the nature of your data is very sensitive (medical, etc) my biggest concern about security would be controlling the user sessions.
If you're using forms authentication then you're storing the authenticated identity in a cookie or in a token (or if you're using sticky sessions then you're sending the session Id, but for the case it's the same). The problem arises if user B has phisical access to the machine where user A works. If user A leaves it's workplace (for a while or forever) and he doesn't explicitly close it's session in your web app, then his identity has been left around, at least until his cookie/token expires, and user B can use it since the identity system of ASP.NET hasn't performed a SignOut. The problem is even worse if you use tokens for authorization, because in all the infamous Microsoft implementations of the Identity System you're responsible of providing a way to invalidate such tokens (and make them dissapear from the client machine) when the user signs out, since they would stay valid until it's expiration. This can be addressed (but no completely thus not very satisfactorily for high security requirements) issuing short living refresh tokens, but that's another story, and I don't know if it's your case. If you're going with cookies then when user A signs out it's cookie is invalidated and removed from the request/response cicle, so this problem is mitigated. Anyway you should ensure that your users close their sessions in your web app or/and configure the cookies with short lives or short sliding expirations.
Other security concerns may be related with CSRF, wich you can prevent using the Antiforgery Token infrastructure of ASP.NET, but these kind of attacks are methods that are very far away from the tipical user (I don't know anything about the nature of your user and if your app is exposed to public on internet or it's only accesible on an intranet), but If you worry for such specialised attacks and have so sensitive data, maybe you should go with something more complex than forms authentication (two factor, biometrical, etc)

Trusting an IIS self-issued certificate

So I'm using a self-signed certificate on my localhost for HTTPS.
I'm running my Web Api 2 web service on it for testing while I develop a client website that links into the api but the client website can't establish a connection with the api because of trust issues concerning the certificate is self-signed and thus, not to be trusted.
Here's what Firefox says when I browse to the web service:
The client website is developed using MVC. So far I'm using WebClient to query data from the web service.
The code I'm currently working on to access the web server is a simple login. The MVC site sends the login data to the web service which uses it to select a record from the database. It sends the record back to the MVC site if it gets one.
Here's the code:
private bool DoLogin(string EmailAddress, string Password)
{
WebClient client = new WebClient();
NameValueCollection credentials = new NameValueCollection
{
{ "EmailAddress", EmailAddress },
{ "Password", Password }
};
client.QueryString = credentials;
string result = client.DownloadString(new Uri("https://localhost/mywebservice/api/User/"));
// just return true so we can debug to see values
return true;
}
Currently I'm only getting a non-descript WebException that just says "Internal Server Error" (Status Code 503).
Its my understanding now that I need to use a certificate that isn't self-signed, but I can't create one in IIS. I'm trying to create a domain certificate but I'm confused about the Certification Authority and from what I understand, this isn't going to help me get my web app to accept the certificate anyway.
Anyway, as an alternative, I'm looking at this MSDN blog and I've done all those things to get it right (see screenshot below) but it doesn't seem to have helped anything as I still get the untrusted connection screen in my browser.
I'm pretty much at a loss what I should do now...

X.509 Self Signed Certificates

I'm trying to understand more about X.509 Digital Certificates. There seems to be lots of contradiction around. I am using Bouncy Castle to generate a key pair, using
public static void SaveToFile(X509Certificate newCert, AsymmetricCipherKeyPair kp, string filePath, string certAlias, string password)
{
var newStore = new Pkcs12Store();
var certEntry = new X509CertificateEntry(newCert);
newStore.SetCertificateEntry(certAlias, certEntry);
newStore.SetKeyEntry(certAlias,
new AsymmetricKeyEntry(kp.Private), new[] { certEntry });
using (var certFile = File.Create(filePath))
newStore.Save(certFile, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator()));
}
This saves the generated certificate to disk. Some articles tell us there is no need to password protect the certificate as there is no PRIVATE KEY stored in there. Then this article says the certificate does indeed contain the PRIVATE KEY.
I guess I have two questions that will hopefully help me understand this:
If I generate my keys in this way, should the password be the SAME as the passphrase for the PRIVATE KEY?
Do I distribute the X.509 certificate to prove the PUBLIC KEY is mine (being paired to my name in the certificate) or should the certificate be kept as safe and secret as the PRIVATE KEY and what use is a self-signed certificate?
A PKCS#12 file can contain both the certificate and the private key. They are, however, stored as separate, distinct objects. The certificate itself has the public key embedded within it. Since the certificate only contains the public key, it is considered "public" as well. You can feel free to distribute the certificate, as it does not contain the private key, which should be kept confidential. This is the basis of the security in asymmetric cryptography.
Because a PKCS#12 file contains both items, it is encrypted with a password to protect the private key within it. That said, you would use the private key to prove that the certificate you distribute belongs to you. For example, through the use of a digital signature on a document.
Hope that helps!
Certificate is actually the block of information which binds your identity (i.e. your name, email, whatever else) to some public key. It is public so everyone can know that this key belongs to you.
So when you will sign something they will know that actually you signed this.
The other thing is validating certificate - that's for what trusted root certificates are used.
Private key is your own secret information, and MUST be kept secret.

Resources