it just supports a "trusted OCSP global root CA".
Changes between 0.9.6 and 0.9.7 [xx XXX 2000]
+ *) Initial OCSP certificate verification added to OCSP_basic_verify()
+ and related routines. This uses the standard OpenSSL certificate
+ verify routines to perform initial checks (just CA validity) and
+ to obtain the certificate chain. Then additional checks will be
+ performed on the chain. Currently the root CA is checked to see
+ if it is explicitly trusted for OCSP signing. This is used to set
+ a root CA as a global signing root: that is any certificate that
+ chains to that CA is an acceptable OCSP signing certificate.
+ [Steve Henson]
+
*) New '-extfile ...' option to 'openssl ca' for reading X.509v3
extensions from a separate configuration file.
As when reading extensions from the main configuration file,
}
}
+X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath)
+{
+ X509_STORE *store;
+ X509_LOOKUP *lookup;
+ if(!(store = X509_STORE_new())) goto end;
+ lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
+ if (lookup == NULL) goto end;
+ if (CAfile) {
+ if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
+ BIO_printf(bp, "Error loading file %s\n", CAfile);
+ goto end;
+ }
+ } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
+
+ lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+ if (lookup == NULL) goto end;
+ if (CApath) {
+ if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
+ BIO_printf(bp, "Error loading directory %s\n", CApath);
+ goto end;
+ }
+ } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
+
+ ERR_clear_error();
+ return store;
+ end:
+ X509_STORE_free(store);
+ return NULL;
+}
EVP_PKEY *load_key(BIO *err, char *file, int format, char *pass, ENGINE *e);
EVP_PKEY *load_pubkey(BIO *err, char *file, int format, ENGINE *e);
STACK_OF(X509) *load_certs(BIO *err, char *file, int format);
+X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath);
#define FORMAT_UNDEF 0
#define FORMAT_ASN1 1
int add_nonce = 1;
OCSP_REQUEST *req = NULL;
OCSP_RESPONSE *resp = NULL;
+ OCSP_BASICRESP *bs = NULL;
X509 *issuer = NULL, *cert = NULL;
X509 *signer = NULL;
EVP_PKEY *key = NULL;
BIO *cbio = NULL, *derbio = NULL;
BIO *out = NULL;
int req_text = 0, resp_text = 0;
+ char *CAfile = NULL, *CApath = NULL;
+ X509_STORE *store = NULL;
int ret = 1;
int badarg = 0;
+ int i;
if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
ERR_load_crypto_strings();
args = argv + 1;
}
else badarg = 1;
}
+ else if (!strcmp (*args, "-CAfile"))
+ {
+ if (args[1])
+ {
+ args++;
+ CAfile = *args;
+ }
+ else badarg = 1;
+ }
+ else if (!strcmp (*args, "-CApath"))
+ {
+ if (args[1])
+ {
+ args++;
+ CApath = *args;
+ }
+ else badarg = 1;
+ }
else if (!strcmp(*args, "-signkey"))
{
if (args[1])
if (resp_text) OCSP_RESPONSE_print(out, resp, 0);
+ store = setup_verify(bio_err, CAfile, CApath);
+ if(!store) goto end;
+
+ bs = OCSP_response_get1_basic(resp);
+
+ i = OCSP_basic_verify(bs, NULL, store, 0);
+
+ if(i <= 0)
+ {
+ BIO_printf(bio_err, "Response verify error (%d)\n", i);
+ ERR_print_errors(bio_err);
+ }
+
ret = 0;
end:
ERR_print_errors(bio_err);
X509_free(signer);
+ X509_STORE_free(store);
EVP_PKEY_free(key);
X509_free(issuer);
X509_free(cert);
BIO_free(out);
OCSP_REQUEST_free(req);
OCSP_RESPONSE_free(resp);
+ OCSP_BASICRESP_free(bs);
EXIT(ret);
}
#undef PROG
#define PROG smime_main
-static X509_STORE *setup_verify(char *CAfile, char *CApath);
static int save_certs(char *signerfile, STACK_OF(X509) *signers);
#define SMIME_OP 0x10
}
if(operation == SMIME_VERIFY) {
- if(!(store = setup_verify(CAfile, CApath))) goto end;
+ if(!(store = setup_verify(bio_err, CAfile, CApath))) goto end;
}
ret = 3;
return (ret);
}
-static X509_STORE *setup_verify(char *CAfile, char *CApath)
-{
- X509_STORE *store;
- X509_LOOKUP *lookup;
- if(!(store = X509_STORE_new())) goto end;
- lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
- if (lookup == NULL) goto end;
- if (CAfile) {
- if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
- BIO_printf(bio_err, "Error loading file %s\n", CAfile);
- goto end;
- }
- } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
-
- lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
- if (lookup == NULL) goto end;
- if (CApath) {
- if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
- BIO_printf(bio_err, "Error loading directory %s\n", CApath);
- goto end;
- }
- } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
-
- ERR_clear_error();
- return store;
- end:
- X509_STORE_free(store);
- return NULL;
-}
-
static int save_certs(char *signerfile, STACK_OF(X509) *signers)
{
int i;
#define OCSP_NOCERTS 0x1
#define OCSP_NOINTERN 0x2
#define OCSP_NOSIGS 0x4
+#define OCSP_NOCHAIN 0x8
+#define OCSP_NOVERIFY 0x10
+#define OCSP_NOEXPLICIT 0x20
+#define OCSP_NOCASIGN 0x40
+#define OCSP_NODELEGATED 0x80
+#define OCSP_NOCHECKS 0x100
/* CertID ::= SEQUENCE {
* hashAlgorithm AlgorithmIdentifier,
int OCSP_request_verify(OCSP_REQUEST *req, EVP_PKEY *pkey);
+int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
OCSP_BASICRESP *OCSP_basic_response_new(int tag,
/* Reason codes. */
#define OCSP_R_BAD_DATA 108
#define OCSP_R_BAD_TAG 100
+#define OCSP_R_CERTIFICATE_VERIFY_ERROR 126
#define OCSP_R_DIGEST_ERR 101
#define OCSP_R_FAILED_TO_OPEN 109
#define OCSP_R_FAILED_TO_READ 110
#define OCSP_R_NO_RESPONSE_DATA 104
#define OCSP_R_NO_SIGNATURE 105
#define OCSP_R_REVOKED_NO_TIME 106
+#define OCSP_R_ROOT_CA_NOT_TRUSTED 127
#define OCSP_R_SERVER_READ_ERROR 116
#define OCSP_R_SERVER_RESPONSE_ERROR 117
#define OCSP_R_SERVER_RESPONSE_PARSE_ERROR 118
{
{OCSP_R_BAD_DATA ,"bad data"},
{OCSP_R_BAD_TAG ,"bad tag"},
+{OCSP_R_CERTIFICATE_VERIFY_ERROR ,"certificate verify error"},
{OCSP_R_DIGEST_ERR ,"digest err"},
{OCSP_R_FAILED_TO_OPEN ,"failed to open"},
{OCSP_R_FAILED_TO_READ ,"failed to read"},
{OCSP_R_NO_RESPONSE_DATA ,"no response data"},
{OCSP_R_NO_SIGNATURE ,"no signature"},
{OCSP_R_REVOKED_NO_TIME ,"revoked no time"},
+{OCSP_R_ROOT_CA_NOT_TRUSTED ,"root ca not trusted"},
{OCSP_R_SERVER_READ_ERROR ,"server read error"},
{OCSP_R_SERVER_RESPONSE_ERROR ,"server response error"},
{OCSP_R_SERVER_RESPONSE_PARSE_ERROR ,"server response parse error"},
return NULL;
}
-int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
+
+int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
{
int ret;
ret = OBJ_cmp(a->hashAlgorithm->algorithm, b->hashAlgorithm->algorithm);
if (ret) return ret;
ret = ASN1_OCTET_STRING_cmp(a->issuerNameHash, b->issuerNameHash);
if (ret) return ret;
- ret = ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash);
+ return ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash);
+ }
+
+int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
+ {
+ int ret;
+ ret = OCSP_id_issuer_cmp(a, b);
if (ret) return ret;
return ASN1_INTEGER_cmp(a->serialNumber, b->serialNumber);
}
int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,
X509_STORE *st, unsigned long flags)
{
- X509 *signer;
- int ret;
+ X509 *signer, *x;
+ STACK_OF(X509) *chain = NULL;
+ X509_STORE_CTX ctx;
+ int i, ret = 0;
signer = ocsp_find_signer(bs, certs, st, flags);
if (!signer)
{
OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND);
- return 0;
+ goto end;
}
if(!(flags & OCSP_NOSIGS))
{
if(ret <= 0)
{
OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNATURE_FAILURE);
- return 0;
+ goto end;
}
}
+ if(!(flags & OCSP_NOVERIFY))
+ {
+ if(flags & OCSP_NOCHAIN)
+ X509_STORE_CTX_init(&ctx, st, signer, NULL);
+ else
+ X509_STORE_CTX_init(&ctx, st, signer, bs->certs);
+
+ X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
+ ret = X509_verify_cert(&ctx);
+ chain = X509_STORE_CTX_get1_chain(&ctx);
+ X509_STORE_CTX_cleanup(&ctx);
+ if (ret <= 0)
+ {
+ i = X509_STORE_CTX_get_error(&ctx);
+ OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,OCSP_R_CERTIFICATE_VERIFY_ERROR);
+ ERR_add_error_data(2, "Verify error:",
+ X509_verify_cert_error_string(i));
+ goto end;
+ }
+ if(flags & OCSP_NOCHECKS)
+ {
+ ret = 1;
+ goto end;
+ }
+ /* At this point we have a valid certificate chain
+ * need to verify it against the OCSP criteria.
+ */
+#if 0
+ if(ocsp_check_issuer(bs, chain, flags))
+ {
+ ret = 1;
+ goto end;
+ }
+#endif
+
+ /* Easy case: explicitly trusted. Get root CA and
+ * check for explicit trust
+ */
+ if(flags & OCSP_NOEXPLICIT) goto end;
+
+ x = sk_X509_value(chain, sk_X509_num(chain) - 1);
+ if(X509_check_trust(x, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED)
+ {
+ OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,OCSP_R_ROOT_CA_NOT_TRUSTED);
+ goto end;
+ }
+ ret = 1;
+ }
+
+
+
+ end:
+ if(chain) sk_X509_pop_free(chain, X509_free);
return 1;
}
#define X509_TRUST_SSL_SERVER 3
#define X509_TRUST_EMAIL 4
#define X509_TRUST_OBJECT_SIGN 5
+#define X509_TRUST_OCSP_SIGN 6
/* Keep these up to date! */
#define X509_TRUST_MIN 1
-#define X509_TRUST_MAX 5
+#define X509_TRUST_MAX 6
/* trust_flags values */
static void trtable_free(X509_TRUST *p);
static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags);
+static int trust_1oid(X509_TRUST *trust, X509 *x, int flags);
static int trust_compat(X509_TRUST *trust, X509 *x, int flags);
static int obj_trust(int id, X509 *x, int flags);
{X509_TRUST_SSL_CLIENT, 0, trust_1oidany, "SSL Client", NID_client_auth, NULL},
{X509_TRUST_SSL_SERVER, 0, trust_1oidany, "SSL Client", NID_server_auth, NULL},
{X509_TRUST_EMAIL, 0, trust_1oidany, "S/MIME email", NID_email_protect, NULL},
+{X509_TRUST_OCSP_SIGN, 0, trust_1oid, "OCSP responder", NID_OCSP_sign, NULL}
};
#define X509_TRUST_COUNT (sizeof(trstandard)/sizeof(X509_TRUST))
return trust_compat(trust, x, flags);
}
+static int trust_1oid(X509_TRUST *trust, X509 *x, int flags)
+{
+ if(x->aux) return obj_trust(trust->arg1, x, flags);
+ return X509_TRUST_UNTRUSTED;
+}
+
static int trust_compat(X509_TRUST *trust, X509 *x, int flags)
{
X509_check_purpose(x, -1, 0);
#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
-
static void x509v3_cache_extensions(X509 *x);
static int ca_check(const X509 *x);
static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, int ca);
static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca);
static int xp_cmp(const X509_PURPOSE * const *a,
const X509_PURPOSE * const *b);
{X509_PURPOSE_SMIME_ENCRYPT, X509_TRUST_EMAIL, 0, check_purpose_smime_encrypt, "S/MIME encryption", "smimeencrypt", NULL},
{X509_PURPOSE_CRL_SIGN, X509_TRUST_COMPAT, 0, check_purpose_crl_sign, "CRL signing", "crlsign", NULL},
{X509_PURPOSE_ANY, X509_TRUST_DEFAULT, 0, no_check, "Any Purpose", "any", NULL},
+ {X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, ocsp_helper, "OCSP helper", "ocsphelper", NULL},
};
#define X509_PURPOSE_COUNT (sizeof(xstandard)/sizeof(X509_PURPOSE))
return -1;
}
-
int X509_PURPOSE_get_by_id(int purpose)
{
X509_PURPOSE tmp;
case NID_ms_sgc:
case NID_ns_sgc:
x->ex_xkusage |= XKU_SGC;
+ break;
+
+ case NID_OCSP_sign:
+ x->ex_xkusage |= XKU_OCSP_SIGN;
+ break;
+
+ case NID_time_stamp:
+ x->ex_xkusage |= XKU_TIMESTAMP;
+ break;
}
}
sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free);
return 1;
}
+/* OCSP helper: this is *not* a full OCSP check. It just checks that
+ * each CA is valid. Additional checks must be made on the chain.
+ */
+
+static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca)
+{
+ /* Must be a valid CA */
+ if(ca) {
+ int ca_ret;
+ ca_ret = ca_check(x);
+ if(ca_ret != 2) return ca_ret;
+ if(x->ex_flags & EXFLAG_NSCERT) {
+ if(x->ex_nscert & NS_ANY_CA) return ca_ret;
+ return 0;
+ }
+ return 0;
+ }
+ /* leaf certificate is checked in OCSP_verify() */
+ return 1;
+}
+
static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca)
{
return 1;
#define NS_SSL_CA 0x04
#define NS_SMIME_CA 0x02
#define NS_OBJSIGN_CA 0x01
+#define NS_ANY_CA (NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA)
#define XKU_SSL_SERVER 0x1
#define XKU_SSL_CLIENT 0x2
#define XKU_SMIME 0x4
#define XKU_CODE_SIGN 0x8
#define XKU_SGC 0x10
+#define XKU_OCSP_SIGN 0x20
+#define XKU_TIMESTAMP 0x40
#define X509_PURPOSE_DYNAMIC 0x1
#define X509_PURPOSE_DYNAMIC_NAME 0x2
#define X509_PURPOSE_SMIME_ENCRYPT 5
#define X509_PURPOSE_CRL_SIGN 6
#define X509_PURPOSE_ANY 7
+#define X509_PURPOSE_OCSP_HELPER 8
#define X509_PURPOSE_MIN 1
-#define X509_PURPOSE_MAX 7
+#define X509_PURPOSE_MAX 8
/* Flags for X509V3_EXT_print() */