From dc80fea1f67b469812a4ad82863c4a6398adf638 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Tue, 17 Jan 2023 00:43:36 +0800 Subject: [PATCH] Update CRL related to be continue ... --- include/gmssl/asn1.h | 6 +- include/gmssl/oid.h | 4 +- include/gmssl/x509_ext.h | 45 +++- src/x509_cer.c | 1 + src/x509_crl.c | 4 + src/x509_ext.c | 498 ++++++++++++++++++++++++++++++++++----- src/x509_oid.c | 4 +- tools/certgen.c | 17 +- 8 files changed, 513 insertions(+), 66 deletions(-) diff --git a/include/gmssl/asn1.h b/include/gmssl/asn1.h index 06e7847b..6c3fbd1b 100644 --- a/include/gmssl/asn1.h +++ b/include/gmssl/asn1.h @@ -278,8 +278,9 @@ int asn1_generalized_time_from_der_ex(int tag, time_t *tv, const uint8_t **in, s #define asn1_implicit_generalized_time_to_der(i,tv,out,outlen) asn1_generalized_time_to_der_ex(ASN1_TAG_IMPLICIT(i),tv,out,outlen) #define asn1_implicit_generalized_time_from_der(i,tv,in,inlen) asn1_generalized_time_from_der_ex(ASN1_TAG_IMPLICIT(i),tv,in,inlen) -#define asn1_sequence_to_der(d,dlen,out,outlen) asn1_nonempty_type_to_der(ASN1_TAG_SEQUENCE,d,dlen,out,outlen) -#define asn1_sequence_from_der(d,dlen,in,inlen) asn1_nonempty_type_from_der(ASN1_TAG_SEQUENCE,d,dlen,in,inlen) +// BasicConstraints might be an empty sequence in entity certificates +#define asn1_sequence_to_der(d,dlen,out,outlen) asn1_type_to_der(ASN1_TAG_SEQUENCE,d,dlen,out,outlen) +#define asn1_sequence_from_der(d,dlen,in,inlen) asn1_type_from_der(ASN1_TAG_SEQUENCE,d,dlen,in,inlen) #define asn1_implicit_sequence_to_der(i,d,dlen,out,outlen) asn1_nonempty_type_to_der(ASN1_TAG_EXPLICIT(i),d,dlen,out,outlen) #define asn1_implicit_sequence_from_der(i,d,dlen,in,inlen) asn1_nonempty_type_from_der(ASN1_TAG_EXPLICIT(i),d,dlen,in,inlen) @@ -305,6 +306,7 @@ int asn1_header_to_der(int tag, size_t dlen, uint8_t **out, size_t *outlen); #define asn1_octet_string_header_to_der(dlen,out,outlen) asn1_header_to_der(ASN1_TAG_OCTET_STRING,dlen,out,outlen) +#define asn1_sequence_header_to_der_ex(tag,dlen,out,outlen) asn1_header_to_der(tag,dlen,out,outlen) #define asn1_sequence_header_to_der(dlen,out,outlen) asn1_header_to_der(ASN1_TAG_SEQUENCE,dlen,out,outlen) #define asn1_implicit_sequence_header_to_der(i,dlen,out,outlen) asn1_header_to_der(ASN1_TAG_EXPLICIT(i),dlen,out,outlen) diff --git a/include/gmssl/oid.h b/include/gmssl/oid.h index 977dc50c..9e1ceb3d 100644 --- a/include/gmssl/oid.h +++ b/include/gmssl/oid.h @@ -85,9 +85,11 @@ enum { OID_ce_freshest_crl, OID_netscape_cert_type, OID_netscape_cert_comment, - OID_cert_authority_info_access, OID_ct_precertificate_scts, + OID_ad_ca_issuers, + OID_ad_ocsp, + // CRL Extensions //OID_ce_authority_key_identifier, //OID_ce_issuer_alt_name, diff --git a/include/gmssl/x509_ext.h b/include/gmssl/x509_ext.h index 06f791bc..144b47b9 100644 --- a/include/gmssl/x509_ext.h +++ b/include/gmssl/x509_ext.h @@ -67,6 +67,9 @@ int x509_exts_add_ext_key_usage(uint8_t *exts, size_t *extslen, size_t maxlen, i int x509_exts_add_crl_distribution_points(uint8_t *exts, size_t *extslen, size_t maxlen, int critical, const uint8_t *d, size_t dlen); int x509_exts_add_inhibit_any_policy(uint8_t *exts, size_t *extslen, size_t maxlen, int critical, int skip_certs); int x509_exts_add_freshest_crl(uint8_t *exts, size_t *extslen, size_t maxlen, int critical, const uint8_t *d, size_t dlen); +int x509_exts_add_authority_info_access(uint8_t *exts, size_t *extslen, size_t maxlen, int critical, + const char *crt_uri, size_t crt_urilen, // crt_uri is the URI (http://examaple.com/subCA.crt) of DER-encoded CA cert + const char *ocsp_uri, size_t ocsp_urilen); int x509_exts_add_sequence(uint8_t *exts, size_t *extslen, size_t maxlen, int oid, int critical, const uint8_t *d, size_t dlen); @@ -466,6 +469,10 @@ DistributionPointName ::= CHOICE { fullName [0] IMPLICIT GeneralNames, -- SEQUENCE OF nameRelativeToCRLIssuer [1] IMPLICIT RelativeDistinguishedName } -- SET OF */ +int x509_uri_as_general_names_to_der_ex(int tag, const char *uri, size_t urilen, uint8_t **out, size_t *outlen); +int x509_uri_as_distribution_point_name_to_der(const char *uri, size_t urilen, uint8_t **out, size_t *outlen); +int x509_uri_as_explicit_distribution_point_name_to_der(int index, const char *uri, size_t urilen, uint8_t **out, size_t *outlen); + int x509_distribution_point_name_to_der(int choice, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen); int x509_distribution_point_name_from_der(int *choice, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen); int x509_distribution_point_name_print(FILE *fp, int fmt, int ind, const char *label,const uint8_t *a, size_t alen); @@ -480,6 +487,10 @@ DistributionPoint ::= SEQUENCE { reasons [1] IMPLICIT ReasonFlags OPTIONAL, cRLIssuer [2] IMPLICIT GeneralNames OPTIONAL } */ +int x509_uri_as_distribution_point_to_der(const char *uri, size_t urilen, uint8_t **out, size_t *outlen); +int x509_distribution_points_to_der(const char *http_uri, size_t http_urilen, + const char *ldap_uri, size_t ldap_urilen, uint8_t **out, size_t *outlen); + int x509_distribution_point_to_der( int dist_point_choice, const uint8_t *dist_point, size_t dist_point_len, int reasons, const uint8_t *crl_issuer, size_t crl_issuer_len, @@ -497,8 +508,7 @@ int x509_distribution_points_add_distribution_point(uint8_t *d, size_t *dlen, si int dist_point_choice, const uint8_t *dist_point, size_t dist_point_len, int reasons, const uint8_t *crl_issuer, size_t crl_issuer_len); int x509_distribution_points_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen); -#define x509_distribution_points_to_der(d,dlen,out,outlen) asn1_sequence_to_der(d,dlen,out,outlen) -#define x509_distribution_points_from_der(d,dlen,in,inlen) asn1_sequence_from_der(d,dlen,in,inlen) + /* CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint @@ -544,6 +554,37 @@ int x509_netscape_cert_type_print(FILE *fp, int fmt, int ind, const char *label, int x509_exts_validate(const uint8_t *exts, size_t extslen, int cert_type, int *path_len_constraints); +/* +AuthorityInfoAccessSyntax ::= SEQUENCE OF AccessDescription + +AccessDescription ::= SEQUENCE { + accessMethod OBJECT IDENTIFIER, + accessLocation GeneralName } + +accessMethods: + OID_ad_ca_issuers + OID_ad_ocsp +*/ +const char *x509_access_method_name(int oid); +int x509_access_method_from_name(const char *name); +int x509_access_method_to_der(int oid, uint8_t **out, size_t *outlen); +int x509_access_method_from_der(int *oid, const uint8_t **in, size_t *inlen); + +int x509_access_description_to_der(int oid, const char *uri, size_t urilen, uint8_t **out, size_t *outlen); +int x509_access_description_from_der(int *oid, const char **uri, size_t *urilen, const uint8_t **in, size_t *inlen); +int x509_access_description_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen); + +int x509_authority_info_access_to_der( + const char *crt_uri, size_t crt_urilen, + const char *ocsp_uri, size_t ocsp_urilen, + uint8_t **out, size_t *outlen); +int x509_authority_info_access_from_der( + const char **crt_uri, size_t *crt_urilen, + const char **ocsp_uri, size_t *ocsp_urilen, + const uint8_t **in, size_t *inlen); +int x509_authority_info_access_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen); + + #ifdef __cplusplus } #endif diff --git a/src/x509_cer.c b/src/x509_cer.c index f6e29a0b..bd8e4924 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -826,6 +826,7 @@ int x509_ext_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t case OID_ce_freshest_crl: return x509_freshest_crl_print(fp, fmt, ind, name, p, len); case OID_netscape_cert_type: return x509_netscape_cert_type_print(fp, fmt, ind, name, ival); case OID_netscape_cert_comment: return format_string(fp, fmt, ind, name, p, len); + case OID_pe_authority_info_access: return x509_authority_info_access_print(fp, fmt, ind, name, p, len); default: format_bytes(fp, fmt, ind, "extnValue", p, len); } return 1; diff --git a/src/x509_crl.c b/src/x509_crl.c index a3d15313..431607de 100644 --- a/src/x509_crl.c +++ b/src/x509_crl.c @@ -525,6 +525,7 @@ int x509_issuing_distribution_point_to_der( int only_contains_attr_certs, uint8_t **out, size_t *outlen) { + /* size_t len = 0; if (x509_explicit_distribution_point_name_to_der(0, dist_point_choice, dist_point, dist_point_len, NULL, &len) < 0 || asn1_implicit_boolean_to_der(1, only_contains_user_certs, NULL, &len) < 0 @@ -542,6 +543,7 @@ int x509_issuing_distribution_point_to_der( error_print(); return -1; } + */ return 1; } @@ -554,6 +556,7 @@ int x509_issuing_distribution_point_from_der( int *only_contains_attr_certs, const uint8_t **in, size_t *inlen) { + /* int ret; const uint8_t *d; size_t dlen; @@ -572,6 +575,7 @@ int x509_issuing_distribution_point_from_der( error_print(); return -1; } + */ return 1; } diff --git a/src/x509_ext.c b/src/x509_ext.c index 4633f6cb..61dfeee4 100644 --- a/src/x509_ext.c +++ b/src/x509_ext.c @@ -1,4 +1,4 @@ -/* +/* * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the License); you may @@ -288,6 +288,13 @@ int x509_exts_add_crl_distribution_points(uint8_t *exts, size_t *extslen, size_t int critical, const uint8_t *d, size_t dlen) { int oid = OID_ce_crl_distribution_points; + + // The extension SHOULD be non-critical, but this profile + // RECOMMENDS support for this extension by CAs and applications. + if (critical) { + error_print(); + return -1; + } return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen); } @@ -438,11 +445,23 @@ err: return -1; } -// GeneralName CHOICE 中有的是基本类型,有的是SEQUENCE,在设置标签时是否有区别? -// 这里是否支持OPTIONAL?? int x509_general_name_to_der(int choice, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen) { - return asn1_implicit_to_der(choice, d, dlen, out, outlen); + switch (choice) { + case X509_gn_rfc822_name: + case X509_gn_dns_name: + case X509_gn_uniform_resource_identifier: + case X509_gn_ip_address: + if (asn1_implicit_to_der(choice, d, dlen, out, outlen) != 1) { + error_print(); + return -1; + } + default: + error_print(); + return -1; + } + + return 1; } int x509_general_name_from_der(int *choice, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen) @@ -1284,6 +1303,11 @@ int x509_basic_constraints_from_der(int *ca, int *path_len_cons, const uint8_t * if (ret < 0) error_print(); return ret; } + if (!d || !dlen) { + *ca = -1; + *path_len_cons = -1; + return 1; + } if (asn1_boolean_from_der(ca, &d, &dlen) < 0 || asn1_int_from_der(path_len_cons, &d, &dlen) < 0 || asn1_length_is_zero(dlen) != 1) { @@ -1294,18 +1318,32 @@ int x509_basic_constraints_from_der(int *ca, int *path_len_cons, const uint8_t * error_print(); return -1; } - if (*ca < 0) *ca = 0; + // from_der() MUST NOT set default value to *ca return 1; } int x509_basic_constraints_validate(int ca, int path_len_cons, int cert_type) { + /* + entity_cert: + ca = -1 or 0 + path_len_constraints = -1 + first_ca_cert: + ca = 1 + path_len_constraints = 0 + middle_ca_cert: + ca = 1 + path_len_constraints = -1 or > 0 + root_ca_cert: + ca = 1 + path_len_constraints = -1 or > 0 (=0 might be ok?) + */ if (cert_type == X509_cert_ca) { if (ca != 1) { error_print(); return -1; } - if (path_len_cons < 0 || path_len_cons > 6) { + if (path_len_cons < 0 || path_len_cons > 6) { error_print(); return -1; } @@ -1325,6 +1363,11 @@ int x509_basic_constraints_print(FILE *fp, int fmt, int ind, const char *label, format_print(fp, fmt, ind, "%s\n", label); ind += 4; + // BasicConstraints might be an empty sequence in entity certificates + if (!d || !dlen) { + return 1; + } + if ((ret = asn1_boolean_from_der(&val, &d, &dlen)) < 0) goto err; if (ret) format_print(fp, fmt, ind, "cA: %s\n", asn1_boolean_name(val)); else format_print(fp, fmt, ind, "cA: %s\n", asn1_boolean_name(0)); // 特殊对待,无论cA值是否编码均输出结果 @@ -1656,6 +1699,35 @@ int x509_ext_key_usage_print(FILE *fp, int fmt, int ind, const char *label, cons return 1; } + +/* +CRL Distribution Points + + * 假设证书的cRLIssuer和issuer一致,即签发证书的CA也签发了证书所属的CRL + 不支持可选的cRLIssuer编码,解码时忽略cRLIssuer + 编解、解码时均要求DistributionPoint中包含distributionPoint + + * 如果证书扩展中不包含reasons,那么在CRL中必须包含完整的reasons。 + 证书扩展中包含reasons,逻辑上意味由不同的CRL包含因不同原因注销的证书,RFC不推荐这种方式 + + * 编码时最多支持2个DistributionPoint,分别用于HTTP和LDAP的URI + 即DistributionPointName CHOICE GeneralNames,其中只有一个GeneralName CHOICE uri + + * 解码时对每个解析成功的DistributionPoint的uri进行判断 + 最多返回一个http和一个ldap,其他协议的uri被忽略 + + * RFC要求每个DistributionPoint中至少包含一个HTTP或LDAP uri + + * 解码时不支持DistributionPointName为nameRelativeToCRLIssuer + 解码时DistributionPointName为(GeneralNames)fullName时,只返回第一个CHOICE为uri的GeneralName + + * 当uri为HTTP时,CRL文件为DER编码 + * 当uri为LDAP时 + 如 ldap://ldap.example.com/cn=example%20CA,dc=example,dc=com?certificateRevocationList;binary + 如 ldap:///cn=example%20CA,dc=example,dc=com?certificateRevocationList;binary + 必须包含 DN, certificateRevocationList, 应包含host部分 +*/ + static const char *x509_revoke_reasons[] = { "unused", "keyCompromise", @@ -1706,20 +1778,68 @@ int x509_revoke_reasons_print(FILE *fp, int fmt, int ind, const char *label, int return asn1_bits_print(fp, fmt, ind, label, x509_revoke_reasons, x509_revoke_reasons_count, bits); } -int x509_distribution_point_name_to_der(int choice, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen) +int x509_uri_as_general_names_to_der_ex(int tag, const char *uri, size_t urilen, + uint8_t **out, size_t *outlen) { - switch (choice) { - case 0: - case 1: - if (asn1_implicit_to_der(choice, d, dlen, out, outlen) != 1) { - error_print(); - return -1; - } - return 1; - default: + int choice = X509_gn_uniform_resource_identifier; + size_t len = 0; + if (x509_general_name_to_der(choice, (uint8_t *)uri, urilen, NULL, &len) != 1 + || asn1_sequence_header_to_der_ex(tag, len, out, outlen) != 1 + || x509_general_name_to_der(choice, (uint8_t *)uri, urilen, out, outlen) != 1) { error_print(); return -1; } + return 1; +} + +int x509_uri_as_distribution_point_name_to_der(const char *uri, size_t urilen, + uint8_t **out, size_t *outlen) +{ + if (x509_uri_as_general_names_to_der_ex(ASN1_TAG_EXPLICIT(0), uri, urilen, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int x509_uri_as_explicit_distribution_point_name_to_der(int index, + const char *uri, size_t urilen, uint8_t **out, size_t *outlen) +{ + size_t len = 0; + if (x509_uri_as_distribution_point_name_to_der(uri, urilen, NULL, &len) != 1 + || asn1_explicit_header_to_der(index, len, out, outlen) != 1 + || x509_uri_as_distribution_point_name_to_der(uri, urilen, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int x509_uri_as_distribution_point_to_der(const char *uri, size_t urilen, uint8_t **out, size_t *outlen) +{ + size_t len = 0; + if (x509_uri_as_explicit_distribution_point_name_to_der(0, uri, urilen, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || x509_uri_as_explicit_distribution_point_name_to_der(0, uri, urilen, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int x509_distribution_points_to_der(const char *http_uri, size_t http_urilen, + const char *ldap_uri, size_t ldap_urilen, uint8_t **out, size_t *outlen) +{ + size_t len = 0; + if (x509_uri_as_distribution_point_to_der(http_uri, http_urilen, NULL, &len) < 0 + || x509_uri_as_distribution_point_to_der(ldap_uri, ldap_urilen, NULL, &len) < 0 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || x509_uri_as_distribution_point_to_der(http_uri, http_urilen, out, outlen) < 0 + || x509_uri_as_distribution_point_to_der(ldap_uri, ldap_urilen, out, outlen) < 0) { + error_print(); + return -1; + } + return 1; } int x509_distribution_point_name_from_der(int *choice, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen) @@ -1741,20 +1861,6 @@ int x509_distribution_point_name_from_der(int *choice, const uint8_t **d, size_t return 1; } -int x509_explicit_distribution_point_name_to_der(int index, int choice, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen) -{ - // 注意:要能够解决d == NULL的情况 - error_print(); - return -1; -} - -int x509_explicit_distribution_point_name_from_der(int index, int *choice, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen) -{ - // 注意:要能够解决d == NULL的情况 - error_print(); - return -1; -} - int x509_distribution_point_name_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *a, size_t alen) { int tag; @@ -1783,6 +1889,7 @@ int x509_distribution_point_to_der( int reasons, const uint8_t *crl_issuer, size_t crl_issuer_len, uint8_t **out, size_t *outlen) { + /* size_t len = 0; if (x509_explicit_distribution_point_name_to_der(0, dist_point_choice, dist_point, dist_point_len, NULL, &len) < 0 || asn1_implicit_bits_to_der(1, reasons, NULL, &len) < 0 @@ -1794,6 +1901,7 @@ int x509_distribution_point_to_der( error_print(); return -1; } + */ return 1; } @@ -1802,6 +1910,7 @@ int x509_distribution_point_from_der( int *reasons, const uint8_t **crl_issuer, size_t *crl_issuer_len, const uint8_t **in, size_t *inlen) { + /* int ret; const uint8_t *d; size_t dlen; @@ -1817,6 +1926,7 @@ int x509_distribution_point_from_der( error_print(); return -1; } + */ return 1; } @@ -1845,34 +1955,6 @@ err: return -1; } -/* - extnID: CRLDistributionPoints (2.5.29.31) - DistributionPoint - distributionPoint - fullName - GeneralName - URI: http://www.rootca.gov.cn/Civil_Servant_arl/Civil_Servant_ARL.crl - DistributionPoint - distributionPoint - fullName - GeneralName - URI: ldap://ldap.rootca.gov.cn:390/CN=Civil_Servant_ARL,OU=ARL,O=NRCAC,C=CN - -*/ - -int x509_distribution_points_add_url(uint8_t *d, size_t *dlen, size_t maxlen, const char *url) -{ - return 0; -} - -int x509_distribution_points_add_distribution_point(uint8_t *d, size_t *dlen, size_t maxlen, - int dist_point_choice, const uint8_t *dist_point, size_t dist_point_len, - int reasons, const uint8_t *crl_issuer, size_t crl_issuer_len) -{ - error_print(); - return -1; -} - int x509_distribution_points_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen) { const uint8_t *p; @@ -1992,4 +2074,304 @@ int x509_exts_validate(const uint8_t *exts, size_t extslen, int cert_type, return 1; } +// AuthorityInfoAccess Extension + +static uint32_t oid_ad_ocsp[] = { oid_ad,1 }; +static uint32_t oid_ad_ca_issuers[] = { oid_ad,2 }; + +#define cnt(oid) (sizeof(oid)/sizeof((oid)[0])) + +static const ASN1_OID_INFO access_methods[] = { + { OID_ad_ocsp, "OCSP", oid_ad_ocsp, oid_cnt(oid_ad_ocsp) }, + { OID_ad_ca_issuers, "CAIssuers", oid_ad_ca_issuers, oid_cnt(oid_ad_ca_issuers) }, +}; + +const char *x509_access_method_name(int oid) +{ + const ASN1_OID_INFO *info; + if (!(info = asn1_oid_info_from_oid(access_methods, cnt(access_methods), oid))) { + error_print(); + return NULL; + } + return info->name; +} + +int x509_access_method_from_name(const char *name) +{ + const ASN1_OID_INFO *info; + if (!(info = asn1_oid_info_from_name(access_methods, cnt(access_methods), name))) { + error_print(); + return OID_undef; + } + return info->oid; +} + +int x509_access_method_to_der(int oid, uint8_t **out, size_t *outlen) +{ + const ASN1_OID_INFO *info; + if (!(info = asn1_oid_info_from_oid(access_methods, cnt(access_methods), oid))) { + error_print(); + return -1; + } + if (asn1_object_identifier_to_der(info->nodes, info->nodes_cnt, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int x509_access_method_from_der(int *oid, const uint8_t **in, size_t *inlen) +{ + int ret; + const ASN1_OID_INFO *info; + uint32_t nodes[32]; + size_t nodes_cnt; + + if ((ret = asn1_oid_info_from_der_ex(&info, nodes, &nodes_cnt, access_methods, cnt(access_methods), in, inlen)) != 1) { + if (ret < 0) error_print(); + else *oid = -1; + return ret; + } + *oid = info->oid; + return 1; +} + +// currently AccessDescription not support values of SubjectInfoAccess extension +int x509_access_description_to_der(int oid, const char *uri, size_t urilen, uint8_t **out, size_t *outlen) +{ + const int uri_choice = X509_gn_uniform_resource_identifier; + size_t len = 0; + + if (oid != OID_ad_ocsp && oid != OID_ad_ca_issuers) { + error_print(); + return -1; + } + if (!uri || !urilen) { + error_print(); + return -1; + } + if (x509_access_method_to_der(oid, NULL, &len) != 1 + || x509_general_name_to_der(uri_choice, (const uint8_t *)uri, urilen, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || x509_access_method_to_der(oid, out, outlen) != 1 + || x509_general_name_to_der(uri_choice, (const uint8_t *)uri, urilen, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int x509_access_description_from_der(int *oid, const char **uri, size_t *urilen, const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + int uri_choice; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + else { + *oid = -1; + *uri = NULL; + *urilen = 0; + } + return ret; + } + if (x509_access_method_from_der(oid, &d, &dlen) != 1 + || x509_general_name_from_der(&uri_choice, (const uint8_t **)uri, urilen, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + if (uri_choice != X509_gn_uniform_resource_identifier) { + error_print(); + return -1; + } + if (*uri == NULL || *urilen == 0) { + error_print(); + return -1; + } + return 1; +} + +int x509_access_description_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen) +{ + int oid; + int choice; + const uint8_t *p; + size_t len; + + format_print(fp, fmt, ind, "%s\n", label); + ind += 4; + + if (x509_access_method_from_der(&oid, &d, &dlen) != 1) { + error_print(); + return -1; + } + format_print(fp, fmt, ind, "accessMethod: %s\n", x509_access_method_name(oid)); + + if (x509_general_name_from_der(&choice, &p, &len, &d, &dlen) != 1) { + error_print(); + return -1; + } + x509_general_name_print(fp, fmt, ind, "GeneralName", choice, p, len); + + if (dlen) { + error_print(); + return -1; + } + return 1; +} + +int x509_authority_info_access_to_der( + const char *crt_uri, size_t crt_urilen, + const char *ocsp_uri, size_t ocsp_urilen, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (crt_uri && crt_urilen) { + if (x509_access_description_to_der(OID_ad_ca_issuers, crt_uri, crt_urilen, NULL, &len) != 1) { + error_print(); + return -1; + } + } + if (ocsp_uri && ocsp_urilen) { + if (x509_access_description_to_der(OID_ad_ocsp, ocsp_uri, ocsp_urilen, NULL, &len) != 1) { + error_print(); + return -1; + } + } + if (!len) { + error_print(); + return -1; + } + if (asn1_sequence_header_to_der(len, out, outlen) != 1) { + error_print(); + return -1; + } + if (crt_uri && crt_urilen) { + if (x509_access_description_to_der(OID_ad_ca_issuers, crt_uri, crt_urilen, out, outlen) != 1) { + error_print(); + return -1; + } + } + if (ocsp_uri && ocsp_urilen) { + if (x509_access_description_to_der(OID_ad_ocsp, ocsp_uri, ocsp_urilen, out, outlen) != 1) { + error_print(); + return -1; + } + } + return 1; +} + +int x509_authority_info_access_from_der( + const char **crt_uri, size_t *crt_urilen, + const char **ocsp_uri, size_t *ocsp_urilen, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + const uint8_t *ad; + size_t adlen; + + if (!crt_uri || !crt_urilen || !ocsp_uri || !ocsp_urilen || !in || !(*in) || !inlen) { + error_print(); + return -1; + } + + *crt_uri = NULL; + *crt_urilen = 0; + *ocsp_uri = NULL; + *ocsp_urilen = 0; + + if ((ret = asn1_sequence_of_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + + while (dlen) { + int oid; + const char *uri; + size_t urilen; + + if (x509_access_description_from_der(&oid, &uri, &urilen, &d, &dlen) != 1) { + error_print(); + return -1; + } + switch (oid) { + case OID_ad_ca_issuers: + if (*crt_uri) { + error_print(); + return -1; + } + *crt_uri = uri; + *crt_urilen = urilen; + break; + case OID_ad_ocsp: + if (*ocsp_uri) { + error_print(); + return -1; + } + *ocsp_uri = uri; + *ocsp_urilen = urilen; + break; + default: + error_print(); + return -1; + } + } + return 1; +} + +int x509_authority_info_access_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen) +{ + const uint8_t *p; + size_t len; + + format_print(fp, fmt, ind, "%s\n", label); + ind += 4; + + while (dlen) { + if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) { + error_print(); + return -1; + } + x509_access_description_print(fp, fmt, ind, "AccessDescription", p, len); + } + return 1; +} + +int x509_exts_add_authority_info_access(uint8_t *exts, size_t *extslen, size_t maxlen, int critical, + const char *crt_uri, size_t crt_urilen, const char *ocsp_uri, size_t ocsp_urilen) +{ + int oid = OID_pe_authority_info_access; + size_t curlen = *extslen; + uint8_t val[256]; + uint8_t *p = val; + size_t vlen = 0; + size_t len = 0; + + // Conforming CAs MUST mark this extension as non-critical. + if (critical == 1) { + error_print(); + return -1; + } + if (x509_authority_info_access_to_der(crt_uri, crt_urilen, ocsp_uri, ocsp_urilen, NULL, &len) != 1 + || asn1_length_le(len, sizeof(val)) != 1 + || x509_authority_info_access_to_der(crt_uri, crt_urilen, ocsp_uri, ocsp_urilen, &p, &vlen) != 1) { + error_print(); + return -1; + } + exts += *extslen; + if (x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1 + || asn1_length_le(curlen, maxlen) != 1 + || x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) { + error_print(); + return -1; + } + return 1; +} + diff --git a/src/x509_oid.c b/src/x509_oid.c index af26cc28..868b2524 100644 --- a/src/x509_oid.c +++ b/src/x509_oid.c @@ -134,7 +134,7 @@ static uint32_t oid_ce_certificate_issuer[] = { oid_ce,29 }; // crl_entry_ext #define OID_CE_CNT sizeof(oid_ce_subject_directory_attributes)/sizeof(int) static uint32_t oid_netscape_cert_type[] = { 2,16,840,1,113730,1,1 }; static uint32_t oid_netscape_cert_comment[] = { 2,16,840,1,113730,1,13 }; -static uint32_t oid_cert_authority_info_access[] = { 1,3,6,1,5,5,7,1,1 }; +static uint32_t oid_pe_authority_info_access[] = { 1,3,6,1,5,5,7,1,1 }; static uint32_t oid_ct_precertificate_scts[] = { 1,3,6,1,4,1,11129,2,4,2 }; static const ASN1_OID_INFO x509_ext_ids[] = { @@ -158,7 +158,7 @@ static const ASN1_OID_INFO x509_ext_ids[] = { { OID_ce_certificate_issuer, "CertificateIssuer", oid_ce_certificate_issuer, OID_CE_CNT }, { OID_netscape_cert_type, "NetscapeCertType", oid_netscape_cert_type, sizeof(oid_netscape_cert_type)/sizeof(int) }, { OID_netscape_cert_comment, "NetscapeCertComment", oid_netscape_cert_comment, sizeof(oid_netscape_cert_comment)/sizeof(int) }, - { OID_cert_authority_info_access, "CertificateAuthorityInformationAccess", oid_cert_authority_info_access, sizeof(oid_cert_authority_info_access)/sizeof(int) }, + { OID_pe_authority_info_access, "AuthorityInformationAccess", oid_pe_authority_info_access, sizeof(oid_pe_authority_info_access)/sizeof(int) }, { OID_ct_precertificate_scts, "CT-PrecertificateSCTs", oid_ct_precertificate_scts, sizeof(oid_ct_precertificate_scts)/sizeof(int) }, }; diff --git a/tools/certgen.c b/tools/certgen.c index 8e119ed2..71bde5a6 100644 --- a/tools/certgen.c +++ b/tools/certgen.c @@ -23,7 +23,7 @@ static const char *options = "[-C str] [-ST str] [-L str] [-O str] [-OU str] -CN str -days num " "-key file [-pass pass] " - "[-key_usage str]* [-out file]"; + "[-key_usage str]* [-ocsp uri] [-crl uri] [-out file]"; static int ext_key_usage_set(int *usages, const char *usage_name) @@ -49,6 +49,8 @@ int certgen_main(int argc, char **argv) char *common_name = NULL; int days = 0; int key_usage = 0; + char *ocsp = NULL; + char *crl = NULL; char *keyfile = NULL; char *pass = NULL; char *outfile = NULL; @@ -112,6 +114,12 @@ int certgen_main(int argc, char **argv) fprintf(stderr, "%s: invalid -key_usage value '%s'\n", prog, usage); goto end; } + } else if (!strcmp(*argv, "-ocsp")) { + if (--argc < 1) goto bad; + ocsp = *(++argv); + } else if (!strcmp(*argv, "-crl")) { + if (--argc < 1) goto bad; + crl = *(++argv); } else if (!strcmp(*argv, "-key")) { if (--argc < 1) goto bad; keyfile = *(++argv); @@ -173,6 +181,13 @@ bad: fprintf(stderr, "%s: inner error\n", prog); goto end; } + if (ocsp) { + if (x509_exts_add_authority_info_access(exts, &extslen, sizeof(exts), 0, + ocsp, strlen(ocsp), crl, strlen(crl)) != 1) { + fprintf(stderr, "%s: error\n", prog); + goto end; + } + } time(¬_before); if (rand_bytes(serial, sizeof(serial)) != 1