Add OpenSSLCertUtils functions for DER encoding/decoding.
authorKyle Nekritz <knekritz@fb.com>
Wed, 7 Jun 2017 14:31:37 +0000 (07:31 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Wed, 7 Jun 2017 14:35:46 +0000 (07:35 -0700)
Reviewed By: anirudhvr

Differential Revision: D5193205

fbshipit-source-id: 5b427ee4f31008518078f5e54e85c0f0f2201da5

folly/ssl/OpenSSLCertUtils.cpp
folly/ssl/OpenSSLCertUtils.h
folly/ssl/test/OpenSSLCertUtilsTest.cpp

index b1bdf180e737e8dbdfa5e4f1d0566d38e905aafa..d45daa6d2aef34e5b2638df47883f3249baa1fe5 100644 (file)
@@ -173,5 +173,28 @@ std::string OpenSSLCertUtils::getDateTimeStr(const ASN1_TIME* time) {
   return std::string(bioData, bioLen);
 }
 
   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<IOBuf> 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
 } // ssl
 } // folly
index be8fbec9187d930796215513037e6409049f272f..373fde01b37c6e96beb88120cabd7e3ec9b5f5ed 100644 (file)
@@ -19,7 +19,9 @@
 #include <vector>
 
 #include <folly/Optional.h>
 #include <vector>
 
 #include <folly/Optional.h>
+#include <folly/io/IOBuf.h>
 #include <folly/portability/OpenSSL.h>
 #include <folly/portability/OpenSSL.h>
+#include <folly/ssl/OpenSSLPtrTypes.h>
 
 namespace folly {
 namespace ssl {
 
 namespace folly {
 namespace ssl {
@@ -60,6 +62,20 @@ class OpenSSLCertUtils {
    */
   static folly::Optional<std::string> toString(X509& x509);
 
    */
   static folly::Optional<std::string> 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<IOBuf> derEncode(X509&);
+
  private:
   static std::string getDateTimeStr(const ASN1_TIME* time);
 };
  private:
   static std::string getDateTimeStr(const ASN1_TIME* time);
 };
index a9ca22b260b848b98875ac573ce19f793c2543b9..a337b4ff02e9043333eb3ac3da8fc632d2b9a1d2 100644 (file)
 
 #include <folly/Range.h>
 #include <folly/String.h>
 
 #include <folly/Range.h>
 #include <folly/String.h>
-#include <folly/ssl/OpenSSLPtrTypes.h>
+#include <folly/io/async/SSLContext.h>
 #include <folly/portability/GTest.h>
 #include <folly/portability/OpenSSL.h>
 #include <folly/portability/GTest.h>
 #include <folly/portability/OpenSSL.h>
+#include <folly/ssl/OpenSSLPtrTypes.h>
 
 using namespace testing;
 using namespace folly;
 
 using namespace testing;
 using namespace folly;
@@ -56,6 +57,13 @@ const std::string kTestCertWithSan = folly::stripLeftMargin(R"(
   -----END CERTIFICATE-----
 )");
 
   -----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) {
 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));
 }
 
       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);
   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);
 }
 
   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);
   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");
 }
 
   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);
   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");
 }
 
   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);
   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");
 }
 
   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);
   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");
 }
       "            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);
+}