NAME

generate-test-pki - Generate a PKI for the Net-SSLeay test suite

VERSION

This document describes version 1.94 of generate-test-pki.

USAGE

# With openssl >= 3.0.0-alpha7 in PATH, and a version of Net::SSLeay built
# against OpenSSL >= 3.0.0-alpha7 in PERL5LIB:
generate-test-pki \
    -c pki.cfg \
    -o pki-output-dir

DESCRIPTION

The Net-SSLeay test suite relies on a dummy X.509 public key infrastructure (PKI). Occasionally, this PKI needs to be modified - for example, to add a certificate with certain properties when writing a new test - but maintaining it by hand is time-consuming, difficult, and error-prone.

generate-test-pki simplifies maintenance of the PKI by generating it from scratch using the OpenSSL command line utility, based on the structure defined in a simple configuration file. The files it generates can then be used in Net-SSLeay test scripts.

DEPENDENCIES

generate-test-pki requires at least version 3.0.0-alpha7 of the OpenSSL command line utility to be present either in PATH as openssl or at the path given by the -b option (see "OPTIONS"). Additionally, the first occurrance of Net::SSLeay in PERL5LIB must be built against at least version 3.0.0-alpha7 of OpenSSL.

LibreSSL is not supported, since its command line utility lacks some of the functionality relied on by this program.

OPTIONS

generate-test-pki accepts the following command line options:

  • -b FILE, --openssl-binary=FILE: the path to the OpenSSL binary to invoke when performing PKI generation operations. Defaults to openssl (i.e. the first occurrence of openssl in PATH).

  • -c FILE, --config=FILE: the path to the configuration file defining the PKI to generate. See "CONFIGURATION" for a description of the expected format.

  • -o DIR, --output=DIR: the path to the directory to which the PKI's files (see "OUTPUT") will be written. The directory must already exist. Existing files whose names collide with files written by this program will be overwritten without warning; other existing files will be left alone.

  • -v, --verbose: show the output of openssl and examples/x509_cert_details.pl when they are invoked.

CONFIGURATION

The configuration file is an anonymous Perl hash whose keys define the names of the PKI's entities and whose values define each entity's properties:

{
    'entity-name' => {
        'key'    => { ... },  # Private key properties
        'csr'    => { ... },  # Certificate signing request (CSR) properties
        'cert'   => { ... },  # Certificate properties
        'pkcs12' => { ... },  # PKCS#12 archive properties
        'crl'    => { ... },  # Certificate revocation list (CRL) properties
                              # (optional; for CA entities only)
    },
    ...
}

key

An anonymous hash defining properties relating to the entity's private key.

Valid keys:

  • algorithm: the public key algorithm to use when generating the private key. Must be one of ec, ed25519, ed448, rsa, rsa-pss, x25519, or x448.

  • passphrase: the passphrase under which to encrypt the private key. Used only when generating encrypted forms of the key.

  • size: the size of the public key to generate, in bits. Used only when algorithm is ec, rsa, or rsa-pss.

csr

An anonymous hash defining properties relating to the entity's PKCS#10 certificate signing request (CSR). The value of the subject key in "cert" will be used to generate a subject name for the CSR.

Valid keys:

  • md_algorithm: the message digest algorithm used to sign the CSR. May be any value supported by openssl dgst; commonly-supported values include md5, sha1, and sha256.

cert

An anonymous hash defining properties relating to the entity's X.509 v3 certificate.

