From ba690cad3c4138fce4e55b319237bb9e01ca030f Mon Sep 17 00:00:00 2001 From: Kyle Nekritz Date: Wed, 7 Jun 2017 07:31:37 -0700 Subject: [PATCH 1/1] Add OpenSSLCertUtils functions for DER encoding/decoding. Reviewed By: anirudhvr Differential Revision: D5193205 fbshipit-source-id: 5b427ee4f31008518078f5e54e85c0f0f2201da5 --- folly/ssl/OpenSSLCertUtils.cpp | 23 ++++++++++ folly/ssl/OpenSSLCertUtils.h | 16 +++++++ folly/ssl/test/OpenSSLCertUtilsTest.cpp | 57 ++++++++++++++++++------- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/folly/ssl/OpenSSLCertUtils.cpp b/folly/ssl/OpenSSLCertUtils.cpp index b1bdf180..d45daa6d 100644 --- a/folly/ssl/OpenSSLCertUtils.cpp +++ b/folly/ssl/OpenSSLCertUtils.cpp @@ -173,5 +173,28 @@ std::string OpenSSLCertUtils::getDateTimeStr(const ASN1_TIME* time) { return std::string(bioData, bioLen); } +X509UniquePtr OpenSSLCertUtils::derDecode(ByteRange range) { + auto begin = range.data(); + X509UniquePtr cert(d2i_X509(nullptr, &begin, range.size())); + if (!cert) { + throw std::runtime_error("could not read cert"); + } + return cert; +} + +std::unique_ptr OpenSSLCertUtils::derEncode(X509& x509) { + auto len = i2d_X509(&x509, nullptr); + if (len < 0) { + throw std::runtime_error("Error computing length"); + } + auto buf = IOBuf::create(len); + auto dataPtr = buf->writableData(); + len = i2d_X509(&x509, &dataPtr); + if (len < 0) { + throw std::runtime_error("Error converting cert to DER"); + } + buf->append(len); + return buf; +} } // ssl } // folly diff --git a/folly/ssl/OpenSSLCertUtils.h b/folly/ssl/OpenSSLCertUtils.h index be8fbec9..373fde01 100644 --- a/folly/ssl/OpenSSLCertUtils.h +++ b/folly/ssl/OpenSSLCertUtils.h @@ -19,7 +19,9 @@ #include #include +#include #include +#include namespace folly { namespace ssl { @@ -60,6 +62,20 @@ class OpenSSLCertUtils { */ static folly::Optional toString(X509& x509); + /** + * Decodes the DER representation of an X509 certificate. + * + * Throws on error (if a valid certificate can't be decoded). + */ + static X509UniquePtr derDecode(ByteRange); + + /** + * DER encodes an X509 certificate. + * + * Throws on error. + */ + static std::unique_ptr derEncode(X509&); + private: static std::string getDateTimeStr(const ASN1_TIME* time); }; diff --git a/folly/ssl/test/OpenSSLCertUtilsTest.cpp b/folly/ssl/test/OpenSSLCertUtilsTest.cpp index a9ca22b2..a337b4ff 100644 --- a/folly/ssl/test/OpenSSLCertUtilsTest.cpp +++ b/folly/ssl/test/OpenSSLCertUtilsTest.cpp @@ -18,9 +18,10 @@ #include #include -#include +#include #include #include +#include using namespace testing; using namespace folly; @@ -56,6 +57,13 @@ const std::string kTestCertWithSan = folly::stripLeftMargin(R"( -----END CERTIFICATE----- )"); +class OpenSSLCertUtilsTest : public Test { + public: + void SetUp() override { + SSLContext::initializeOpenSSL(); + } +}; + static folly::ssl::X509UniquePtr readCertFromFile(const std::string& filename) { folly::ssl::BioUniquePtr bio(BIO_new(BIO_s_file())); if (!bio) { @@ -79,9 +87,7 @@ static folly::ssl::X509UniquePtr readCertFromData( PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); } -TEST(OpenSSLCertUtilsTest, TestX509CN) { - OpenSSL_add_all_algorithms(); - +TEST_F(OpenSSLCertUtilsTest, TestX509CN) { auto x509 = readCertFromFile(kTestCertWithoutSan); EXPECT_NE(x509, nullptr); auto identity = folly::ssl::OpenSSLCertUtils::getCommonName(*x509); @@ -90,9 +96,7 @@ TEST(OpenSSLCertUtilsTest, TestX509CN) { EXPECT_EQ(sans.size(), 0); } -TEST(OpenSSLCertUtilsTest, TestX509Sans) { - OpenSSL_add_all_algorithms(); - +TEST_F(OpenSSLCertUtilsTest, TestX509Sans) { auto x509 = readCertFromData(kTestCertWithSan); EXPECT_NE(x509, nullptr); auto identity = folly::ssl::OpenSSLCertUtils::getCommonName(*x509); @@ -103,9 +107,7 @@ TEST(OpenSSLCertUtilsTest, TestX509Sans) { EXPECT_EQ(altNames[1], "*.thirdexample.com"); } -TEST(OpenSSLCertUtilsTest, TestX509IssuerAndSubject) { - OpenSSL_add_all_algorithms(); - +TEST_F(OpenSSLCertUtilsTest, TestX509IssuerAndSubject) { auto x509 = readCertFromData(kTestCertWithSan); EXPECT_NE(x509, nullptr); auto issuer = folly::ssl::OpenSSLCertUtils::getIssuer(*x509); @@ -116,9 +118,7 @@ TEST(OpenSSLCertUtilsTest, TestX509IssuerAndSubject) { EXPECT_EQ(subj.value(), "C = US, O = Asox, CN = 127.0.0.1"); } -TEST(OpenSSLCertUtilsTest, TestX509Dates) { - OpenSSL_add_all_algorithms(); - +TEST_F(OpenSSLCertUtilsTest, TestX509Dates) { auto x509 = readCertFromData(kTestCertWithSan); EXPECT_NE(x509, nullptr); auto notBefore = folly::ssl::OpenSSLCertUtils::getNotBeforeTime(*x509); @@ -127,9 +127,7 @@ TEST(OpenSSLCertUtilsTest, TestX509Dates) { EXPECT_EQ(notAfter, "Jul 1 23:21:03 2044 GMT"); } -TEST(OpenSSLCertUtilsTest, TestX509Summary) { - OpenSSL_add_all_algorithms(); - +TEST_F(OpenSSLCertUtilsTest, TestX509Summary) { auto x509 = readCertFromData(kTestCertWithSan); EXPECT_NE(x509, nullptr); auto summary = folly::ssl::OpenSSLCertUtils::toString(*x509); @@ -154,3 +152,30 @@ TEST(OpenSSLCertUtilsTest, TestX509Summary) { " Authority Information Access: \n" " CA Issuers - URI:https://phabricator.fb.com/diffusion/FBCODE/browse/master/ti/test_certs/ca_cert.pem?view=raw\n\n"); } + +TEST_F(OpenSSLCertUtilsTest, TestDerEncodeDecode) { + auto x509 = readCertFromData(kTestCertWithSan); + + auto der = folly::ssl::OpenSSLCertUtils::derEncode(*x509); + auto decoded = folly::ssl::OpenSSLCertUtils::derDecode(der->coalesce()); + + EXPECT_EQ( + folly::ssl::OpenSSLCertUtils::toString(*x509), + folly::ssl::OpenSSLCertUtils::toString(*decoded)); +} + +TEST_F(OpenSSLCertUtilsTest, TestDerDecodeJunkData) { + StringPiece junk{"MyFakeCertificate"}; + EXPECT_THROW( + folly::ssl::OpenSSLCertUtils::derDecode(junk), std::runtime_error); +} + +TEST_F(OpenSSLCertUtilsTest, TestDerDecodeTooShort) { + auto x509 = readCertFromData(kTestCertWithSan); + + auto der = folly::ssl::OpenSSLCertUtils::derEncode(*x509); + der->trimEnd(1); + EXPECT_THROW( + folly::ssl::OpenSSLCertUtils::derDecode(der->coalesce()), + std::runtime_error); +} -- 2.34.1