You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by to...@apache.org on 2017/01/25 01:10:43 UTC
[2/2] kudu git commit: [security] separated OpenSSL wrappers from CA
[security] separated OpenSSL wrappers from CA
Moved common wrappers around OpenSSL objects from cert_management.h
into a openssl_util.h header. The motivation for this change is to
allow other components to use those generic wrappers without dependency
on CA-specific code.
Change-Id: I37729a865739c5702e92238b9d2375f5bf71543d
Reviewed-on: http://gerrit.cloudera.org:8080/5779
Reviewed-by: Dan Burkert <da...@apache.org>
Tested-by: Kudu Jenkins
Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/e367b4e0
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/e367b4e0
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/e367b4e0
Branch: refs/heads/master
Commit: e367b4e0b6e1cae60ffb8cbf2a661589b92fa9c5
Parents: 6719da0
Author: Alexey Serbin <as...@cloudera.com>
Authored: Mon Jan 23 23:19:03 2017 -0800
Committer: Alexey Serbin <as...@cloudera.com>
Committed: Tue Jan 24 23:29:09 2017 +0000
----------------------------------------------------------------------
src/kudu/security/ca/cert_management.cc | 234 ++-------------------------
src/kudu/security/ca/cert_management.h | 90 -----------
src/kudu/security/openssl_util.cc | 209 +++++++++++++++++++++++-
src/kudu/security/openssl_util.h | 123 ++++++++++++++
4 files changed, 347 insertions(+), 309 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kudu/blob/e367b4e0/src/kudu/security/ca/cert_management.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/ca/cert_management.cc b/src/kudu/security/ca/cert_management.cc
index f699a9e..99168aa 100644
--- a/src/kudu/security/ca/cert_management.cc
+++ b/src/kudu/security/ca/cert_management.cc
@@ -47,231 +47,29 @@ using std::shared_ptr;
using std::string;
using strings::Substitute;
-#define CERT_CHECK_OK(call) \
- CHECK_GT((call), 0)
-
-#define CERT_RET_NOT_OK(call, msg) \
- if ((call) <= 0) { \
- return Status::RuntimeError(Substitute("$0: $1", \
- (msg), GetOpenSSLErrors())); \
- }
-
-#define CERT_RET_IF_NULL(call, msg) \
- if ((call) == nullptr) { \
- return Status::RuntimeError(Substitute("$0: $1", \
- (msg), GetOpenSSLErrors())); \
- }
-
namespace kudu {
namespace security {
-namespace ca {
-
-namespace {
-
-// Writing the private key from an EVP_PKEY has a different
-// signature than the rest of the write functions, so we
-// have to provide this wrapper.
-int PemWritePrivateKey(BIO* bio, EVP_PKEY* key);
-
-// For each SSL type, the Traits class provides the important OpenSSL
-// API functions.
-template<class SSL_TYPE>
-struct SslTypeTraits {};
-
-template<> struct SslTypeTraits<X509> {
- static constexpr auto free = &X509_free;
- static constexpr auto read_pem = &PEM_read_bio_X509;
- static constexpr auto read_der = &d2i_X509_bio;
- static constexpr auto write_pem = &PEM_write_bio_X509;
- static constexpr auto write_der = &i2d_X509_bio;
-};
-template<> struct SslTypeTraits<X509_REQ> {
- static constexpr auto free = &X509_REQ_free;
- static constexpr auto read_pem = &PEM_read_bio_X509_REQ;
- static constexpr auto read_der = &d2i_X509_REQ_bio;
- static constexpr auto write_pem = &PEM_write_bio_X509_REQ;
- static constexpr auto write_der = &i2d_X509_REQ_bio;
-};
-template<> struct SslTypeTraits<EVP_PKEY> {
- static constexpr auto free = &EVP_PKEY_free;
- static constexpr auto read_pem = &PEM_read_bio_PrivateKey;
- static constexpr auto read_der = &d2i_PrivateKey_bio;
- static constexpr auto write_pem = &PemWritePrivateKey;
- static constexpr auto write_der = &i2d_PrivateKey_bio;
-};
template<> struct SslTypeTraits<ASN1_INTEGER> {
static constexpr auto free = &ASN1_INTEGER_free;
};
-template<> struct SslTypeTraits<BIO> {
- static constexpr auto free = &BIO_free;
-};
template<> struct SslTypeTraits<BIGNUM> {
static constexpr auto free = &BN_free;
};
-template<> struct SslTypeTraits<RSA> {
- static constexpr auto free = &RSA_free;
+template<> struct SslTypeTraits<X509> {
+ static constexpr auto free = &X509_free;
+};
+template<> struct SslTypeTraits<X509_REQ> {
+ static constexpr auto free = &X509_REQ_free;
};
template<> struct SslTypeTraits<X509_EXTENSION> {
static constexpr auto free = &X509_EXTENSION_free;
};
+template<> struct SslTypeTraits<EVP_PKEY> {
+ static constexpr auto free = &EVP_PKEY_free;
+};
-template<class SSL_TYPE>
-static c_unique_ptr<SSL_TYPE> make_ssl_unique(SSL_TYPE* d) {
- return {d, SslTypeTraits<SSL_TYPE>::free};
-}
-
-int PemWritePrivateKey(BIO* bio, EVP_PKEY* key) {
- auto rsa = make_ssl_unique(EVP_PKEY_get1_RSA(key));
- return PEM_write_bio_RSAPrivateKey(
- bio, rsa.get(), nullptr, nullptr, 0, nullptr, nullptr);
-}
-
-template<class TYPE>
-Status ToBIO(BIO* bio, DataFormat format, TYPE* obj) {
- using Traits = SslTypeTraits<TYPE>;
- CHECK(bio);
- CHECK(obj);
- switch (format) {
- case DataFormat::DER:
- CERT_RET_NOT_OK(Traits::write_der(bio, obj), "error exporting DER format");
- break;
- case DataFormat::PEM:
- CERT_RET_NOT_OK(Traits::write_pem(bio, obj), "error exporting PEM format");
- break;
- }
- CERT_RET_NOT_OK(BIO_flush(bio), "error flushing BIO");
- return Status::OK();
-}
-
-template<class TYPE>
-Status FromBIO(BIO* bio, DataFormat format, c_unique_ptr<TYPE>* ret) {
- using Traits = SslTypeTraits<TYPE>;
- CHECK(bio);
- switch (format) {
- case DataFormat::DER:
- *ret = make_ssl_unique(Traits::read_der(bio, nullptr));
- break;
- case DataFormat::PEM:
- *ret = make_ssl_unique(Traits::read_pem(bio, nullptr, nullptr, nullptr));
- break;
- }
- if (PREDICT_FALSE(!*ret)) {
- return Status::RuntimeError(GetOpenSSLErrors());
- }
- return Status::OK();
-}
-} // anonymous namespace
-
-
-const string& DataFormatToString(DataFormat fmt) {
- static const string kStrFormatUnknown = "UNKNOWN";
- static const string kStrFormatDer = "DER";
- static const string kStrFormatPem = "PEM";
- switch (fmt) {
- case DataFormat::DER:
- return kStrFormatDer;
- case DataFormat::PEM:
- return kStrFormatPem;
- default:
- return kStrFormatUnknown;
- }
-}
-
-Status BasicWrapper::FromFile(const string& fpath, DataFormat format) {
- auto bio = make_ssl_unique(BIO_new(BIO_s_file()));
- CERT_RET_NOT_OK(BIO_read_filename(bio.get(), fpath.c_str()),
- Substitute("$0: could not read from file", fpath));
- RETURN_NOT_OK_PREPEND(FromBIO(bio.get(), format),
- Substitute("$0: unable to load data key from file",
- fpath));
- return Status::OK();
-}
-
-Status BasicWrapper::FromString(const string& data, DataFormat format) {
- const void* mdata = reinterpret_cast<const void*>(data.data());
- auto bio = make_ssl_unique(BIO_new_mem_buf(
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
- const_cast<void*>(mdata),
-#else
- mdata,
-#endif
- data.size()));
- RETURN_NOT_OK_PREPEND(FromBIO(bio.get(), format),
- "unable to load data from memory");
- return Status::OK();
-}
-
-Status BasicWrapper::ToString(std::string* data, DataFormat format) const {
- CHECK(data);
- auto bio = make_ssl_unique(BIO_new(BIO_s_mem()));
- RETURN_NOT_OK_PREPEND(ToBIO(bio.get(), format), "error serializing data");
- BUF_MEM* membuf;
- CERT_CHECK_OK(BIO_get_mem_ptr(bio.get(), &membuf));
- data->assign(membuf->data, membuf->length);
- return Status::OK();
-}
-
-void Key::AdoptRawData(RawDataType* data) {
- data_ = make_ssl_unique(data);
-}
-
-Status Key::FromBIO(BIO* bio, DataFormat format) {
- RETURN_NOT_OK_PREPEND(ca::FromBIO(bio, format, &data_),
- "unable to read private key");
- return Status::OK();
-}
-
-Status Key::ToBIO(BIO* bio, DataFormat format) const {
- RETURN_NOT_OK_PREPEND(ca::ToBIO(bio, format, data_.get()), "could not export cert");
- return Status::OK();
-}
-
-void Cert::AdoptRawData(RawDataType* data) {
- data_ = make_ssl_unique(data);
-}
-
-Status Cert::FromBIO(BIO* bio, DataFormat format) {
- RETURN_NOT_OK_PREPEND(ca::FromBIO(bio, format, &data_), "could not read cert");
- return Status::OK();
-}
-
-Status Cert::ToBIO(BIO* bio, DataFormat format) const {
- RETURN_NOT_OK_PREPEND(ca::ToBIO(bio, format, data_.get()), "could not export cert");
- return Status::OK();
-}
-
-void CertSignRequest::AdoptRawData(RawDataType* data) {
- data_ = make_ssl_unique(data);
-}
-
-Status CertSignRequest::FromBIO(BIO* bio, DataFormat format) {
- RETURN_NOT_OK_PREPEND(ca::FromBIO(bio, format, &data_), "could not read X509 CSR");
- return Status::OK();
-}
-
-Status CertSignRequest::ToBIO(BIO* bio, DataFormat format) const {
- RETURN_NOT_OK_PREPEND(ca::ToBIO(bio, format, data_.get()), "could not export X509 CSR");
- return Status::OK();
-}
-
-Status GeneratePrivateKey(int num_bits, Key* ret) {
- CHECK(ret);
- InitializeOpenSSL();
- auto key = make_ssl_unique(EVP_PKEY_new());
- {
- auto bn = make_ssl_unique(BN_new());
- CERT_CHECK_OK(BN_set_word(bn.get(), RSA_F4));
- auto rsa = make_ssl_unique(RSA_new());
- CERT_RET_NOT_OK(RSA_generate_key_ex(rsa.get(), num_bits, bn.get(), nullptr),
- "error generating RSA key");
- CERT_RET_NOT_OK(EVP_PKEY_set1_RSA(key.get(), rsa.get()),
- "error assigning RSA key");
- }
- ret->AdoptRawData(key.release());
-
- return Status::OK();
-}
+namespace ca {
CertRequestGeneratorBase::CertRequestGeneratorBase(Config config)
: config_(move(config)) {
@@ -285,7 +83,7 @@ Status CertRequestGeneratorBase::GenerateRequest(const Key& key,
CertSignRequest* ret) const {
CHECK(ret);
CHECK(Initialized());
- auto req = make_ssl_unique(X509_REQ_new());
+ auto req = ssl_make_unique(X509_REQ_new());
CERT_RET_NOT_OK(X509_REQ_set_pubkey(req.get(), key.GetRawData()),
"error setting X509 public key");
X509_NAME* name = X509_REQ_get_subject_name(req.get());
@@ -322,7 +120,7 @@ Status CertRequestGeneratorBase::GenerateRequest(const Key& key,
Status CertRequestGeneratorBase::PushExtension(stack_st_X509_EXTENSION* st,
int32_t nid, const char* value) {
- auto ex = make_ssl_unique(
+ auto ex = ssl_make_unique(
X509V3_EXT_conf_nid(nullptr, nullptr, nid, const_cast<char*>(value)));
if (!ex) {
return Status::RuntimeError("error configuring extension");
@@ -554,7 +352,7 @@ const Key& CertSigner::ca_private_key() const {
Status CertSigner::Sign(const CertSignRequest& req, Cert* ret) const {
CHECK(ret);
CHECK(Initialized());
- auto x509 = make_ssl_unique(X509_new());
+ auto x509 = ssl_make_unique(X509_new());
RETURN_NOT_OK(FillCertTemplateFromRequest(req.GetRawData(), x509.get()));
RETURN_NOT_OK(DoSign(EVP_sha256(), exp_interval_sec_, x509.get()));
ret->AdoptRawData(x509.release());
@@ -578,7 +376,7 @@ Status CertSigner::CopyExtensions(X509_REQ* req, X509* x) {
if (idx != -1) {
// If extension exits, delete all extensions of same type.
do {
- auto tmpext = make_ssl_unique(X509_get_ext(x, idx));
+ auto tmpext = ssl_make_unique(X509_get_ext(x, idx));
X509_delete_ext(x, idx);
idx = X509_get_ext_by_OBJ(x, obj, -1);
} while (idx != -1);
@@ -597,7 +395,7 @@ Status CertSigner::FillCertTemplateFromRequest(X509_REQ* req, X509* tmpl) {
!req->req_info->pubkey->public_key->data) {
return Status::RuntimeError("corrupted CSR: no public key");
}
- auto pub_key = make_ssl_unique(X509_REQ_get_pubkey(req));
+ auto pub_key = ssl_make_unique(X509_REQ_get_pubkey(req));
if (!pub_key) {
return Status::RuntimeError("error unpacking public key from CSR");
}
@@ -622,10 +420,10 @@ Status CertSigner::DigestSign(const EVP_MD* md, EVP_PKEY* pkey, X509* x) {
}
Status CertSigner::GenerateSerial(c_unique_ptr<ASN1_INTEGER>* ret) {
- auto btmp = make_ssl_unique(BN_new());
+ auto btmp = ssl_make_unique(BN_new());
CERT_RET_NOT_OK(BN_pseudo_rand(btmp.get(), 64, 0, 0),
"error generating random number");
- auto serial = make_ssl_unique(ASN1_INTEGER_new());
+ auto serial = ssl_make_unique(ASN1_INTEGER_new());
CERT_RET_IF_NULL(BN_to_ASN1_INTEGER(btmp.get(), serial.get()),
"error converting number into ASN1 representation");
if (ret) {
http://git-wip-us.apache.org/repos/asf/kudu/blob/e367b4e0/src/kudu/security/ca/cert_management.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/ca/cert_management.h b/src/kudu/security/ca/cert_management.h
index 2d623d8..4a222f4 100644
--- a/src/kudu/security/ca/cert_management.h
+++ b/src/kudu/security/ca/cert_management.h
@@ -34,103 +34,13 @@
// in addition to openssl_util.h.
typedef struct asn1_string_st ASN1_INTEGER;
typedef struct env_md_st EVP_MD;
-typedef struct evp_pkey_st EVP_PKEY;
typedef struct rsa_st RSA;
-typedef struct x509_st X509;
-typedef struct X509_req_st X509_REQ;
struct stack_st_X509_EXTENSION; // STACK_OF(X509_EXTENSION)
namespace kudu {
namespace security {
namespace ca {
-template <typename T>
-using c_unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;
-
-// Acceptable formats for X509 certificates, X509 CSRs, and private keys.
-enum class DataFormat {
- DER = 0, // DER/ASN1 format (binary)
- PEM = 1, // PEM format (ASCII)
-};
-
-// Data format representation as a string.
-const std::string& DataFormatToString(DataFormat fmt);
-
-// Basic wrapper for objects of xxx_st type in the OpenSSL crypto library.
-class BasicWrapper {
- public:
- virtual ~BasicWrapper() = default;
-
- Status FromFile(const std::string& fpath, DataFormat format);
- Status FromString(const std::string& data, DataFormat format);
-
- Status ToString(std::string* data, DataFormat format) const;
-
- protected:
- virtual Status FromBIO(BIO* bio, DataFormat format) = 0;
- virtual Status ToBIO(BIO* bio, DataFormat format) const = 0;
-};
-
-// A wrapper for a private key.
-class Key : public BasicWrapper {
- public:
- typedef EVP_PKEY RawDataType;
-
- RawDataType* GetRawData() const {
- return data_.get();
- }
-
- void AdoptRawData(RawDataType* data);
-
- protected:
- Status FromBIO(BIO* bio, DataFormat format) override;
- Status ToBIO(BIO* bio, DataFormat format) const override;
-
- private:
- c_unique_ptr<RawDataType> data_;
-};
-
-// A wrapper for a X509 certificate.
-class Cert : public BasicWrapper {
- public:
- typedef X509 RawDataType;
-
- RawDataType* GetRawData() const {
- return data_.get();
- }
-
- void AdoptRawData(RawDataType* data);
-
- protected:
- Status FromBIO(BIO* bio, DataFormat format) override;
- Status ToBIO(BIO* bio, DataFormat format) const override;
-
- private:
- c_unique_ptr<RawDataType> data_;
-};
-
-// A wrapper for a X509 CSR (certificate signing request).
-class CertSignRequest : public BasicWrapper {
- public:
- typedef X509_REQ RawDataType;
-
- RawDataType* GetRawData() const {
- return data_.get();
- }
-
- void AdoptRawData(RawDataType* data);
-
- protected:
- Status FromBIO(BIO* bio, DataFormat format) override;
- Status ToBIO(BIO* bio, DataFormat format) const override;
-
- private:
- c_unique_ptr<RawDataType> data_;
-};
-
-// Utility method to generate private RSA keys.
-Status GeneratePrivateKey(int num_bits, Key* ret);
-
// Base utility class for issuing X509 CSRs.
class CertRequestGeneratorBase {
public:
http://git-wip-us.apache.org/repos/asf/kudu/blob/e367b4e0/src/kudu/security/openssl_util.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/openssl_util.cc b/src/kudu/security/openssl_util.cc
index c6699db..e460611 100644
--- a/src/kudu/security/openssl_util.cc
+++ b/src/kudu/security/openssl_util.cc
@@ -17,22 +17,28 @@
#include "kudu/security/openssl_util.h"
+#include <cstdio>
+#include <cstdlib>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
+#include <glog/logging.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
+#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/debug/leakcheck_disabler.h"
#include "kudu/util/mutex.h"
+#include "kudu/util/status.h"
#include "kudu/util/thread.h"
using std::ostringstream;
using std::string;
using std::vector;
+using strings::Substitute;
namespace kudu {
namespace security {
@@ -77,7 +83,8 @@ void DoInitializeOpenSSL() {
CRYPTO_set_locking_callback(LockingCB);
CRYPTO_THREADID_set_callback(ThreadIdCB);
}
-} // namespace
+
+} // anonymous namespace
void InitializeOpenSSL() {
static std::once_flag ssl_once;
@@ -107,5 +114,205 @@ string GetOpenSSLErrors() {
return serr.str();
}
+namespace {
+
+// Writing the private key from an EVP_PKEY has a different
+// signature than the rest of the write functions, so we
+// have to provide this wrapper.
+int PemWritePrivateKey(BIO* bio, EVP_PKEY* key) {
+ auto rsa = ssl_make_unique(EVP_PKEY_get1_RSA(key));
+ return PEM_write_bio_RSAPrivateKey(
+ bio, rsa.get(), nullptr, nullptr, 0, nullptr, nullptr);
+}
+
+} // anonymous namespace
+
+template<> struct SslTypeTraits<BIO> {
+ static constexpr auto free = &BIO_free;
+};
+template<> struct SslTypeTraits<BIGNUM> {
+ static constexpr auto free = &BN_free;
+};
+template<> struct SslTypeTraits<EVP_PKEY> {
+ static constexpr auto free = &EVP_PKEY_free;
+ static constexpr auto read_pem = &PEM_read_bio_PrivateKey;
+ static constexpr auto read_der = &d2i_PrivateKey_bio;
+ static constexpr auto write_pem = &PemWritePrivateKey;
+ static constexpr auto write_der = &i2d_PrivateKey_bio;
+};
+template<> struct SslTypeTraits<RSA> {
+ static constexpr auto free = &RSA_free;
+};
+template<> struct SslTypeTraits<X509> {
+ static constexpr auto free = &X509_free;
+ static constexpr auto read_pem = &PEM_read_bio_X509;
+ static constexpr auto read_der = &d2i_X509_bio;
+ static constexpr auto write_pem = &PEM_write_bio_X509;
+ static constexpr auto write_der = &i2d_X509_bio;
+};
+template<> struct SslTypeTraits<X509_REQ> {
+ static constexpr auto free = &X509_REQ_free;
+ static constexpr auto read_pem = &PEM_read_bio_X509_REQ;
+ static constexpr auto read_der = &d2i_X509_REQ_bio;
+ static constexpr auto write_pem = &PEM_write_bio_X509_REQ;
+ static constexpr auto write_der = &i2d_X509_REQ_bio;
+};
+
+namespace {
+
+template<class TYPE>
+Status ToBIO(BIO* bio, DataFormat format, TYPE* obj) {
+ using Traits = SslTypeTraits<TYPE>;
+ CHECK(bio);
+ CHECK(obj);
+ switch (format) {
+ case DataFormat::DER:
+ CERT_RET_NOT_OK(Traits::write_der(bio, obj),
+ "error exporting data in DER format");
+ break;
+ case DataFormat::PEM:
+ CERT_RET_NOT_OK(Traits::write_pem(bio, obj),
+ "error exporting data in PEM format");
+ break;
+ }
+ CERT_RET_NOT_OK(BIO_flush(bio), "error flushing BIO");
+ return Status::OK();
+}
+
+template<class TYPE>
+Status FromBIO(BIO* bio, DataFormat format, c_unique_ptr<TYPE>* ret) {
+ using Traits = SslTypeTraits<TYPE>;
+ CHECK(bio);
+ switch (format) {
+ case DataFormat::DER:
+ *ret = ssl_make_unique(Traits::read_der(bio, nullptr));
+ break;
+ case DataFormat::PEM:
+ *ret = ssl_make_unique(Traits::read_pem(bio, nullptr, nullptr, nullptr));
+ break;
+ }
+ if (PREDICT_FALSE(!*ret)) {
+ return Status::RuntimeError(GetOpenSSLErrors());
+ }
+ return Status::OK();
+}
+
+} // anonymous namespace
+
+
+const string& DataFormatToString(DataFormat fmt) {
+ static const string kStrFormatUnknown = "UNKNOWN";
+ static const string kStrFormatDer = "DER";
+ static const string kStrFormatPem = "PEM";
+ switch (fmt) {
+ case DataFormat::DER:
+ return kStrFormatDer;
+ case DataFormat::PEM:
+ return kStrFormatPem;
+ default:
+ return kStrFormatUnknown;
+ }
+}
+
+Status BasicWrapper::FromFile(const string& fpath, DataFormat format) {
+ auto bio = ssl_make_unique(BIO_new(BIO_s_file()));
+ CERT_RET_NOT_OK(BIO_read_filename(bio.get(), fpath.c_str()),
+ Substitute("$0: could not read from file", fpath));
+ RETURN_NOT_OK_PREPEND(FromBIO(bio.get(), format),
+ Substitute("$0: unable to load data key from file",
+ fpath));
+ return Status::OK();
+}
+
+Status BasicWrapper::FromString(const string& data, DataFormat format) {
+ const void* mdata = reinterpret_cast<const void*>(data.data());
+ auto bio = ssl_make_unique(BIO_new_mem_buf(
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ const_cast<void*>(mdata),
+#else
+ mdata,
+#endif
+ data.size()));
+ RETURN_NOT_OK_PREPEND(FromBIO(bio.get(), format),
+ "unable to load data from memory");
+ return Status::OK();
+}
+
+Status BasicWrapper::ToString(std::string* data, DataFormat format) const {
+ CHECK(data);
+ auto bio = ssl_make_unique(BIO_new(BIO_s_mem()));
+ RETURN_NOT_OK_PREPEND(ToBIO(bio.get(), format), "error serializing data");
+ BUF_MEM* membuf;
+ CERT_CHECK_OK(BIO_get_mem_ptr(bio.get(), &membuf));
+ data->assign(membuf->data, membuf->length);
+ return Status::OK();
+}
+
+void Key::AdoptRawData(RawDataType* data) {
+ data_ = ssl_make_unique(data);
+}
+
+Status Key::FromBIO(BIO* bio, DataFormat format) {
+ RETURN_NOT_OK_PREPEND(::kudu::security::FromBIO(bio, format, &data_),
+ "unable to read private key");
+ return Status::OK();
+}
+
+Status Key::ToBIO(BIO* bio, DataFormat format) const {
+ RETURN_NOT_OK_PREPEND(::kudu::security::ToBIO(bio, format, data_.get()),
+ "could not export cert");
+ return Status::OK();
+}
+
+void Cert::AdoptRawData(RawDataType* data) {
+ data_ = ssl_make_unique(data);
+}
+
+Status Cert::FromBIO(BIO* bio, DataFormat format) {
+ RETURN_NOT_OK_PREPEND(::kudu::security::FromBIO(bio, format, &data_),
+ "could not read cert");
+ return Status::OK();
+}
+
+Status Cert::ToBIO(BIO* bio, DataFormat format) const {
+ RETURN_NOT_OK_PREPEND(::kudu::security::ToBIO(bio, format, data_.get()),
+ "could not export cert");
+ return Status::OK();
+}
+
+void CertSignRequest::AdoptRawData(RawDataType* data) {
+ data_ = ssl_make_unique(data);
+}
+
+Status CertSignRequest::FromBIO(BIO* bio, DataFormat format) {
+ RETURN_NOT_OK_PREPEND(::kudu::security::FromBIO(bio, format, &data_),
+ "could not read X509 CSR");
+ return Status::OK();
+}
+
+Status CertSignRequest::ToBIO(BIO* bio, DataFormat format) const {
+ RETURN_NOT_OK_PREPEND(::kudu::security::ToBIO(bio, format, data_.get()),
+ "could not export X509 CSR");
+ return Status::OK();
+}
+
+Status GeneratePrivateKey(int num_bits, Key* ret) {
+ CHECK(ret);
+ InitializeOpenSSL();
+ auto key = ssl_make_unique(EVP_PKEY_new());
+ {
+ auto bn = ssl_make_unique(BN_new());
+ CERT_CHECK_OK(BN_set_word(bn.get(), RSA_F4));
+ auto rsa = ssl_make_unique(RSA_new());
+ CERT_RET_NOT_OK(RSA_generate_key_ex(rsa.get(), num_bits, bn.get(), nullptr),
+ "error generating RSA key");
+ CERT_RET_NOT_OK(EVP_PKEY_set1_RSA(key.get(), rsa.get()),
+ "error assigning RSA key");
+ }
+ ret->AdoptRawData(key.release());
+
+ return Status::OK();
+}
+
} // namespace security
} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/e367b4e0/src/kudu/security/openssl_util.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/openssl_util.h b/src/kudu/security/openssl_util.h
index 1bfdb0d..426f0a1 100644
--- a/src/kudu/security/openssl_util.h
+++ b/src/kudu/security/openssl_util.h
@@ -17,12 +17,37 @@
#pragma once
+#include <functional>
+#include <memory>
#include <string>
+#include <glog/logging.h>
+
+#include "kudu/util/status.h"
+#include "kudu/gutil/strings/substitute.h"
+
// Forward declarations for the OpenSSL typedefs.
typedef struct bio_st BIO;
+typedef struct evp_pkey_st EVP_PKEY;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_st SSL;
+typedef struct x509_st X509;
+typedef struct X509_req_st X509_REQ;
+
+#define CERT_CHECK_OK(call) \
+ CHECK_GT((call), 0)
+
+#define CERT_RET_NOT_OK(call, msg) \
+ if ((call) <= 0) { \
+ return Status::RuntimeError(Substitute("$0: $1", \
+ (msg), GetOpenSSLErrors())); \
+ }
+
+#define CERT_RET_IF_NULL(call, msg) \
+ if ((call) == nullptr) { \
+ return Status::RuntimeError(Substitute("$0: $1", \
+ (msg), GetOpenSSLErrors())); \
+ }
namespace kudu {
namespace security {
@@ -35,5 +60,103 @@ void InitializeOpenSSL();
// Fetch the last error message from the OpenSSL library.
std::string GetOpenSSLErrors();
+// A generic wrapper for OpenSSL structures.
+template <typename T>
+using c_unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;
+
+// For each SSL type, the Traits class provides the important OpenSSL
+// API functions.
+template<class SSL_TYPE>
+struct SslTypeTraits {};
+
+template<class SSL_TYPE>
+c_unique_ptr<SSL_TYPE> ssl_make_unique(SSL_TYPE* d) {
+ return {d, SslTypeTraits<SSL_TYPE>::free};
+}
+
+// Acceptable formats for X509 certificates, X509 CSRs, and private keys.
+enum class DataFormat {
+ DER = 0, // DER/ASN1 format (binary)
+ PEM = 1, // PEM format (ASCII)
+};
+
+// Data format representation as a string.
+const std::string& DataFormatToString(DataFormat fmt);
+
+// Basic wrapper for objects of xxx_st type in the OpenSSL crypto library.
+class BasicWrapper {
+ public:
+ virtual ~BasicWrapper() = default;
+
+ Status FromFile(const std::string& fpath, DataFormat format);
+ Status FromString(const std::string& data, DataFormat format);
+
+ Status ToString(std::string* data, DataFormat format) const;
+
+ protected:
+ virtual Status FromBIO(BIO* bio, DataFormat format) = 0;
+ virtual Status ToBIO(BIO* bio, DataFormat format) const = 0;
+};
+
+// A wrapper for a private key.
+class Key : public BasicWrapper {
+ public:
+ typedef EVP_PKEY RawDataType;
+
+ RawDataType* GetRawData() const {
+ return data_.get();
+ }
+
+ void AdoptRawData(RawDataType* data);
+
+ protected:
+ Status FromBIO(BIO* bio, DataFormat format) override;
+ Status ToBIO(BIO* bio, DataFormat format) const override;
+
+ private:
+ c_unique_ptr<RawDataType> data_;
+};
+
+// A wrapper for a X509 certificate.
+class Cert : public BasicWrapper {
+ public:
+ typedef X509 RawDataType;
+
+ RawDataType* GetRawData() const {
+ return data_.get();
+ }
+
+ void AdoptRawData(RawDataType* data);
+
+ protected:
+ Status FromBIO(BIO* bio, DataFormat format) override;
+ Status ToBIO(BIO* bio, DataFormat format) const override;
+
+ private:
+ c_unique_ptr<RawDataType> data_;
+};
+
+// A wrapper for a X509 CSR (certificate signing request).
+class CertSignRequest : public BasicWrapper {
+ public:
+ typedef X509_REQ RawDataType;
+
+ RawDataType* GetRawData() const {
+ return data_.get();
+ }
+
+ void AdoptRawData(RawDataType* data);
+
+ protected:
+ Status FromBIO(BIO* bio, DataFormat format) override;
+ Status ToBIO(BIO* bio, DataFormat format) const override;
+
+ private:
+ c_unique_ptr<RawDataType> data_;
+};
+
+// Utility method to generate private RSA keys.
+Status GeneratePrivateKey(int num_bits, Key* ret);
+
} // namespace security
} // namespace kudu