2 * Copyright (c) 2008-2009 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecOCSPResponse.c - Wrapper to decode ocsp responses.
28 #include <securityd/SecOCSPResponse.h>
29 #include <Security/SecCertificateInternal.h>
30 #include <Security/SecFramework.h>
31 #include <Security/SecKeyPriv.h>
32 #include <AssertMacros.h>
33 #include <security_utilities/debugging.h>
34 #include <security_asn1/SecAsn1Coder.h>
35 #include <security_asn1/ocspTemplates.h>
36 #include <security_asn1/oidsalg.h>
37 #include <security_asn1/oidsocsp.h>
38 #include <CommonCrypto/CommonDigest.h>
41 #include "SecInternal.h"
43 #define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
44 #define ocspdHttpDebug(args...) secdebug("ocspdHttp", ## args)
45 #define ocspdDebug(args...) secdebug("ocsp", ## args)
49 OCSPResponse ::= SEQUENCE {
50 responseStatus OCSPResponseStatus,
51 responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
53 OCSPResponseStatus ::= ENUMERATED {
54 successful (0), --Response has valid confirmations
55 malformedRequest (1), --Illegal confirmation request
56 internalError (2), --Internal error in issuer
57 tryLater (3), --Try again later
59 sigRequired (5), --Must sign the request
60 unauthorized (6) --Request unauthorized
63 ResponseBytes ::= SEQUENCE {
64 responseType OBJECT IDENTIFIER,
65 response OCTET STRING }
67 id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
68 id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
70 The value for response SHALL be the DER encoding of
73 BasicOCSPResponse ::= SEQUENCE {
74 tbsResponseData ResponseData,
75 signatureAlgorithm AlgorithmIdentifier,
77 certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
79 The value for signature SHALL be computed on the hash of the DER
80 encoding ResponseData.
82 ResponseData ::= SEQUENCE {
83 version [0] EXPLICIT Version DEFAULT v1,
84 responderID ResponderID,
85 producedAt GeneralizedTime,
86 responses SEQUENCE OF SingleResponse,
87 responseExtensions [1] EXPLICIT Extensions OPTIONAL }
89 ResponderID ::= CHOICE {
93 KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
94 (excluding the tag and length fields)
96 SingleResponse ::= SEQUENCE {
98 certStatus CertStatus,
99 thisUpdate GeneralizedTime,
100 nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
101 singleExtensions [1] EXPLICIT Extensions OPTIONAL }
103 CertStatus ::= CHOICE {
104 good [0] IMPLICIT NULL,
105 revoked [1] IMPLICIT RevokedInfo,
106 unknown [2] IMPLICIT UnknownInfo }
108 RevokedInfo ::= SEQUENCE {
109 revocationTime GeneralizedTime,
110 revocationReason [0] EXPLICIT CRLReason OPTIONAL }
112 UnknownInfo ::= NULL -- this can be replaced with an enumeration
115 static CFAbsoluteTime
genTimeToCFAbsTime(const SecAsn1Item
*datetime
)
117 return SecAbsoluteTimeFromDateContent(SEC_ASN1_GENERALIZED_TIME
,
118 datetime
->Data
, datetime
->Length
);
121 void SecOCSPSingleResponseDestroy(SecOCSPSingleResponseRef
this) {
125 static SecOCSPSingleResponseRef
SecOCSPSingleResponseCreate(
126 SecAsn1OCSPSingleResponse
*resp
, SecAsn1CoderRef coder
) {
127 assert(resp
!= NULL
);
128 SecOCSPSingleResponseRef
this;
129 require(this = (SecOCSPSingleResponseRef
)
130 malloc(sizeof(struct __SecOCSPSingleResponse
)), errOut
);
131 this->certStatus
= CS_NotParsed
;
132 this->thisUpdate
= NULL_TIME
;
133 this->nextUpdate
= NULL_TIME
;
134 this->revokedTime
= NULL_TIME
;
135 this->crlReason
= kSecRevocationReasonUndetermined
;
136 //this->extensions = NULL;
138 if ((resp
->certStatus
.Data
== NULL
) || (resp
->certStatus
.Length
== 0)) {
139 ocspdErrorLog("OCSPSingleResponse: bad certStatus");
142 this->certStatus
= (SecAsn1OCSPCertStatusTag
)(resp
->certStatus
.Data
[0] & SEC_ASN1_TAGNUM_MASK
);
143 if (this->certStatus
== CS_Revoked
) {
144 /* Decode further to get SecAsn1OCSPRevokedInfo */
145 SecAsn1OCSPCertStatus certStatus
;
146 memset(&certStatus
, 0, sizeof(certStatus
));
147 if (SecAsn1DecodeData(coder
, &resp
->certStatus
,
148 kSecAsn1OCSPCertStatusRevokedTemplate
, &certStatus
)) {
149 ocspdErrorLog("OCSPSingleResponse: err decoding certStatus");
152 SecAsn1OCSPRevokedInfo
*revokedInfo
= certStatus
.revokedInfo
;
153 if (revokedInfo
!= NULL
) {
154 /* Treat this as optional even for CS_Revoked */
155 this->revokedTime
= genTimeToCFAbsTime(&revokedInfo
->revocationTime
);
156 const SecAsn1Item
*revReason
= revokedInfo
->revocationReason
;
157 if((revReason
!= NULL
) &&
158 (revReason
->Data
!= NULL
) &&
159 (revReason
->Length
!= 0)) {
160 this->crlReason
= revReason
->Data
[0];
164 this->thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
165 if (resp
->nextUpdate
!= NULL
) {
166 this->nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
168 //mExtensions = new OCSPExtensions(resp->singleExtensions);
169 ocspdDebug("status %d reason %d", (int)this->certStatus
,
170 (int)this->crlReason
);
174 SecOCSPSingleResponseDestroy(this);
178 /* Calculate temporal validity; set latestNextUpdate and expireTime. Only
179 called from SecOCSPResponseCreate. Returns true if valid, else returns
181 static bool SecOCSPResponseCalculateValidity(SecOCSPResponseRef
this,
182 CFTimeInterval maxAge
, CFTimeInterval defaultTTL
)
184 this->latestNextUpdate
= NULL_TIME
;
185 CFAbsoluteTime now
= this->verifyTime
= CFAbsoluteTimeGetCurrent();
187 if (this->producedAt
> now
) {
188 ocspdErrorLog("OCSPResponse: producedAt later than current time");
192 /* Make this->latestNextUpdate be the date farthest in the future
193 of any of the singleResponses nextUpdate fields. */
194 SecAsn1OCSPSingleResponse
**responses
;
195 for (responses
= this->responseData
.responses
; *responses
; ++responses
) {
196 SecAsn1OCSPSingleResponse
*resp
= *responses
;
198 /* thisUpdate later than 'now' invalidates the whole response. */
199 CFAbsoluteTime thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
200 if (thisUpdate
> now
) {
201 ocspdErrorLog("OCSPResponse: thisUpdate later than current time");
205 /* Keep track of latest nextUpdate. */
206 if (resp
->nextUpdate
!= NULL
) {
207 CFAbsoluteTime nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
208 if (nextUpdate
> this->latestNextUpdate
) {
209 this->latestNextUpdate
= nextUpdate
;
212 #ifdef STRICT_RFC5019
214 /* RFC 5019 section 2.2.4 states on nextUpdate:
215 Responders MUST always include this value to aid in
216 response caching. See Section 6 for additional
217 information on caching.
219 ocspdErrorLog("OCSPResponse: nextUpdate not present");
225 /* Now that we have this->latestNextUpdate, we figure out the latest
226 date at which we will expire this response from our cache. To comply
229 6.1. Caching at the Client
231 To minimize bandwidth usage, clients MUST locally cache authoritative
232 OCSP responses (i.e., a response with a signature that has been
233 successfully validated and that indicate an OCSPResponseStatus of
236 Most OCSP clients will send OCSPRequests at or near the nextUpdate
237 time (when a cached response expires). To avoid large spikes in
238 responder load that might occur when many clients refresh cached
239 responses for a popular certificate, responders MAY indicate when the
240 client should fetch an updated OCSP response by using the cache-
241 control:max-age directive. Clients SHOULD fetch the updated OCSP
242 Response on or after the max-age time. To ensure that clients
243 receive an updated OCSP response, OCSP responders MUST refresh the
244 OCSP response before the max-age time.
248 we need to take the cache-control:max-age directive into account.
250 The way the code below is written we ignore a max-age=0 in the
251 http header. Since a value of 0 (NULL_TIME) also means there
252 was no max-age in the header. This seems ok since that would imply
253 no-cache so we also ignore negative values for the same reason,
254 instead we'll expire whenever this->latestNextUpdate tells us to,
255 which is the signed value if max-age is too low, since we don't
256 want to refetch multilple times for a single page load in a browser. */
257 if (this->latestNextUpdate
== NULL_TIME
) {
258 /* See comment above on RFC 5019 section 2.2.4. */
259 /* Absolute expire time = current time plus defaultTTL */
260 this->expireTime
= now
+ defaultTTL
;
261 } else if (this->latestNextUpdate
< now
) {
262 ocspdErrorLog("OCSPResponse: now > latestNextUpdate");
264 } else if (maxAge
> 0) {
265 /* Beware of double overflows such as:
267 now + maxAge < this->latestNextUpdate
269 in the math below since an attacker could create any positive
271 if (maxAge
< this->latestNextUpdate
- now
) {
272 /* maxAge header wants us to expire the cache entry sooner than
273 nextUpdate would allow, to balance server load. */
274 this->expireTime
= now
+ maxAge
;
276 /* maxAge http header attempting to make us cache the response
277 longer than it's valid for, bad http header! Ignoring you. */
278 ocspdErrorLog("OCSPResponse: now + maxAge > latestNextUpdate,"
279 " using latestNextUpdate");
280 this->expireTime
= this->latestNextUpdate
;
283 /* No maxAge provided, just use latestNextUpdate. */
284 this->expireTime
= this->latestNextUpdate
;
290 SecOCSPResponseRef
SecOCSPResponseCreate(CFDataRef ocspResponse
,
291 CFTimeInterval maxAge
) {
292 SecAsn1OCSPResponse topResp
= {};
293 SecOCSPResponseRef
this;
295 require(this = (SecOCSPResponseRef
)calloc(1, sizeof(struct __SecOCSPResponse
)),
297 require_noerr(SecAsn1CoderCreate(&this->coder
), errOut
);
299 this->data
= ocspResponse
;
300 CFRetain(ocspResponse
);
303 resp
.Length
= CFDataGetLength(ocspResponse
);
304 resp
.Data
= (uint8_t *)CFDataGetBytePtr(ocspResponse
);
305 if (SecAsn1DecodeData(this->coder
, &resp
, kSecAsn1OCSPResponseTemplate
,
307 ocspdErrorLog("OCSPResponse: decode failure at top level");
309 /* remainder is valid only on RS_Success */
310 if ((topResp
.responseStatus
.Data
== NULL
) ||
311 (topResp
.responseStatus
.Length
== 0)) {
312 ocspdErrorLog("OCSPResponse: no responseStatus");
315 this->responseStatus
= topResp
.responseStatus
.Data
[0];
316 if (this->responseStatus
!= kSecOCSPSuccess
) {
317 secdebug("ocsp", "OCSPResponse: status: %d", this->responseStatus
);
318 /* not a failure of our constructor; this object is now useful, but
319 * only for this one byte of status info */
322 if (topResp
.responseBytes
== NULL
) {
323 /* I don't see how this can be legal on RS_Success */
324 ocspdErrorLog("OCSPResponse: empty responseBytes");
327 if (!SecAsn1OidCompare(&topResp
.responseBytes
->responseType
,
328 &OID_PKIX_OCSP_BASIC
)) {
329 ocspdErrorLog("OCSPResponse: unknown responseType");
334 /* decode the SecAsn1OCSPBasicResponse */
335 if (SecAsn1DecodeData(this->coder
, &topResp
.responseBytes
->response
,
336 kSecAsn1OCSPBasicResponseTemplate
, &this->basicResponse
)) {
337 ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse");
341 /* signature and cert evaluation done externally */
343 /* decode the SecAsn1OCSPResponseData */
344 if (SecAsn1DecodeData(this->coder
, &this->basicResponse
.tbsResponseData
,
345 kSecAsn1OCSPResponseDataTemplate
, &this->responseData
)) {
346 ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData");
349 this->producedAt
= genTimeToCFAbsTime(&this->responseData
.producedAt
);
350 if (this->producedAt
== NULL_TIME
) {
351 ocspdErrorLog("OCSPResponse: bad producedAt");
355 if (this->responseData
.responderID
.Data
== NULL
) {
356 ocspdErrorLog("OCSPResponse: bad responderID");
360 /* Choice processing for ResponderID */
361 this->responderIdTag
= (SecAsn1OCSPResponderIDTag
)
362 (this->responseData
.responderID
.Data
[0] & SEC_ASN1_TAGNUM_MASK
);
363 const SecAsn1Template
*templ
;
364 switch(this->responderIdTag
) {
366 /* @@@ Since we don't use the decoded byName value we could skip
367 decoding it but we do it anyway for validation. */
368 templ
= kSecAsn1OCSPResponderIDAsNameTemplate
;
371 templ
= kSecAsn1OCSPResponderIDAsKeyTemplate
;
374 ocspdErrorLog("OCSPResponse: bad responderID tag");
377 if (SecAsn1DecodeData(this->coder
, &this->responseData
.responderID
, templ
,
378 &this->responderID
)) {
379 ocspdErrorLog("OCSPResponse: decode failure at responderID");
383 /* We should probably get the defaultTTL from the policy.
384 For now defaultTTL is hardcoded to 24 hours. */
385 CFTimeInterval defaultTTL
= 24 * 60 * 60;
386 /* Check temporal validity, default TTL 24 hours. */
387 require_quiet(SecOCSPResponseCalculateValidity(this, maxAge
, defaultTTL
), errOut
);
390 /* Individual responses looked into when we're asked for a specific one
391 via SecOCSPResponseCopySingleResponse(). */
392 mExtensions
= new OCSPExtensions(mResponseData
.responseExtensions
);
398 SecOCSPResponseFinalize(this);
403 CFDataRef
SecOCSPResponseGetData(SecOCSPResponseRef
this) {
407 SecOCSPResponseStatus
SecOCSPGetResponseStatus(SecOCSPResponseRef
this) {
408 return this->responseStatus
;
411 CFAbsoluteTime
SecOCSPResponseGetExpirationTime(SecOCSPResponseRef
this) {
412 return this->expireTime
;
415 CFDataRef
SecOCSPResponseGetNonce(SecOCSPResponseRef
this) {
419 CFAbsoluteTime
SecOCSPResponseProducedAt(SecOCSPResponseRef
this) {
420 return this->producedAt
;
423 CFAbsoluteTime
SecOCSPResponseVerifyTime(SecOCSPResponseRef
this) {
424 return this->verifyTime
;
427 CFArrayRef
SecOCSPResponseCopySigners(SecOCSPResponseRef
this) {
431 void SecOCSPResponseFinalize(SecOCSPResponseRef
this) {
432 CFReleaseSafe(this->data
);
433 SecAsn1CoderRelease(this->coder
);
437 SecOCSPSingleResponseRef
SecOCSPResponseCopySingleResponse(
438 SecOCSPResponseRef
this, SecOCSPRequestRef request
) {
439 SecOCSPSingleResponseRef sr
= NULL
;
441 CFDataRef issuer
= SecCertificateCopyIssuerSequence(request
->certificate
);
442 const DERItem
*publicKey
= SecCertificateGetPublicKeyData(request
->issuer
);
443 CFDataRef serial
= SecCertificateCopySerialNumber(request
->certificate
);
444 CFDataRef issuerNameHash
= NULL
;
445 CFDataRef issuerPubKeyHash
= NULL
;
446 SecAsn1Oid
*algorithm
= NULL
;
447 SecAsn1Item
*parameters
= NULL
;
449 /* We should probably get the defaultTTL from the policy.
450 For now defaultTTL is hardcoded to 24 hours. This is how long we trust
451 a response without a nextUpdate field. */
452 CFTimeInterval defaultTTL
= 24 * 60 * 60;
454 SecAsn1OCSPSingleResponse
**responses
;
455 for (responses
= this->responseData
.responses
; *responses
; ++responses
) {
456 SecAsn1OCSPSingleResponse
*resp
= *responses
;
457 SecAsn1OCSPCertID
*certId
= &resp
->certID
;
458 /* First check the easy part, serial number should match. */
459 if (certId
->serialNumber
.Length
!= (size_t)CFDataGetLength(serial
) ||
460 memcmp(CFDataGetBytePtr(serial
), certId
->serialNumber
.Data
,
461 certId
->serialNumber
.Length
)) {
462 /* Serial # mismatch, skip this singleResponse. */
466 /* Calcluate the issuerKey and issuerName digests using the
467 hashAlgorithm and parameters specified in the certId, if
468 they differ from the ones we already computed. */
469 if (!SecAsn1OidCompare(algorithm
, &certId
->algId
.algorithm
) ||
470 !SecAsn1OidCompare(parameters
, &certId
->algId
.parameters
)) {
471 algorithm
= &certId
->algId
.algorithm
;
472 parameters
= &certId
->algId
.parameters
;
473 CFReleaseSafe(issuerNameHash
);
474 CFReleaseSafe(issuerPubKeyHash
);
475 issuerNameHash
= SecDigestCreate(kCFAllocatorDefault
, algorithm
,
476 parameters
, CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
477 issuerPubKeyHash
= SecDigestCreate(kCFAllocatorDefault
, algorithm
,
478 parameters
, publicKey
->data
, publicKey
->length
);
481 if (certId
->issuerNameHash
.Length
== (size_t)CFDataGetLength(issuerNameHash
)
482 && !memcmp(CFDataGetBytePtr(issuerNameHash
),
483 certId
->issuerNameHash
.Data
, certId
->issuerNameHash
.Length
)
484 && certId
->issuerPubKeyHash
.Length
== (size_t)CFDataGetLength(issuerPubKeyHash
)
485 && !memcmp(CFDataGetBytePtr(issuerPubKeyHash
),
486 certId
->issuerPubKeyHash
.Data
, certId
->issuerPubKeyHash
.Length
)) {
488 CFAbsoluteTime thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
489 if (thisUpdate
> this->verifyTime
) {
490 ocspdErrorLog("OCSPSingleResponse: thisUpdate > now");
494 if (resp
->nextUpdate
== NULL
) {
495 /* rfc2560 section 2.4 states: "If nextUpdate is not set, the
496 responder is indicating that newer revocation information
497 is available all the time".
498 Let's ensure that thisUpdate isn't more than defaultTTL in
500 if (this->verifyTime
> thisUpdate
+ defaultTTL
) {
501 ocspdErrorLog("OCSPSingleResponse: no nextUpdate present "
502 "and now > thisUpdate + defaultTTL");
506 CFAbsoluteTime nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
507 if (this->verifyTime
> nextUpdate
) {
508 ocspdErrorLog("OCSPSingleResponse: now > nextUpdate");
513 /* resp matches the certificate in request, so let's use it. */
514 sr
= SecOCSPSingleResponseCreate(resp
, this->coder
);
516 ocspdDebug("found matching singleResponse");
522 CFReleaseSafe(issuerPubKeyHash
);
523 CFReleaseSafe(issuerNameHash
);
524 CFReleaseSafe(serial
);
525 CFReleaseSafe(issuer
);
528 ocspdDebug("certID not found");
534 static bool SecOCSPResponseVerifySignature(SecOCSPResponseRef
this,
536 /* Beware this->basicResponse.sig: on decode, length is in BITS */
537 return SecKeyDigestAndVerify(key
, &this->basicResponse
.algId
,
538 this->basicResponse
.tbsResponseData
.Data
,
539 this->basicResponse
.tbsResponseData
.Length
,
540 this->basicResponse
.sig
.Data
,
541 this->basicResponse
.sig
.Length
/ 8) == noErr
;
544 static bool SecOCSPResponseIsIssuer(SecOCSPResponseRef
this,
545 SecCertificatePathRef issuer
) {
546 bool shouldBeSigner
= false;
547 SecCertificateRef signer
= SecCertificatePathGetCertificateAtIndex(issuer
, 0);
548 if (this->responderIdTag
== RIT_Name
) {
549 /* Name inside response must == signer's SubjectName. */
550 CFDataRef subject
= SecCertificateCopySubjectSequence(signer
);
552 ocspdDebug("error on SecCertificateCopySubjectSequence");
555 if ((size_t)CFDataGetLength(subject
) == this->responderID
.byName
.Length
&&
556 !memcmp(this->responderID
.byName
.Data
, CFDataGetBytePtr(subject
),
557 this->responderID
.byName
.Length
)) {
558 ocspdDebug("good ResponderID.byName");
559 shouldBeSigner
= true;
561 ocspdDebug("BAD ResponderID.byName");
564 } else /* if (this->responderIdTag == RIT_Key) */ {
565 /* ResponderID.byKey must == SHA1(signer's public key) */
566 CFDataRef pubKeyDigest
= SecCertificateCopyPublicKeySHA1Digest(signer
);
567 if ((size_t)CFDataGetLength(pubKeyDigest
) == this->responderID
.byKey
.Length
&&
568 !memcmp(this->responderID
.byKey
.Data
, CFDataGetBytePtr(pubKeyDigest
),
569 this->responderID
.byKey
.Length
)) {
570 ocspdDebug("good ResponderID.byKey");
571 shouldBeSigner
= true;
573 ocspdDebug("BAD ResponderID.byKey");
575 CFRelease(pubKeyDigest
);
578 if (shouldBeSigner
) {
579 SecKeyRef key
= SecCertificatePathCopyPublicKeyAtIndex(issuer
, 0);
581 shouldBeSigner
= SecOCSPResponseVerifySignature(this, key
);
582 ocspdDebug("ocsp response signature %sok", shouldBeSigner
? "" : "not ");
585 ocspdDebug("Failed to extract key from leaf certificate");
586 shouldBeSigner
= false;
590 return shouldBeSigner
;
593 /* Returns the SecCertificatePathRef who's leaf signed this ocspResponse if
594 we can find one and NULL if we can't find a valid signer. */
595 SecCertificatePathRef
SecOCSPResponseCopySigner(SecOCSPResponseRef
this,
596 SecCertificatePathRef issuer
) {
597 SecCertificateRef issuerCert
= SecCertificatePathGetCertificateAtIndex(issuer
, 0);
598 CFDataRef issuerSubject
= SecCertificateGetNormalizedSubjectContent(issuerCert
);
599 /* Look though any certs that came with the response and see if they were
600 both issued by the issuerPath and signed the response. */
602 for (certs
= this->basicResponse
.certs
; certs
&& *certs
; ++certs
) {
603 SecCertificateRef cert
= SecCertificateCreateWithBytes(
604 kCFAllocatorDefault
, (*certs
)->Data
, (*certs
)->Length
);
606 CFDataRef certIssuer
= SecCertificateGetNormalizedIssuerContent(cert
);
607 if (CFEqual(issuerSubject
, certIssuer
)) {
608 SecCertificatePathRef signer
= SecCertificatePathCopyAddingLeaf(issuer
, cert
);
611 if (SecOCSPResponseIsIssuer(this, signer
)) {
614 ocspdErrorLog("ocsp response cert not signed by issuer.");
619 ocspdErrorLog("ocsp response cert issuer doesn't match issuer subject.");
622 ocspdErrorLog("ocsp response cert failed to parse");
626 /* If none of the returned certs work, try the issuer of the certificate
627 being checked directly. */
628 if (SecOCSPResponseIsIssuer(this, issuer
)) {
633 /* We couldn't find who signed this ocspResponse, give up. */