From: Jelte Jansen Date: Wed, 14 Nov 2012 12:38:17 +0000 (+0100) Subject: [1044] Added b10-certgen tool X-Git-Tag: bind10-1.0.0-beta-release~46^2~22^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=35491a7585e0e7ed13ea9012ff03415ffb85103a;p=thirdparty%2Fkea.git [1044] Added b10-certgen tool To verify and update the b10-cmdctl certificate, using a few hardcoded certificate options (cn=localhost, o=BIND10, country=US). The tool can also be used to update the existing certificate if it has expired (it is only valid for 1 year) --- diff --git a/configure.ac b/configure.ac index 95f95c6d0b..18f7f4cf9f 100644 --- a/configure.ac +++ b/configure.ac @@ -663,6 +663,12 @@ if test "x${BOTAN_CONFIG}" != "x" then BOTAN_LIBS=`${BOTAN_CONFIG} --libs` BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags` + echo "XXX" + echo "XXX ${BOTAN_CONFIG}" + echo "XXX libs: ${BOTAN_LIBS}" + echo "XXX includes: ${BOTAN_INCLUDES}" + echo "XXX" + echo "XXX" # We expect botan-config --libs to contain -L, but # this is not always the case. As a heuristics workaround we add @@ -705,12 +711,13 @@ fi AC_SUBST(BOTAN_LDFLAGS) AC_SUBST(BOTAN_LIBS) AC_SUBST(BOTAN_INCLUDES) - +echo "XXX substed: ${BOTAN_INCLUDES}" # Even though chances are high we already performed a real compilation check # in the search for the right (pkg)config data, we try again here, to # be sure. CPPFLAGS_SAVED=$CPPFLAGS CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS" +echo "XXX CPPFLAGS: ${CPPFLAGS}" LIBS_SAVED="$LIBS" LIBS="$LIBS $BOTAN_LIBS" AC_CHECK_HEADERS([botan/botan.h],,AC_MSG_ERROR([Missing required header files.])) diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am index 3b88f4b6c3..8dcfe5c5c1 100644 --- a/src/bin/cmdctl/Makefile.am +++ b/src/bin/cmdctl/Makefile.am @@ -54,12 +54,17 @@ b10-cmdctl: cmdctl.py $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@ chmod a+x $@ +bin_PROGRAMS = b10-certgen +b10_certgen_SOURCES = b10-certgen.cc +b10_certgen_CXXFLAGS = $(BOTAN_INCLUDES) +b10_certgen_LDFLAGS = $(BOTAN_LIBS) + if INSTALL_CONFIGURATIONS # Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA) # because these file will contain sensitive information. install-data-local: - $(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@ + $(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@ for f in $(CMDCTL_CONFIGURATIONS) ; do \ if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then \ ${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \ diff --git a/src/bin/cmdctl/b10-certgen.cc b/src/bin/cmdctl/b10-certgen.cc new file mode 100644 index 0000000000..d85743ed6a --- /dev/null +++ b/src/bin/cmdctl/b10-certgen.cc @@ -0,0 +1,360 @@ +// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include +using namespace Botan; + +#include +#include +#include +#include + +// For cleaner 'does not exist or is not readable' output than +// botan provides +#include +#include + +// This is a simple tool that creates a self-signed PEM certificate +// for use with BIND 10. It creates a simple certificate for initial +// setup. Currently, all values are hardcoded defaults. For future +// versions, we may want to add more options for administrators. + +// It will create a PEM file containing a certificate with the following +// values: +// common name: localhost +// organization: BIND10 +// country code: US + +// Additional error return codes; these are specifically +// chosen to be distinct from validation error codes as +// provided by Botan. Their main use is to distinguish +// error cases in the unit tests. +const int DECODING_ERROR = 100; +const int BAD_OPTIONS = 101; +const int READ_ERROR = 102; +const int WRITE_ERROR = 103; +const int UNKNOWN_ERROR = 104; +const int NO_SUCH_FILE = 105; + +void +usage() { + std::cout << "Usage: b10-certgen [OPTION]..." << std::endl; + std::cout << "Validate, create, or update a self-signed certificate for " + "use with b10-cmdctl" << std::endl; + std::cout << "" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << "-c, --certfile=FILE\t\tfile to read or store the certificate" + << std::endl; + std::cout << "-f, --force\t\t\toverwrite existing certficate even if it" + << std::endl <<"\t\t\t\tis valid" << std::endl; + std::cout << "-h, --help\t\t\tshow this help" << std::endl; + std::cout << "-k, --keyfile=FILE\t\tfile to store the generated private key" + << std::endl; + std::cout << "-w, --write\t\t\tcreate a new certificate if the given file " + "does not exist, or if is is not valid" << std::endl; + std::cout << "-q, --quiet\t\t\tprint no output when creating or validating" + << std::endl; +} + +/// \brief Returns true if the given file exists and is readable +/// +/// \param filename The file to check +/// \return true if file exists and is readable +bool +fileExists(const std::string& filename) { + return (access(filename.c_str(), R_OK) == 0); +} + +/// \brief Helper function for readable error output; +/// +/// Returns string representation of X509 result code +/// This does not appear to be provided by Botan itself +/// +/// \param code An \c X509_Code instance +/// \return A human-readable c string +const char* +X509CodeToString(const X509_Code& code) { + // note that this list provides more than we would + // need in this context, it is just the enum from + // the source code of Botan. + switch (code) { + case VERIFIED: + return ("verified"); + case UNKNOWN_X509_ERROR: + return ("unknown x509 error"); + case CANNOT_ESTABLISH_TRUST: + return ("cannot establish trust"); + case CERT_CHAIN_TOO_LONG: + return ("certificate chain too long"); + case SIGNATURE_ERROR: + return ("signature error"); + case POLICY_ERROR: + return ("policy error"); + case INVALID_USAGE: + return ("invalid usage"); + case CERT_FORMAT_ERROR: + return ("certificate format error"); + case CERT_ISSUER_NOT_FOUND: + return ("certificate issuer not found"); + case CERT_NOT_YET_VALID: + return ("certificate not yet valid"); + case CERT_HAS_EXPIRED: + return ("certificate has expired"); + case CERT_IS_REVOKED: + return ("certificate has been revoked"); + case CRL_FORMAT_ERROR: + return ("crl format error"); + case CRL_NOT_YET_VALID: + return ("crl not yet valid"); + case CRL_HAS_EXPIRED: + return ("crl has expired"); + case CA_CERT_CANNOT_SIGN: + return ("CA cert cannot sign"); + case CA_CERT_NOT_FOR_CERT_ISSUER: + return ("CA certificate not for certificate issuer"); + case CA_CERT_NOT_FOR_CRL_ISSUER: + return ("CA certificate not for crl issuer"); + default: + return ("Unknown X509 code"); + } +} + +class CertificateTool { +public: + CertificateTool(bool quiet) : quiet_(quiet) {} + + int + createKeyAndCertificate(const std::string& key_file_name, + const std::string& cert_file_name) { + try { + AutoSeeded_RNG rng; + + // Create and store a private key + RSA_PrivateKey key(rng, 2048); + + print("Creating key file " + key_file_name); + std::ofstream key_file(key_file_name.c_str()); + key_file << PKCS8::PEM_encode(key, rng, ""); + if (!key_file.good()) { + print(std::string("Error writing to ") + key_file_name + + ": " + strerror(errno)); + return WRITE_ERROR; + } + key_file.close(); + + // Certificate options, currently hardcoded. + // For a future version we may want to make these + // settable. + X509_Cert_Options opts; + opts.common_name = "localhost"; + opts.organization = "BIND10"; + opts.country = "US"; + + opts.CA_key(); + + print("Creating certificate file " + cert_file_name); + + // The exact call changed aftert 1.8, adding the + // hash function option +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0) + X509_Certificate cert = + X509::create_self_signed_cert(opts, key, "SHA-256", rng); +#else + X509_Certificate cert = + X509::create_self_signed_cert(opts, key, rng); +#endif + + std::ofstream cert_file(cert_file_name.c_str()); + if (!cert_file.good()) { + print(std::string("Error writing to ") + cert_file_name + + ": " + strerror(errno)); + return (WRITE_ERROR); + } + cert_file << cert.PEM_encode(); + cert_file.close(); + } catch(std::exception& e) { + std::cout << "Error creating key or certificate: " << e.what() + << std::endl; + return (UNKNOWN_ERROR); + } + return (0); + } + + int + validateCertificate(const std::string& certfile) { + // Since we are dealing with a self-signed certificate here, we + // also use the certificate to check itself; i.e. we add it + // as a trusted certificate, then validate the certficate itself. + //const X509_Certificate cert(certfile); + try { + X509_Store store; + DataSource_Stream in(certfile); + store.add_trusted_certs(in); + + const X509_Code result = store.validate_cert(certfile); + + if (result == VERIFIED) { + print(certfile + " is valid"); + } else { + print(certfile + " failed to verify: " + + X509CodeToString(result)); + } + return (result); + } catch (const Botan::Decoding_Error& bde) { + print(certfile + " failed to verify: " + bde.what()); + return (DECODING_ERROR); + } catch (const Botan::Stream_IO_Error& bsie) { + print(certfile + " not read: " + bsie.what()); + return (READ_ERROR); + } + } + + /// \brief Runs the tool + /// + /// \param create_cert Create certificate if true, validate if false. + /// Does nothing if certificate exists and is valid. + /// \param force_create Create new certificate even if it is valid. + /// \param certfile Certificate file to read to or write from. + /// \param keyfile Key file to write if certificate is created. + /// Ignored if create_cert is false + /// \return zero on success, non-zero on failure + int + run(bool create_cert, bool force_create, const std::string& certfile, + const std::string& keyfile) + { + if (create_cert) { + // Unless force is given, only create it if the current + // one is not OK + if (force_create || !fileExists(certfile) || + validateCertificate(certfile) != VERIFIED) { + return (createKeyAndCertificate(keyfile, certfile)); + } else { + print(certfile + " exists and is valid. Not creating a new one"); + } + } else { + if (!fileExists(certfile)) { + print(certfile + ": " + strerror(errno)); + return (NO_SUCH_FILE); + } + int result = validateCertificate(certfile); + if (result != 0 && !quiet_) { + print("Running with -w would overwrite the certificate"); + } + return (result); + } + return (0); + } +private: + /// Prints the message to stdout unless quiet_ is true + void print(const std::string& msg) { + if (!quiet_) { + std::cout << msg << std::endl; + } + } + + bool quiet_; +}; + +int +main(int argc, char* argv[]) +{ + Botan::LibraryInitializer init; + + // create or check certificate + bool create_cert = false; + // force creation even if not necessary + bool force_create = false; + // don't print any output + bool quiet = false; + + // default certificate file + std::string certfile("cmdctl-certfile.pem"); + // default key file + std::string keyfile("cmdctl-keyfile.pem"); + + // whether or not the above values have been + // overridden (used in command line checking) + bool certfile_default = true; + bool keyfile_default = true; + + struct option long_options[] = { + { "certfile", required_argument, 0, 'c' }, + { "force", no_argument, 0, 'f' }, + { "help", no_argument, 0, 'h' }, + { "keyfile", required_argument, 0, 'k' }, + { "write", no_argument, 0, 'w' }, + { "quiet", no_argument, 0, 'q' }, + { 0, 0, 0, 0 } + }; + + int opt, option_index; + while ((opt = getopt_long(argc, argv, "c:fhk:wq", long_options, + &option_index)) != -1) { + switch (opt) { + case 'c': + certfile = optarg; + certfile_default = false; + break; + case 'f': + force_create = true; + break; + case 'h': + usage(); + return (0); + break; + case 'k': + keyfile = optarg; + keyfile_default = false; + break; + case 'w': + create_cert = true; + break; + case 'q': + quiet = true; + break; + default: + // A message will have already been output about the error. + return (BAD_OPTIONS); + } + } + + if (optind < argc) { + std::cout << "Error: extraneous arguments" << std::endl << std::endl; + usage(); + return (BAD_OPTIONS); + } + + // Some sanity checks on option combinations + if ((create_cert && certfile_default && !keyfile_default) || + (create_cert && !certfile_default && keyfile_default)) { + std::cout << "Error: keyfile and certfile must both be specified " + "if one of them is when calling b10-certgen in write " + "mode." << std::endl; + return (BAD_OPTIONS); + } + if (!create_cert && !keyfile_default) { + std::cout << "Error: keyfile is not used when not in write mode" + << std::endl; + return (BAD_OPTIONS); + } + + // Initialize the tool and perform the appropriate action(s) + CertificateTool tool(quiet); + return (tool.run(create_cert, force_create, certfile, keyfile)); +} diff --git a/src/bin/cmdctl/tests/Makefile.am b/src/bin/cmdctl/tests/Makefile.am index b5b65f6b33..20d49a75ed 100644 --- a/src/bin/cmdctl/tests/Makefile.am +++ b/src/bin/cmdctl/tests/Makefile.am @@ -1,5 +1,5 @@ PYCOVERAGE_RUN=@PYCOVERAGE_RUN@ -PYTESTS = cmdctl_test.py +PYTESTS = cmdctl_test.py b10-certgen_test.py EXTRA_DIST = $(PYTESTS) # If necessary (rare cases), explicitly specify paths to dynamic libraries @@ -9,10 +9,12 @@ if SET_ENV_LIBRARY_PATH LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH) endif +CLEANFILES = test-keyfile.pem test-certfile.pem + # test using command-line arguments, so use check-local target instead of TESTS check-local: if ENABLE_PYTHON_COVERAGE - touch $(abs_top_srcdir)/.coverage + touch $(abs_top_srcdir)/.coverage rm -f .coverage ${LN_S} $(abs_top_srcdir)/.coverage .coverage endif diff --git a/src/bin/cmdctl/tests/b10-certgen_test.py b/src/bin/cmdctl/tests/b10-certgen_test.py new file mode 100644 index 0000000000..7632366014 --- /dev/null +++ b/src/bin/cmdctl/tests/b10-certgen_test.py @@ -0,0 +1,170 @@ +# Copyright (C) 2012 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# Note: the main code is in C++, but what we are mostly testing is +# options and behaviour (output/file creation, etc), which is easier +# to test in python. + +import unittest +import os +from subprocess import call +import subprocess +import ssl + +def run(command): + """ + Small helper function that returns a tuple of (rcode, stdout, stderr) after + running the given command (an array of command and arguments, as passed on + to subprocess). + """ + subp = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = subp.communicate() + return (subp.returncode, stdout, stderr) + +class FileDeleterContext: + """ + Simple Context Manager that deletes a given set of files when the context + is left. + """ + def __init__(self, files): + self.files = files + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + for f in self.files: + if os.path.exists(f): + os.unlink(f) + +def read_file_data(filename): + """ + Simple text file reader that returns its contents as an array + """ + with open(filename) as f: + return f.readlines() + +class TestCertGenTool(unittest.TestCase): + TOOL = '../b10-certgen' + + def run_check(self, expected_returncode, expected_stdout, expected_stderr, command): + """ + Runs the given command, and checks return code, and outputs (if provided). + Arguments: + expected_returncode, return code of the command + expected_stdout, (multiline) string that is checked agains stdout. + May be None, in which case the check is skipped. + expected_stderr, (multiline) string that is checked agains stderr. + May be None, in which case the check is skipped. + """ + (returncode, stdout, stderr) = run(command) + self.assertEqual(expected_returncode, returncode, " ".join(command)) + if expected_stdout is not None: + self.assertEqual(expected_stdout, stdout.decode()) + if expected_stderr is not None: + self.assertEqual(expected_stderr, stderr.decode()) + + def validate_certificate(self, expected_result, certfile): + """ + Validate a certificate, using the quiet option of the tool; it runs + the check option (-c) for the given base name of the certificate (-f + ), and compares the return code to the given + expected_result value + """ + self.run_check(expected_result, '', '', + [self.TOOL, '-q', '-c', certfile]) + # Same with long options + self.run_check(expected_result, '', '', + [self.TOOL, '--quiet', '--certfile', certfile]) + + + def test_basic_creation(self): + """ + Tests whether basic creation with no arguments (except output + file name) successfully creates a key and certificate + """ + keyfile = 'test-keyfile.pem' + certfile = 'test-certfile.pem' + command = [ self.TOOL, '-q', '-w', '-c', certfile, '-k', keyfile ] + self.creation_helper(command, certfile, keyfile) + # Do same with long options + command = [ self.TOOL, '--quiet', '--write', '--certfile=' + certfile, '--keyfile=' + keyfile ] + self.creation_helper(command, certfile, keyfile) + + def creation_helper(self, command, certfile, keyfile): + """ + Helper method for test_basic_creation. + Performs the actual checks + """ + with FileDeleterContext([keyfile, certfile]): + self.assertFalse(os.path.exists(keyfile)) + self.assertFalse(os.path.exists(certfile)) + self.run_check(0, '', '', command) + self.assertTrue(os.path.exists(keyfile)) + self.assertTrue(os.path.exists(certfile)) + + # Validate the certificate that was just created + self.validate_certificate(0, certfile) + + # When run with the same options, it should *not* create it again, + # as the current certificate should still be valid + certdata = read_file_data(certfile) + keydata = read_file_data(keyfile) + + self.run_check(0, '', '', command) + + self.assertEqual(certdata, read_file_data(certfile)) + self.assertEqual(keydata, read_file_data(keyfile)) + + # but if we add -f, it should force a new creation + command.append('-f') + self.run_check(0, '', '', command) + self.assertNotEqual(certdata, read_file_data(certfile)) + self.assertNotEqual(keydata, read_file_data(keyfile)) + + def test_check_bad_certificates(self): + """ + Tests a few pre-created certificates with the -c option + """ + self.validate_certificate(10, 'testdata/expired-certfile.pem') + self.validate_certificate(100, 'testdata/mangled-certfile.pem') + self.validate_certificate(17, 'testdata/noca-certfile.pem') + + def test_bad_options(self): + """ + Tests some combinations of commands that should fail. + """ + # specify -c but not -k + self.run_check(101, + 'Error: keyfile and certfile must both be specified ' + 'if one of them is when calling b10-certgen in write ' + 'mode.\n', + '', [self.TOOL, '-w', '-c', 'foo']) + self.run_check(101, + 'Error: keyfile and certfile must both be specified ' + 'if one of them is when calling b10-certgen in write ' + 'mode.\n', + '', [self.TOOL, '-w', '-k', 'foo']) + self.run_check(101, + 'Error: keyfile is not used when not in write mode\n', + '', [self.TOOL, '-k', 'foo']) + # Extraneous argument + self.run_check(101, None, None, [self.TOOL, 'foo']) + # No such file + self.run_check(105, None, None, [self.TOOL, '-c', 'foo']) + +if __name__== '__main__': + unittest.main() + diff --git a/src/bin/cmdctl/tests/testdata/expired-certfile.pem b/src/bin/cmdctl/tests/testdata/expired-certfile.pem new file mode 100644 index 0000000000..384a22223f --- /dev/null +++ b/src/bin/cmdctl/tests/testdata/expired-certfile.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhzCCAvCgAwIBAgIJALwngNFik7ONMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD +VQQGEwJjbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwG +A1UEChMFY25uaWMxDjAMBgNVBAsTBWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3Vu +MSIwIAYJKoZIhvcNAQkBFhN6aGFuZ2xpa3VuQGNubmljLmNuMB4XDTEwMDEwNzEy +NDcxOFoXDTExMDEwNzEyNDcxOFowgYoxCzAJBgNVBAYTAmNuMRAwDgYDVQQIEwdi +ZWlqaW5nMRAwDgYDVQQHEwdiZWlqaW5nMQ4wDAYDVQQKEwVjbm5pYzEOMAwGA1UE +CxMFY25uaWMxEzARBgNVBAMTCnpoYW5nbGlrdW4xIjAgBgkqhkiG9w0BCQEWE3po +YW5nbGlrdW5AY25uaWMuY24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOkg +JbEkYoy9SEsU9t/mfxlaiCqNhxCqqgeodVEdiPKJ7LoVl21mRjazWBiHQbQ1e2Ka +UiCJz68RwV7u92bIqe1bsNgNqoCPQqsQPtEoCPzfbiM1tIke0s/h6+8l6ne+yg21 +O825x5Anjq+6THLGCDcO4L2RWo+4PwJnVGrgBPKLAgMBAAGjgfIwge8wHQYDVR0O +BBYEFJKM/O0ViGlwtb3JEci/DLTO/7DaMIG/BgNVHSMEgbcwgbSAFJKM/O0ViGlw +tb3JEci/DLTO/7DaoYGQpIGNMIGKMQswCQYDVQQGEwJjbjEQMA4GA1UECBMHYmVp +amluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwGA1UEChMFY25uaWMxDjAMBgNVBAsT +BWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3VuMSIwIAYJKoZIhvcNAQkBFhN6aGFu +Z2xpa3VuQGNubmljLmNuggkAvCeA0WKTs40wDAYDVR0TBAUwAwEB/zANBgkqhkiG +9w0BAQUFAAOBgQBh5N6isMAQAFFD+pbfpppjQlO4vUNcEdzPdeuBFaf9CsX5ZdxV +jmn1ZuGm6kRzqUPwPSxvCIAY0wuSu1g7YREPAZ3XBVwcg6262iGOA6n7E+nv5PLz +EuZ1oUg+IfykUIoflKH6xZB4MyPL+EgkMT+i9BrngaXHXF8tEO30YppMiA== +-----END CERTIFICATE----- diff --git a/src/bin/cmdctl/tests/testdata/mangled-certfile.pem b/src/bin/cmdctl/tests/testdata/mangled-certfile.pem new file mode 100644 index 0000000000..7c47fda511 --- /dev/null +++ b/src/bin/cmdctl/tests/testdata/mangled-certfile.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhzCCAvCgAwIBAgIJALwngNFik7ONMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD +VQQGEwJjbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwG +A1UEChMFY25uaWMxDjAMBgNVBAsTBWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3Vu +MSIwIAYJKoZIhvcNAQkBFhN6aGFuZ2xpa3VuQGNubmljLmNuMB4XDTEwMDEwNzEy +NDcxOFoXDTExMDEwNzEyNDcxOFowgYoxCzAJBgNVBAYTAmNuMRAwDgYDVQQIEwdi +ZWlqaW5nMraWDgYDVQQHEwdiZWlqaW5nMQ4wDAYDVQQKEwVjbm5pYzEOMAwGA1UE +CxMFY25uaWMxeZaRBgNVBAMTCnpoYW5nbGlrdW4xIjAgBgkqhkiG9w0BCQEWE3po +YW5nbGlrdW5AY25UAwMuY24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOkg +JbEkYoy9SEsU9t/mfxLAICqNhxCqqgeodVEdiPKJ7LoVl21mRjazWBiHQbQ1e2Ka +UiCJz68RwV7u92bIqe1bsNgNqoCPQqsQPtEoCPzfbiM1tIke0s/h6+8l6ne+yg21 +O825x5Anjq+6THLGCDcO4L2RWo+4PwJnVGrgBPKLAgMBAAGjgfIwge8wHQYDVR0O +BBYEFJKM/O0ViGlwtb3JEci/DLTO/7DaMIG/BgNVHSMEgbcwgbSAFJKM/O0ViGlw +tb3JEci/DLTO/7DaoYGQpIGNMIGKMQswCQYDVQQGEwJjbjEQMA4GA1UECBMHYmVp +amluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwGA1UEChMFY25uaWMxDjAMBgNVBAsT +BWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3VuMSIwIAYJKoZIhvcNAQkBFhN6aGFu +Z2xpa3VuQGNubmljLmNuggkAvCeA0WKTs40wDAYDVR0TBAUwAwEB/zANBgkqhkiG +9w0BAQUFAAOBgQBh5N6isMAQAFFD+pbfpppjQlO4vUNcEdzPdeuBFaf9CsX5ZdxV +jmn1ZuGm6kRzqUPwPSxvCIAY0wuSu1g7YREPAZ3XBVwcg6262iGOA6n7E+nv5PLz +EuZ1oUg+IfykUIoflKH6xZB4MyPL+EgkMT+i9BrngaXHXF8tEO30YppMiA== +-----END CERTIFICATE----- diff --git a/src/bin/cmdctl/tests/testdata/noca-certfile.pem b/src/bin/cmdctl/tests/testdata/noca-certfile.pem new file mode 100644 index 0000000000..56368696cf --- /dev/null +++ b/src/bin/cmdctl/tests/testdata/noca-certfile.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe6gAwIBAgIRALUIj3nnW5uDE/+fglPvUDwwDQYJKoZIhvcNAQELBQAw +HjELMAkGA1UEBhMCVVMxDzANBgNVBAMTBkJJTkQxMDAeFw0xMjExMTQxMjQ5MjVa +Fw0xMzExMTQxMjQ5MjVaMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQDEwZCSU5EMTAw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkIOPfs3Aw9kNDu1JqA2w3 +84/n9oUgAwAlHVmuJv7ZDw1MDaIKHjsh3DW09z+nv67GVksI7pFtAw5O4mnTDxpa +JT0NKzhvYGfe8VdV/hWDogTIdk1QBJNZ2/id8z0h8z5001sARXPf+4mHBJslenH3 +YtZs22BG5RBLULtZ/2Nr7JkdfLlc6D5PCoDG22r1OiFkYVdCWfLDjisVIbSYPBtY +BlKAIrvbmOtWcaGM+vQAhl0T5N8WRCKhaQH0DEmzQNckkYd7rSECo57KYiuvOdzp +d+3bWTgGGy2ff0o3LZypv0O5s0TDC2H6hYtN4bUbcChUJbFu9b5sVZaOEVZtUsyD +AgMBAAGjPzA9MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgTwMB0GA1UdDgQW +BBSqGzsEDNs9E7gBL5pD6XVAwUo4DTANBgkqhkiG9w0BAQsFAAOCAQEAMTNB8NCU +dnLFZ0jNpvecbECkX/OWGlBYU4/CsoNiibwp4CtUYS2A4NFVjWAyuzLSHhRQi0vJ +CCWLpKL4VTkaDN5Oft42iUhvEXMnriJqpfXHnjCiBwFFSPl5WKfMIaRNK+tF4zbB +F+FGNEEmYG3t/ni82orDLq4oy+7CoQwzZNzj5yoV6q7O9kLR9OMPNwJrc27A4erB +7VMRZslSrNA4uA6YhMZl8iEvO1H801ct0zTxawrCihPOZOCSLew35xjztO7d3YH8 +YavOu5kzeu7AgZ2n75H/qU47ZgBjbonn9Osvrct+RIwZuWTB2bDML8JhNaZCq0aA +TDBC0QWqIYypLg== +-----END CERTIFICATE-----