apps: ca,req,x509: Add explicit start and end dates options

- Added options `-not_before` (start date) and `-not-after` (end date)
  for explicit setting of the validity period of a certificate in the
  apps `ca`, `req` and `x509`
- The new options accept time strings or "today"
- In app `ca`, use the new options as aliases of the already existing
  options `-startdate` and `-enddate`
- When used in apps `req` and `x509`, the end date must be >= the start
  date, in app `ca` end date < start date is also accepted
- In any case, `-not-after` overrides the `-days` option
- Added helper function `check_cert_time_string` to validate given
  certificate time strings
- Use the new helper function in apps `ca`, `req` and `x509`
- Moved redundant code for time string checking into `set_cert_times`
  helper function.
- Added tests for explicit start and end dates in apps `req` and `x509`
- test: Added auxiliary functions for parsing fields from `-text`
  formatted output to `tconversion.pl`
- CHANGES: Added to new section 3.4

Signed-off-by: Stephan Wurm <atomisirsi@gsklan.de>

Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21716)
This commit is contained in:
Stephan Wurm 2023-08-09 09:07:46 +02:00 committed by Tomas Mraz
parent 4514e02cdf
commit 8120223773
12 changed files with 289 additions and 67 deletions

View File

@ -12,6 +12,7 @@ appropriate release branch.
OpenSSL Releases
----------------
- [OpenSSL 3.4](#openssl-34)
- [OpenSSL 3.3](#openssl-33)
- [OpenSSL 3.2](#openssl-32)
- [OpenSSL 3.1](#openssl-31)
@ -28,7 +29,12 @@ OpenSSL 3.4
### Changes between 3.3 and 3.4 [xx XXX xxxx]
* None yet
* Added options `-not_before` and `-not_after` for explicit setting
start and end dates of certificates created with the `req` and `x509`
apps. Added the same options also to `ca` app as alias for
`-startdate` and `-enddate` options.
*Stephan Wurm*
OpenSSL 3.3
-----------

View File

@ -150,7 +150,7 @@ typedef enum OPTION_choice {
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,
OPT_CRLDAYS, OPT_CRLHOURS, OPT_CRLSEC, OPT_NOT_BEFORE, OPT_NOT_AFTER,
OPT_INFILES, OPT_SS_CERT, OPT_SPKAC, OPT_REVOKE, OPT_VALID,
OPT_EXTENSIONS, OPT_EXTFILE, OPT_STATUS, OPT_UPDATEDB, OPT_CRLEXTS,
OPT_RAND_SERIAL, OPT_QUIET,
@ -199,10 +199,13 @@ const OPTIONS ca_options[] = {
"Always create a random serial; do not store it"},
{"multivalue-rdn", OPT_MULTIVALUE_RDN, '-',
"Deprecated; multi-valued RDNs support is always on."},
{"startdate", OPT_STARTDATE, 's', "Cert notBefore, YYMMDDHHMMSSZ"},
{"startdate", OPT_STARTDATE, 's',
"[CC]YYMMDDHHMMSSZ value for notBefore certificate field"},
{"not_before", OPT_NOT_BEFORE, 's', "An alias for -startdate"},
{"enddate", OPT_ENDDATE, 's',
"YYMMDDHHMMSSZ cert notAfter (overrides -days)"},
{"days", OPT_DAYS, 'p', "Number of days to certify the cert for"},
"[CC]YYMMDDHHMMSSZ value for notAfter certificate field, overrides -days"},
{"not_after", OPT_NOT_AFTER, 's', "An alias for -enddate"},
{"days", OPT_DAYS, 'p', "Number of days from today to certify the cert for"},
{"extensions", OPT_EXTENSIONS, 's',
"Extension section (override value in config file)"},
{"extfile", OPT_EXTFILE, '<',
@ -359,9 +362,11 @@ opthelp:
/* obsolete */
break;
case OPT_STARTDATE:
case OPT_NOT_BEFORE:
startdate = opt_arg();
break;
case OPT_ENDDATE:
case OPT_NOT_AFTER:
enddate = opt_arg();
break;
case OPT_DAYS:
@ -874,22 +879,8 @@ end_of_options:
if (startdate == NULL)
startdate =
app_conf_try_string(conf, section, ENV_DEFAULT_STARTDATE);
if (startdate != NULL && !ASN1_TIME_set_string_X509(NULL, startdate)) {
BIO_printf(bio_err,
"start date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n");
goto end;
}
if (startdate == NULL)
startdate = "today";
if (enddate == NULL)
enddate = app_conf_try_string(conf, section, ENV_DEFAULT_ENDDATE);
if (enddate != NULL && !ASN1_TIME_set_string_X509(NULL, enddate)) {
BIO_printf(bio_err,
"end date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n");
goto end;
}
if (days == 0) {
if (!app_conf_try_number(conf, section, ENV_DEFAULT_DAYS, &days))
days = 0;
@ -898,6 +889,9 @@ end_of_options:
BIO_printf(bio_err, "cannot lookup how many days to certify for\n");
goto end;
}
if (days != 0 && enddate != NULL)
BIO_printf(bio_err,
"Warning: -enddate or -not_after option overriding -days option\n");
if (rand_ser) {
if ((serial = BN_new()) == NULL || !rand_serial(serial, NULL)) {
@ -1671,7 +1665,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509,
goto end;
}
if (!set_cert_times(ret, startdate, enddate, days))
if (!set_cert_times(ret, startdate, enddate, days, 0))
goto end;
if (enddate != NULL) {

View File

@ -82,8 +82,12 @@ int has_stdin_waiting(void);
# endif
void corrupt_signature(const ASN1_STRING *signature);
/* Helpers for setting X509v3 certificate fields notBefore and notAfter */
int check_cert_time_string(const char *time, const char *desc);
int set_cert_times(X509 *x, const char *startdate, const char *enddate,
int days);
int days, int strict_compare_times);
int set_crl_lastupdate(X509_CRL *crl, const char *lastupdate);
int set_crl_nextupdate(X509_CRL *crl, const char *nextupdate,
long days, long hours, long secs);

View File

@ -3275,23 +3275,54 @@ void corrupt_signature(const ASN1_STRING *signature)
s[signature->length - 1] ^= 0x1;
}
int set_cert_times(X509 *x, const char *startdate, const char *enddate,
int days)
int check_cert_time_string(const char *time, const char *desc)
{
if (time == NULL || strcmp(time, "today") == 0
|| ASN1_TIME_set_string_X509(NULL, time))
return 1;
BIO_printf(bio_err,
"%s is invalid, it should be \"today\" or have format [CC]YYMMDDHHMMSSZ\n",
desc);
return 0;
}
int set_cert_times(X509 *x, const char *startdate, const char *enddate,
int days, int strict_compare_times)
{
if (!check_cert_time_string(startdate, "start date"))
return 0;
if (!check_cert_time_string(enddate, "end date"))
return 0;
if (startdate == NULL || strcmp(startdate, "today") == 0) {
if (X509_gmtime_adj(X509_getm_notBefore(x), 0) == NULL)
if (X509_gmtime_adj(X509_getm_notBefore(x), 0) == NULL) {
BIO_printf(bio_err, "Error setting notBefore certificate field\n");
return 0;
}
} else {
if (!ASN1_TIME_set_string_X509(X509_getm_notBefore(x), startdate))
if (!ASN1_TIME_set_string_X509(X509_getm_notBefore(x), startdate)) {
BIO_printf(bio_err, "Error setting notBefore certificate field\n");
return 0;
}
}
if (enddate != NULL && strcmp(enddate, "today") == 0) {
enddate = NULL;
days = 0;
}
if (enddate == NULL) {
if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL)
== NULL)
if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL) == NULL) {
BIO_printf(bio_err, "Error setting notAfter certificate field\n");
return 0;
}
} else if (!ASN1_TIME_set_string_X509(X509_getm_notAfter(x), enddate)) {
BIO_printf(bio_err, "Error setting notAfter certificate field\n");
return 0;
}
if (ASN1_TIME_compare(X509_get0_notAfter(x), X509_get0_notBefore(x)) < 0) {
BIO_printf(bio_err, "%s: end date before start date\n",
strict_compare_times ? "Error" : "Warning");
if (strict_compare_times)
return 0;
}
return 1;
}

View File

@ -43,7 +43,7 @@
#define DEFAULT_KEY_LENGTH 2048
#define MIN_KEY_LENGTH 512
#define DEFAULT_DAYS 30 /* default cert validity period in days */
#define DEFAULT_DAYS 30 /* default certificate validity period in days */
#define UNSET_DAYS -2 /* -1 may be used for testing expiration checks */
#define EXT_COPY_UNSET -1
@ -87,7 +87,7 @@ typedef enum OPTION_choice {
OPT_VERIFY, OPT_NOENC, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8,
OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT,
OPT_X509, OPT_X509V1, OPT_CA, OPT_CAKEY,
OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL,
OPT_MULTIVALUE_RDN, OPT_NOT_BEFORE, OPT_NOT_AFTER, OPT_DAYS, OPT_SET_SERIAL,
OPT_COPY_EXTENSIONS, OPT_EXTENSIONS, OPT_REQEXTS, OPT_ADDEXT,
OPT_PRECERT, OPT_MD,
OPT_SECTION, OPT_QUIET,
@ -127,7 +127,11 @@ const OPTIONS req_options[] = {
"Print the subject of the output request or cert"},
{"multivalue-rdn", OPT_MULTIVALUE_RDN, '-',
"Deprecated; multi-valued RDNs support is always on."},
{"days", OPT_DAYS, 'p', "Number of days cert is valid for"},
{"not_before", OPT_NOT_BEFORE, 's',
"[CC]YYMMDDHHMMSSZ value for notBefore certificate field"},
{"not_after", OPT_NOT_AFTER, 's',
"[CC]YYMMDDHHMMSSZ value for notAfter certificate field, overrides -days"},
{"days", OPT_DAYS, 'p', "Number of days certificate is valid for"},
{"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"},
{"copy_extensions", OPT_COPY_EXTENSIONS, 's',
"copy extensions from request when using -x509"},
@ -259,6 +263,7 @@ int req_main(int argc, char **argv)
char *template = default_config_file, *keyout = NULL;
const char *keyalg = NULL;
OPTION_CHOICE o;
char *not_before = NULL, *not_after = NULL;
int days = UNSET_DAYS;
int ret = 1, gen_x509 = 0, i = 0, newreq = 0, verbose = 0, progress = 1;
int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, keyform = FORMAT_UNDEF;
@ -423,9 +428,15 @@ int req_main(int argc, char **argv)
case OPT_CAKEY:
CAkeyfile = opt_arg();
break;
case OPT_NOT_BEFORE:
not_before = opt_arg();
break;
case OPT_NOT_AFTER:
not_after = opt_arg();
break;
case OPT_DAYS:
days = atoi(opt_arg());
if (days < -1) {
if (days <= UNSET_DAYS) {
BIO_printf(bio_err, "%s: -days parameter arg must be >= -1\n",
prog);
goto end;
@ -494,9 +505,13 @@ int req_main(int argc, char **argv)
if (!gen_x509) {
if (days != UNSET_DAYS)
BIO_printf(bio_err, "Ignoring -days without -x509; not generating a certificate\n");
BIO_printf(bio_err, "Warning: Ignoring -days without -x509; not generating a certificate\n");
if (not_before != NULL)
BIO_printf(bio_err, "Warning: Ignoring -not_before without -x509; not generating a certificate\n");
if (not_after != NULL)
BIO_printf(bio_err, "Warning: Ignoring -not_after without -x509; not generating a certificate\n");
if (ext_copy == EXT_COPY_NONE)
BIO_printf(bio_err, "Ignoring -copy_extensions 'none' when -x509 is not given\n");
BIO_printf(bio_err, "Warning: Ignoring -copy_extensions 'none' when -x509 is not given\n");
}
if (infile == NULL) {
if (gen_x509)
@ -802,10 +817,11 @@ int req_main(int argc, char **argv)
if (!X509_set_issuer_name(new_x509, issuer))
goto end;
if (days == UNSET_DAYS) {
if (days == UNSET_DAYS)
days = DEFAULT_DAYS;
}
if (!set_cert_times(new_x509, NULL, NULL, days))
else if (not_after != NULL)
BIO_printf(bio_err,"Warning: -not_after option overriding -days option\n");
if (!set_cert_times(new_x509, not_before, not_after, days, 1))
goto end;
if (!X509_set_subject_name(new_x509, n_subj))
goto end;

View File

@ -29,8 +29,8 @@
#undef POSTFIX
#define POSTFIX ".srl"
#define DEFAULT_DAYS 30 /* default cert validity period in days */
#define UNSET_DAYS -2 /* -1 is used for testing expiration checks */
#define DEFAULT_DAYS 30 /* default certificate validity period in days */
#define UNSET_DAYS -2 /* -1 may be used for testing expiration checks */
#define EXT_COPY_UNSET -1
static int callb(int ok, X509_STORE_CTX *ctx);
@ -54,6 +54,7 @@ typedef enum OPTION_choice {
OPT_CLRREJECT, OPT_ALIAS, OPT_CACREATESERIAL, OPT_CLREXT, OPT_OCSPID,
OPT_SUBJECT_HASH_OLD, OPT_ISSUER_HASH_OLD, OPT_COPY_EXTENSIONS,
OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT, OPT_PRESERVE_DATES,
OPT_NOT_BEFORE, OPT_NOT_AFTER,
OPT_R_ENUM, OPT_PROV_ENUM, OPT_EXT
} OPTION_CHOICE;
@ -135,6 +136,10 @@ const OPTIONS x509_options[] = {
"Serial number to use, overrides -CAserial"},
{"next_serial", OPT_NEXT_SERIAL, '-',
"Increment current certificate serial number"},
{"not_before", OPT_NOT_BEFORE, 's',
"[CC]YYMMDDHHMMSSZ value for notBefore certificate field"},
{"not_after", OPT_NOT_AFTER, 's',
"[CC]YYMMDDHHMMSSZ value for notAfter certificate field, overrides -days"},
{"days", OPT_DAYS, 'n',
"Number of days until newly generated certificate expires - default 30"},
{"preserve_dates", OPT_PRESERVE_DATES, '-',
@ -279,7 +284,7 @@ int x509_main(int argc, char **argv)
char *ext_names = NULL;
char *extsect = NULL, *extfile = NULL, *passin = NULL, *passinarg = NULL;
char *infile = NULL, *outfile = NULL, *privkeyfile = NULL, *CAfile = NULL;
char *prog;
char *prog, *not_before = NULL, *not_after = NULL;
int days = UNSET_DAYS; /* not explicitly set */
int x509toreq = 0, modulus = 0, print_pubkey = 0, pprint = 0;
int CAformat = FORMAT_UNDEF, CAkeyformat = FORMAT_UNDEF;
@ -376,9 +381,15 @@ int x509_main(int argc, char **argv)
if (!vfyopts || !sk_OPENSSL_STRING_push(vfyopts, opt_arg()))
goto opthelp;
break;
case OPT_NOT_BEFORE:
not_before = opt_arg();
break;
case OPT_NOT_AFTER:
not_after = opt_arg();
break;
case OPT_DAYS:
days = atoi(opt_arg());
if (days < -1) {
if (days <= UNSET_DAYS) {
BIO_printf(bio_err, "%s: -days parameter arg must be >= -1\n",
prog);
goto err;
@ -610,12 +621,22 @@ int x509_main(int argc, char **argv)
if (!opt_check_md(digest))
goto opthelp;
if (preserve_dates && not_before != NULL) {
BIO_printf(bio_err, "Cannot use -preserve_dates with -not_before option\n");
goto err;
}
if (preserve_dates && not_after != NULL) {
BIO_printf(bio_err, "Cannot use -preserve_dates with -not_after option\n");
goto err;
}
if (preserve_dates && days != UNSET_DAYS) {
BIO_printf(bio_err, "Cannot use -preserve_dates with -days option\n");
goto err;
}
if (days == UNSET_DAYS)
days = DEFAULT_DAYS;
else if (not_after != NULL)
BIO_printf(bio_err, "Warning: -not_after option overriding -days option\n");
if (!app_passwd(passinarg, NULL, &passin, NULL)) {
BIO_printf(bio_err, "Error getting password\n");
@ -837,7 +858,7 @@ int x509_main(int argc, char **argv)
goto end;
if (reqfile || newcert || privkey != NULL || CAfile != NULL) {
if (!preserve_dates && !set_cert_times(x, NULL, NULL, days))
if (!preserve_dates && !set_cert_times(x, not_before, not_after, days, 1))
goto end;
if (fissu != NULL) {
if (!X509_set_issuer_name(x, fissu))

View File

@ -30,7 +30,9 @@ B<openssl> B<ca>
[B<-crlsec> I<seconds>]
[B<-crlexts> I<section>]
[B<-startdate> I<date>]
[B<-not_before> I<date>]
[B<-enddate> I<date>]
[B<-not_after> I<date>]
[B<-days> I<arg>]
[B<-md> I<arg>]
[B<-policy> I<arg>]
@ -226,23 +228,32 @@ Don't output the text form of a certificate to the output file.
Specify the date output format. Values are: rfc_822 and iso_8601.
Defaults to rfc_822.
=item B<-startdate> I<date>
=item B<-startdate> I<date>, B<-not_before> I<date>
This allows the start date to be explicitly set. The format of the
date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure), or
YYYYMMDDHHMMSSZ (the same as an ASN1 GeneralizedTime structure). In
both formats, seconds SS and timezone Z must be present.
Alternatively, you can also use "today".
=item B<-enddate> I<date>
=item B<-enddate> I<date>, B<-not_after> I<date>
This allows the expiry date to be explicitly set. The format of the
date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure), or
YYYYMMDDHHMMSSZ (the same as an ASN1 GeneralizedTime structure). In
both formats, seconds SS and timezone Z must be present.
Alternatively, you can also use "today".
This overrides the B<-days> option.
=item B<-days> I<arg>
The number of days to certify the certificate for.
The number of days from today to certify the certificate for.
Regardless of the option B<-not_before>, the days are always counted from
today.
When used together with the option B<-not_after>/B<-startdate>, the explicit
expiry date takes precedence.
=item B<-md> I<alg>
@ -502,7 +513,7 @@ not necessary anymore, see the L</HISTORY> section.
=item B<default_days>
The same as the B<-days> option. The number of days to certify
The same as the B<-days> option. The number of days from today to certify
a certificate for.
=item B<default_startdate>

View File

@ -36,6 +36,8 @@ B<openssl> B<req>
[B<-x509v1>]
[B<-CA> I<filename>|I<uri>]
[B<-CAkey> I<filename>|I<uri>]
[B<-not_before> I<date>]
[B<-not_after> I<date>]
[B<-days> I<n>]
[B<-set_serial> I<n>]
[B<-newhdr>]
@ -327,12 +329,37 @@ Sets the "CA" private key to sign a certificate with.
The private key must match the public key of the certificate given with B<-CA>.
If this option is not provided then the key must be present in the B<-CA> input.
=item B<-not_before> I<date>
When B<-x509> is in use this allows the start date to be explicitly set,
otherwise it is ignored. The format of I<date> is YYMMDDHHMMSSZ (the
same as an ASN1 UTCTime structure), or YYYYMMDDHHMMSSZ (the same as an
ASN1 GeneralizedTime structure). In both formats, seconds SS and
timezone Z must be present.
Alternatively, you can also use "today".
=item B<-not_after> I<date>
When B<-x509> is in use this allows the expiry date to be explicitly
set, otherwise it is ignored. The format of I<date> is YYMMDDHHMMSSZ
(the same as an ASN1 UTCTime structure), or YYYYMMDDHHMMSSZ (the same as
an ASN1 GeneralizedTime structure). In both formats, seconds SS and
timezone Z must be present.
Alternatively, you can also use "today".
This overrides the B<-days> option.
=item B<-days> I<n>
When B<-x509> is in use this specifies the number of
days to certify the certificate for, otherwise it is ignored. I<n> should
When B<-x509> is in use this specifies the number of days from today to
certify the certificate for, otherwise it is ignored. I<n> should
be a positive integer. The default is 30 days.
Regardless of the option B<-not_before>, the days are always counted from
today.
When used together with the option B<-not_after>, the explicit expiry
date takes precedence.
=item B<-set_serial> I<n>
Serial number to use when outputting a self-signed certificate.

View File

@ -54,6 +54,8 @@ B<openssl> B<x509>
[B<-checkip> I<ipaddr>]
[B<-set_serial> I<n>]
[B<-next_serial>]
[B<-not_before> I<date>]
[B<-not_after> I<date>]
[B<-days> I<arg>]
[B<-preserve_dates>]
[B<-set_issuer> I<arg>]
@ -183,6 +185,8 @@ It sets the issuer name to the subject name (i.e., makes it self-issued).
Unless the B<-preserve_dates> option is supplied,
it sets the validity start date to the current time
and the end date to a value determined by the B<-days> option.
Start date and end date can also be explicitly supplied with options
B<-not_before> and B<-not_after>.
=item B<-signkey> I<filename>|I<uri>
@ -376,17 +380,40 @@ The serial number can be decimal or hex (if preceded by C<0x>).
Set the serial to be one more than the number in the certificate.
=item B<-not_before> I<date>
This allows the start date to be explicitly set. The format of the
date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure), or
YYYYMMDDHHMMSSZ (the same as an ASN1 GeneralizedTime structure). In
both formats, seconds SS and timezone Z must be present.
Alternatively, you can also use "today".
Cannot be used together with the B<-preserve_dates> option.
=item B<-not_after> I<date>
This allows the expiry date to be explicitly set. The format of the
date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure), or
YYYYMMDDHHMMSSZ (the same as an ASN1 GeneralizedTime structure). In
both formats, seconds SS and timezone Z must be present.
Alternatively, you can also use "today".
Cannot be used together with the B<-preserve_dates> option.
This overrides the option B<-days>.
=item B<-days> I<arg>
Specifies the number of days until a newly generated certificate expires.
Specifies the number of days from today until a newly generated certificate expires.
The default is 30.
Cannot be used together with the B<-preserve_dates> option.
Cannot be used together with the option B<-preserve_dates>.
If option B<-not_after> is set, the explicit expiry date takes precedence.
=item B<-preserve_dates>
When signing a certificate, preserve "notBefore" and "notAfter" dates of any
input certificate instead of adjusting them to current time and duration.
Cannot be used together with the B<-days> option.
Cannot be used together with the options B<-days>, B<-not_before> and B<-not_after>.
=item B<-set_issuer> I<arg>

View File

@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_req");
plan tests => 108;
plan tests => 109;
require_ok(srctop_file('test', 'recipes', 'tconversion.pl'));
@ -607,3 +607,15 @@ ok(run(app(["openssl", "req", "-x509", "-new", "-days", "365",
# Verify cert
ok(run(app(["openssl", "x509", "-in", "testreq-cert.pem",
"-noout", "-text"])), "cert verification");
# Generate cert with explicit start and end dates
my $today = strftime("%Y-%m-%d", localtime);
my $cert = "self-signed_explicit_date.pem";
ok(run(app(["openssl", "req", "-x509", "-new", "-text",
"-config", srctop_file('test', 'test.cnf'),
"-key", srctop_file("test", "testrsa.pem"),
"-not_before", "today",
"-not_after", "today",
"-out", $cert]))
&& get_not_before_date($cert) eq $today
&& get_not_after_date($cert) eq $today, "explicit start and end dates");

View File

@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_x509");
plan tests => 46;
plan tests => 51;
# Prevent MSys2 filename munging for arguments that look like file paths but
# aren't
@ -187,20 +187,6 @@ ok(!run(app(["openssl", "x509", "-noout", "-dates", "-dateopt", "invalid_format"
"-in", srctop_file("test/certs", "ca-cert.pem")])),
"Run with invalid -dateopt format");
# extracts issuer from a -text formatted-output
sub get_issuer {
my $f = shift(@_);
my $issuer = "";
open my $fh, $f or die;
while (my $line = <$fh>) {
if ($line =~ /Issuer:/) {
$issuer = $line;
}
}
close $fh;
return $issuer;
}
# Tests for signing certs (broken in 1.1.1o)
my $a_key = "a-key.pem";
my $a_cert = "a-cert.pem";
@ -224,7 +210,7 @@ ok(run(app(["openssl", "x509", "-in", $a_cert, "-CA", $ca_cert,
"-CAkey", $ca_key, "-set_serial", "1234567890",
"-preserve_dates", "-sha256", "-text", "-out", $a2_cert])));
# verify issuer is CA
ok (get_issuer($a2_cert) =~ /CN=ca.example.com/);
ok(get_issuer($a2_cert) =~ /CN=ca.example.com/);
my $in_csr = srctop_file('test', 'certs', 'x509-check.csr');
my $in_key = srctop_file('test', 'certs', 'x509-check-key.pem');
@ -268,6 +254,51 @@ ok(run(app(["openssl", "x509", "-req", "-text", "-CAcreateserial",
"-in", $b_csr])));
ok(-e $ca_serial_dot_in_dir);
# Tests for explict start and end dates of certificates
my $today;
my $enddate;
$today = strftime("%Y-%m-%d", localtime);
ok(run(app(["openssl", "x509", "-req", "-text",
"-key", $b_key,
"-not_before", "20231031000000Z",
"-not_after", "today",
"-in", $b_csr, "-out", $b_cert]))
&& get_not_before($b_cert) =~ /Oct 31 00:00:00 2023 GMT/
&& get_not_after_date($b_cert) eq $today);
# explicit start and end dates
ok(run(app(["openssl", "x509", "-req", "-text",
"-key", $b_key,
"-not_before", "20231031000000Z",
"-not_after", "20231231000000Z",
"-days", "99",
"-in", $b_csr, "-out", $b_cert]))
&& get_not_before($b_cert) =~ /Oct 31 00:00:00 2023 GMT/
&& get_not_after($b_cert) =~ /Dec 31 00:00:00 2023 GMT/);
# start date today and days
$today = strftime("%Y-%m-%d", localtime);
$enddate = strftime("%Y-%m-%d", localtime(time + 99 * 24 * 60 * 60));
ok(run(app(["openssl", "x509", "-req", "-text",
"-key", $b_key,
"-not_before", "today",
"-days", "99",
"-in", $b_csr, "-out", $b_cert]))
&& get_not_before_date($b_cert) eq $today
&& get_not_after_date($b_cert) eq $enddate);
# end date before start date
ok(!run(app(["openssl", "x509", "-req", "-text",
"-key", $b_key,
"-not_before", "today",
"-not_after", "20231031000000Z",
"-in", $b_csr, "-out", $b_cert])));
# default days option
$today = strftime("%Y-%m-%d", localtime);
$enddate = strftime("%Y-%m-%d", localtime(time + 30 * 24 * 60 * 60));
ok(run(app(["openssl", "x509", "-req", "-text",
"-key", $b_key,
"-in", $b_csr, "-out", $b_cert]))
&& get_not_before_date($b_cert) eq $today
&& get_not_after_date($b_cert) eq $enddate);
SKIP: {
skip "EC is not supported by this OpenSSL build", 1
if disabled("ec");

View File

@ -13,6 +13,8 @@ use warnings;
use File::Compare qw/compare_text/;
use File::Copy;
use OpenSSL::Test qw/:DEFAULT/;
use Time::Piece;
use POSIX qw(strftime);
my %conversionforms = (
# Default conversion forms. Other series may be added with
@ -176,4 +178,44 @@ sub cert_ext_has_n_different_lines {
# not unlinking $out
}
# extracts string value of certificate field from a -text formatted-output
sub get_field {
my ($f, $field) = @_;
my $string = "";
open my $fh, $f or die;
while (my $line = <$fh>) {
if ($line =~ /$field:\s+(.*)/) {
$string = $1;
}
}
close $fh;
return $string;
}
sub get_issuer {
return get_field(@_, "Issuer");
}
sub get_not_before {
return get_field(@_, "Not Before");
}
# Date as yyyy-mm-dd
sub get_not_before_date {
return Time::Piece->strptime(
get_not_before(@_),
"%b %d %T %Y %Z")->date;
}
sub get_not_after {
return get_field(@_, "Not After ");
}
# Date as yyyy-mm-dd
sub get_not_after_date {
return Time::Piece->strptime(
get_not_after(@_),
"%b %d %T %Y %Z")->date;
}
1;