blob: 356c2eac44d3e935284210baed4f669e281bec52 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <openssl/asn1.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <cstdint>
#include <memory>
#include <string>
#include <boost/optional/optional.hpp>
#include <glog/logging.h>
#include "kudu/gutil/macros.h"
#include "kudu/gutil/port.h"
#include "kudu/gutil/strings/stringpiece.h"
#include "kudu/util/locks.h"
#include "kudu/util/openssl_util.h"
#include "kudu/util/monotime.h"
#include "kudu/util/status.h"
// Forward declarations for the relevant OpenSSL typedefs
// in addition to openssl_util.h.
typedef struct asn1_string_st ASN1_INTEGER;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
typedef struct env_md_st EVP_MD;
#else
typedef struct evp_md_st EVP_MD;
#endif
typedef struct rsa_st RSA;
typedef struct x509_st X509;
typedef struct X509_req_st X509_REQ;
// STACK_OF(X509_EXTENSION)
struct stack_st_X509_EXTENSION; // IWYU pragma: keep
namespace kudu {
namespace security {
class Cert;
class CertSignRequest;
class PrivateKey;
namespace ca {
// Base utility class for issuing X509 CSRs.
class CertRequestGeneratorBase {
public:
CertRequestGeneratorBase() = default;
virtual ~CertRequestGeneratorBase() = default;
virtual Status Init() = 0;
virtual bool Initialized() const = 0;
// Generate X509 CSR using the specified key. To obtain the key,
// call the GeneratePrivateKey() function.
Status GenerateRequest(const PrivateKey& key, CertSignRequest* ret) const WARN_UNUSED_RESULT;
protected:
// Push the specified extension into the stack provided.
static Status PushExtension(stack_st_X509_EXTENSION* st,
int32_t nid,
StringPiece value) WARN_UNUSED_RESULT;
// Set the certificate-specific subject fields into the specified request.
virtual Status SetSubject(X509_REQ* req) const = 0;
// Set the certificate-specific extensions into the specified request.
virtual Status SetExtensions(X509_REQ* req) const = 0;
private:
DISALLOW_COPY_AND_ASSIGN(CertRequestGeneratorBase);
};
// An utility class that facilitates issuing certificate signing requests
// (a.k.a. X509 CSRs).
class CertRequestGenerator : public CertRequestGeneratorBase {
public:
// Properties for the generated X509 CSR. The 'hostname' is for the name of
// the machine the requestor is to use the certificate at. Valid configuration
// should contain non-empty 'hostname' field.
struct Config {
// FQDN name to put into the 'DNS' fields of the subjectAltName extension.
std::string hostname;
// userId (UID)
boost::optional<std::string> user_id;
// Our custom extension which stores the full Kerberos principal for IPKI certs.
boost::optional<std::string> kerberos_principal;
};
// 'config' contains the properties to fill into the X509 attributes of the
// CSR.
explicit CertRequestGenerator(Config config);
~CertRequestGenerator();
Status Init() override WARN_UNUSED_RESULT;
bool Initialized() const override;
CertRequestGenerator& enable_self_signing() {
CHECK(!is_initialized_);
for_self_signing_ = true;
return *this;
}
protected:
Status SetSubject(X509_REQ* req) const override WARN_UNUSED_RESULT;
Status SetExtensions(X509_REQ* req) const override WARN_UNUSED_RESULT;
private:
const Config config_;
stack_st_X509_EXTENSION* extensions_ = nullptr;
bool is_initialized_ = false;
bool for_self_signing_ = false;
};
// An utility class that facilitates issuing of root CA self-signed certificate
// signing requests.
class CaCertRequestGenerator : public CertRequestGeneratorBase {
public:
// Properties for the generated X509 CA CSR.
struct Config {
// Common name (CN); e.g. 'master 239D6D2F-BDD2-4463-8933-78D9559C2124'.
// Don't put hostname/FQDN in here: for CA cert it does not make sense and
// it might be longer than 64 characters which is the limit specified
// by RFC5280. The limit is enforced by the OpenSSL library.
std::string cn;
};
explicit CaCertRequestGenerator(Config config);
~CaCertRequestGenerator();
Status Init() override WARN_UNUSED_RESULT;
bool Initialized() const override;
protected:
Status SetSubject(X509_REQ* req) const override WARN_UNUSED_RESULT;
Status SetExtensions(X509_REQ* req) const override WARN_UNUSED_RESULT;
private:
const Config config_;
stack_st_X509_EXTENSION* extensions_;
mutable simple_spinlock lock_;
bool is_initialized_; // protected by lock_
};
// An utility class for issuing and signing certificates.
//
// This is used in "fluent" style. For example:
//
// CHECK_OK(CertSigner(&my_ca_cert, &my_ca_key)
// .set_expiration_interval(MonoDelta::FromSeconds(3600))
// .Sign(csr, &cert));
//
// As such, this class is not guaranteed thread-safe.
class CertSigner {
public:
// Generate a self-signed certificate authority using the given key
// and CSR configuration.
static Status SelfSignCA(const PrivateKey& key,
CaCertRequestGenerator::Config config,
int64_t cert_expiration_seconds,
Cert* cert) WARN_UNUSED_RESULT;
// Generate a self-signed certificate using the given key and CSR
// configuration.
static Status SelfSignCert(const PrivateKey& key,
CertRequestGenerator::Config config,
Cert* cert) WARN_UNUSED_RESULT;
// Create a CertSigner.
//
// The given cert and key must stay valid for the lifetime of the
// cert signer. See class documentation above for recommended usage.
//
// 'ca_cert' may be nullptr in order to perform self-signing (though
// the SelfSignCA() static method above is recommended).
CertSigner(const Cert* ca_cert, const PrivateKey* ca_private_key);
~CertSigner() = default;
// Set the expiration interval for certs signed by this signer.
// This may be changed at any point.
CertSigner& set_expiration_interval(MonoDelta expiration) {
exp_interval_sec_ = expiration.ToSeconds();
return *this;
}
Status Sign(const CertSignRequest& req, Cert* ret) const WARN_UNUSED_RESULT;
private:
static Status CopyExtensions(X509_REQ* req, X509* x) WARN_UNUSED_RESULT;
static Status FillCertTemplateFromRequest(X509_REQ* req, X509* tmpl) WARN_UNUSED_RESULT;
static Status DigestSign(const EVP_MD* md, EVP_PKEY* pkey, X509* x) WARN_UNUSED_RESULT;
static Status GenerateSerial(c_unique_ptr<ASN1_INTEGER>* ret) WARN_UNUSED_RESULT;
Status DoSign(const EVP_MD* digest, int32_t exp_seconds, X509 *ret) const WARN_UNUSED_RESULT;
// The expiration interval of certs signed by this signer.
int32_t exp_interval_sec_ = 24 * 60 * 60;
// The CA cert. null if this CertSigner is configured for self-signing.
const Cert* const ca_cert_;
// The CA private key. If configured for self-signing, this is the
// private key associated with the target cert.
const PrivateKey* const ca_private_key_;
DISALLOW_COPY_AND_ASSIGN(CertSigner);
};
} // namespace ca
} // namespace security
} // namespace kudu