Valid keys:

  • extensions: optional; an anonymous hash defining the X.509 v3 extensions that should be specified in the certificate. Keys are expected to be extension field names as they appear in x509v3_config(5), and values are expected to be either strings or anonymous arrays of strings (whose elements will be concatenated and delimited with commas), e.g.:

    {
        basicConstraints    => 'critical,CA:false',
        certificatePolicies => [ '1.2.3', '4.5.6' ],  # Becomes '1.2.3,4.5.6'
    }
  • issuer: a top-level key denoting the entity that should sign this certificate. If undefined, the entity's certificate will be self-signed.

  • md_algorithm: the message digest algorithm used to sign the certificate. May be any value supported by openssl dgst; commonly-supported values include md5, sha1, and sha256.

  • purpose: a string describing the purpose of the certificate. The value given here will define reasonable values for the keyUsage, extendedKeyUsage, basicConstraints, and/or subjectKeyIdentifier X.509 v3 extension fields. Must be one of ca, server, client, email, or custom (in which case no default values will be defined for any of the aforementioned fields, allowing for complete control of the fields that appear in the certificate via the extensions key).

  • revoke_reason: optional; the reason for revoking the certificate. Must be one of affiliationChanged, CACompromise, certificateHold, cessationOfOperation, keyCompromise, superseded, or unspecified.

  • revoke_time: optional; a timestamp string in YYYY-MM-DD hh:mm:ss format denoting the time at which the certificate was revoked, in the UTC time zone. Must be specified if revoke_reason is specified.

  • serial: a decimal integer denoting the certificate's serial number. Must be unique among the serial numbers of all certificates issued by the entity given in issuer.

  • subject: an anonymous array denoting the certificate's subject name; elements are expected to alternate between field names in either short or long format and values for those fields, e.g.:

    [
        C          => 'PL',
        O          => 'Net-SSLeay',
        OU         => 'Test Suite',
        commonName => 'test.net-ssleay.example',
    ]

    The order of the fields is preserved when generating the Distinguished Name string.

  • valid_from: a timestamp string in YYYY-MM-DD hh:mm:ss format denoting the time from which the certificate is valid, in the UTC time zone.

  • valid_to: a timestamp string in YYYY-MM-DD hh:mm:ss format denoting the time until which the certificate is valid, in the UTC time zone.

pkcs12

An anonymous hash defining properties relating to the entity's PKCS#12 archives.

Valid keys:

  • passphrase: the passphrase under which to encrypt the private key stored in the archive. Used only when generating archives that contain encrypted forms of the private key.

crl

An anonymous hash defining properties relating to the entity's certificate revocation list (CRL). Only used when the entity is a certificate authority and at least one of the certificates it issues requires revocation.

Valid keys:

  • last_update: a timestamp string in YYYY-MM-DD hh:mm:ss format denoting the time at which the CRL was last updated, in the UTC time zone.

  • md_algorithm: the message digest algorithm used to sign the CRL. May be any value supported by openssl dgst; commonly-supported values include md5, sha1, and sha256.

  • next_update: a timestamp string in YYYY-MM-DD hh:mm:ss format denoting the time at which the CRL is next expected to be updated, in the UTC time zone.

  • number: a decimal integer denoting the CRL number.

OUTPUT

For each entity E declared in the configuration file, generate-test-pki ensures the following set of files exists:

  • E.key.pem: a private key in PEM format. Will not be generated if it already exists; the key in the existing file will be used instead.

  • E.key.enc.pem: the file above, encrypted with AES-128 using the passphrase given in the configuration file (see "CONFIGURATION").

  • E.key.der: E.key.pem in DER format.

  • E.key.enc.der: the file above, encrypted with AES-128 using the passphrase given in the configuration file (see "CONFIGURATION").

  • E.csr.pem: a certificate signing request in PEM format.

  • E.csr.der: the file above in DER format.

  • E.cert.pem: a certificate in PEM format, signed by the entity given in the configuration file (see "CONFIGURATION").

  • E.cert.der: the file above in DER format.

  • E.cert.dump: the output of examples/x509_cert_details.pl -dump -pem E.cert.pem. x509_cert_details.pl is a Net-SSLeay example script whose output is used by the test suite to verify the correct operation of various libssl certificate information functions.

  • E.cert.certchain.pem: the certificate chain in PEM format, starting with E's certificate and ending with the root CA certificate.

  • E.cert.certchain.der: the file above, with certificates in DER format.

  • E.p12: a PKCS#12 archive containing a private key and a certificate.

  • E.enc.p12: the file above, with the private key encrypted with AES-128 using the passphrase given in the configuration file (see "CONFIGURATION").

  • E.certchain.p12: a PKCS#12 archive containing a private key and a certificate chain starting with E's certificate and ending with the root CA certificate.

  • E.certchain.enc.p12: the file above, with the private key encrypted with AES-128 using the passphrase given in the configuration file (see "CONFIGURATION").

Additionally, for entities that sign and then revoke at least one certificate, generate-test-pki outputs the following files:

  • E.crl.pem: a certificate revocation list (version 2) in PEM format.

  • E.crl.der: the file above in DER format.

DIAGNOSTICS

