Key-Cert Pair Validation (#26471)

* Functions for checking if a private key and a certificate chain match
pull/26659/head
itsemmanuelfrancis 4 years ago committed by GitHub
parent b9a69481c2
commit 994ee5da0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      BUILD
  2. 49
      src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc
  3. 7
      src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h
  4. 49
      test/core/security/grpc_tls_certificate_provider_test.cc

@ -2254,6 +2254,7 @@ grpc_cc_library(
"absl/strings:str_format", "absl/strings:str_format",
"absl/time", "absl/time",
"libcrypto", "libcrypto",
"libssl",
], ],
language = "c++", language = "c++",
public_hdrs = GRPC_SECURE_PUBLIC_HDRS, public_hdrs = GRPC_SECURE_PUBLIC_HDRS,

@ -21,6 +21,7 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/string_util.h> #include <grpc/support/string_util.h>
#include <openssl/ssl.h>
#include "src/core/lib/gprpp/stat.h" #include "src/core/lib/gprpp/stat.h"
#include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_internal.h"
@ -364,6 +365,54 @@ FileWatcherCertificateProvider::ReadIdentityKeyCertPairFromFiles(
return absl::nullopt; return absl::nullopt;
} }
absl::StatusOr<bool> PrivateKeyAndCertificateMatch(
absl::string_view private_key, absl::string_view cert_chain) {
if (private_key.empty()) {
return absl::InvalidArgumentError("Private key string is empty.");
}
if (cert_chain.empty()) {
return absl::InvalidArgumentError("Certificate string is empty.");
}
BIO* cert_bio = BIO_new_mem_buf(cert_chain.data(), cert_chain.size());
if (cert_bio == nullptr) {
return absl::InvalidArgumentError(
"Conversion from certificate string to BIO failed.");
}
// Reads the first cert from the cert_chain which is expected to be the leaf
// cert
X509* x509 = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
BIO_free(cert_bio);
if (x509 == nullptr) {
return absl::InvalidArgumentError(
"Conversion from PEM string to X509 failed.");
}
EVP_PKEY* public_evp_pkey = X509_get_pubkey(x509);
X509_free(x509);
if (public_evp_pkey == nullptr) {
return absl::InvalidArgumentError(
"Extraction of public key from x.509 certificate failed.");
}
BIO* private_key_bio =
BIO_new_mem_buf(private_key.data(), private_key.size());
if (private_key_bio == nullptr) {
EVP_PKEY_free(public_evp_pkey);
return absl::InvalidArgumentError(
"Conversion from private key string to BIO failed.");
}
EVP_PKEY* private_evp_pkey =
PEM_read_bio_PrivateKey(private_key_bio, nullptr, nullptr, nullptr);
BIO_free(private_key_bio);
if (private_evp_pkey == nullptr) {
EVP_PKEY_free(public_evp_pkey);
return absl::InvalidArgumentError(
"Conversion from PEM string to EVP_PKEY failed.");
}
bool result = EVP_PKEY_cmp(private_evp_pkey, public_evp_pkey) == 1;
EVP_PKEY_free(private_evp_pkey);
EVP_PKEY_free(public_evp_pkey);
return result;
}
} // namespace grpc_core } // namespace grpc_core
/** -- Wrapper APIs declared in grpc_security.h -- **/ /** -- Wrapper APIs declared in grpc_security.h -- **/

@ -23,6 +23,7 @@
#include <string.h> #include <string.h>
#include "absl/container/inlined_vector.h" #include "absl/container/inlined_vector.h"
#include "absl/status/statusor.h"
#include "src/core/lib/gprpp/ref_counted.h" #include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/ref_counted_ptr.h"
@ -133,6 +134,12 @@ class FileWatcherCertificateProvider final
std::map<std::string, WatcherInfo> watcher_info_; std::map<std::string, WatcherInfo> watcher_info_;
}; };
// Checks if the private key matches the certificate's public key.
// Returns a not-OK status on failure, or a bool indicating
// whether the key/cert pair matches.
absl::StatusOr<bool> PrivateKeyAndCertificateMatch(
absl::string_view private_key, absl::string_view cert_chain);
} // namespace grpc_core } // namespace grpc_core
#endif // GRPC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CERTIFICATE_PROVIDER_H #endif // GRPC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CERTIFICATE_PROVIDER_H

@ -495,8 +495,55 @@ TEST_F(GrpcTlsCertificateProviderTest,
CancelWatch(watcher_state_1); CancelWatch(watcher_state_1);
} }
} // namespace testing TEST_F(GrpcTlsCertificateProviderTest, FailedKeyCertMatchOnEmptyPrivateKey) {
absl::StatusOr<bool> status =
PrivateKeyAndCertificateMatch(/*private_key=*/"", cert_chain_);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.status().message(), "Private key string is empty.");
}
TEST_F(GrpcTlsCertificateProviderTest, FailedKeyCertMatchOnEmptyCertificate) {
absl::StatusOr<bool> status =
PrivateKeyAndCertificateMatch(private_key_2_, /*cert_chain=*/"");
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.status().message(), "Certificate string is empty.");
}
TEST_F(GrpcTlsCertificateProviderTest, FailedKeyCertMatchOnInvalidCertFormat) {
absl::StatusOr<bool> status =
PrivateKeyAndCertificateMatch(private_key_2_, "invalid_certificate");
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.status().message(),
"Conversion from PEM string to X509 failed.");
}
TEST_F(GrpcTlsCertificateProviderTest,
FailedKeyCertMatchOnInvalidPrivateKeyFormat) {
absl::StatusOr<bool> status =
PrivateKeyAndCertificateMatch("invalid_private_key", cert_chain_2_);
EXPECT_EQ(status.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.status().message(),
"Conversion from PEM string to EVP_PKEY failed.");
}
TEST_F(GrpcTlsCertificateProviderTest, SuccessfulKeyCertMatch) {
absl::StatusOr<bool> status =
PrivateKeyAndCertificateMatch(private_key_2_, cert_chain_2_);
EXPECT_TRUE(status.ok());
EXPECT_TRUE(*status);
}
TEST_F(GrpcTlsCertificateProviderTest, FailedKeyCertMatchOnInvalidPair) {
absl::StatusOr<bool> status =
PrivateKeyAndCertificateMatch(private_key_2_, cert_chain_);
EXPECT_TRUE(status.ok());
EXPECT_FALSE(*status);
}
} // namespace testing
} // namespace grpc_core } // namespace grpc_core
int main(int argc, char** argv) { int main(int argc, char** argv) {

Loading…
Cancel
Save