I'm currently working on a project where I need to validate Https Requests based on known TLSA records. The steps are:
- I perform a DNS lookup and obtain all TLSA records of the domain
- I perform the Https request and use the
ServicePointManager.ServerCertificateValidationCallback
to perform the validation - On receiving the validation the certificate, I follow the DANE rfc to validate the HTTPS request:
- Based on the TLSA record's
CertificateUsage
, the correct certificate in the chain is selected.
At this step I'm having the following issue. In section 5 of the rfc it is stated that for different kind of certificate usages (i.e. PKIX-TA(0), PKIX-EE(1), DANE-TA(2) and DANE-EE(3)), the certificate chain needs to be authenticated in different ways.
From what I understand, this authentication is based on the SSLPolicyErrors
, but I'm struggling to translate the rfc's language into the SSLPolicyErrors.
I've currently implemented it as follows, based on a combination of ARSoft.Tools.Net and the rfc:
- For both PKIX values (0 and 1), any kind of policy errors will imply that the the certificate is invalid.
- For Dane-TA(2), it is stated that
With usage DANE-TA(2), the server certificates will need to have names that match one of the client's reference identifiers
. I have translated this into: the name mismatch enum value is not allowed - For Dane-EE(3), it is stated that
Authentication via certificate usage DANE-EE(3) TLSA records involves simply checking that the server's leaf certificate matches the TLSA record.
. I have translated this into: Any type of SSL policy error is allowed.
This brings me to the following code:
public IEnumerable<SystemX509Certificates.X509Certificate> GetCorrectCertificate(SystemX509Certificates.X509Certificate certificate, X509Chain certificateChain, SslPolicyErrors sslPolicyErrors)
{
switch (_tlsaRecord.CertificateUsage)
{
case 0: // PKIX-TA, the full certificate chain needs to be valid
if(sslPolicyErrors == SslPolicyErrors.None)
{
// use any certificate
return certificateChain.ChainElements.Cast<X509ChainElement>().Select(x => x.Certificate);
}
else
{
return null;
}
case 1: //PKIX-EE, the full certificate chain needs to be valid
if (sslPolicyErrors == SslPolicyErrors.None)
{
// use end entity certificate
return Enumerable.AsEnumerable(new List<SystemX509Certificates.X509Certificate> { certificate });
}
else
{
return null;
}
case 2: //DANE-TA, only name mismatch is not allowed
if ((sslPolicyErrors | SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
{
// use any certificate
return certificateChain.ChainElements.Cast<X509ChainElement>().Select(x => x.Certificate);
}
else
{
return null;
}
case 3: //DANE-EE, only compare leaf certificate with TLSA record
// use end entity certificate
return Enumerable.AsEnumerable(new List<SystemX509Certificates.X509Certificate> { certificate });
default:
return Enumerable.Empty<X509Certificate2>();
}
}
However, I have no idea whether my interpretation of the rfc, and the implementation in C#, are correct. Can anybody tell me whether I'm doing the correct SSL policy check for the different kind of TLSA certificate usages?