|
| StreamSec Tools 4.1.1.349 - 4.1.3.357 - manual fix P01 An OCSP response is accepted from a responder the CA never authorized. | |
| Henrick Wibell Hellström 2026-09-25 18:32:32 Registered user |
WHAT GOES WRONG
A CA can let another certificate sign OCSP responses on its behalf: a delegated responder. RFC 6960 section 4.2.2.2 allows that only for a certificate the CA issued that INCLUDES id-kp-OCSPSigning in an extendedKeyUsage extension. TstAbstractOcsp.VerifyResponseSignature tests the usage with iCertificate.CheckExtKeyUsage, which answers True for a certificate that has no extendedKeyUsage extension at all. So any certificate the CA issued without one is accepted as a responder for that CA, and whoever holds its key can sign OCSP responses the library accepts: "good" for a certificate that has been revoked, or "revoked" for one that has not. Public TLS certificates carry an extendedKeyUsage extension; many certificates from private CAs do not. The responder certificate's validity period is not checked either, so an expired delegate is accepted too. The response, and the certificates in it, come from whoever sends it: the responder, anyone who can intercept the plain-HTTP request to it, or, for a response stapled to a TLS handshake, the server itself. From 4.1.1.350 the TLS client checks a stapled response first, and one that verifies as "good" ends the revocation check before a live query is made or a CRL is consulted. WHAT THE FIX DOES A delegated responder is accepted only when its certificate was issued by the CA, carries an extendedKeyUsage extension that names id-kp-OCSPSigning, and is within its validity period. A response signed by the CA itself is accepted as before. Where RequireSignatureCheck has been set to False, no response signature is required at all, with or without the fix. WHO NEEDS IT Applications that check certificate revocation with OCSP: a TsmOcsp assigned to the Ocsp property of the TLS components, or any other use of TsmOcsp, TstOcsp or another TstAbstractOcsp descendant. OCSP is used only when an application sets it up; without it this code does not run. APPLIES TO StreamSec Tools 4.1.1.349 through 4.1.3.357, in the unit StreamSec.DSI.AbstractOcsp.pas. The function is the same in all of them. 4.1.3.358 and later carry the fix. HOW TO APPLY 1. Open StreamSec.DSI.AbstractOcsp.pas. 2. Find the implementation of function TstAbstractOcsp.VerifyResponseSignature and replace it, from "function" to its final "end;", with the function below. 3. Rebuild the packages, and every application that uses OCSP. The fix is pure Pascal; no assembler is involved. -- replacement ----------------------------------------------------------------- function TstAbstractOcsp.VerifyResponseSignature(const aBasic: iBasicOCSPResponse; const aIssuer: iCertificate): Boolean; var lRID: iResponderID; lSigner: iCertificate; lCand: iCertificate; lMPKey: iMPPublicKey; i: Integer; function Matches(const aCert: iCertificate): Boolean; begin Result := False; if not Assigned(aCert) then Exit; case lRID.Choice of ridByName: Result := aCert.tbsCertificate.subject.GetStruct.Compare(lRID.AsByName.GetStruct); ridByKey: if Supports(aCert.subjectPublicKeyInfo,iMPPublicKey,lMPKey) then Result := lMPKey.PublicKeyIdentifier = lRID.AsByKey; end; end; // RFC 6960 4.2.2.2: a delegated responder's certificate must INCLUDE // id-kp-OCSPSigning in an extendedKeyUsage extension. CheckExtKeyUsage alone // answers True for a certificate with no such extension at all, so the // extension's presence is tested first. function AuthorizedToSignOCSP(const aCert: iCertificate): Boolean; begin Result := Assigned(aCert.tbsCertificate.extensions.FindExtension(evCeExtKeyUsage)) and aCert.CheckExtKeyUsage(id_kp_OCSPSigning); end; begin Result := False; lRID := aBasic.tbsResponseData.responderID; if not Assigned(lRID) then Exit; // Identify the signer certificate. The issuer itself is always a candidate; // additional candidates are carried inside the response (a delegated, CA // authorized OCSP responder). lSigner := nil; if Matches(aIssuer) then lSigner := aIssuer else for i := 0 to aBasic.certificateCount - 1 do begin lCand := aBasic.certificates[i]; if Matches(lCand) then begin // A delegated responder must be issued by the same CA and carry the // id-kp-OCSPSigning extended key usage (RFC 6960 4.2.2.2). if lCand.CheckSignature(aIssuer) and AuthorizedToSignOCSP(lCand) then begin lSigner := lCand; Break; end; end; end; if not Assigned(lSigner) then begin FLastError := 'OCSP response signer is not authorized by the certificate issuer'; Exit; end; // Defend against a self referential keyHash claim: a delegated signer must // really be issued by the CA, hold the OCSP signing usage, and be within its // validity period. if lSigner <> aIssuer then begin if not (lSigner.CheckSignature(aIssuer) and AuthorizedToSignOCSP(lSigner)) then begin FLastError := 'OCSP responder certificate is not delegated by the issuer'; Exit; end; if not lSigner.CheckValidity then begin FLastError := 'OCSP responder certificate is outside its validity period'; Exit; end; end; Result := aBasic.CheckSignature(lSigner); if not Result then FLastError := 'OCSP response signature verification failed'; end; -------------------------------------------------------------------------------- TESTED A test builds OCSP responses for one certificate, signed by its CA, by a delegate carrying id-kp-OCSPSigning, by an expired such delegate, by a certificate the CA issued with no extendedKeyUsage, by one it issued for serverAuth only, and by a certificate of another CA. Unpatched, 4.1.1.349 and 4.1.3.357 accept the responses signed by the certificate without extendedKeyUsage and by the expired delegate - 4.1.3.357 also as a stapled TLS response, "good" and "revoked" alike. Patched, both refuse them and still accept the responses of the CA and of the proper delegate, on Win32 and Win64. |