Add utility to create stores
authorNeel Goyal <ngoyal@fb.com>
Fri, 5 Jan 2018 01:56:20 +0000 (17:56 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Fri, 5 Jan 2018 02:06:10 +0000 (18:06 -0800)
Summary: Add methods to create a X509StoreUniquePtr from a ca file or buffer.

Reviewed By: yfeldblum

Differential Revision: D6662538

fbshipit-source-id: 646f61596f2787756b2fa5998a43f36d75a91d90

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

index ac7cd7414f2159e68be9ce587e0624080cd9aa2e..f07096d29d2b9b2c313c80c76e04ee5a36e658ef 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <folly/ssl/OpenSSLCertUtils.h>
 
+#include <folly/FileUtil.h>
 #include <folly/ScopeGuard.h>
 #include <folly/String.h>
 #include <folly/ssl/OpenSSLPtrTypes.h>
@@ -240,6 +241,32 @@ std::array<uint8_t, SHA256_DIGEST_LENGTH> OpenSSLCertUtils::getDigestSha256(
   return md;
 }
 
+X509StoreUniquePtr OpenSSLCertUtils::readStoreFromFile(std::string caFile) {
+  std::string certData;
+  if (!folly::readFile(caFile.c_str(), certData)) {
+    throw std::runtime_error(
+        folly::to<std::string>("Could not read store file: ", caFile));
+  }
+  auto certRange = folly::ByteRange(folly::StringPiece(certData));
+  return readStoreFromBuffer(std::move(certRange));
+}
 
+X509StoreUniquePtr OpenSSLCertUtils::readStoreFromBuffer(ByteRange certRange) {
+  auto certs = readCertsFromBuffer(certRange);
+  ERR_clear_error();
+  folly::ssl::X509StoreUniquePtr store(X509_STORE_new());
+  for (auto& caCert : certs) {
+    if (X509_STORE_add_cert(store.get(), caCert.get()) != 1) {
+      auto err = ERR_get_error();
+      if (ERR_GET_LIB(err) != ERR_LIB_X509 ||
+          ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+        throw std::runtime_error(folly::to<std::string>(
+            "Could not insert CA certificate into store: ",
+            std::string(ERR_error_string(err, nullptr))));
+      }
+    }
+  }
+  return store;
+}
 } // namespace ssl
 } // namespace folly
index 31273d02ae797814d643ad0ca99b6802bf0ebd6b..edf3498c66e67eb1740a10b3d9ced26d16c3fbfd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -90,6 +90,12 @@ class OpenSSLCertUtils {
   static std::array<uint8_t, SHA_DIGEST_LENGTH> getDigestSha1(X509& x509);
   static std::array<uint8_t, SHA256_DIGEST_LENGTH> getDigestSha256(X509& x509);
 
+  /**
+   * Reads a store from a file (or buffer).  Throws on error.
+   */
+  static X509StoreUniquePtr readStoreFromFile(std::string caFile);
+  static X509StoreUniquePtr readStoreFromBuffer(ByteRange);
+
  private:
   static std::string getDateTimeStr(const ASN1_TIME* time);
 };
index ac747b3321b9487415108b08ce1d3d31a1c5dd9a..de4f40fdf23a5914920c25e14e6ab9ccb4df060f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include <folly/ssl/OpenSSLCertUtils.h>
 
 #include <folly/Format.h>
@@ -29,6 +28,7 @@ using namespace testing;
 using namespace folly;
 
 const char* kTestCertWithoutSan = "folly/io/async/test/certs/tests-cert.pem";
+const char* kTestCa = "folly/io/async/test/certs/ca-cert.pem";
 
 // Test key
 // -----BEGIN EC PRIVATE KEY-----
@@ -273,3 +273,15 @@ TEST_F(OpenSSLCertUtilsTest, TestX509Digest) {
       folly::hexlify(folly::range(sha2Digest)),
       "364d3a6a0b10d0635ce59b40c0b7f505ab2cd9fd0a06661cdc61d9cb8c9c9821");
 }
+
+TEST_F(OpenSSLCertUtilsTest, TestX509Store) {
+  auto store = folly::ssl::OpenSSLCertUtils::readStoreFromFile(kTestCa);
+  EXPECT_NE(store, nullptr);
+
+  auto x509 = readCertFromFile(kTestCertWithoutSan);
+  folly::ssl::X509StoreCtxUniquePtr ctx(X509_STORE_CTX_new());
+  auto rc = X509_STORE_CTX_init(ctx.get(), store.get(), x509.get(), nullptr);
+  EXPECT_EQ(rc, 1);
+  rc = X509_verify_cert(ctx.get());
+  EXPECT_EQ(rc, 1);
+}