2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @page page_revoke Revocation methods
37 * There are two revocation method for PKIX/X.509: CRL and OCSP.
38 * Revocation is needed if the private key is lost and
39 * stolen. Depending on how picky you are, you might want to make
40 * revocation for destroyed private keys too (smartcard broken), but
41 * that should not be a problem.
43 * CRL is a list of certifiates that have expired.
45 * OCSP is an online checking method where the requestor sends a list
46 * of certificates to the OCSP server to return a signed reply if they
47 * are valid or not. Some services sends a OCSP reply as part of the
48 * hand-shake to make the revoktion decision simpler/faster for the
58 CRLCertificateList crl
;
66 OCSPBasicOCSPResponse ocsp
;
72 struct hx509_revoke_ctx_data
{
75 struct revoke_crl
*val
;
79 struct revoke_ocsp
*val
;
85 * Allocate a revokation context. Free with hx509_revoke_free().
87 * @param context A hx509 context.
88 * @param ctx returns a newly allocated revokation context.
90 * @return An hx509 error code, see hx509_get_error_string().
92 * @ingroup hx509_revoke
96 hx509_revoke_init(hx509_context context
, hx509_revoke_ctx
*ctx
)
98 *ctx
= calloc(1, sizeof(**ctx
));
103 (*ctx
)->crls
.len
= 0;
104 (*ctx
)->crls
.val
= NULL
;
105 (*ctx
)->ocsps
.len
= 0;
106 (*ctx
)->ocsps
.val
= NULL
;
112 _hx509_revoke_ref(hx509_revoke_ctx ctx
)
117 _hx509_abort("revoke ctx refcount <= 0");
120 _hx509_abort("revoke ctx refcount == 0");
125 free_ocsp(struct revoke_ocsp
*ocsp
)
128 free_OCSPBasicOCSPResponse(&ocsp
->ocsp
);
129 hx509_certs_free(&ocsp
->certs
);
130 hx509_cert_free(ocsp
->signer
);
134 * Free a hx509 revokation context.
136 * @param ctx context to be freed
138 * @ingroup hx509_revoke
142 hx509_revoke_free(hx509_revoke_ctx
*ctx
)
146 if (ctx
== NULL
|| *ctx
== NULL
)
149 if ((*ctx
)->ref
<= 0)
150 _hx509_abort("revoke ctx refcount <= 0 on free");
151 if (--(*ctx
)->ref
> 0)
154 for (i
= 0; i
< (*ctx
)->crls
.len
; i
++) {
155 free((*ctx
)->crls
.val
[i
].path
);
156 free_CRLCertificateList(&(*ctx
)->crls
.val
[i
].crl
);
159 for (i
= 0; i
< (*ctx
)->ocsps
.len
; i
++)
160 free_ocsp(&(*ctx
)->ocsps
.val
[i
]);
161 free((*ctx
)->ocsps
.val
);
163 free((*ctx
)->crls
.val
);
165 memset(*ctx
, 0, sizeof(**ctx
));
171 verify_ocsp(hx509_context context
,
172 struct revoke_ocsp
*ocsp
,
177 hx509_cert signer
= NULL
;
181 _hx509_query_clear(&q
);
184 * Need to match on issuer too in case there are two CA that have
185 * issued the same name to a certificate. One example of this is
186 * the www.openvalidation.org test's ocsp validator.
189 q
.match
= HX509_QUERY_MATCH_ISSUER_NAME
;
190 q
.issuer_name
= &_hx509_get_cert(parent
)->tbsCertificate
.issuer
;
192 switch(ocsp
->ocsp
.tbsResponseData
.responderID
.element
) {
193 case choice_OCSPResponderID_byName
:
194 q
.match
|= HX509_QUERY_MATCH_SUBJECT_NAME
;
195 q
.subject_name
= &ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byName
;
197 case choice_OCSPResponderID_byKey
:
198 q
.match
|= HX509_QUERY_MATCH_KEY_HASH_SHA1
;
199 q
.keyhash_sha1
= &ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byKey
;
203 ret
= hx509_certs_find(context
, certs
, &q
, &signer
);
204 if (ret
&& ocsp
->certs
)
205 ret
= hx509_certs_find(context
, ocsp
->certs
, &q
, &signer
);
210 * If signer certificate isn't the CA certificate, lets check the
211 * it is the CA that signed the signer certificate and the OCSP EKU
214 if (hx509_cert_cmp(signer
, parent
) != 0) {
215 Certificate
*p
= _hx509_get_cert(parent
);
216 Certificate
*s
= _hx509_get_cert(signer
);
218 ret
= _hx509_cert_is_parent_cmp(s
, p
, 0);
220 ret
= HX509_PARENT_NOT_CA
;
221 hx509_set_error_string(context
, 0, ret
, "Revoke OSCP signer is "
222 "doesn't have CA as signer certificate");
226 ret
= _hx509_verify_signature_bitstring(context
,
228 &s
->signatureAlgorithm
,
229 &s
->tbsCertificate
._save
,
232 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
233 "OSCP signer signature invalid");
237 ret
= hx509_cert_check_eku(context
, signer
,
238 oid_id_pkix_kp_OCSPSigning(), 0);
243 ret
= _hx509_verify_signature_bitstring(context
,
244 _hx509_get_cert(signer
),
245 &ocsp
->ocsp
.signatureAlgorithm
,
246 &ocsp
->ocsp
.tbsResponseData
._save
,
247 &ocsp
->ocsp
.signature
);
249 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
250 "OSCP signature invalid");
254 ocsp
->signer
= signer
;
258 hx509_cert_free(signer
);
268 parse_ocsp_basic(const void *data
, size_t length
, OCSPBasicOCSPResponse
*basic
)
274 memset(basic
, 0, sizeof(*basic
));
276 ret
= decode_OCSPResponse(data
, length
, &resp
, &size
);
279 if (length
!= size
) {
280 free_OCSPResponse(&resp
);
281 return ASN1_EXTRA_DATA
;
284 switch (resp
.responseStatus
) {
288 free_OCSPResponse(&resp
);
289 return HX509_REVOKE_WRONG_DATA
;
292 if (resp
.responseBytes
== NULL
) {
293 free_OCSPResponse(&resp
);
297 ret
= der_heim_oid_cmp(&resp
.responseBytes
->responseType
,
298 oid_id_pkix_ocsp_basic());
300 free_OCSPResponse(&resp
);
301 return HX509_REVOKE_WRONG_DATA
;
304 ret
= decode_OCSPBasicOCSPResponse(resp
.responseBytes
->response
.data
,
305 resp
.responseBytes
->response
.length
,
309 free_OCSPResponse(&resp
);
312 if (size
!= resp
.responseBytes
->response
.length
) {
313 free_OCSPResponse(&resp
);
314 free_OCSPBasicOCSPResponse(basic
);
315 return ASN1_EXTRA_DATA
;
317 free_OCSPResponse(&resp
);
327 load_ocsp(hx509_context context
, struct revoke_ocsp
*ocsp
)
329 OCSPBasicOCSPResponse basic
;
330 hx509_certs certs
= NULL
;
336 ret
= _hx509_map_file(ocsp
->path
, &data
, &length
, &sb
);
340 ret
= parse_ocsp_basic(data
, length
, &basic
);
341 _hx509_unmap_file(data
, length
);
343 hx509_set_error_string(context
, 0, ret
,
344 "Failed to parse OCSP response");
351 ret
= hx509_certs_init(context
, "MEMORY:ocsp-certs", 0,
354 free_OCSPBasicOCSPResponse(&basic
);
358 for (i
= 0; i
< basic
.certs
->len
; i
++) {
361 ret
= hx509_cert_init(context
, &basic
.certs
->val
[i
], &c
);
365 ret
= hx509_certs_add(context
, certs
, c
);
372 ocsp
->last_modfied
= sb
.st_mtime
;
374 free_OCSPBasicOCSPResponse(&ocsp
->ocsp
);
375 hx509_certs_free(&ocsp
->certs
);
376 hx509_cert_free(ocsp
->signer
);
386 * Add a OCSP file to the revokation context.
388 * @param context hx509 context
389 * @param ctx hx509 revokation context
390 * @param path path to file that is going to be added to the context.
392 * @return An hx509 error code, see hx509_get_error_string().
394 * @ingroup hx509_revoke
398 hx509_revoke_add_ocsp(hx509_context context
,
399 hx509_revoke_ctx ctx
,
406 if (strncmp(path
, "FILE:", 5) != 0) {
407 hx509_set_error_string(context
, 0, HX509_UNSUPPORTED_OPERATION
,
408 "unsupport type in %s", path
);
409 return HX509_UNSUPPORTED_OPERATION
;
414 for (i
= 0; i
< ctx
->ocsps
.len
; i
++) {
415 if (strcmp(ctx
->ocsps
.val
[0].path
, path
) == 0)
419 data
= realloc(ctx
->ocsps
.val
,
420 (ctx
->ocsps
.len
+ 1) * sizeof(ctx
->ocsps
.val
[0]));
422 hx509_clear_error_string(context
);
426 ctx
->ocsps
.val
= data
;
428 memset(&ctx
->ocsps
.val
[ctx
->ocsps
.len
], 0,
429 sizeof(ctx
->ocsps
.val
[0]));
431 ctx
->ocsps
.val
[ctx
->ocsps
.len
].path
= strdup(path
);
432 if (ctx
->ocsps
.val
[ctx
->ocsps
.len
].path
== NULL
) {
433 hx509_clear_error_string(context
);
437 ret
= load_ocsp(context
, &ctx
->ocsps
.val
[ctx
->ocsps
.len
]);
439 free(ctx
->ocsps
.val
[ctx
->ocsps
.len
].path
);
452 verify_crl(hx509_context context
,
453 hx509_revoke_ctx ctx
,
454 CRLCertificateList
*crl
,
464 t
= _hx509_Time2time_t(&crl
->tbsCertList
.thisUpdate
);
466 hx509_set_error_string(context
, 0, HX509_CRL_USED_BEFORE_TIME
,
467 "CRL used before time");
468 return HX509_CRL_USED_BEFORE_TIME
;
471 if (crl
->tbsCertList
.nextUpdate
== NULL
) {
472 hx509_set_error_string(context
, 0, HX509_CRL_INVALID_FORMAT
,
473 "CRL missing nextUpdate");
474 return HX509_CRL_INVALID_FORMAT
;
477 t
= _hx509_Time2time_t(crl
->tbsCertList
.nextUpdate
);
479 hx509_set_error_string(context
, 0, HX509_CRL_USED_AFTER_TIME
,
480 "CRL used after time");
481 return HX509_CRL_USED_AFTER_TIME
;
484 _hx509_query_clear(&q
);
487 * If it's the signer have CRLSIGN bit set, use that as the signer
488 * cert for the certificate, otherwise, search for a certificate.
490 if (_hx509_check_key_usage(context
, parent
, 1 << 6, FALSE
) == 0) {
491 signer
= hx509_cert_ref(parent
);
493 q
.match
= HX509_QUERY_MATCH_SUBJECT_NAME
;
494 q
.match
|= HX509_QUERY_KU_CRLSIGN
;
495 q
.subject_name
= &crl
->tbsCertList
.issuer
;
497 ret
= hx509_certs_find(context
, certs
, &q
, &signer
);
499 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
500 "Failed to find certificate for CRL");
505 ret
= _hx509_verify_signature_bitstring(context
,
506 _hx509_get_cert(signer
),
507 &crl
->signatureAlgorithm
,
508 &crl
->tbsCertList
._save
,
509 &crl
->signatureValue
);
511 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
512 "CRL signature invalid");
517 * If signer is not CA cert, need to check revoke status of this
518 * CRL signing cert too, this include all parent CRL signer cert
519 * up to the root *sigh*, assume root at least hve CERTSIGN flag
522 while (_hx509_check_key_usage(context
, signer
, 1 << 5, TRUE
)) {
523 hx509_cert crl_parent
;
525 _hx509_query_clear(&q
);
527 q
.match
= HX509_QUERY_MATCH_SUBJECT_NAME
;
528 q
.match
|= HX509_QUERY_KU_CRLSIGN
;
529 q
.subject_name
= &_hx509_get_cert(signer
)->tbsCertificate
.issuer
;
531 ret
= hx509_certs_find(context
, certs
, &q
, &crl_parent
);
533 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
534 "Failed to find parent of CRL signer");
538 ret
= hx509_revoke_verify(context
,
544 hx509_cert_free(signer
);
547 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
548 "Failed to verify revoke "
549 "status of CRL signer");
555 hx509_cert_free(signer
);
561 load_crl(const char *path
, time_t *t
, CRLCertificateList
*crl
)
568 memset(crl
, 0, sizeof(*crl
));
570 ret
= _hx509_map_file(path
, &data
, &length
, &sb
);
576 ret
= decode_CRLCertificateList(data
, length
, crl
, &size
);
577 _hx509_unmap_file(data
, length
);
581 /* check signature is aligned */
582 if (crl
->signatureValue
.length
& 7) {
583 free_CRLCertificateList(crl
);
584 return HX509_CRYPTO_SIG_INVALID_FORMAT
;
590 * Add a CRL file to the revokation context.
592 * @param context hx509 context
593 * @param ctx hx509 revokation context
594 * @param path path to file that is going to be added to the context.
596 * @return An hx509 error code, see hx509_get_error_string().
598 * @ingroup hx509_revoke
602 hx509_revoke_add_crl(hx509_context context
,
603 hx509_revoke_ctx ctx
,
610 if (strncmp(path
, "FILE:", 5) != 0) {
611 hx509_set_error_string(context
, 0, HX509_UNSUPPORTED_OPERATION
,
612 "unsupport type in %s", path
);
613 return HX509_UNSUPPORTED_OPERATION
;
619 for (i
= 0; i
< ctx
->crls
.len
; i
++) {
620 if (strcmp(ctx
->crls
.val
[0].path
, path
) == 0)
624 data
= realloc(ctx
->crls
.val
,
625 (ctx
->crls
.len
+ 1) * sizeof(ctx
->crls
.val
[0]));
627 hx509_clear_error_string(context
);
630 ctx
->crls
.val
= data
;
632 memset(&ctx
->crls
.val
[ctx
->crls
.len
], 0, sizeof(ctx
->crls
.val
[0]));
634 ctx
->crls
.val
[ctx
->crls
.len
].path
= strdup(path
);
635 if (ctx
->crls
.val
[ctx
->crls
.len
].path
== NULL
) {
636 hx509_clear_error_string(context
);
641 &ctx
->crls
.val
[ctx
->crls
.len
].last_modfied
,
642 &ctx
->crls
.val
[ctx
->crls
.len
].crl
);
644 free(ctx
->crls
.val
[ctx
->crls
.len
].path
);
654 * Check that a certificate is not expired according to a revokation
655 * context. Also need the parent certificte to the check OCSP
658 * @param context hx509 context
659 * @param ctx hx509 revokation context
665 * @return An hx509 error code, see hx509_get_error_string().
667 * @ingroup hx509_revoke
672 hx509_revoke_verify(hx509_context context
,
673 hx509_revoke_ctx ctx
,
677 hx509_cert parent_cert
)
679 const Certificate
*c
= _hx509_get_cert(cert
);
680 const Certificate
*p
= _hx509_get_cert(parent_cert
);
681 unsigned long i
, j
, k
;
684 hx509_clear_error_string(context
);
686 for (i
= 0; i
< ctx
->ocsps
.len
; i
++) {
687 struct revoke_ocsp
*ocsp
= &ctx
->ocsps
.val
[i
];
690 /* check this ocsp apply to this cert */
692 /* check if there is a newer version of the file */
693 ret
= stat(ocsp
->path
, &sb
);
694 if (ret
== 0 && ocsp
->last_modfied
!= sb
.st_mtime
) {
695 ret
= load_ocsp(context
, ocsp
);
700 /* verify signature in ocsp if not already done */
701 if (ocsp
->signer
== NULL
) {
702 ret
= verify_ocsp(context
, ocsp
, now
, certs
, parent_cert
);
707 for (j
= 0; j
< ocsp
->ocsp
.tbsResponseData
.responses
.len
; j
++) {
708 heim_octet_string os
;
710 ret
= der_heim_integer_cmp(&ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certID
.serialNumber
,
711 &c
->tbsCertificate
.serialNumber
);
715 /* verify issuer hashes hash */
716 ret
= _hx509_verify_signature(context
,
718 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].certID
.hashAlgorithm
,
719 &c
->tbsCertificate
.issuer
._save
,
720 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].certID
.issuerNameHash
);
724 os
.data
= p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.data
;
725 os
.length
= p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.length
/ 8;
727 ret
= _hx509_verify_signature(context
,
729 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certID
.hashAlgorithm
,
731 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certID
.issuerKeyHash
);
735 switch (ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certStatus
.element
) {
736 case choice_OCSPCertStatus_good
:
738 case choice_OCSPCertStatus_revoked
:
739 hx509_set_error_string(context
, 0,
741 "Certificate revoked by issuer in OCSP");
742 return HX509_CERT_REVOKED
;
743 case choice_OCSPCertStatus_unknown
:
747 /* don't allow the update to be in the future */
748 if (ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].thisUpdate
>
749 now
+ context
->ocsp_time_diff
)
752 /* don't allow the next update to be in the past */
753 if (ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].nextUpdate
) {
754 if (*ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].nextUpdate
< now
)
757 /* Should force a refetch, but can we ? */;
763 for (i
= 0; i
< ctx
->crls
.len
; i
++) {
764 struct revoke_crl
*crl
= &ctx
->crls
.val
[i
];
767 /* check if cert.issuer == crls.val[i].crl.issuer */
768 ret
= _hx509_name_cmp(&c
->tbsCertificate
.issuer
,
769 &crl
->crl
.tbsCertList
.issuer
);
773 ret
= stat(crl
->path
, &sb
);
774 if (ret
== 0 && crl
->last_modfied
!= sb
.st_mtime
) {
775 CRLCertificateList cl
;
777 ret
= load_crl(crl
->path
, &crl
->last_modfied
, &cl
);
779 free_CRLCertificateList(&crl
->crl
);
782 crl
->failed_verify
= 0;
785 if (crl
->failed_verify
)
788 /* verify signature in crl if not already done */
789 if (crl
->verified
== 0) {
790 ret
= verify_crl(context
, ctx
, &crl
->crl
, now
, certs
, parent_cert
);
792 crl
->failed_verify
= 1;
798 if (crl
->crl
.tbsCertList
.crlExtensions
) {
799 for (j
= 0; j
< crl
->crl
.tbsCertList
.crlExtensions
->len
; j
++) {
800 if (crl
->crl
.tbsCertList
.crlExtensions
->val
[j
].critical
) {
801 hx509_set_error_string(context
, 0,
802 HX509_CRL_UNKNOWN_EXTENSION
,
803 "Unknown CRL extension");
804 return HX509_CRL_UNKNOWN_EXTENSION
;
809 if (crl
->crl
.tbsCertList
.revokedCertificates
== NULL
)
812 /* check if cert is in crl */
813 for (j
= 0; j
< crl
->crl
.tbsCertList
.revokedCertificates
->len
; j
++) {
816 ret
= der_heim_integer_cmp(&crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].userCertificate
,
817 &c
->tbsCertificate
.serialNumber
);
821 t
= _hx509_Time2time_t(&crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].revocationDate
);
825 if (crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].crlEntryExtensions
)
826 for (k
= 0; k
< crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].crlEntryExtensions
->len
; k
++)
827 if (crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].crlEntryExtensions
->val
[k
].critical
)
828 return HX509_CRL_UNKNOWN_EXTENSION
;
830 hx509_set_error_string(context
, 0,
832 "Certificate revoked by issuer in CRL");
833 return HX509_CERT_REVOKED
;
840 if (context
->flags
& HX509_CTX_VERIFY_MISSING_OK
)
842 hx509_set_error_string(context
, HX509_ERROR_APPEND
,
843 HX509_REVOKE_STATUS_MISSING
,
844 "No revoke status found for "
846 return HX509_REVOKE_STATUS_MISSING
;
849 struct ocsp_add_ctx
{
852 const AlgorithmIdentifier
*digest
;
857 add_to_req(hx509_context context
, void *ptr
, hx509_cert cert
)
859 struct ocsp_add_ctx
*ctx
= ptr
;
860 OCSPInnerRequest
*one
;
861 hx509_cert parent
= NULL
;
862 Certificate
*p
, *c
= _hx509_get_cert(cert
);
863 heim_octet_string os
;
868 d
= realloc(ctx
->req
->requestList
.val
,
869 sizeof(ctx
->req
->requestList
.val
[0]) *
870 (ctx
->req
->requestList
.len
+ 1));
873 ctx
->req
->requestList
.val
= d
;
875 one
= &ctx
->req
->requestList
.val
[ctx
->req
->requestList
.len
];
876 memset(one
, 0, sizeof(*one
));
878 _hx509_query_clear(&q
);
880 q
.match
|= HX509_QUERY_FIND_ISSUER_CERT
;
883 ret
= hx509_certs_find(context
, ctx
->certs
, &q
, &parent
);
888 if (hx509_cert_cmp(ctx
->parent
, parent
) != 0) {
889 ret
= HX509_REVOKE_NOT_SAME_PARENT
;
890 hx509_set_error_string(context
, 0, ret
,
891 "Not same parent certifate as "
892 "last certificate in request");
896 ctx
->parent
= hx509_cert_ref(parent
);
898 p
= _hx509_get_cert(parent
);
900 ret
= copy_AlgorithmIdentifier(ctx
->digest
, &one
->reqCert
.hashAlgorithm
);
904 ret
= _hx509_create_signature(context
,
906 &one
->reqCert
.hashAlgorithm
,
907 &c
->tbsCertificate
.issuer
._save
,
909 &one
->reqCert
.issuerNameHash
);
913 os
.data
= p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.data
;
915 p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.length
/ 8;
917 ret
= _hx509_create_signature(context
,
919 &one
->reqCert
.hashAlgorithm
,
922 &one
->reqCert
.issuerKeyHash
);
926 ret
= copy_CertificateSerialNumber(&c
->tbsCertificate
.serialNumber
,
927 &one
->reqCert
.serialNumber
);
931 ctx
->req
->requestList
.len
++;
933 hx509_cert_free(parent
);
935 free_OCSPInnerRequest(one
);
936 memset(one
, 0, sizeof(*one
));
943 * Create an OCSP request for a set of certificates.
945 * @param context a hx509 context
946 * @param reqcerts list of certificates to request ocsp data for
947 * @param pool certificate pool to use when signing
948 * @param signer certificate to use to sign the request
949 * @param digest the signing algorithm in the request, if NULL use the
950 * default signature algorithm,
951 * @param request the encoded request, free with free_heim_octet_string().
952 * @param nonce nonce in the request, free with free_heim_octet_string().
954 * @return An hx509 error code, see hx509_get_error_string().
956 * @ingroup hx509_revoke
960 hx509_ocsp_request(hx509_context context
,
961 hx509_certs reqcerts
,
964 const AlgorithmIdentifier
*digest
,
965 heim_octet_string
*request
,
966 heim_octet_string
*nonce
)
971 struct ocsp_add_ctx ctx
;
974 memset(&req
, 0, sizeof(req
));
977 digest
= _hx509_crypto_default_digest_alg
;
979 ctx
.req
= &req
.tbsRequest
;
984 ret
= hx509_certs_iter(context
, reqcerts
, add_to_req
, &ctx
);
985 hx509_cert_free(ctx
.parent
);
990 req
.tbsRequest
.requestExtensions
=
991 calloc(1, sizeof(*req
.tbsRequest
.requestExtensions
));
992 if (req
.tbsRequest
.requestExtensions
== NULL
) {
997 es
= req
.tbsRequest
.requestExtensions
;
999 es
->val
= calloc(es
->len
, sizeof(es
->val
[0]));
1000 if (es
->val
== NULL
) {
1006 ret
= der_copy_oid(oid_id_pkix_ocsp_nonce(), &es
->val
[0].extnID
);
1008 free_OCSPRequest(&req
);
1012 es
->val
[0].extnValue
.data
= malloc(10);
1013 if (es
->val
[0].extnValue
.data
== NULL
) {
1017 es
->val
[0].extnValue
.length
= 10;
1019 ret
= RAND_bytes(es
->val
[0].extnValue
.data
,
1020 es
->val
[0].extnValue
.length
);
1022 ret
= HX509_CRYPTO_INTERNAL_ERROR
;
1025 ret
= der_copy_octet_string(nonce
, &es
->val
[0].extnValue
);
1032 ASN1_MALLOC_ENCODE(OCSPRequest
, request
->data
, request
->length
,
1034 free_OCSPRequest(&req
);
1037 if (size
!= request
->length
)
1038 _hx509_abort("internal ASN.1 encoder error");
1043 free_OCSPRequest(&req
);
1048 printable_time(time_t t
)
1051 strlcpy(s
, ctime(&t
)+ 4, sizeof(s
));
1057 * Print the OCSP reply stored in a file.
1059 * @param context a hx509 context
1060 * @param path path to a file with a OCSP reply
1061 * @param out the out FILE descriptor to print the reply on
1063 * @return An hx509 error code, see hx509_get_error_string().
1065 * @ingroup hx509_revoke
1069 hx509_revoke_ocsp_print(hx509_context context
, const char *path
, FILE *out
)
1071 struct revoke_ocsp ocsp
;
1077 memset(&ocsp
, 0, sizeof(ocsp
));
1079 ocsp
.path
= strdup(path
);
1080 if (ocsp
.path
== NULL
)
1083 ret
= load_ocsp(context
, &ocsp
);
1089 fprintf(out
, "signer: ");
1091 switch(ocsp
.ocsp
.tbsResponseData
.responderID
.element
) {
1092 case choice_OCSPResponderID_byName
: {
1095 _hx509_name_from_Name(&ocsp
.ocsp
.tbsResponseData
.responderID
.u
.byName
, &n
);
1096 hx509_name_to_string(n
, &s
);
1097 hx509_name_free(&n
);
1098 fprintf(out
, " byName: %s\n", s
);
1102 case choice_OCSPResponderID_byKey
: {
1104 hex_encode(ocsp
.ocsp
.tbsResponseData
.responderID
.u
.byKey
.data
,
1105 ocsp
.ocsp
.tbsResponseData
.responderID
.u
.byKey
.length
,
1107 fprintf(out
, " byKey: %s\n", s
);
1112 _hx509_abort("choice_OCSPResponderID unknown");
1116 fprintf(out
, "producedAt: %s\n",
1117 printable_time(ocsp
.ocsp
.tbsResponseData
.producedAt
));
1119 fprintf(out
, "replies: %d\n", ocsp
.ocsp
.tbsResponseData
.responses
.len
);
1121 for (i
= 0; i
< ocsp
.ocsp
.tbsResponseData
.responses
.len
; i
++) {
1123 switch (ocsp
.ocsp
.tbsResponseData
.responses
.val
[i
].certStatus
.element
) {
1124 case choice_OCSPCertStatus_good
:
1127 case choice_OCSPCertStatus_revoked
:
1130 case choice_OCSPCertStatus_unknown
:
1134 status
= "element unknown";
1137 fprintf(out
, "\t%d. status: %s\n", i
, status
);
1139 fprintf(out
, "\tthisUpdate: %s\n",
1140 printable_time(ocsp
.ocsp
.tbsResponseData
.responses
.val
[i
].thisUpdate
));
1141 if (ocsp
.ocsp
.tbsResponseData
.responses
.val
[i
].nextUpdate
)
1142 fprintf(out
, "\tproducedAt: %s\n",
1143 printable_time(ocsp
.ocsp
.tbsResponseData
.responses
.val
[i
].thisUpdate
));
1147 fprintf(out
, "appended certs:\n");
1149 ret
= hx509_certs_iter(context
, ocsp
.certs
, hx509_ci_print_names
, out
);
1156 * Verify that the certificate is part of the OCSP reply and it's not
1157 * expired. Doesn't verify signature the OCSP reply or it's done by a
1158 * authorized sender, that is assumed to be already done.
1160 * @param context a hx509 context
1161 * @param now the time right now, if 0, use the current time.
1162 * @param cert the certificate to verify
1163 * @param flags flags control the behavior
1164 * @param data pointer to the encode ocsp reply
1165 * @param length the length of the encode ocsp reply
1166 * @param expiration return the time the OCSP will expire and need to
1169 * @return An hx509 error code, see hx509_get_error_string().
1171 * @ingroup hx509_verify
1175 hx509_ocsp_verify(hx509_context context
,
1179 const void *data
, size_t length
,
1182 const Certificate
*c
= _hx509_get_cert(cert
);
1183 OCSPBasicOCSPResponse basic
;
1191 ret
= parse_ocsp_basic(data
, length
, &basic
);
1193 hx509_set_error_string(context
, 0, ret
,
1194 "Failed to parse OCSP response");
1198 for (i
= 0; i
< basic
.tbsResponseData
.responses
.len
; i
++) {
1200 ret
= der_heim_integer_cmp(&basic
.tbsResponseData
.responses
.val
[i
].certID
.serialNumber
,
1201 &c
->tbsCertificate
.serialNumber
);
1205 /* verify issuer hashes hash */
1206 ret
= _hx509_verify_signature(context
,
1208 &basic
.tbsResponseData
.responses
.val
[i
].certID
.hashAlgorithm
,
1209 &c
->tbsCertificate
.issuer
._save
,
1210 &basic
.tbsResponseData
.responses
.val
[i
].certID
.issuerNameHash
);
1214 switch (basic
.tbsResponseData
.responses
.val
[i
].certStatus
.element
) {
1215 case choice_OCSPCertStatus_good
:
1217 case choice_OCSPCertStatus_revoked
:
1218 case choice_OCSPCertStatus_unknown
:
1222 /* don't allow the update to be in the future */
1223 if (basic
.tbsResponseData
.responses
.val
[i
].thisUpdate
>
1224 now
+ context
->ocsp_time_diff
)
1227 /* don't allow the next update to be in the past */
1228 if (basic
.tbsResponseData
.responses
.val
[i
].nextUpdate
) {
1229 if (*basic
.tbsResponseData
.responses
.val
[i
].nextUpdate
< now
)
1231 *expiration
= *basic
.tbsResponseData
.responses
.val
[i
].nextUpdate
;
1235 free_OCSPBasicOCSPResponse(&basic
);
1239 free_OCSPBasicOCSPResponse(&basic
);
1245 ret
= hx509_cert_get_subject(cert
, &name
);
1247 hx509_clear_error_string(context
);
1250 ret
= hx509_name_to_string(name
, &subject
);
1251 hx509_name_free(&name
);
1253 hx509_clear_error_string(context
);
1256 hx509_set_error_string(context
, 0, HX509_CERT_NOT_IN_OCSP
,
1257 "Certificate %s not in OCSP response "
1263 return HX509_CERT_NOT_IN_OCSP
;
1267 hx509_certs revoked
;
1272 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1274 * @param context a hx509 context.
1275 * @param crl return pointer to a newly allocated CRL context.
1277 * @return An hx509 error code, see hx509_get_error_string().
1279 * @ingroup hx509_verify
1283 hx509_crl_alloc(hx509_context context
, hx509_crl
*crl
)
1287 *crl
= calloc(1, sizeof(**crl
));
1289 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1293 ret
= hx509_certs_init(context
, "MEMORY:crl", 0, NULL
, &(*crl
)->revoked
);
1304 * Add revoked certificate to an CRL context.
1306 * @param context a hx509 context.
1307 * @param crl the CRL to add the revoked certificate to.
1308 * @param certs keyset of certificate to revoke.
1310 * @return An hx509 error code, see hx509_get_error_string().
1312 * @ingroup hx509_verify
1316 hx509_crl_add_revoked_certs(hx509_context context
,
1320 return hx509_certs_merge(context
, crl
->revoked
, certs
);
1324 * Set the lifetime of a CRL context.
1326 * @param context a hx509 context.
1327 * @param crl a CRL context
1328 * @param delta delta time the certificate is valid, library adds the
1329 * current time to this.
1331 * @return An hx509 error code, see hx509_get_error_string().
1333 * @ingroup hx509_verify
1337 hx509_crl_lifetime(hx509_context context
, hx509_crl crl
, int delta
)
1339 crl
->expire
= time(NULL
) + delta
;
1344 * Free a CRL context.
1346 * @param context a hx509 context.
1347 * @param crl a CRL context to free.
1349 * @ingroup hx509_verify
1353 hx509_crl_free(hx509_context context
, hx509_crl
*crl
)
1357 hx509_certs_free(&(*crl
)->revoked
);
1358 memset(*crl
, 0, sizeof(**crl
));
1364 add_revoked(hx509_context context
, void *ctx
, hx509_cert cert
)
1366 TBSCRLCertList
*c
= ctx
;
1371 num
= c
->revokedCertificates
->len
;
1372 ptr
= realloc(c
->revokedCertificates
->val
,
1373 (num
+ 1) * sizeof(c
->revokedCertificates
->val
[0]));
1375 hx509_clear_error_string(context
);
1378 c
->revokedCertificates
->val
= ptr
;
1380 ret
= hx509_cert_get_serialnumber(cert
,
1381 &c
->revokedCertificates
->val
[num
].userCertificate
);
1383 hx509_clear_error_string(context
);
1386 c
->revokedCertificates
->val
[num
].revocationDate
.element
=
1387 choice_Time_generalTime
;
1388 c
->revokedCertificates
->val
[num
].revocationDate
.u
.generalTime
=
1389 time(NULL
) - 3600 * 24;
1390 c
->revokedCertificates
->val
[num
].crlEntryExtensions
= NULL
;
1392 c
->revokedCertificates
->len
++;
1398 * Sign a CRL and return an encode certificate.
1400 * @param context a hx509 context.
1401 * @param signer certificate to sign the CRL with
1402 * @param crl the CRL to sign
1403 * @param os return the signed and encoded CRL, free with
1404 * free_heim_octet_string()
1406 * @return An hx509 error code, see hx509_get_error_string().
1408 * @ingroup hx509_verify
1412 hx509_crl_sign(hx509_context context
,
1415 heim_octet_string
*os
)
1417 const AlgorithmIdentifier
*sigalg
= _hx509_crypto_default_sig_alg
;
1418 CRLCertificateList c
;
1421 hx509_private_key signerkey
;
1423 memset(&c
, 0, sizeof(c
));
1425 signerkey
= _hx509_cert_private_key(signer
);
1426 if (signerkey
== NULL
) {
1427 ret
= HX509_PRIVATE_KEY_MISSING
;
1428 hx509_set_error_string(context
, 0, ret
,
1429 "Private key missing for CRL signing");
1433 c
.tbsCertList
.version
= malloc(sizeof(*c
.tbsCertList
.version
));
1434 if (c
.tbsCertList
.version
== NULL
) {
1435 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1439 *c
.tbsCertList
.version
= 1;
1441 ret
= copy_AlgorithmIdentifier(sigalg
, &c
.tbsCertList
.signature
);
1443 hx509_clear_error_string(context
);
1447 ret
= copy_Name(&_hx509_get_cert(signer
)->tbsCertificate
.issuer
,
1448 &c
.tbsCertList
.issuer
);
1450 hx509_clear_error_string(context
);
1454 c
.tbsCertList
.thisUpdate
.element
= choice_Time_generalTime
;
1455 c
.tbsCertList
.thisUpdate
.u
.generalTime
= time(NULL
) - 24 * 3600;
1457 c
.tbsCertList
.nextUpdate
= malloc(sizeof(*c
.tbsCertList
.nextUpdate
));
1458 if (c
.tbsCertList
.nextUpdate
== NULL
) {
1459 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1465 time_t next
= crl
->expire
;
1467 next
= time(NULL
) + 24 * 3600 * 365;
1469 c
.tbsCertList
.nextUpdate
->element
= choice_Time_generalTime
;
1470 c
.tbsCertList
.nextUpdate
->u
.generalTime
= next
;
1473 c
.tbsCertList
.revokedCertificates
=
1474 calloc(1, sizeof(*c
.tbsCertList
.revokedCertificates
));
1475 if (c
.tbsCertList
.revokedCertificates
== NULL
) {
1476 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1480 c
.tbsCertList
.crlExtensions
= NULL
;
1482 ret
= hx509_certs_iter(context
, crl
->revoked
, add_revoked
, &c
.tbsCertList
);
1486 /* if not revoked certs, remove OPTIONAL entry */
1487 if (c
.tbsCertList
.revokedCertificates
->len
== 0) {
1488 free(c
.tbsCertList
.revokedCertificates
);
1489 c
.tbsCertList
.revokedCertificates
= NULL
;
1492 ASN1_MALLOC_ENCODE(TBSCRLCertList
, os
->data
, os
->length
,
1493 &c
.tbsCertList
, &size
, ret
);
1495 hx509_set_error_string(context
, 0, ret
, "failed to encode tbsCRL");
1498 if (size
!= os
->length
)
1499 _hx509_abort("internal ASN.1 encoder error");
1502 ret
= _hx509_create_signature_bitstring(context
,
1506 &c
.signatureAlgorithm
,
1510 ASN1_MALLOC_ENCODE(CRLCertificateList
, os
->data
, os
->length
,
1512 free_CRLCertificateList(&c
);
1514 hx509_set_error_string(context
, 0, ret
, "failed to encode CRL");
1517 if (size
!= os
->length
)
1518 _hx509_abort("internal ASN.1 encoder error");
1523 free_CRLCertificateList(&c
);