2 * Copyright (c) 2008-2009,2012-2014 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>
31 #include <AssertMacros.h>
32 #include <CommonCrypto/CommonDigest.h>
33 #include <Security/SecCertificateInternal.h>
34 #include <Security/SecFramework.h>
35 #include <Security/SecKeyPriv.h>
36 #include <security_asn1/SecAsn1Coder.h>
37 #include <security_asn1/ocspTemplates.h>
38 #include <security_asn1/oidsalg.h>
39 #include <security_asn1/oidsocsp.h>
41 #include "SecInternal.h"
42 #include <utilities/SecCFWrappers.h>
44 #define ocspdErrorLog(args, ...) secerror(args, ## __VA_ARGS__)
45 #define ocspdHttpDebug(args...) secdebug("ocspdHttp", ## args)
46 #define ocspdDebug(args...) secdebug("ocsp", ## args)
50 OCSPResponse ::= SEQUENCE {
51 responseStatus OCSPResponseStatus,
52 responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
54 OCSPResponseStatus ::= ENUMERATED {
55 successful (0), --Response has valid confirmations
56 malformedRequest (1), --Illegal confirmation request
57 internalError (2), --Internal error in issuer
58 tryLater (3), --Try again later
60 sigRequired (5), --Must sign the request
61 unauthorized (6) --Request unauthorized
64 ResponseBytes ::= SEQUENCE {
65 responseType OBJECT IDENTIFIER,
66 response OCTET STRING }
68 id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
69 id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
71 The value for response SHALL be the DER encoding of
74 BasicOCSPResponse ::= SEQUENCE {
75 tbsResponseData ResponseData,
76 signatureAlgorithm AlgorithmIdentifier,
78 certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
80 The value for signature SHALL be computed on the hash of the DER
81 encoding ResponseData.
83 ResponseData ::= SEQUENCE {
84 version [0] EXPLICIT Version DEFAULT v1,
85 responderID ResponderID,
86 producedAt GeneralizedTime,
87 responses SEQUENCE OF SingleResponse,
88 responseExtensions [1] EXPLICIT Extensions OPTIONAL }
90 ResponderID ::= CHOICE {
94 KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
95 (excluding the tag and length fields)
97 SingleResponse ::= SEQUENCE {
99 certStatus CertStatus,
100 thisUpdate GeneralizedTime,
101 nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
102 singleExtensions [1] EXPLICIT Extensions OPTIONAL }
104 CertStatus ::= CHOICE {
105 good [0] IMPLICIT NULL,
106 revoked [1] IMPLICIT RevokedInfo,
107 unknown [2] IMPLICIT UnknownInfo }
109 RevokedInfo ::= SEQUENCE {
110 revocationTime GeneralizedTime,
111 revocationReason [0] EXPLICIT CRLReason OPTIONAL }
113 UnknownInfo ::= NULL -- this can be replaced with an enumeration
116 static CFAbsoluteTime
genTimeToCFAbsTime(const SecAsn1Item
*datetime
)
118 return SecAbsoluteTimeFromDateContent(SEC_ASN1_GENERALIZED_TIME
,
119 datetime
->Data
, datetime
->Length
);
122 void SecOCSPSingleResponseDestroy(SecOCSPSingleResponseRef
this) {
126 static SecOCSPSingleResponseRef
SecOCSPSingleResponseCreate(
127 SecAsn1OCSPSingleResponse
*resp
, SecAsn1CoderRef coder
) {
128 assert(resp
!= NULL
);
129 SecOCSPSingleResponseRef
this;
130 require(this = (SecOCSPSingleResponseRef
)
131 malloc(sizeof(struct __SecOCSPSingleResponse
)), errOut
);
132 this->certStatus
= CS_NotParsed
;
133 this->thisUpdate
= NULL_TIME
;
134 this->nextUpdate
= NULL_TIME
;
135 this->revokedTime
= NULL_TIME
;
136 this->crlReason
= kSecRevocationReasonUndetermined
;
137 //this->extensions = NULL;
139 if ((resp
->certStatus
.Data
== NULL
) || (resp
->certStatus
.Length
== 0)) {
140 ocspdErrorLog("OCSPSingleResponse: bad certStatus");
143 this->certStatus
= (SecAsn1OCSPCertStatusTag
)(resp
->certStatus
.Data
[0] & SEC_ASN1_TAGNUM_MASK
);
144 if (this->certStatus
== CS_Revoked
) {
145 /* Decode further to get SecAsn1OCSPRevokedInfo */
146 SecAsn1OCSPCertStatus certStatus
;
147 memset(&certStatus
, 0, sizeof(certStatus
));
148 if (SecAsn1DecodeData(coder
, &resp
->certStatus
,
149 kSecAsn1OCSPCertStatusRevokedTemplate
, &certStatus
)) {
150 ocspdErrorLog("OCSPSingleResponse: err decoding certStatus");
153 SecAsn1OCSPRevokedInfo
*revokedInfo
= certStatus
.revokedInfo
;
154 if (revokedInfo
!= NULL
) {
155 /* Treat this as optional even for CS_Revoked */
156 this->revokedTime
= genTimeToCFAbsTime(&revokedInfo
->revocationTime
);
157 const SecAsn1Item
*revReason
= revokedInfo
->revocationReason
;
158 if((revReason
!= NULL
) &&
159 (revReason
->Data
!= NULL
) &&
160 (revReason
->Length
!= 0)) {
161 this->crlReason
= revReason
->Data
[0];
165 this->thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
166 if (resp
->nextUpdate
!= NULL
) {
167 this->nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
169 //mExtensions = new OCSPExtensions(resp->singleExtensions);
170 ocspdDebug("status %d reason %d", (int)this->certStatus
,
171 (int)this->crlReason
);
175 SecOCSPSingleResponseDestroy(this);
179 #define LEEWAY (4500.0)
181 /* Calculate temporal validity; set latestNextUpdate and expireTime. Only
182 called from SecOCSPResponseCreate. Returns true if valid, else returns
184 static bool SecOCSPResponseCalculateValidity(SecOCSPResponseRef
this,
185 CFTimeInterval maxAge
, CFTimeInterval defaultTTL
)
188 this->latestNextUpdate
= NULL_TIME
;
189 CFAbsoluteTime now
= this->verifyTime
= CFAbsoluteTimeGetCurrent();
190 CFStringRef hexResp
= CFDataCopyHexString(this->data
);
192 if (this->producedAt
> now
+ LEEWAY
) {
193 ocspdErrorLog("OCSPResponse: producedAt more than 1:15 from now %@", hexResp
);
197 /* Make this->latestNextUpdate be the date farthest in the future
198 of any of the singleResponses nextUpdate fields. */
199 SecAsn1OCSPSingleResponse
**responses
;
200 for (responses
= this->responseData
.responses
; *responses
; ++responses
) {
201 SecAsn1OCSPSingleResponse
*resp
= *responses
;
203 /* thisUpdate later than 'now' invalidates the whole response. */
204 CFAbsoluteTime thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
205 if (thisUpdate
> now
+ LEEWAY
) {
206 ocspdErrorLog("OCSPResponse: thisUpdate more than 1:15 from now %@", hexResp
);
210 /* Keep track of latest nextUpdate. */
211 if (resp
->nextUpdate
!= NULL
) {
212 CFAbsoluteTime nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
213 if (nextUpdate
> this->latestNextUpdate
) {
214 this->latestNextUpdate
= nextUpdate
;
218 /* RFC 5019 section 2.2.4 states on nextUpdate:
219 Responders MUST always include this value to aid in
220 response caching. See Section 6 for additional
221 information on caching.
223 ocspdErrorLog("OCSPResponse: nextUpdate not present %@", hexResp
);
224 #ifdef STRICT_RFC5019
230 /* Now that we have this->latestNextUpdate, we figure out the latest
231 date at which we will expire this response from our cache. To comply
234 6.1. Caching at the Client
236 To minimize bandwidth usage, clients MUST locally cache authoritative
237 OCSP responses (i.e., a response with a signature that has been
238 successfully validated and that indicate an OCSPResponseStatus of
241 Most OCSP clients will send OCSPRequests at or near the nextUpdate
242 time (when a cached response expires). To avoid large spikes in
243 responder load that might occur when many clients refresh cached
244 responses for a popular certificate, responders MAY indicate when the
245 client should fetch an updated OCSP response by using the cache-
246 control:max-age directive. Clients SHOULD fetch the updated OCSP
247 Response on or after the max-age time. To ensure that clients
248 receive an updated OCSP response, OCSP responders MUST refresh the
249 OCSP response before the max-age time.
253 we need to take the cache-control:max-age directive into account.
255 The way the code below is written we ignore a max-age=0 in the
256 http header. Since a value of 0 (NULL_TIME) also means there
257 was no max-age in the header. This seems ok since that would imply
258 no-cache so we also ignore negative values for the same reason,
259 instead we'll expire whenever this->latestNextUpdate tells us to,
260 which is the signed value if max-age is too low, since we don't
261 want to refetch multilple times for a single page load in a browser. */
262 if (this->latestNextUpdate
== NULL_TIME
) {
263 /* See comment above on RFC 5019 section 2.2.4. */
264 /* Absolute expire time = current time plus defaultTTL */
265 this->expireTime
= now
+ defaultTTL
;
266 } else if (this->latestNextUpdate
< now
- LEEWAY
) {
267 ocspdErrorLog("OCSPResponse: latestNextUpdate more than 1:15 ago %@", hexResp
);
269 } else if (maxAge
> 0) {
270 /* Beware of double overflows such as:
272 now + maxAge < this->latestNextUpdate
274 in the math below since an attacker could create any positive
276 if (maxAge
< this->latestNextUpdate
- now
) {
277 /* maxAge header wants us to expire the cache entry sooner than
278 nextUpdate would allow, to balance server load. */
279 this->expireTime
= now
+ maxAge
;
281 /* maxAge http header attempting to make us cache the response
282 longer than it's valid for, bad http header! Ignoring you. */
283 ocspdErrorLog("OCSPResponse: now + maxAge > latestNextUpdate,"
284 " using latestNextUpdate %@", hexResp
);
285 this->expireTime
= this->latestNextUpdate
;
288 /* No maxAge provided, just use latestNextUpdate. */
289 this->expireTime
= this->latestNextUpdate
;
294 CFReleaseSafe(hexResp
);
298 SecOCSPResponseRef
SecOCSPResponseCreate(CFDataRef ocspResponse
,
299 CFTimeInterval maxAge
) {
300 CFStringRef hexResp
= CFDataCopyHexString(ocspResponse
);
301 SecAsn1OCSPResponse topResp
= {};
302 SecOCSPResponseRef
this;
304 require(this = (SecOCSPResponseRef
)calloc(1, sizeof(struct __SecOCSPResponse
)),
306 require_noerr(SecAsn1CoderCreate(&this->coder
), errOut
);
308 this->data
= ocspResponse
;
309 CFRetain(ocspResponse
);
312 resp
.Length
= CFDataGetLength(ocspResponse
);
313 resp
.Data
= (uint8_t *)CFDataGetBytePtr(ocspResponse
);
314 if (SecAsn1DecodeData(this->coder
, &resp
, kSecAsn1OCSPResponseTemplate
,
316 ocspdErrorLog("OCSPResponse: decode failure at top level %@", hexResp
);
318 /* remainder is valid only on RS_Success */
319 if ((topResp
.responseStatus
.Data
== NULL
) ||
320 (topResp
.responseStatus
.Length
== 0)) {
321 ocspdErrorLog("OCSPResponse: no responseStatus %@", hexResp
);
324 this->responseStatus
= topResp
.responseStatus
.Data
[0];
325 if (this->responseStatus
!= kSecOCSPSuccess
) {
326 secdebug("ocsp", "OCSPResponse: status: %d %@", this->responseStatus
, hexResp
);
327 /* not a failure of our constructor; this object is now useful, but
328 * only for this one byte of status info */
331 if (topResp
.responseBytes
== NULL
) {
332 /* I don't see how this can be legal on RS_Success */
333 ocspdErrorLog("OCSPResponse: empty responseBytes %@", hexResp
);
336 if (!SecAsn1OidCompare(&topResp
.responseBytes
->responseType
,
337 &OID_PKIX_OCSP_BASIC
)) {
338 ocspdErrorLog("OCSPResponse: unknown responseType %@", hexResp
);
343 /* decode the SecAsn1OCSPBasicResponse */
344 if (SecAsn1DecodeData(this->coder
, &topResp
.responseBytes
->response
,
345 kSecAsn1OCSPBasicResponseTemplate
, &this->basicResponse
)) {
346 ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse %@", hexResp
);
350 /* signature and cert evaluation done externally */
352 /* decode the SecAsn1OCSPResponseData */
353 if (SecAsn1DecodeData(this->coder
, &this->basicResponse
.tbsResponseData
,
354 kSecAsn1OCSPResponseDataTemplate
, &this->responseData
)) {
355 ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData %@", hexResp
);
358 this->producedAt
= genTimeToCFAbsTime(&this->responseData
.producedAt
);
359 if (this->producedAt
== NULL_TIME
) {
360 ocspdErrorLog("OCSPResponse: bad producedAt %@", hexResp
);
364 if (this->responseData
.responderID
.Data
== NULL
) {
365 ocspdErrorLog("OCSPResponse: bad responderID %@", hexResp
);
369 /* Choice processing for ResponderID */
370 this->responderIdTag
= (SecAsn1OCSPResponderIDTag
)
371 (this->responseData
.responderID
.Data
[0] & SEC_ASN1_TAGNUM_MASK
);
372 const SecAsn1Template
*templ
;
373 switch(this->responderIdTag
) {
375 /* @@@ Since we don't use the decoded byName value we could skip
376 decoding it but we do it anyway for validation. */
377 templ
= kSecAsn1OCSPResponderIDAsNameTemplate
;
380 templ
= kSecAsn1OCSPResponderIDAsKeyTemplate
;
383 ocspdErrorLog("OCSPResponse: bad responderID tag %@", hexResp
);
386 if (SecAsn1DecodeData(this->coder
, &this->responseData
.responderID
, templ
,
387 &this->responderID
)) {
388 ocspdErrorLog("OCSPResponse: decode failure at responderID %@", hexResp
);
392 /* We should probably get the defaultTTL from the policy.
393 For now defaultTTL is hardcoded to 24 hours. */
394 CFTimeInterval defaultTTL
= 24 * 60 * 60;
395 /* Check temporal validity, default TTL 24 hours. */
396 require_quiet(SecOCSPResponseCalculateValidity(this, maxAge
, defaultTTL
), errOut
);
399 /* Individual responses looked into when we're asked for a specific one
400 via SecOCSPResponseCopySingleResponse(). */
401 mExtensions
= new OCSPExtensions(mResponseData
.responseExtensions
);
405 CFReleaseSafe(hexResp
);
408 CFReleaseSafe(hexResp
);
410 SecOCSPResponseFinalize(this);
415 CFDataRef
SecOCSPResponseGetData(SecOCSPResponseRef
this) {
419 SecOCSPResponseStatus
SecOCSPGetResponseStatus(SecOCSPResponseRef
this) {
420 return this->responseStatus
;
423 CFAbsoluteTime
SecOCSPResponseGetExpirationTime(SecOCSPResponseRef
this) {
424 return this->expireTime
;
427 CFDataRef
SecOCSPResponseGetNonce(SecOCSPResponseRef
this) {
431 CFAbsoluteTime
SecOCSPResponseProducedAt(SecOCSPResponseRef
this) {
432 return this->producedAt
;
435 CFAbsoluteTime
SecOCSPResponseVerifyTime(SecOCSPResponseRef
this) {
436 return this->verifyTime
;
439 CFArrayRef
SecOCSPResponseCopySigners(SecOCSPResponseRef
this) {
443 void SecOCSPResponseFinalize(SecOCSPResponseRef
this) {
444 CFReleaseSafe(this->data
);
445 CFReleaseSafe(this->nonce
);
446 SecAsn1CoderRelease(this->coder
);
450 SecOCSPSingleResponseRef
SecOCSPResponseCopySingleResponse(
451 SecOCSPResponseRef
this, SecOCSPRequestRef request
) {
452 SecOCSPSingleResponseRef sr
= NULL
;
454 CFDataRef issuer
= SecCertificateCopyIssuerSequence(request
->certificate
);
455 const DERItem
*publicKey
= SecCertificateGetPublicKeyData(request
->issuer
);
456 CFDataRef serial
= SecCertificateCopySerialNumber(request
->certificate
);
457 CFDataRef issuerNameHash
= NULL
;
458 CFDataRef issuerPubKeyHash
= NULL
;
459 SecAsn1Oid
*algorithm
= NULL
;
460 SecAsn1Item
*parameters
= NULL
;
462 /* We should probably get the defaultTTL from the policy.
463 For now defaultTTL is hardcoded to 24 hours. This is how long we trust
464 a response without a nextUpdate field. */
465 CFTimeInterval defaultTTL
= 24 * 60 * 60;
467 SecAsn1OCSPSingleResponse
**responses
;
468 for (responses
= this->responseData
.responses
; *responses
; ++responses
) {
469 SecAsn1OCSPSingleResponse
*resp
= *responses
;
470 SecAsn1OCSPCertID
*certId
= &resp
->certID
;
471 /* First check the easy part, serial number should match. */
472 if (certId
->serialNumber
.Length
!= (size_t)CFDataGetLength(serial
) ||
473 memcmp(CFDataGetBytePtr(serial
), certId
->serialNumber
.Data
,
474 certId
->serialNumber
.Length
)) {
475 /* Serial # mismatch, skip this singleResponse. */
479 /* Calcluate the issuerKey and issuerName digests using the
480 hashAlgorithm and parameters specified in the certId, if
481 they differ from the ones we already computed. */
482 if (!SecAsn1OidCompare(algorithm
, &certId
->algId
.algorithm
) ||
483 !SecAsn1OidCompare(parameters
, &certId
->algId
.parameters
)) {
484 algorithm
= &certId
->algId
.algorithm
;
485 parameters
= &certId
->algId
.parameters
;
486 CFReleaseSafe(issuerNameHash
);
487 CFReleaseSafe(issuerPubKeyHash
);
488 issuerNameHash
= SecDigestCreate(kCFAllocatorDefault
, algorithm
,
489 parameters
, CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
490 issuerPubKeyHash
= SecDigestCreate(kCFAllocatorDefault
, algorithm
,
491 parameters
, publicKey
->data
, publicKey
->length
);
494 if (certId
->issuerNameHash
.Length
== (size_t)CFDataGetLength(issuerNameHash
)
495 && !memcmp(CFDataGetBytePtr(issuerNameHash
),
496 certId
->issuerNameHash
.Data
, certId
->issuerNameHash
.Length
)
497 && certId
->issuerPubKeyHash
.Length
== (size_t)CFDataGetLength(issuerPubKeyHash
)
498 && !memcmp(CFDataGetBytePtr(issuerPubKeyHash
),
499 certId
->issuerPubKeyHash
.Data
, certId
->issuerPubKeyHash
.Length
)) {
501 CFAbsoluteTime thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
502 if (thisUpdate
> this->verifyTime
) {
503 ocspdErrorLog("OCSPSingleResponse: thisUpdate > now");
507 if (resp
->nextUpdate
== NULL
) {
508 /* rfc2560 section 2.4 states: "If nextUpdate is not set, the
509 responder is indicating that newer revocation information
510 is available all the time".
511 Let's ensure that thisUpdate isn't more than defaultTTL in
513 if (this->verifyTime
> thisUpdate
+ defaultTTL
) {
514 ocspdErrorLog("OCSPSingleResponse: no nextUpdate present "
515 "and now > thisUpdate + defaultTTL");
519 CFAbsoluteTime nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
520 if (this->verifyTime
> nextUpdate
) {
521 ocspdErrorLog("OCSPSingleResponse: now > nextUpdate");
526 /* resp matches the certificate in request, so let's use it. */
527 sr
= SecOCSPSingleResponseCreate(resp
, this->coder
);
529 ocspdDebug("found matching singleResponse");
535 CFReleaseSafe(issuerPubKeyHash
);
536 CFReleaseSafe(issuerNameHash
);
537 CFReleaseSafe(serial
);
538 CFReleaseSafe(issuer
);
541 ocspdDebug("certID not found");
547 static bool SecOCSPResponseVerifySignature(SecOCSPResponseRef
this,
549 /* Beware this->basicResponse.sig: on decode, length is in BITS */
550 return SecKeyDigestAndVerify(key
, &this->basicResponse
.algId
,
551 this->basicResponse
.tbsResponseData
.Data
,
552 this->basicResponse
.tbsResponseData
.Length
,
553 this->basicResponse
.sig
.Data
,
554 this->basicResponse
.sig
.Length
/ 8) == errSecSuccess
;
557 static bool SecOCSPResponseIsIssuer(SecOCSPResponseRef
this,
558 SecCertificatePathRef issuer
) {
559 bool shouldBeSigner
= false;
560 SecCertificateRef signer
= SecCertificatePathGetCertificateAtIndex(issuer
, 0);
561 if (this->responderIdTag
== RIT_Name
) {
562 /* Name inside response must == signer's SubjectName. */
563 CFDataRef subject
= SecCertificateCopySubjectSequence(signer
);
565 ocspdDebug("error on SecCertificateCopySubjectSequence");
568 if ((size_t)CFDataGetLength(subject
) == this->responderID
.byName
.Length
&&
569 !memcmp(this->responderID
.byName
.Data
, CFDataGetBytePtr(subject
),
570 this->responderID
.byName
.Length
)) {
571 ocspdDebug("good ResponderID.byName");
572 shouldBeSigner
= true;
574 ocspdDebug("BAD ResponderID.byName");
577 } else /* if (this->responderIdTag == RIT_Key) */ {
578 /* ResponderID.byKey must == SHA1(signer's public key) */
579 CFDataRef pubKeyDigest
= SecCertificateCopyPublicKeySHA1Digest(signer
);
580 if ((size_t)CFDataGetLength(pubKeyDigest
) == this->responderID
.byKey
.Length
&&
581 !memcmp(this->responderID
.byKey
.Data
, CFDataGetBytePtr(pubKeyDigest
),
582 this->responderID
.byKey
.Length
)) {
583 ocspdDebug("good ResponderID.byKey");
584 shouldBeSigner
= true;
586 ocspdDebug("BAD ResponderID.byKey");
588 CFRelease(pubKeyDigest
);
591 if (shouldBeSigner
) {
592 SecKeyRef key
= SecCertificatePathCopyPublicKeyAtIndex(issuer
, 0);
594 shouldBeSigner
= SecOCSPResponseVerifySignature(this, key
);
595 ocspdDebug("ocsp response signature %sok", shouldBeSigner
? "" : "not ");
598 ocspdDebug("Failed to extract key from leaf certificate");
599 shouldBeSigner
= false;
603 return shouldBeSigner
;
606 /* Returns the SecCertificatePathRef who's leaf signed this ocspResponse if
607 we can find one and NULL if we can't find a valid signer. */
608 SecCertificatePathRef
SecOCSPResponseCopySigner(SecOCSPResponseRef
this,
609 SecCertificatePathRef issuer
) {
610 SecCertificateRef issuerCert
= SecCertificatePathGetCertificateAtIndex(issuer
, 0);
611 CFDataRef issuerSubject
= SecCertificateGetNormalizedSubjectContent(issuerCert
);
612 /* Look though any certs that came with the response and see if they were
613 both issued by the issuerPath and signed the response. */
615 for (certs
= this->basicResponse
.certs
; certs
&& *certs
; ++certs
) {
616 SecCertificateRef cert
= SecCertificateCreateWithBytes(
617 kCFAllocatorDefault
, (*certs
)->Data
, (*certs
)->Length
);
619 CFDataRef certIssuer
= SecCertificateGetNormalizedIssuerContent(cert
);
620 if (CFEqual(issuerSubject
, certIssuer
)) {
621 SecCertificatePathRef signer
= SecCertificatePathCopyAddingLeaf(issuer
, cert
);
624 if (SecOCSPResponseIsIssuer(this, signer
)) {
627 ocspdErrorLog("ocsp response cert not signed by issuer.");
632 ocspdErrorLog("ocsp response cert issuer doesn't match issuer subject.");
635 ocspdErrorLog("ocsp response cert failed to parse");
639 /* If none of the returned certs work, try the issuer of the certificate
640 being checked directly. */
641 if (SecOCSPResponseIsIssuer(this, issuer
)) {
646 /* We couldn't find who signed this ocspResponse, give up. */