Implement the "pki sign-csr" command

refs #7274
This commit is contained in:
Gunnar Beutner 2014-10-15 16:01:15 +02:00
parent 425dbba35b
commit c1b92756b5
5 changed files with 185 additions and 35 deletions

View File

@ -253,8 +253,7 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
Log(LogInformation, "base", "Writing private key to '" + keyfile + "'."); Log(LogInformation, "base", "Writing private key to '" + keyfile + "'.");
BIO *bio = BIO_new(BIO_s_file()); BIO *bio = BIO_new_file(const_cast<char *>(keyfile.CStr()), "w");
BIO_write_filename(bio, const_cast<char *>(keyfile.CStr()));
PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL); PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL);
BIO_free(bio); BIO_free(bio);
@ -262,40 +261,16 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
chmod(keyfile.CStr(), 0600); chmod(keyfile.CStr(), 0600);
#endif /* _WIN32 */ #endif /* _WIN32 */
X509_REQ *req = X509_REQ_new();
if (!req)
return 0;
EVP_PKEY *key = EVP_PKEY_new(); EVP_PKEY *key = EVP_PKEY_new();
EVP_PKEY_assign_RSA(key, rsa); EVP_PKEY_assign_RSA(key, rsa);
if (!certfile.IsEmpty()) { if (!certfile.IsEmpty()) {
X509 *cert = X509_new(); X509_NAME *subject = X509_NAME_new();
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0);
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), 365 * 24 * 60 * 60 * 30);
X509_set_pubkey(cert, key);
X509_NAME *name = X509_get_subject_name(cert); X509 *cert = CreateCert(key, subject, subject, key, ca);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0);
X509_set_issuer_name(cert, name);
if (ca) { X509_NAME_free(subject);
X509_EXTENSION *ext;
X509V3_CTX ctx;
X509V3_set_ctx_nodb(&ctx);
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ext = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast<char *>("critical,CA:TRUE"));
if (ext)
X509_add_ext(cert, ext, -1);
X509_EXTENSION_free(ext);
}
X509_sign(cert, key, EVP_sha1());
Log(LogInformation, "base", "Writing X509 certificate to '" + certfile + "'."); Log(LogInformation, "base", "Writing X509 certificate to '" + certfile + "'.");
@ -308,6 +283,11 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
} }
if (!csrfile.IsEmpty()) { if (!csrfile.IsEmpty()) {
X509_REQ *req = X509_REQ_new();
if (!req)
return 0;
X509_REQ_set_version(req, 0); X509_REQ_set_version(req, 0);
X509_REQ_set_pubkey(req, key); X509_REQ_set_pubkey(req, key);
@ -331,6 +311,35 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
return 1; return 1;
} }
X509 *CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca, const String& serialfile)
{
X509 *cert = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), 365 * 24 * 60 * 60 * 30);
X509_set_pubkey(cert, pubkey);
X509_set_subject_name(cert, subject);
X509_set_issuer_name(cert, issuer);
if (ca) {
X509_EXTENSION *ext;
X509V3_CTX ctx;
X509V3_set_ctx_nodb(&ctx);
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ext = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast<char *>("critical,CA:TRUE"));
if (ext)
X509_add_ext(cert, ext, -1);
X509_EXTENSION_free(ext);
}
X509_sign(cert, cakey, EVP_sha1());
return cert;
}
String SHA256(const String& s) String SHA256(const String& s)
{ {
std::ostringstream msgbuf; std::ostringstream msgbuf;

View File

@ -39,6 +39,7 @@ void I2_BASE_API AddCRLToSSLContext(const shared_ptr<SSL_CTX>& context, const St
String I2_BASE_API GetCertificateCN(const shared_ptr<X509>& certificate); String I2_BASE_API GetCertificateCN(const shared_ptr<X509>& certificate);
shared_ptr<X509> I2_BASE_API GetX509Certificate(const String& pemfile); shared_ptr<X509> I2_BASE_API GetX509Certificate(const String& pemfile);
int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), bool ca = false); int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), bool ca = false);
X509 * I2_BASE_API CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca, const String& serialfile = String());
String I2_BASE_API SHA256(const String& s); String I2_BASE_API SHA256(const String& s);
class I2_BASE_API openssl_error : virtual public std::exception, virtual public boost::exception { }; class I2_BASE_API openssl_error : virtual public std::exception, virtual public boost::exception { };

View File

@ -18,7 +18,7 @@
set(cli_SOURCES set(cli_SOURCES
featureenablecommand.cpp featuredisablecommand.cpp featurelistcommand.cpp featureenablecommand.cpp featuredisablecommand.cpp featurelistcommand.cpp
objectlistcommand.cpp objectlistcommand.cpp
pkinewcacommand.cpp pkinewcertcommand.cpp pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp
daemoncommand.cpp daemoncommand.cpp
) )

View File

@ -0,0 +1,90 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/pkisigncsrcommand.hpp"
#include "base/logger_fwd.hpp"
#include "base/clicommand.hpp"
#include "base/tlsutility.hpp"
#include "base/application.hpp"
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("pki/sign-csr", PKISignCSRCommand);
String PKISignCSRCommand::GetDescription(void) const
{
return "Reads a Certificate Signing Request from stdin and prints a signed certificate on stdout.";
}
String PKISignCSRCommand::GetShortDescription(void) const
{
return "signs a CSR";
}
void PKISignCSRCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc,
ArgumentCompletionDescription& argCompletionDesc) const
{
/* Command doesn't support any parameters. */
}
/**
* The entry point for the "pki sign-csr" CLI command.
*
* @returns An exit status.
*/
int PKISignCSRCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
BIO *csrbio = BIO_new_fp(stdin, BIO_NOCLOSE);
X509_REQ *req;
PEM_read_bio_X509_REQ(csrbio, &req, NULL, NULL);
BIO_free(csrbio);
String cadir = Application::GetLocalStateDir() + "/lib/icinga2/ca";
String cakeyfile = cadir + "/ca.key";
RSA *rsa;
BIO *cakeybio = BIO_new_file(const_cast<char *>(cakeyfile.CStr()), "r");
rsa = PEM_read_bio_RSAPrivateKey(cakeybio, NULL, NULL, NULL);
BIO_free(cakeybio);
String cacertfile = cadir + "/ca.crt";
BIO *cacertbio = BIO_new_file(const_cast<char *>(cacertfile.CStr()), "r");
X509 *cacert = PEM_read_bio_X509(cacertbio, NULL, NULL, NULL);
BIO_free(cacertbio);
EVP_PKEY *privkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(privkey, rsa);
EVP_PKEY *pubkey = X509_REQ_get_pubkey(req);
X509 *cert = CreateCert(pubkey, X509_REQ_get_subject_name(req), X509_get_subject_name(cacert), privkey, false);
X509_free(cacert);
BIO *certbio = BIO_new_fp(stdout, BIO_NOCLOSE);
PEM_write_bio_X509(certbio, cert);
BIO_free(certbio);
return 0;
}

View File

@ -0,0 +1,50 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef PKISIGNCSRCOMMAND_H
#define PKISIGNCSRCOMMAND_H
#include "base/qstring.hpp"
#include "base/clicommand.hpp"
namespace icinga
{
/**
* The "pki sign-csr" command.
*
* @ingroup cli
*/
class PKISignCSRCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(PKISignCSRCommand);
virtual String GetDescription(void) const;
virtual String GetShortDescription(void) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc,
ArgumentCompletionDescription& argCompletionDesc) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
};
}
#endif /* PKISIGNCSRCOMMAND_H */