diff options
author | William Edmisten <wcedmisten@gmail.com> | 2021-03-01 18:33:29 -0500 |
---|---|---|
committer | Tomas Mraz <tomas@openssl.org> | 2021-06-11 12:39:46 +0200 |
commit | 8c5bff2220c4f39b48660afda40005871f53250d (patch) | |
tree | bc1d608ac356c10b1e460a296f4200dd310505e3 | |
parent | 3eb4b5bfe66ba0911fffa05ff1e3a00f4c54d641 (diff) |
Add support for ISO 8601 datetime format
Fixes #5430
Added the configuration file option "date_opt" to the openssl applications ca,
crl and x509.
Added ASN1_TIME_print_ex which supports the new datetime format using the
flag ASN1_DTFLGS_ISO8601
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14384)
-rw-r--r-- | CHANGES.md | 5 | ||||
-rwxr-xr-x | apps/ca.c | 40 | ||||
-rw-r--r-- | apps/crl.c | 12 | ||||
-rw-r--r-- | apps/include/apps.h | 1 | ||||
-rw-r--r-- | apps/lib/apps.c | 9 | ||||
-rw-r--r-- | apps/x509.c | 16 | ||||
-rw-r--r-- | crypto/asn1/a_time.c | 36 | ||||
-rw-r--r-- | crypto/x509/t_x509.c | 4 | ||||
-rw-r--r-- | doc/man1/openssl-ca.pod.in | 6 | ||||
-rw-r--r-- | doc/man1/openssl-crl.pod.in | 6 | ||||
-rw-r--r-- | doc/man1/openssl-x509.pod.in | 6 | ||||
-rw-r--r-- | doc/man3/ASN1_TIME_set.pod | 15 | ||||
-rw-r--r-- | include/crypto/asn1.h | 2 | ||||
-rw-r--r-- | include/openssl/asn1.h.in | 8 | ||||
-rw-r--r-- | test/x509_time_test.c | 66 | ||||
-rw-r--r-- | util/libcrypto.num | 1 |
16 files changed, 191 insertions, 42 deletions
diff --git a/CHANGES.md b/CHANGES.md index 5b0f0df11e..9eb5eeb19b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,11 @@ breaking changes, and mappings for the large list of deprecated functions. ### Changes between 1.1.1 and 3.0 [xx XXX xxxx] + * Add a configurable flag to output date formats as ISO 8601. Does not + change the default date format. + + *William Edmisten* + * Version of MSVC earlier than 1300 could get link warnings, which could be suppressed if the undocumented -DI_CAN_LIVE_WITH_LNK4049 was set. Support for this flag has been removed. @@ -99,7 +99,7 @@ static int certify(X509 **xret, const char *infile, int informat, const char *enddate, long days, int batch, const char *ext_sect, CONF *conf, int verbose, unsigned long certopt, unsigned long nameopt, - int default_op, int ext_copy, int selfsign); + int default_op, int ext_copy, int selfsign, unsigned long dateopt); static int certify_cert(X509 **xret, const char *infile, int certformat, const char *passin, EVP_PKEY *pkey, X509 *x509, const char *dgst, @@ -110,7 +110,7 @@ static int certify_cert(X509 **xret, const char *infile, int certformat, int multirdn, int email_dn, const char *startdate, const char *enddate, long days, int batch, const char *ext_sect, CONF *conf, int verbose, unsigned long certopt, - unsigned long nameopt, int default_op, int ext_copy); + unsigned long nameopt, int default_op, int ext_copy, unsigned long dateopt); static int certify_spkac(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, const char *dgst, STACK_OF(OPENSSL_STRING) *sigopts, @@ -119,7 +119,7 @@ static int certify_spkac(X509 **xret, const char *infile, EVP_PKEY *pkey, int multirdn, int email_dn, const char *startdate, const char *enddate, long days, const char *ext_sect, CONF *conf, int verbose, unsigned long certopt, - unsigned long nameopt, int default_op, int ext_copy); + unsigned long nameopt, int default_op, int ext_copy, unsigned long dateopt); static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const char *dgst, STACK_OF(OPENSSL_STRING) *sigopts, STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, @@ -127,7 +127,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, int email_dn, const char *startdate, const char *enddate, long days, int batch, int verbose, X509_REQ *req, const char *ext_sect, CONF *conf, unsigned long certopt, unsigned long nameopt, - int default_op, int ext_copy, int selfsign); + int default_op, int ext_copy, int selfsign, unsigned long dateopt); static int get_certificate_status(const char *ser_status, CA_DB *db); static int do_updatedb(CA_DB *db); static int check_time_format(const char *str); @@ -148,7 +148,7 @@ typedef enum OPTION_choice { OPT_CREATE_SERIAL, OPT_MULTIVALUE_RDN, OPT_STARTDATE, OPT_ENDDATE, OPT_DAYS, OPT_MD, OPT_POLICY, OPT_KEYFILE, OPT_KEYFORM, OPT_PASSIN, OPT_KEY, OPT_CERT, OPT_CERTFORM, OPT_SELFSIGN, - OPT_IN, OPT_INFORM, OPT_OUT, OPT_OUTDIR, OPT_VFYOPT, + OPT_IN, OPT_INFORM, OPT_OUT, OPT_DATEOPT, OPT_OUTDIR, OPT_VFYOPT, OPT_SIGOPT, OPT_NOTEXT, OPT_BATCH, OPT_PRESERVEDN, OPT_NOEMAILDN, OPT_GENCRL, OPT_MSIE_HACK, OPT_CRL_LASTUPDATE, OPT_CRL_NEXTUPDATE, OPT_CRLDAYS, OPT_CRLHOURS, OPT_CRLSEC, @@ -171,6 +171,7 @@ const OPTIONS ca_options[] = { {"inform", OPT_INFORM, 'F', "CSR input format (DER or PEM); default PEM"}, {"infiles", OPT_INFILES, '-', "The last argument, requests to process"}, {"out", OPT_OUT, '>', "Where to put the output file(s)"}, + {"dateopt", OPT_DATEOPT, 's', "Datetime format used for printing. (rfc_822/iso_8601). Default is rfc_822."}, {"notext", OPT_NOTEXT, '-', "Do not print the generated certificate"}, {"batch", OPT_BATCH, '-', "Don't ask questions"}, {"msie_hack", OPT_MSIE_HACK, '-', @@ -275,6 +276,7 @@ int ca_main(int argc, char **argv) char *dgst = NULL, *policy = NULL, *keyfile = NULL; char *certfile = NULL, *crl_ext = NULL, *crlnumberfile = NULL; int certformat = FORMAT_UNDEF, informat = FORMAT_UNDEF; + unsigned long dateopt = ASN1_DTFLGS_RFC822; const char *infile = NULL, *spkac_file = NULL, *ss_cert_file = NULL; const char *extensions = NULL, *extfile = NULL, *passinarg = NULL; char *passin = NULL; @@ -323,6 +325,10 @@ opthelp: case OPT_OUT: outfile = opt_arg(); break; + case OPT_DATEOPT: + if (!set_dateopt(&dateopt, opt_arg())) + goto opthelp; + break; case OPT_VERBOSE: verbose = 1; break; @@ -947,7 +953,7 @@ end_of_options: attribs, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, extensions, conf, verbose, certopt, get_nameopt(), default_op, - ext_copy); + ext_copy, dateopt); if (j < 0) goto end; if (j > 0) { @@ -968,7 +974,7 @@ end_of_options: db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, extensions, conf, verbose, certopt, get_nameopt(), default_op, - ext_copy); + ext_copy, dateopt); if (j < 0) goto end; if (j > 0) { @@ -988,7 +994,7 @@ end_of_options: sigopts, vfyopts, attribs, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, extensions, conf, verbose, - certopt, get_nameopt(), default_op, ext_copy, selfsign); + certopt, get_nameopt(), default_op, ext_copy, selfsign, dateopt); if (j < 0) goto end; if (j > 0) { @@ -1009,7 +1015,7 @@ end_of_options: attribs, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, extensions, conf, verbose, - certopt, get_nameopt(), default_op, ext_copy, selfsign); + certopt, get_nameopt(), default_op, ext_copy, selfsign, dateopt); if (j < 0) goto end; if (j > 0) { @@ -1357,7 +1363,7 @@ static int certify(X509 **xret, const char *infile, int informat, const char *enddate, long days, int batch, const char *ext_sect, CONF *lconf, int verbose, unsigned long certopt, unsigned long nameopt, - int default_op, int ext_copy, int selfsign) + int default_op, int ext_copy, int selfsign, unsigned long dateopt) { X509_REQ *req = NULL; EVP_PKEY *pktmp = NULL; @@ -1396,7 +1402,7 @@ static int certify(X509 **xret, const char *infile, int informat, ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, verbose, req, ext_sect, lconf, certopt, nameopt, default_op, - ext_copy, selfsign); + ext_copy, selfsign, dateopt); end: ERR_print_errors(bio_err); @@ -1414,7 +1420,7 @@ static int certify_cert(X509 **xret, const char *infile, int certformat, int multirdn, int email_dn, const char *startdate, const char *enddate, long days, int batch, const char *ext_sect, CONF *lconf, int verbose, unsigned long certopt, - unsigned long nameopt, int default_op, int ext_copy) + unsigned long nameopt, int default_op, int ext_copy, unsigned long dateopt) { X509 *template_cert = NULL; X509_REQ *rreq = NULL; @@ -1453,7 +1459,7 @@ static int certify_cert(X509 **xret, const char *infile, int certformat, ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, verbose, rreq, ext_sect, lconf, certopt, nameopt, default_op, - ext_copy, 0); + ext_copy, 0, dateopt); end: X509_REQ_free(rreq); @@ -1468,7 +1474,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, int email_dn, const char *startdate, const char *enddate, long days, int batch, int verbose, X509_REQ *req, const char *ext_sect, CONF *lconf, unsigned long certopt, unsigned long nameopt, - int default_op, int ext_copy, int selfsign) + int default_op, int ext_copy, int selfsign, unsigned long dateopt) { const X509_NAME *name = NULL; X509_NAME *CAname = NULL, *subject = NULL; @@ -1877,7 +1883,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, } BIO_printf(bio_err, "Certificate is to be certified until "); - ASN1_TIME_print(bio_err, X509_get0_notAfter(ret)); + ASN1_TIME_print_ex(bio_err, X509_get0_notAfter(ret), dateopt); if (days) BIO_printf(bio_err, " (%ld days)", days); BIO_printf(bio_err, "\n"); @@ -1970,7 +1976,7 @@ static int certify_spkac(X509 **xret, const char *infile, EVP_PKEY *pkey, int multirdn, int email_dn, const char *startdate, const char *enddate, long days, const char *ext_sect, CONF *lconf, int verbose, unsigned long certopt, - unsigned long nameopt, int default_op, int ext_copy) + unsigned long nameopt, int default_op, int ext_copy, unsigned long dateopt) { STACK_OF(CONF_VALUE) *sk = NULL; LHASH_OF(CONF_VALUE) *parms = NULL; @@ -2083,7 +2089,7 @@ static int certify_spkac(X509 **xret, const char *infile, EVP_PKEY *pkey, ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, 1, verbose, req, ext_sect, lconf, certopt, nameopt, default_op, - ext_copy, 0); + ext_copy, 0, dateopt); end: X509_REQ_free(req); CONF_free(parms); diff --git a/apps/crl.c b/apps/crl.c index ff7c314717..2158a107e5 100644 --- a/apps/crl.c +++ b/apps/crl.c @@ -23,7 +23,7 @@ typedef enum OPTION_choice { OPT_INFORM, OPT_IN, OPT_OUTFORM, OPT_OUT, OPT_KEYFORM, OPT_KEY, OPT_ISSUER, OPT_LASTUPDATE, OPT_NEXTUPDATE, OPT_FINGERPRINT, OPT_CRLNUMBER, OPT_BADSIG, OPT_GENDELTA, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE, - OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_VERIFY, OPT_TEXT, OPT_HASH, + OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_VERIFY, OPT_DATEOPT, OPT_TEXT, OPT_HASH, OPT_HASH_OLD, OPT_NOOUT, OPT_NAMEOPT, OPT_MD, OPT_PROV_ENUM } OPTION_CHOICE; @@ -41,6 +41,7 @@ const OPTIONS crl_options[] = { OPT_SECTION("Output"), {"out", OPT_OUT, '>', "output file - default stdout"}, {"outform", OPT_OUTFORM, 'F', "Output format - default PEM"}, + {"dateopt", OPT_DATEOPT, 's', "Datetime format used for printing. (rfc_822/iso_8601). Default is rfc_822."}, {"text", OPT_TEXT, '-', "Print out a text format version"}, {"hash", OPT_HASH, '-', "Print hash value"}, #ifndef OPENSSL_NO_MD5 @@ -91,6 +92,7 @@ int crl_main(int argc, char **argv) int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, keyformat = FORMAT_UNDEF; int ret = 1, num = 0, badsig = 0, fingerprint = 0, crlnumber = 0; int text = 0, do_ver = 0, noCAfile = 0, noCApath = 0, noCAstore = 0; + unsigned long dateopt = ASN1_DTFLGS_RFC822; int i; #ifndef OPENSSL_NO_MD5 int hash_old = 0; @@ -161,6 +163,10 @@ int crl_main(int argc, char **argv) case OPT_VERIFY: do_ver = 1; break; + case OPT_DATEOPT: + if (!set_dateopt(&dateopt, opt_arg())) + goto opthelp; + break; case OPT_TEXT: text = 1; break; @@ -327,13 +333,13 @@ int crl_main(int argc, char **argv) #endif if (lastupdate == i) { BIO_printf(bio_out, "lastUpdate="); - ASN1_TIME_print(bio_out, X509_CRL_get0_lastUpdate(x)); + ASN1_TIME_print_ex(bio_out, X509_CRL_get0_lastUpdate(x), dateopt); BIO_printf(bio_out, "\n"); } if (nextupdate == i) { BIO_printf(bio_out, "nextUpdate="); if (X509_CRL_get0_nextUpdate(x)) - ASN1_TIME_print(bio_out, X509_CRL_get0_nextUpdate(x)); + ASN1_TIME_print_ex(bio_out, X509_CRL_get0_nextUpdate(x), dateopt); else BIO_printf(bio_out, "NONE"); BIO_printf(bio_out, "\n"); diff --git a/apps/include/apps.h b/apps/include/apps.h index c9f77f6067..4b5c34f2e2 100644 --- a/apps/include/apps.h +++ b/apps/include/apps.h @@ -100,6 +100,7 @@ void print_bignum_var(BIO *, const BIGNUM *, const char*, void print_array(BIO *, const char *, int, const unsigned char *); int set_nameopt(const char *arg); unsigned long get_nameopt(void); +int set_dateopt(unsigned long *dateopt, const char *arg); int set_cert_ex(unsigned long *flags, const char *arg); int set_name_ex(unsigned long *flags, const char *arg); int set_ext_copy(int *copy_type, const char *arg); diff --git a/apps/lib/apps.c b/apps/lib/apps.c index 9aae725fc6..79fe4f8409 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -1214,6 +1214,15 @@ int set_name_ex(unsigned long *flags, const char *arg) return 1; } +int set_dateopt(unsigned long *dateopt, const char *arg) +{ + if (strcasecmp(arg, "rfc_822") == 0) + *dateopt = ASN1_DTFLGS_RFC822; + else if (strcasecmp(arg, "iso_8601") == 0) + *dateopt = ASN1_DTFLGS_ISO8601; + return 0; +} + int set_ext_copy(int *copy_type, const char *arg) { if (strcasecmp(arg, "none") == 0) diff --git a/apps/x509.c b/apps/x509.c index 8ec6ba2db5..25c75e8574 100644 --- a/apps/x509.c +++ b/apps/x509.c @@ -44,7 +44,7 @@ typedef enum OPTION_choice { OPT_CAKEYFORM, OPT_VFYOPT, OPT_SIGOPT, OPT_DAYS, OPT_PASSIN, OPT_EXTFILE, OPT_EXTENSIONS, OPT_IN, OPT_OUT, OPT_KEY, OPT_SIGNKEY, OPT_CA, OPT_CAKEY, OPT_CASERIAL, OPT_SET_SERIAL, OPT_NEW, OPT_FORCE_PUBKEY, OPT_SUBJ, - OPT_ADDTRUST, OPT_ADDREJECT, OPT_SETALIAS, OPT_CERTOPT, OPT_NAMEOPT, + OPT_ADDTRUST, OPT_ADDREJECT, OPT_SETALIAS, OPT_CERTOPT, OPT_DATEOPT, OPT_NAMEOPT, OPT_EMAIL, OPT_OCSP_URI, OPT_SERIAL, OPT_NEXT_SERIAL, OPT_MODULUS, OPT_PUBKEY, OPT_X509TOREQ, OPT_TEXT, OPT_HASH, OPT_ISSUER_HASH, OPT_SUBJECT, OPT_ISSUER, OPT_FINGERPRINT, OPT_DATES, @@ -87,6 +87,7 @@ const OPTIONS x509_options[] = { OPT_SECTION("Certificate printing"), {"text", OPT_TEXT, '-', "Print the certificate in text form"}, + {"dateopt", OPT_DATEOPT, 's', "Datetime format used for printing. (rfc_822/iso_8601). Default is rfc_822."}, {"certopt", OPT_CERTOPT, 's', "Various certificate text printing options"}, {"fingerprint", OPT_FINGERPRINT, '-', "Print the certificate fingerprint"}, {"alias", OPT_ALIAS, '-', "Print certificate alias"}, @@ -267,6 +268,7 @@ int x509_main(int argc, char **argv) int days = UNSET_DAYS; /* not explicitly set */ int x509toreq = 0, modulus = 0, print_pubkey = 0, pprint = 0; int CAformat = FORMAT_UNDEF, CAkeyformat = FORMAT_UNDEF; + unsigned long dateopt = ASN1_DTFLGS_RFC822; int fingerprint = 0, reqfile = 0, checkend = 0; int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, keyformat = FORMAT_UNDEF; int next_serial = 0, subject_hash = 0, issuer_hash = 0, ocspid = 0; @@ -330,6 +332,14 @@ int x509_main(int argc, char **argv) case OPT_REQ: reqfile = 1; break; + + case OPT_DATEOPT: + if (!set_dateopt(&dateopt, opt_arg())) { + BIO_printf(bio_err, + "Invalid date format: %s\n", opt_arg()); + goto end; + } + break; case OPT_COPY_EXTENSIONS: if (!set_ext_copy(&ext_copy, opt_arg())) { BIO_printf(bio_err, @@ -956,11 +966,11 @@ int x509_main(int argc, char **argv) X509_print_ex(out, x, get_nameopt(), certflag); } else if (i == startdate) { BIO_puts(out, "notBefore="); - ASN1_TIME_print(out, X509_get0_notBefore(x)); + ASN1_TIME_print_ex(out, X509_get0_notBefore(x), dateopt); BIO_puts(out, "\n"); } else if (i == enddate) { BIO_puts(out, "notAfter="); - ASN1_TIME_print(out, X509_get0_notAfter(x)); + ASN1_TIME_print_ex(out, X509_get0_notAfter(x), dateopt); BIO_puts(out, "\n"); } else if (i == fingerprint) { unsigned int n; diff --git a/crypto/asn1/a_time.c b/crypto/asn1/a_time.c index fb3bd2aca6..9b3074e47e 100644 --- a/crypto/asn1/a_time.c +++ b/crypto/asn1/a_time.c @@ -470,14 +470,22 @@ static const char _asn1_mon[12][4] = { "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -/* returns 1 on success, 0 on BIO write error or parse failure */ +/* prints the time with the default date format (RFC 822) */ int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm) { - return ossl_asn1_time_print_ex(bp, tm) > 0; + return ASN1_TIME_print_ex(bp, tm, ASN1_DTFLGS_RFC822); +} + +/* returns 1 on success, 0 on BIO write error or parse failure */ +int ASN1_TIME_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags) +{ + return ossl_asn1_time_print_ex(bp, tm, flags) > 0; } + +/* prints the time with the date format of ISO 8601 */ /* returns 0 on BIO write error, else -1 in case of parse failure, else 1 */ -int ossl_asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm) +int ossl_asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags) { char *v; int gmt = 0, l; @@ -508,15 +516,33 @@ int ossl_asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm) ++f_len; } - return BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s", + if ((flags & ASN1_DTFLGS_TYPE_MASK) == ASN1_DTFLGS_ISO8601) { + return BIO_printf(bp, "%4d-%02d-%02d %02d:%02d:%02d%.*s%s", + stm.tm_year + 1900, stm.tm_mon + 1, + stm.tm_mday, stm.tm_hour, + stm.tm_min, stm.tm_sec, f_len, f, + (gmt ? "Z" : "")) > 0; + } + else { + return BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s", _asn1_mon[stm.tm_mon], stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, f_len, f, stm.tm_year + 1900, (gmt ? " GMT" : "")) > 0; + } } else { - return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", + if ((flags & ASN1_DTFLGS_TYPE_MASK) == ASN1_DTFLGS_ISO8601) { + return BIO_printf(bp, "%4d-%02d-%02d %02d:%02d:%02d%s", + stm.tm_year + 1900, stm.tm_mon + 1, + stm.tm_mday, stm.tm_hour, + stm.tm_min, stm.tm_sec, + (gmt ? "Z" : "")) > 0; + } + else { + return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", _asn1_mon[stm.tm_mon], stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, stm.tm_year + 1900, (gmt ? " GMT" : "")) > 0; + } } } diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c index 8b84792b05..fdbdfd5b09 100644 --- a/crypto/x509/t_x509.c +++ b/crypto/x509/t_x509.c @@ -140,11 +140,11 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, goto err; if (BIO_write(bp, " Not Before: ", 24) <= 0) goto err; - if (ossl_asn1_time_print_ex(bp, X509_get0_notBefore(x)) == 0) + if (ossl_asn1_time_print_ex(bp, X509_get0_notBefore(x), ASN1_DTFLGS_RFC822) == 0) goto err; if (BIO_write(bp, "\n Not After : ", 25) <= 0) goto err; - if (ossl_asn1_time_print_ex(bp, X509_get0_notAfter(x)) == 0) + if (ossl_asn1_time_print_ex(bp, X509_get0_notAfter(x), ASN1_DTFLGS_RFC822) == 0) goto err; if (BIO_write(bp, "\n", 1) <= 0) goto err; diff --git a/doc/man1/openssl-ca.pod.in b/doc/man1/openssl-ca.pod.in index fc0b00c032..1d497e848e 100644 --- a/doc/man1/openssl-ca.pod.in +++ b/doc/man1/openssl-ca.pod.in @@ -44,6 +44,7 @@ B<openssl> B<ca> [B<-inform> B<DER>|<PEM>] [B<-out> I<file>] [B<-notext>] +[B<-dateopt>] [B<-outdir> I<dir>] [B<-infiles>] [B<-spkac> I<file>] @@ -209,6 +210,11 @@ self-signed certificate. Don't output the text form of a certificate to the output file. +=item B<-dateopt> + +Specify the date output format. Values are: rfc_822 and iso_8601. +Defaults to rfc_822. + =item B<-startdate> I<date> This allows the start date to be explicitly set. The format of the diff --git a/doc/man1/openssl-crl.pod.in b/doc/man1/openssl-crl.pod.in index e642f5c117..7e15f6445a 100644 --- a/doc/man1/openssl-crl.pod.in +++ b/doc/man1/openssl-crl.pod.in @@ -13,6 +13,7 @@ B<openssl> B<crl> [B<-outform> B<DER>|B<PEM>] [B<-key> I<filename>] [B<-keyform> B<DER>|B<PEM>|B<P12>] +[B<-dateopt>] [B<-text>] [B<-in> I<filename>] [B<-out> I<filename>] @@ -81,6 +82,11 @@ Output a comparison of the main CRL and the one specified here. Corrupt the signature before writing it; this can be useful for testing. +=item B<-dateopt> + +Specify the date output format. Values are: rfc_822 and iso_8601. +Defaults to rfc_822. + =item B<-text> Print out the CRL in text form. diff --git a/doc/man1/openssl-x509.pod.in b/doc/man1/openssl-x509.pod.in index 65b6487481..0e073d6b05 100644 --- a/doc/man1/openssl-x509.pod.in +++ b/doc/man1/openssl-x509.pod.in @@ -24,6 +24,7 @@ B<openssl> B<x509> [B<-outform> B<DER>|B<PEM>] [B<-nocert>] [B<-noout>] +[B<-dateopt>] [B<-text>] [B<-certopt> I<option>] [B<-fingerprint>] @@ -208,6 +209,11 @@ but are described in the L</Trust Settings> section. =over 4 +=item B<-dateopt> + +Specify the date output format. Values are: rfc_822 and iso_8601. +Defaults to rfc_822. + =item B<-text> Prints out the certificate in text form. Full details are printed including the diff --git a/doc/man3/ASN1_TIME_set.pod b/doc/man3/ASN1_TIME_set.pod index 3226049781..66d9fefe1a 100644 --- a/doc/man3/ASN1_TIME_set.pod +++ b/doc/man3/ASN1_TIME_set.pod @@ -9,7 +9,7 @@ ASN1_TIME_set_string, ASN1_UTCTIME_set_string, ASN1_GENERALIZEDTIME_set_string, ASN1_TIME_set_string_X509, ASN1_TIME_normalize, ASN1_TIME_to_tm, -ASN1_TIME_print, ASN1_UTCTIME_print, ASN1_GENERALIZEDTIME_print, +ASN1_TIME_print, ASN1_TIME_print_ex, ASN1_UTCTIME_print, ASN1_GENERALIZEDTIME_print, ASN1_TIME_diff, ASN1_TIME_cmp_time_t, ASN1_UTCTIME_cmp_time_t, ASN1_TIME_compare, @@ -44,6 +44,7 @@ ASN1_TIME_dup, ASN1_UTCTIME_dup, ASN1_GENERALIZEDTIME_dup - ASN.1 Time functions int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *t); int ASN1_TIME_print(BIO *b, const ASN1_TIME *s); + int ASN1_TIME_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags); int ASN1_UTCTIME_print(BIO *b, const ASN1_UTCTIME *s); int ASN1_GENERALIZEDTIME_print(BIO *b, const ASN1_GENERALIZEDTIME *s); @@ -107,6 +108,9 @@ If the time structure has invalid format it prints out "Bad time value" and returns an error. The output for generalized time may include a fractional part following the second. +ASN1_TIME_print_ex() provides I<flags> to specify the output format of the +datetime. This can be either B<ASN1_DTFLGS_RFC822> or B<ASN1_DTFLGS_ISO8601>. + ASN1_TIME_to_tm() converts the time I<s> to the standard I<tm> structure. If I<s> is NULL, then the current time is converted. The output time is GMT. The I<tm_sec>, I<tm_min>, I<tm_hour>, I<tm_mday>, I<tm_wday>, I<tm_yday>, @@ -177,12 +181,13 @@ either format. =head1 BUGS -ASN1_TIME_print(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() -do not print out the timezone: it either prints out "GMT" or nothing. But all +ASN1_TIME_print(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() do +not print out the timezone: it either prints out "GMT" or nothing. But all certificates complying with RFC5280 et al use GMT anyway. -ASN1_TIME_print(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() -do not distinguish if they fail because of an I/O error or invalid time format. +ASN1_TIME_print(), ASN1_TIME_print_ex(), ASN1_UTCTIME_print() and +ASN1_GENERALIZEDTIME_print() do not distinguish if they fail because +of an I/O error or invalid time format. Use the ASN1_TIME_normalize() function to normalize the time value before printing to get GMT results. diff --git a/include/crypto/asn1.h b/include/crypto/asn1.h index dd0b54aad6..ec76ae6fc6 100644 --- a/include/crypto/asn1.h +++ b/include/crypto/asn1.h @@ -140,7 +140,7 @@ int ossl_x509_algor_new_from_md(X509_ALGOR **palg, const EVP_MD *md); const EVP_MD *ossl_x509_algor_get_md(X509_ALGOR *alg); X509_ALGOR *ossl_x509_algor_mgf1_decode(X509_ALGOR *alg); int ossl_x509_algor_md_to_mgf1(X509_ALGOR **palg, const EVP_MD *mgf1md); -int ossl_asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm); +int ossl_asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags); EVP_PKEY * ossl_d2i_PrivateKey_legacy(int keytype, EVP_PKEY **a, const unsigned char **pp, long length, diff --git a/include/openssl/asn1.h.in b/include/openssl/asn1.h.in index 40b43a227a..6d5094a3ff 100644 --- a/include/openssl/asn1.h.in +++ b/include/openssl/asn1.h.in @@ -391,6 +391,11 @@ typedef const ASN1_ITEM *ASN1_ITEM_EXP (void); # define ASN1_STRFLGS_ESC_CTRL 2 # define ASN1_STRFLGS_ESC_MSB 4 +/* Lower 8 bits are reserved as an output type specifier */ +# define ASN1_DTFLGS_TYPE_MASK 0x0FUL +# define ASN1_DTFLGS_RFC822 0x00UL +# define ASN1_DTFLGS_ISO8601 0x01UL + /* * This flag determines how we do escaping: normally RC2253 backslash only, * set this to use backslash and quote. @@ -800,7 +805,8 @@ int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, const void *x); BIO *ASN1_item_i2d_mem_bio(const ASN1_ITEM *it, const ASN1_VALUE *val); int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a); int ASN1_GENERALIZEDTIME_print(BIO *fp, const ASN1_GENERALIZEDTIME *a); -int ASN1_TIME_print(BIO *fp, const ASN1_TIME *a); +int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm); +int ASN1_TIME_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags); int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v); int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long flags); int ASN1_buf_print(BIO *bp, const unsigned char *buf, size_t buflen, int off); diff --git a/test/x509_time_test.c b/test/x509_time_test.c index 41a88ec069..824131623a 100644 --- a/test/x509_time_test.c +++ b/test/x509_time_test.c @@ -482,7 +482,7 @@ static int test_days(int n) static const struct { ASN1_TIME asn1; const char *readable; -} x509_print_tests [] = { +} x509_print_tests_rfc_822 [] = { /* Generalized Time */ construct_asn1_time("20170731222050Z", V_ASN1_GENERALIZEDTIME, "Jul 31 22:20:50 2017 GMT"), @@ -506,7 +506,62 @@ static const struct { "Jul 31 22:20:00 2017 GMT"), }; -static int test_x509_time_print(int idx) +static const struct { + ASN1_TIME asn1; + const char *readable; +} x509_print_tests_iso_8601 [] = { + /* Generalized Time */ + construct_asn1_time("20170731222050Z", V_ASN1_GENERALIZEDTIME, + "2017-07-31 22:20:50Z"), + /* Generalized Time, no seconds */ + construct_asn1_time("201707312220Z", V_ASN1_GENERALIZEDTIME, + "2017-07-31 22:20:00Z"), + /* Generalized Time, fractional seconds (3 digits) */ + construct_asn1_time("20170731222050.123Z", V_ASN1_GENERALIZEDTIME, + "2017-07-31 22:20:50.123Z"), + /* Generalized Time, fractional seconds (1 digit) */ + construct_asn1_time("20170731222050.1Z", V_ASN1_GENERALIZEDTIME, + "2017-07-31 22:20:50.1Z"), + /* Generalized Time, fractional seconds (0 digit) */ + construct_asn1_time("20170731222050.Z", V_ASN1_GENERALIZEDTIME, + "Bad time value"), + /* UTC Time */ + construct_asn1_time("170731222050Z", V_ASN1_UTCTIME, + "2017-07-31 22:20:50Z"), + /* UTC Time, no seconds */ + construct_asn1_time("1707312220Z", V_ASN1_UTCTIME, + "2017-07-31 22:20:00Z"), +}; + +static int test_x509_time_print_rfc_822(int idx) +{ + BIO *m; + int ret = 0, rv; + char *pp; + const char *readable; + + if (!TEST_ptr(m = BIO_new(BIO_s_mem()))) + goto err; + + rv = ASN1_TIME_print_ex(m, &x509_print_tests_rfc_822[idx].asn1, ASN1_DTFLGS_RFC822); + readable = x509_print_tests_rfc_822[idx].readable; + + if (rv == 0 && !TEST_str_eq(readable, "Bad time value")) { + /* only if the test case intends to fail... */ + goto err; + } + if (!TEST_int_ne(rv = BIO_get_mem_data(m, &pp), 0) + || !TEST_int_eq(rv, (int)strlen(readable)) + || !TEST_strn_eq(pp, readable, rv)) + goto err; + + ret = 1; + err: + BIO_free(m); + return ret; +} + +static int test_x509_time_print_iso_8601(int idx) { BIO *m; int ret = 0, rv; @@ -516,8 +571,8 @@ static int test_x509_time_print(int idx) if (!TEST_ptr(m = BIO_new(BIO_s_mem()))) goto err; - rv = ASN1_TIME_print(m, &x509_print_tests[idx].asn1); - readable = x509_print_tests[idx].readable; + rv = ASN1_TIME_print_ex(m, &x509_print_tests_iso_8601[idx].asn1, ASN1_DTFLGS_ISO8601); + readable = x509_print_tests_iso_8601[idx].readable; if (rv == 0 && !TEST_str_eq(readable, "Bad time value")) { /* only if the test case intends to fail... */ @@ -541,6 +596,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_x509_cmp_time, OSSL_NELEM(x509_cmp_tests)); ADD_ALL_TESTS(test_x509_time, OSSL_NELEM(x509_format_tests)); ADD_ALL_TESTS(test_days, OSSL_NELEM(day_of_week_tests)); - ADD_ALL_TESTS(test_x509_time_print, OSSL_NELEM(x509_print_tests)); + ADD_ALL_TESTS(test_x509_time_print_rfc_822, OSSL_NELEM(x509_print_tests_rfc_822)); + ADD_ALL_TESTS(test_x509_time_print_iso_8601, OSSL_NELEM(x509_print_tests_iso_8601)); return 1; } diff --git a/util/libcrypto.num b/util/libcrypto.num index 8776e721a8..1d99fb420d 100644 --- a/util/libcrypto.num +++ b/ |