generate-test-pki outputs a diagnostic message to stderr and immediately exits with exit code 1 if an error occurs. Error messages listed below indicate invalid input or a problem with the state of the system that can usually be fixed. Error messages not listed below are internal and should never be encountered under normal operation; please report any occurrences of such errors as bugs (see "BUGS").

Error while parsing command line options: PKI configuration file PATH does not exist

The PKI configuration file at PATH, as specified by the -c command line option (or pki.cfg in the same directory as generate-test-pki if a value for -c was not specified), does not exist. Ensure pki.cfg exists, or speicify an alternative path with -c PATH.

Error while parsing command line options: an output directory must be given

The -o option is compulsory, and has no default value. Pass the path to a directory in which the output files described in "OUTPUT" should be written with -o PATH.

Error while parsing command line options: output directory PATH does not exist

generate-test-pki does not attempt to create the directory at the path given by the -o option; it must already exist and be writable.

Could not load PKI configuration file: PATH: REASON

The configuration file at PATH could not be loaded because of REASON, which is probably an OS-level error. Ensure the file at PATH is readable.

Could not load PKI configuration file: syntax error: REASON

The configuration file could not be parsed because of REASON, which is likely a Perl syntax error. Ensure the configuration file is valid Perl and meets the specification given in "CONFIGURATION".

OpenSSL minimum version check failed: `openssl version` exited with exit code N

generate-test-pki attempted to check the version of the OpenSSL command line utility currently in use by invoking openssl version, and expected it to exit with exit code 0 (indicating success) but it actually exited with exit code N (indicating failure). Check that the first occurrence of openssl in PATH is in fact the OpenSSL command line utility, then run generate-test-pki with the -v option to see the full output from openssl version, which may help diagnose the problem further.

OpenSSL minimum version check failed: `openssl` is not the OpenSSL command line utility

generate-test-pki attempted to check the version of the OpenSSL command line utility currently in use by invoking openssl version, but its output was inconsistent with the output format known to be used by OpenSSL. Check that the first occurrence of openssl in PATH is in fact the OpenSSL command line utility (and not the LibreSSL command line utility), then run generate-test-pki with the -v option to see the full output from openssl version, which may help diagnose the problem further.

OpenSSL minimum version check failed: OpenSSL >= MINVER required, but `openssl` is version VER

generate-test-pki relies on features of the OpenSSL command line utility that were added in version MINVER, but the first occurrence of openssl in PATH is version VER, which is insufficient. It may be necessary to compile a newer version of OpenSSL from the source code and prepend the directory containing the command line utility to PATH in order to solve this problem.

OpenSSL minimum version check failed: could not load Net::SSLeay: REASON

generate-test-pki attempted to check the version of OpenSSL that Net::SSLeay is built against, but was unable to import Net::SSLeay because of REASON. Ensure the first occurrence of Net::SSLeay in PERL5LIB can be imported by Perl.

OpenSSL minimum version check failed: Net::SSLeay was not built against OpenSSL

generate-test-pki relies on features of Net::SSLeay that are only available when it is built against OpenSSL, but the first occurrence of Net::SSLeay in PERL5LIB is built against LibreSSL. Rebuild Net::SSLeay against OpenSSL and ensure the rebuilt version is the first occurrence of Net::SSLeay in PERL5LIB.

OpenSSL minimum version check failed: Net::SSLeay must be built against OpenSSL >= MINVER, but it is built against version VER

generate-test-pki relies on features of Net::SSLeay that are only available when it is built against OpenSSL version MINVER, but the first occurrence of Net::SSLeay in PERL5LIB is built against OpenSSL VER. Rebuild Net::SSLeay against a newer version OpenSSL - ideally the same version as the OpenSSL command line utility - and ensure the rebuilt version is the first occurrence of Net::SSLeay in PERL5LIB.

Could not create temporary working directory: REASON

generate-test-pki attempted to create a directory to store some temporary files that are necessary to generate the output files, but was unable to create the directory because of REASON (which is probably an OS-level error). Ensure the system's temporary directory is writable.

Error while building PKI tree: entity 'E': issuer 'I' is not defined

The configuration file defines an entity E whose issuer (per the the value of its {cert}->{issuer} key) does not exist. Check that I is not misnamed and that the value of {cert}->{issuer} for E is correct.

