This is an automated email from the ASF dual-hosted git repository. alexey pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push: new dfdaa69b3 KUDU-3448 Plumbing for encrypting key material dfdaa69b3 is described below commit dfdaa69b34b1030677b7049c6b717957d32c7af4 Author: Attila Bukor <abu...@apache.org> AuthorDate: Mon Mar 13 13:32:41 2023 +0100 KUDU-3448 Plumbing for encrypting key material Key material for the internal PKI and token signing keys are stored in the syscatalog table in clear text, which is okay when volume-level encryption or Kudu's built-in data at rest encryption is used, but in some cases, this is either not used, or it's not enough (FISMA). To allow storing these key materials in encrypted form in the syscatalog table, this patch adds the necessary plumbing in Kudu's OpenSSL wrapper. It is now possible to pass a password callback function to the utility functions responsible for reading from and writing to OpenSSL BIO and strings. Change-Id: I24c5ac8ea0f9a4cab0f35ecccb1b7b00f3acefa8 Reviewed-on: http://gerrit.cloudera.org:8080/19615 Tested-by: Kudu Jenkins Reviewed-by: Alexey Serbin <ale...@apache.org> --- src/kudu/security/crypto.cc | 62 ++++++++++++++++++++++++++++++++++++++++ src/kudu/security/crypto.h | 5 ++++ src/kudu/util/openssl_util_bio.h | 13 +++++---- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/kudu/security/crypto.cc b/src/kudu/security/crypto.cc index 1e0f07a44..be2ebf0f0 100644 --- a/src/kudu/security/crypto.cc +++ b/src/kudu/security/crypto.cc @@ -75,6 +75,12 @@ struct RsaPrivateKeyTraits : public SslTypeTraits<EVP_PKEY> { static constexpr auto kWritePemFunc = &PemWritePrivateKey; static constexpr auto kWriteDerFunc = &i2d_PrivateKey_bio; }; +struct RsaEncryptedPrivateKeyTraits : public SslTypeTraits<EVP_PKEY> { + static constexpr auto kReadPemFunc = &PEM_read_bio_PrivateKey; + static constexpr auto kReadDerFunc = &d2i_PKCS8PrivateKey_bio; + static constexpr auto kWritePemFunc = &PEM_write_bio_PKCS8PrivateKey; + static constexpr auto kWriteDerFunc = &i2d_PKCS8PrivateKey_bio; +}; struct RsaPublicKeyTraits : public SslTypeTraits<EVP_PKEY> { static constexpr auto kReadPemFunc = &PEM_read_bio_PUBKEY; static constexpr auto kReadDerFunc = &d2i_PUBKEY_bio; @@ -92,6 +98,50 @@ template<> struct SslTypeTraits<EVP_MD_CTX> { #endif }; +template<> +Status FromBIO<EVP_PKEY, RsaEncryptedPrivateKeyTraits>(BIO* bio, DataFormat format, + c_unique_ptr<EVP_PKEY>* ret, const PasswordCallback& cb) { + CHECK(bio); + switch (format) { + case DataFormat::DER: + *ret = ssl_make_unique(RsaEncryptedPrivateKeyTraits::kReadDerFunc( + bio, nullptr, &TLSPasswordCB, const_cast<PasswordCallback*>(&cb))); + break; + case DataFormat::PEM: + *ret = ssl_make_unique(RsaEncryptedPrivateKeyTraits::kReadPemFunc( + bio, nullptr, &TLSPasswordCB, const_cast<PasswordCallback*>(&cb))); + break; + } + if (PREDICT_FALSE(!*ret)) { + return Status::RuntimeError("error reading private key", GetOpenSSLErrors()); + } + return Status::OK(); +} + +template<> +Status ToBIO<EVP_PKEY, RsaEncryptedPrivateKeyTraits>(BIO* bio, DataFormat format, + EVP_PKEY* obj, const PasswordCallback& cb) { + CHECK(bio); + CHECK(obj); + switch (format) { + case DataFormat::DER: + OPENSSL_RET_NOT_OK(RsaEncryptedPrivateKeyTraits::kWriteDerFunc( + bio, obj, EVP_aes_256_cbc(), nullptr, 0, &TLSPasswordCB, + const_cast<PasswordCallback*>(&cb)), + "error exporting data in DER format"); + break; + case DataFormat::PEM: + OPENSSL_RET_NOT_OK(RsaEncryptedPrivateKeyTraits::kWritePemFunc( + bio, obj, EVP_aes_256_cbc(), nullptr, 0, &TLSPasswordCB, + const_cast<PasswordCallback*>(&cb)), + "error exporting data in PEM format"); + break; + } + OPENSSL_RET_NOT_OK(BIO_flush(bio), "error flushing BIO"); + return Status::OK(); +} + + namespace { const EVP_MD* GetMessageDigest(DigestType digest_type) { @@ -184,11 +234,23 @@ Status PrivateKey::FromString(const std::string& data, DataFormat format) { data, format, &data_); } +Status PrivateKey::FromEncryptedString(const std::string& data, DataFormat format, + const PasswordCallback& password_cb) { + return ::kudu::security::FromString<RawDataType, RsaEncryptedPrivateKeyTraits>( + data, format, &data_, password_cb); +} + Status PrivateKey::ToString(std::string* data, DataFormat format) const { return ::kudu::security::ToString<RawDataType, RsaPrivateKeyTraits>( data, format, data_.get()); } +Status PrivateKey::ToEncryptedString(std::string* data, DataFormat format, + const PasswordCallback& password_cb) const { + return ::kudu::security::ToString<RawDataType, RsaEncryptedPrivateKeyTraits>( + data, format, data_.get(), password_cb); +} + Status PrivateKey::FromFile(const std::string& fpath, DataFormat format, const PasswordCallback& password_cb) { return ::kudu::security::FromFile<RawDataType, RsaPrivateKeyTraits>( diff --git a/src/kudu/security/crypto.h b/src/kudu/security/crypto.h index db5d24f83..51fdc2ec6 100644 --- a/src/kudu/security/crypto.h +++ b/src/kudu/security/crypto.h @@ -75,6 +75,11 @@ class PrivateKey : public RawDataWrapper<EVP_PKEY> { Status FromString(const std::string& data, DataFormat format) WARN_UNUSED_RESULT; Status ToString(std::string* data, DataFormat format) const WARN_UNUSED_RESULT; + Status FromEncryptedString(const std::string& data, DataFormat format, + const PasswordCallback& password_cb) WARN_UNUSED_RESULT; + Status ToEncryptedString(std::string* data, DataFormat format, + const PasswordCallback& password_cb) const WARN_UNUSED_RESULT; + // If 'cb' is set, it will be called to obtain the password necessary to decrypt // the private key file in 'fpath'. Status FromFile(const std::string& fpath, DataFormat format, diff --git a/src/kudu/util/openssl_util_bio.h b/src/kudu/util/openssl_util_bio.h index 4eff7682e..92861ac82 100644 --- a/src/kudu/util/openssl_util_bio.h +++ b/src/kudu/util/openssl_util_bio.h @@ -33,7 +33,8 @@ namespace security { template<typename TYPE, typename Traits = SslTypeTraits<TYPE>> -Status ToBIO(BIO* bio, DataFormat format, TYPE* obj) { +Status ToBIO(BIO* bio, DataFormat format, TYPE* obj, + const PasswordCallback& /*cb*/ = PasswordCallback()) { CHECK(bio); CHECK(obj); switch (format) { @@ -85,7 +86,8 @@ Status FromBIO(BIO* bio, DataFormat format, c_unique_ptr<TYPE>* ret, template<typename Type, typename Traits = SslTypeTraits<Type>> Status FromString(const std::string& data, DataFormat format, - c_unique_ptr<Type>* ret) { + c_unique_ptr<Type>* ret, + const PasswordCallback& cb = PasswordCallback()) { const void* mdata = reinterpret_cast<const void*>(data.data()); auto bio = ssl_make_unique(BIO_new_mem_buf( #if OPENSSL_VERSION_NUMBER < 0x10002000L @@ -94,16 +96,17 @@ Status FromString(const std::string& data, DataFormat format, mdata, #endif data.size())); - RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret)), + RETURN_NOT_OK_PREPEND((FromBIO<Type, Traits>(bio.get(), format, ret, cb)), "unable to load data from memory"); return Status::OK(); } template<typename Type, typename Traits = SslTypeTraits<Type>> -Status ToString(std::string* data, DataFormat format, Type* obj) { +Status ToString(std::string* data, DataFormat format, Type* obj, + const PasswordCallback& cb = PasswordCallback()) { CHECK(data); auto bio = ssl_make_unique(BIO_new(BIO_s_mem())); - RETURN_NOT_OK_PREPEND((ToBIO<Type, Traits>(bio.get(), format, obj)), + RETURN_NOT_OK_PREPEND((ToBIO<Type, Traits>(bio.get(), format, obj, cb)), "error serializing data"); BUF_MEM* membuf; OPENSSL_CHECK_OK(BIO_get_mem_ptr(bio.get(), &membuf));