[openssl] master update

tomas at openssl.org tomas at openssl.org
Fri Jun 11 10:40:44 UTC 2021


The branch master has been updated
       via  8c5bff2220c4f39b48660afda40005871f53250d (commit)
      from  3eb4b5bfe66ba0911fffa05ff1e3a00f4c54d641 (commit)


- Log -----------------------------------------------------------------
commit 8c5bff2220c4f39b48660afda40005871f53250d
Author: William Edmisten <wcedmisten at gmail.com>
Date:   Mon Mar 1 18:33:29 2021 -0500

    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 at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14384)

-----------------------------------------------------------------------

Summary of changes:
 CHANGES.md                   |  5 ++++
 apps/ca.c                    | 40 +++++++++++++++------------
 apps/crl.c                   | 12 ++++++--
 apps/include/apps.h          |  1 +
 apps/lib/apps.c              |  9 ++++++
 apps/x509.c                  | 16 +++++++++--
 crypto/asn1/a_time.c         | 36 ++++++++++++++++++++----
 crypto/x509/t_x509.c         |  4 +--
 doc/man1/openssl-ca.pod.in   |  6 ++++
 doc/man1/openssl-crl.pod.in  |  6 ++++
 doc/man1/openssl-x509.pod.in |  6 ++++
 doc/man3/ASN1_TIME_set.pod   | 15 ++++++----
 include/crypto/asn1.h        |  2 +-
 include/openssl/asn1.h.in    |  8 +++++-
 test/x509_time_test.c        | 66 ++++++++++++++++++++++++++++++++++++++++----
 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.
diff --git a/apps/ca.c b/apps/ca.c
index 32abc021a6..24883615ed 100755
--- a/apps/ca.c
+++ b/apps/ca.c
@@ -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/util/libcrypto.num
@@ -5411,3 +5411,4 @@ ASN1_item_new_ex                        ?	3_0_0	EXIST::FUNCTION:
 ASN1_item_d2i_fp_ex                     ?	3_0_0	EXIST::FUNCTION:STDIO
 ASN1_item_d2i_bio_ex                    ?	3_0_0	EXIST::FUNCTION:
 ASN1_item_d2i_ex                        ?	3_0_0	EXIST::FUNCTION:
+ASN1_TIME_print_ex                      ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list