Could not generate 'E.key.pem': missing key algorithm

The configuration file defines an entity E with no value for {key}->{algorithm}. See "key" for a list of acceptable values.

Could not generate 'E.key.pem': unknown key algorithm 'ALGORITHM'

The configuration file defines an entity E with the value ALGORITHM for {key}->{algorithm}, but this is not a known public key algorithm. See "key" for a list of acceptable values.

Could not generate 'E.key.pem': key algorithm 'ALGORITHM' requires a key size

The configuration file defines an entity E with the value ALGORITHM for {key}->{algorithm}, but ALGORITHM requires a key size to be defined in {key}->{size}. Define a valid key size for this entity's private key. See "key" for more information.

Could not generate 'E.csr.pem': invalid key/value pair given in subject

The configuration file defines an entity E with at least one undefined element in its value for {cert}->{subject}. Undefined elements cannot be stringified, so the subject could not be transformed into a Distinguished Name string. See "cert" for more information of the expected format for {cert}->{subject}.

Could not generate 'E.csr.pem': missing message digest algorithm

The configuration file defines an entity E with no value for {csr}->{md_algorithm}. See "csr" for possible values.

Could not generate 'E.cert.pem': valid_from: invalid timestamp

The configuration file defines an entity E with an invalid timestamp for its value of {cert}->{valid_from}. See "cert" for more information on the expected timestamp format.

Could not generate 'E.cert.pem': valid_until: invalid timestamp

The configuration file defines an entity E with an invalid timestamp for its value of {cert}->{valid_to}. See "cert" for more information on the expected timestamp format.

Could not generate 'E.cert.pem': missing message digest algorithm

The configuration file defines an entity E with no value for {cert}->{md_algorithm}. See "cert" for possible values.

Could not generate 'E.cert.pem': could not create directory PATH: REASON

generate-test-pki attempted to create a temporary directory at PATH to store intermediate files that are necessary to generate E's certificate, but was unable to do so because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is writable.

Could not generate 'E.cert.pem': could not write serial file PATH: REASON

generate-test-pki attempted to write an intermediate file to PATH (a subdirectory of a temporary directory it created earlier) that is necessary to generate E's certificate, but was unable to do so because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is writable.

Could not generate 'E.cert.dump': could not write PATH: REASON

generate-test-pki attempted to write information about E's certificate to the file at PATH, but was unable to do so because of REASON, which is probably an OS-level error. Ensure the file at PATH is writable.

Could not generate 'E.cert.dump': could not run examples/x509_cert_details.pl: REASON

generate-test-pki attempted to invoke the Perl script examples/x509_cert_details.pl (part of the Net-SSLeay source distribution) to produce an output file containing information about E's certificate, but was unable to invoke the script because of REASON. Ensure that the script is located at ../examples/x509_cert_details.pl relative to the path to generate-test-pki, that it can be executed given the values of PATH and PERL5LIB that are inherited by generate-test-pki, and that a suitable version of Net::SSLeay is present in PERL5LIB (see "DEPENDENCIES" for more information).

Could not generate 'E.cert.dump': examples/x509_cert_details.pl exited with exit code N

generate-test-pki invoked the Perl script examples/x509_cert_details.pl (part of the Net-SSLeay source distribution) to produce an output file containing information about E's certificate, and expected it to exit with exit code 0 (indicating success) but it actually exited with exit code N (indicating failure). Run generate-test-pki with the -v option to see the full output from examples/x509_cert_details.pl, which may help diagnose the problem further.

Could not generate 'E.certchain.pem': could not write certificate chain file PATH: REASON
Could not generate 'E.certchain.der': could not write certificate chain file PATH: REASON

generate-test-pki attempted to concatenate the certificates in E's issuer chain (in either format) and write them to PATH, but was unable to do so because of REASON, which is probably an OS-level error. Ensure the file at PATH is writable.

Could not generate 'E.certchain.pem': could not read certificate file PATH: REASON
Could not generate 'E.certchain.der': could not read certificate file PATH: REASON

generate-test-pki attempted to read a certificate in E's issuer chain (in either format) at PATH, but was unable to do so because of REASON, which is probably an OS-level error. Ensure the file at PATH is readable.

Could not generate 'E.crl.pem': could not create directory PATH: REASON

generate-test-pki attempted to create a temporary directory at PATH to store intermediate files that are necessary to generate E's CRL, but was unable to do so because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is writable.

