summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Edmisten <wcedmisten@gmail.com>2021-03-01 18:33:29 -0500
committerTomas Mraz <tomas@openssl.org>2021-06-11 12:39:46 +0200
commit8c5bff2220c4f39b48660afda40005871f53250d (patch)
treebc1d608ac356c10b1e460a296f4200dd310505e3
parent3eb4b5bfe66ba0911fffa05ff1e3a00f4c54d641 (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.md5
-rwxr-xr-xapps/ca.c40
-rw-r--r--apps/crl.c12
-rw-r--r--apps/include/apps.h1
-rw-r--r--apps/lib/apps.c9
-rw-r--r--apps/x509.c16
-rw-r--r--crypto/asn1/a_time.c36
-rw-r--r--crypto/x509/t_x509.c4
-rw-r--r--doc/man1/openssl-ca.pod.in6
-rw-r--r--doc/man1/openssl-crl.pod.in6
-rw-r--r--doc/man1/openssl-x509.pod.in6
-rw-r--r--doc/man3/ASN1_TIME_set.pod15
-rw-r--r--include/crypto/asn1.h2
-rw-r--r--include/openssl/asn1.h.in8
-rw-r--r--test/x509_time_test.c66
-rw-r--r--util/libcrypto.num1
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/