openssl/test/recipes/25-test_x509.t
Viktor Dukhovni 1692e0d225 Fix fragile explicit cert date tests.
The tests used localtime to format "today's" date, but then extracted a
GMT date from the cert.  The comparison breaks when run late in the
evening west of UTC, or early in the AM hours east of UTC.

Also took care of case when test runs at stroke of midnight, by
accepting either the "today" before the cert creation, or the
"today" after, should they be different.

Fixes fragile tests in #21716

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24139)
2024-04-18 14:20:54 +02:00

309 lines
12 KiB
Perl

#! /usr/bin/env perl
# Copyright 2015-2024 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
use warnings;
use File::Spec;
use OpenSSL::Test::Utils;
use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_x509");
plan tests => 51;
# Prevent MSys2 filename munging for arguments that look like file paths but
# aren't
$ENV{MSYS2_ARG_CONV_EXCL} = "/CN=";
require_ok(srctop_file("test", "recipes", "tconversion.pl"));
my @certs = qw(test certs);
my $pem = srctop_file(@certs, "cyrillic.pem");
my $out_msb = "out-cyrillic.msb";
my $out_utf8 = "out-cyrillic.utf8";
my $der = "cyrillic.der";
my $der2 = "cyrillic.der";
my $msb = srctop_file(@certs, "cyrillic.msb");
my $utf = srctop_file(@certs, "cyrillic.utf8");
ok(run(app(["openssl", "x509", "-text", "-in", $pem, "-out", $out_msb,
"-nameopt", "esc_msb"])));
is(cmp_text($out_msb, $msb),
0, 'Comparing esc_msb output with cyrillic.msb');
ok(run(app(["openssl", "x509", "-text", "-in", $pem, "-out", $out_utf8,
"-nameopt", "utf8"])));
is(cmp_text($out_utf8, $utf),
0, 'Comparing utf8 output with cyrillic.utf8');
SKIP: {
skip "DES disabled", 1 if disabled("des");
skip "Platform doesn't support command line UTF-8", 1 if $^O =~ /^(VMS|msys)$/;
my $p12 = srctop_file("test", "shibboleth.pfx");
my $p12pass = "σύνθημα γνώρισμα";
my $out_pem = "out.pem";
ok(run(app(["openssl", "x509", "-text", "-in", $p12, "-out", $out_pem,
"-passin", "pass:$p12pass"])));
# not unlinking $out_pem
}
ok(!run(app(["openssl", "x509", "-in", $pem, "-inform", "DER",
"-out", $der, "-outform", "DER"])),
"Checking failure of mismatching -inform DER");
ok(run(app(["openssl", "x509", "-in", $pem, "-inform", "PEM",
"-out", $der, "-outform", "DER"])),
"Conversion to DER");
ok(!run(app(["openssl", "x509", "-in", $der, "-inform", "PEM",
"-out", $der2, "-outform", "DER"])),
"Checking failure of mismatching -inform PEM");
# producing and checking self-issued (but not self-signed) cert
my $subj = "/CN=CA"; # using same DN as in issuer of ee-cert.pem
my $extfile = srctop_file("test", "v3_ca_exts.cnf");
my $pkey = srctop_file(@certs, "ca-key.pem"); # issuer private key
my $pubkey = "ca-pubkey.pem"; # the corresponding issuer public key
# use any (different) key for signing our self-issued cert:
my $key = srctop_file(@certs, "serverkey.pem");
my $selfout = "self-issued.out";
my $testcert = srctop_file(@certs, "ee-cert.pem");
ok(run(app(["openssl", "pkey", "-in", $pkey, "-pubout", "-out", $pubkey]))
&& run(app(["openssl", "x509", "-new", "-force_pubkey", $pubkey, "-subj", $subj,
"-extfile", $extfile, "-key", $key, "-out", $selfout]))
&& run(app(["openssl", "verify", "-no_check_time",
"-trusted", $selfout, "-partial_chain", $testcert])));
# not unlinking $pubkey
# not unlinking $selfout
# test -set_issuer option
my $ca_issu = srctop_file(@certs, "ca-cert.pem"); # issuer cert
my $caout_issu = "ca-issu.out";
ok(run(app(["openssl", "x509", "-new", "-force_pubkey", $key, "-subj", "/CN=EE",
"-set_issuer", "/CN=TEST-CA", "-extfile", $extfile, "-CA", $ca_issu,
"-CAkey", $pkey, "-text", "-out", $caout_issu])));
ok(get_issuer($caout_issu) =~ /CN=TEST-CA/);
# not unlinking $caout
# simple way of directly producing a CA-signed cert with private/pubkey input
my $ca = srctop_file(@certs, "ca-cert.pem"); # issuer cert
my $caout = "ca-issued.out";
ok(run(app(["openssl", "x509", "-new", "-force_pubkey", $key, "-subj", "/CN=EE",
"-extfile", $extfile, "-CA", $ca, "-CAkey", $pkey, "-out", $caout]))
&& run(app(["openssl", "verify", "-no_check_time",
"-trusted", $ca, "-partial_chain", $caout])));
subtest 'x509 -- x.509 v1 certificate' => sub {
tconversion( -type => 'x509', -prefix => 'x509v1',
-in => srctop_file("test", "testx509.pem") );
};
subtest 'x509 -- first x.509 v3 certificate' => sub {
tconversion( -type => 'x509', -prefix => 'x509v3-1',
-in => srctop_file("test", "v3-cert1.pem") );
};
subtest 'x509 -- second x.509 v3 certificate' => sub {
tconversion( -type => 'x509', -prefix => 'x509v3-2',
-in => srctop_file("test", "v3-cert2.pem") );
};
subtest 'x509 -- pathlen' => sub {
ok(run(test(["v3ext", srctop_file(@certs, "pathlen.pem")])));
};
cert_contains(srctop_file(@certs, "fake-gp.pem"),
"2.16.528.1.1003.1.3.5.5.2-1-0000006666-Z-12345678-01.015-12345678",
1, 'x500 -- subjectAltName');
cert_contains(srctop_file(@certs, "ext-noAssertion.pem"),
"No Assertion",
1, 'X.509 Not Assertion Extension');
cert_contains(srctop_file(@certs, "ext-groupAC.pem"),
"Group Attribute Certificate",
1, 'X.509 Group Attribute Certificate Extension');
cert_contains(srctop_file(@certs, "ext-sOAIdentifier.pem"),
"Source of Authority",
1, 'X.509 Source of Authority Extension');
cert_contains(srctop_file(@certs, "ext-noRevAvail.pem"),
"No Revocation Available",
1, 'X.509 No Revocation Available');
cert_contains(srctop_file(@certs, "ext-singleUse.pem"),
"Single Use",
1, 'X509v3 Single Use');
cert_contains(srctop_file(@certs, "ext-indirectIssuer.pem"),
"Indirect Issuer",
1, 'X.509 Indirect Issuer');
sub test_errors { # actually tests diagnostics of OSSL_STORE
my ($expected, $cert, @opts) = @_;
my $infile = srctop_file(@certs, $cert);
my @args = qw(openssl x509 -in);
push(@args, $infile, @opts);
my $tmpfile = 'out.txt';
my $res = grep(/-text/, @opts) ? run(app([@args], stdout => $tmpfile))
: !run(app([@args], stderr => $tmpfile));
my $found = 0;
open(my $in, '<', $tmpfile) or die "Could not open file $tmpfile";
while(<$in>) {
print; # this may help debugging
$res &&= !m/asn1 encoding/; # output must not include ASN.1 parse errors
$found = 1 if m/$expected/; # output must include $expected
}
close $in;
# $tmpfile is kept to help with investigation in case of failure
return $res && $found;
}
# 3 tests for non-existence of spurious OSSL_STORE ASN.1 parse error output.
# This requires provoking a failure exit of the app after reading input files.
ok(test_errors("Bad output format", "root-cert.pem", '-outform', 'http'),
"load root-cert errors");
ok(test_errors("RC2-40-CBC", "v3-certs-RC2.p12", '-passin', 'pass:v3-certs'),
"load v3-certs-RC2 no asn1 errors"); # error msg should mention "RC2-40-CBC"
SKIP: {
skip "sm2 not disabled", 1 if !disabled("sm2");
ok(test_errors("Unable to load Public Key", "sm2.pem", '-text'),
"error loading unsupported sm2 cert");
}
# 3 tests for -dateopts formats
ok(run(app(["openssl", "x509", "-noout", "-dates", "-dateopt", "rfc_822",
"-in", srctop_file("test/certs", "ca-cert.pem")])),
"Run with rfc_8222 -dateopt format");
ok(run(app(["openssl", "x509", "-noout", "-dates", "-dateopt", "iso_8601",
"-in", srctop_file("test/certs", "ca-cert.pem")])),
"Run with iso_8601 -dateopt format");
ok(!run(app(["openssl", "x509", "-noout", "-dates", "-dateopt", "invalid_format",
"-in", srctop_file("test/certs", "ca-cert.pem")])),
"Run with invalid -dateopt format");
# Tests for signing certs (broken in 1.1.1o)
my $a_key = "a-key.pem";
my $a_cert = "a-cert.pem";
my $a2_cert = "a2-cert.pem";
my $ca_key = "ca-key.pem";
my $ca_cert = "ca-cert.pem";
my $cnf = srctop_file('apps', 'openssl.cnf');
# Create cert A
ok(run(app(["openssl", "req", "-x509", "-newkey", "rsa:2048",
"-config", $cnf,
"-keyout", $a_key, "-out", $a_cert, "-days", "365",
"-nodes", "-subj", "/CN=test.example.com"])));
# Create cert CA - note key size
ok(run(app(["openssl", "req", "-x509", "-newkey", "rsa:4096",
"-config", $cnf,
"-keyout", $ca_key, "-out", $ca_cert, "-days", "3650",
"-nodes", "-subj", "/CN=ca.example.com"])));
# Sign cert A with CA (errors on 1.1.1o)
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/);
my $in_csr = srctop_file('test', 'certs', 'x509-check.csr');
my $in_key = srctop_file('test', 'certs', 'x509-check-key.pem');
my $invextfile = srctop_file('test', 'invalid-x509.cnf');
# Test that invalid extensions settings fail
ok(!run(app(["openssl", "x509", "-req", "-in", $in_csr, "-signkey", $in_key,
"-out", "/dev/null", "-days", "3650" , "-extensions", "ext",
"-extfile", $invextfile])));
# Tests for issue #16080 (fixed in 1.1.1o)
my $b_key = "b-key.pem";
my $b_csr = "b-cert.csr";
my $b_cert = "b-cert.pem";
# Create the CSR
ok(run(app(["openssl", "req", "-new", "-newkey", "rsa:4096",
"-keyout", $b_key, "-out", $b_csr, "-nodes",
"-config", $cnf,
"-subj", "/CN=b.example.com"])));
# Sign it - position of "-text" matters!
ok(run(app(["openssl", "x509", "-req", "-text", "-CAcreateserial",
"-CA", $ca_cert, "-CAkey", $ca_key,
"-in", $b_csr, "-out", $b_cert])));
# Verify issuer is CA
ok(get_issuer($b_cert) =~ /CN=ca.example.com/);
# although no explicit extensions given:
has_version($b_cert, 3);
has_SKID($b_cert, 1);
has_AKID($b_cert, 1);
# Tests for https://github.com/openssl/openssl/issues/10442 (fixed in 1.1.1a)
# (incorrect default `-CAcreateserial` if `-CA` path has a dot in it)
my $folder_with_dot = "test_x509.folder";
ok(mkdir $folder_with_dot);
my $ca_cert_dot_in_dir = File::Spec->catfile($folder_with_dot, "ca-cert.pem");
ok(copy($ca_cert,$ca_cert_dot_in_dir));
my $ca_serial_dot_in_dir = File::Spec->catfile($folder_with_dot, "ca-cert.srl");
ok(run(app(["openssl", "x509", "-req", "-text", "-CAcreateserial",
"-CA", $ca_cert_dot_in_dir, "-CAkey", $ca_key,
"-in", $b_csr])));
ok(-e $ca_serial_dot_in_dir);
# Tests for explict start and end dates of certificates
my %today = (strftime("%Y-%m-%d", gmtime) => 1);
my $enddate;
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/
&& ++$today{strftime("%Y-%m-%d", gmtime)}
&& (grep { defined $today{$_} } get_not_after_date($b_cert)));
# 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", gmtime) => 1);
$enddate = strftime("%Y-%m-%d", gmtime(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]))
&& ++$today{strftime("%Y-%m-%d", gmtime)}
&& (grep { defined $today{$_} } get_not_before_date($b_cert))
&& 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", gmtime) => 1);
$enddate = strftime("%Y-%m-%d", gmtime(time + 30 * 24 * 60 * 60));
ok(run(app(["openssl", "x509", "-req", "-text",
"-key", $b_key,
"-in", $b_csr, "-out", $b_cert]))
&& ++$today{strftime("%Y-%m-%d", gmtime)}
&& (grep { defined $today{$_} } get_not_before_date($b_cert))
&& get_not_after_date($b_cert) eq $enddate);
SKIP: {
skip "EC is not supported by this OpenSSL build", 1
if disabled("ec");
ok(run(test(["x509_test"])), "running x509_test");
}