Could not generate 'E.crl.pem': could not read database file PATH: REASON

When revoking a certificate, generate-test-pki looks up the certificate's serial number in its issuing entity's database file, which is created by OpenSSL in a temporary directory created earlier by generate-test-pki. It was unable to read this file on this occasion because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is readable.

Could not generate 'E.crl.pem': could not write database file PATH: REASON

To revoke a certificate, generate-test-pki updates the certificate's entry in its issuing entity's database file, which is created by OpenSSL in a temporary directory created earlier by generate-test-pki. It was unable to update the file on this occasion because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is writable.

Could not generate 'E.crl.pem': could not write CRL number file PATH: REASON

When revoking a certificate, generate-test-pki stores the CRL number for the CRL it outputs in a file in a temporary directory it created earlier. It was unable to write this file on this occasion because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is writable.

Could not generate 'E.certchain.p12': could not create directory PATH: REASON
Could not generate 'E.certchain.enc.p12': could not create directory PATH: REASON

When generating a PKCS#12 archive containing multiple certificates, generate-test-pki concatenates the certificates and writes them to a file in a temporary directory it creates before passing the path to that file in a command line option to openssl. It was unable to create the temporary directory on this occasion because of REASON, which is probably an OS-level error. Ensure the system's temporary directory is writable.

OpenSSL minimum version check failed: could not run `openssl version`: REASON
Could not generate 'PATH': failed to run `openssl COMMAND`: REASON

generate-test-pki attempted to invoke the OpenSSL command line utility, but failed to spawn a new process because of REASON, which is probably an OS-level error.

Could not generate 'PATH': `openssl COMMAND` failed with exit code N

generate-test-pki attempted to generate an output file by invoking the OpenSSL command line utility, and expected it to exit with exit code 0 (indicating success) but it actually exited with exit code N (indicating failure). Check that the PKI defined in the configuration file is sensible, then run generate-test-pki with the -v option to see the full output from openssl.

LIMITATIONS

Although its interface is almost identical to the OpenSSL command line utility, generate-test-pki is incompatible with the LibreSSL command line utility, since it relies on features currently only found in the OpenSSL command line utility.

Only limited error checking is performed on the configuration file; in particular, generate-test-pki will not always complain if required keys are missing. It is recommended to run the program with the -v option after editing the configuration file to ensure openssl is being invoked as expected.

Entities can have their certificates issued by one and only one entity; cross-signed certificates cannot currently be generated.

The uniqueness of serial numbers among the certificates signed by any given issuer is not enforced, and duplication will likely cause odd output from generate-test-pki and breakage when certificates are revoked. Care should be taken when editing serial numbers in the configuration file.

While as much effort as possible has been put into generating output files deterministically, generate-test-pki will still generate different private keys and PKCS#12 archives on every invocation, even when the PKI configuration file has not changed between invocations. generate-test-pki will avoid overwriting the private key for an entity if one already exists, but cannot recreate a private key that has been deleted. PKCS#12 archives cannot be generated deterministically because the PKCS#12 file format uses salts and IVs that the OpenSSL command line utiltity randomly generates on each invocation.

SEE ALSO

The man pages for the OpenSSL command line utility subcommands invoked by generate-test-pki: openssl-ca(1), openssl-crl(1), openssl-genpkey(1), openssl-pkey(1), openssl-req(1), and openssl-x509(1).

BUGS

If you encounter a problem with this program that you believe is a bug, please create a new issue in the Net-SSLeay GitHub repository. Please make sure your bug report includes the following information:

  • the list of command line options passed to generate-test-pki;

  • the full configuration file given by the -c command line option;

  • the full output of generate-test-pki;

  • your operating system name and version;

  • the output of perl -V;

  • the version of Net-SSLeay you are using;

  • the version of OpenSSL you are using.

AUTHORS

Originally written by Chris Novakovic.

Maintained by Chris Novakovic and Heikki Vatiainen.

COPYRIGHT AND LICENSE

Copyright 2020- Chris Novakovic <chris@chrisn.me.uk>.

Copyright 2020- Heikki Vatiainen <hvn@radiatorsoftware.com>.

This module is released under the terms of the Artistic License 2.0. For details, see the LICENSE file distributed with Net-SSLeay's source code.