Reputation: 8850
I have ASP.NET MVC web site which I configured to authenticate through Active Directory Federation Service. Everything worked fine until I tried to enable token encryption. As usual, I created one more self-signed certificate on IIS, added it to Trusted Root authorities on my web server and ADFS server and run application to veryfy how it works.
My application correctly redirected me to ADFS service page to enter credentials. But when I submit my login and password, I immediately get "An error occured
" message on the same login page with not very useful details section:
Activity ID: 00000000-0000-0000-b039-0080010000e4
Relying party: [My relying party name]
Error time: Fri, 21 Oct 2016 18:48:24 GMT
Cookie: enabled
User agent string: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36
I don't get redirected to my web site after that and Network panel doesn't contain any requests.
But I discovered, that if I add the following setting into my web site's web.config, it starts working again:
<certificateValidation certificateValidationMode="None" />
So the error must be related to the fact that my certificate is self-signed. But I have added it to trusted root authorities both on web server and ADFS server (as well as few other "suspicious" certificates).
Does anybody have an idea what could be missing and what can I do to make my test environment work with self-signed certificates, while validating certificate chain?
Upvotes: 0
Views: 2342
Reputation: 8850
It appeared that to resolve an error it was enough to add ADFS Token Signing certificate as Trusted Root Certification Authority on my web server.
PS: I'm not sure why token signing certificate chain validation didn't raise errors when encryption was disabled and what relation does it have to encryption at all, but the fact is that it helped for both environments we've used for testing.
Upvotes: 1
Reputation: 1628
Adding certificates to your CA trusted store only mean you trust the issuer of the certificate, which is the certificate itself in this case because it is a self-signed certificate. What is missing is that certificate validation performs chain-check and revocation check and either one of the two check failed for you. Please note that even if you trust a certificate, it still could have been revoked recently and thus shouldn't no longer be trusted. Therefore, revocation check is always necessary. For testing, disabling revocation check is one way. One ADFS side, you can disable revocation check per relying party. If the check happens on your own code, you can either disable check totally or use Stinky Towel's code to selectively allow some certificates only.
Upvotes: 0
Reputation: 778
I do something similar with an api handler that acts as a pass through and must interrogate certs.
Something that may help your troubleshooting.
Set the cert validation callback to something like:
// validate server cert
ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate;
Then in the validation method you can interrogate the chain:
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// default validation bool to false
var isValid = false;
// If the certificate is a valid, signed certificate, return true to short circuit any add'l processing.
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}
else
{
// cast cert as v2 in order to expose thumbprint prop
var requestCertificate = (X509Certificate2)certificate;
// init string builder for creating a long log entry
var logEntry = new StringBuilder();
// capture initial info for the log entry
logEntry.AppendFormat("Certificate Validation Error - SSL Policy Error: {0} - Cert Issuer: {1} - SubjectName: {2}",
sslPolicyErrors.ToString(),
requestCertificate.Issuer,
requestCertificate.SubjectName.Name);
//init special builder for thumprint mismatches
var thumbprintMismatches = new StringBuilder();
// load valid certificate thumbs for comparison later
var validThumbprints = new string[] { "thumbprint A", "thumbprint N" };
// else if a cert name mismatch then assume api pass thru issue and verify thumb print
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch)
{
// compare thumbprints
var hasMatch = validThumbprints.Contains(requestCertificate.Thumbprint, StringComparer.OrdinalIgnoreCase);
// if match found then we're valid so clear builder and set global valid bool to true
if (hasMatch)
{
thumbprintMismatches.Clear();
isValid = true;
}
// else thumbprint did not match so append to the builder
else
{
thumbprintMismatches.AppendFormat("|Thumbprint mismatch - Issuer: {0} - SubjectName: {1} - Thumbprint: {2}",
requestCertificate.Issuer,
requestCertificate.SubjectName.Name,
requestCertificate.Thumbprint);
}
}
// else if chain issue, then iterate over the chain and attempt find a matching thumbprint
else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) //Root CA problem
{
// check chain status and log
if (chain != null && chain.ChainStatus != null)
{
// check errors in chain and add to log entry
foreach (var chainStatus in chain.ChainStatus)
{
logEntry.AppendFormat("|Chain Status: {0} - {1}", chainStatus.Status.ToString(), chainStatus.StatusInformation.Trim());
}
// check for thumbprint mismatches
foreach (var chainElement in chain.ChainElements)
{
// compare thumbprints
var hasMatch = validThumbprints.Contains(chainElement.Certificate.Thumbprint, StringComparer.OrdinalIgnoreCase);
// if match found then we're valid so break, clear builder and set global valid bool to true
if (hasMatch)
{
thumbprintMismatches.Clear();
isValid = true;
break;
}
// else thumbprint did not match so append to the builder
else
{
thumbprintMismatches.AppendFormat("|Thumbprint mismatch - Issuer: {0} - SubjectName: {1} - Thumbprint: {2}",
chainElement.Certificate.Issuer,
chainElement.Certificate.SubjectName.Name,
chainElement.Certificate.Thumbprint);
}
}
}
}
// if still invalid and thumbprint builder has items, then continue
if (!isValid && thumbprintMismatches != null && thumbprintMismatches.Length > 0)
{
// append thumbprint entries to the logentry as well
logEntry.Append(thumbprintMismatches.ToString());
}
// log as WARN here and not ERROR - if method ends up returning false then it will bubble up and get logged as an ERROR
LogHelper.Instance.Warning((int)ErrorCode.CertificateValidation, logEntry.ToString().Trim());
}
// determine env
var isDev = EnvironmentHelper.IsDevelopment();
var isTest = EnvironmentHelper.IsTest();
// if env is dev or test then ignore cert errors and return true (reference any log entries created from logic above for troubleshooting)
if (isDev || isTest)
isValid = true;
return isValid;
}
NOTE: you'll need to disable/change some of the custom code - thumbprint stuff, logging etc.
Upvotes: 0