/// <summary>
/// Validates the cert with the provided ocsp responses.
/// </summary>
/// <param name="certificate">The cert to validate</param>
/// <param name="issuer">The issuer of the cert to validate</param>
/// <param name="validationTime">The time on which the cert was needed to validated</param>
/// <param name="ocspResponses">The list of ocsp responses to use</param>
/// <returns>The OCSP response that was used, <c>null</c> if none was found</returns>
/// <exception cref="RevocationException{T}">When the certificate was revoked on the provided time</exception>
/// <exception cref="RevocationUnknownException">When the certificate (or the OCSP) can't be validated</exception>
public static BCAO.BasicOcspResponse Verify(this X509Certificate2 certificate, X509Certificate2 issuer, DateTime validationTime, IList <BCAO.BasicOcspResponse> ocspResponses)
{
DateTime minTime = validationTime - ClockSkewness;
DateTime maxTime = validationTime + ClockSkewness;
BCX.X509Certificate certificateBC = DotNetUtilities.FromX509Certificate(certificate);
BCX.X509Certificate issuerBC = DotNetUtilities.FromX509Certificate(issuer);
ValueWithRef <BCO.SingleResp, ValueWithRef <BCO.BasicOcspResp, BCAO.BasicOcspResponse> > singleOcspRespLeaf = ocspResponses
.Select((rsp) => new ValueWithRef <BCO.BasicOcspResp, BCAO.BasicOcspResponse>(new BCO.BasicOcspResp(rsp), rsp)) //convert, but keep the original
.SelectMany((r) => r.Value.Responses.Select(sr => new ValueWithRef <BCO.SingleResp, ValueWithRef <BCO.BasicOcspResp, BCAO.BasicOcspResponse> >(sr, r))) //get the single respononses, but keep the parent
.Where((sr) => sr.Value.GetCertID().SerialNumber.Equals(certificateBC.SerialNumber) && sr.Value.GetCertID().MatchesIssuer(issuerBC)) //is it for this cert?
.Where((sr) => sr.Value.ThisUpdate >= minTime || (sr.Value.NextUpdate != null && sr.Value.NextUpdate.Value >= minTime)) //was it issued on time?
.OrderByDescending((sr) => sr.Value.ThisUpdate) //newest first
.FirstOrDefault();
if (singleOcspRespLeaf == null)
{
return(null);
}
BCO.SingleResp singleOcspResp = singleOcspRespLeaf.Value;
BCO.BasicOcspResp basicOcspResp = singleOcspRespLeaf.Reference.Value;
BCAO.BasicOcspResponse basicOcspResponse = singleOcspRespLeaf.Reference.Reference;
//get the signer name
BCAX.X509Name responderName = basicOcspResp.ResponderId.ToAsn1Object().Name;
if (responderName == null)
{
trace.TraceEvent(TraceEventType.Error, 0, "OCSP response for {0} does not have a ResponderID", certificate.Subject);
throw new RevocationUnknownException("OCSP response for {0} does not have a ResponderID");
}
//Get the signer certificate
var selector = new BCS.X509CertStoreSelector();
selector.Subject = responderName;
BCX.X509Certificate ocspSignerBc = (BCX.X509Certificate)basicOcspResp
.GetCertificates("Collection").GetMatches(selector)
.Cast <BCX.X509Certificate>().FirstOrDefault();
if (ocspSignerBc == null)
{
throw new RevocationUnknownException("The OCSP is signed by a unknown certificate");
}
//verify the response signature
if (!basicOcspResp.Verify(ocspSignerBc.GetPublicKey()))
{
throw new RevocationUnknownException("The OCSP has an invalid signature");
}
//OCSP must be issued by same issuer an the certificate that it validates.
try
{
if (!ocspSignerBc.IssuerDN.Equals(issuerBC.SubjectDN))
{
throw new ApplicationException();
}
ocspSignerBc.Verify(issuerBC.GetPublicKey());
}
catch (Exception e)
{
throw new RevocationUnknownException("The OCSP signer was not issued by the proper CA", e);
}
//verify if the OCSP signer certificate is stil valid
if (!ocspSignerBc.IsValid(basicOcspResp.ProducedAt))
{
throw new RevocationUnknownException("The OCSP signer was not valid at the time the ocsp was issued");
}
//check if the signer may issue OCSP
IList ocspSignerExtKeyUsage = ocspSignerBc.GetExtendedKeyUsage();
if (!ocspSignerExtKeyUsage.Contains("1.3.6.1.5.5.7.3.9"))
{
throw new RevocationUnknownException("The OCSP is signed by a certificate that isn't allowed to sign OCSP");
}
//finally, check if the certificate is revoked or not
var revokedStatus = (BCO.RevokedStatus)singleOcspResp.GetCertStatus();
if (revokedStatus != null)
{
trace.TraceEvent(TraceEventType.Verbose, 0, "OCSP response for {0} indicates that the certificate is revoked on {1}", certificate.Subject, revokedStatus.RevocationTime);
if (maxTime >= revokedStatus.RevocationTime)
{
throw new RevocationException <BCAO.BasicOcspResponse>(basicOcspResponse, "The certificate was revoked on " + revokedStatus.RevocationTime.ToString("o"));
}
}
return(basicOcspResponse);
}