Add a few more methods to OpenSSLCertUtils
authorAnirudh Ramachandran <avr@fb.com>
Tue, 7 Mar 2017 08:26:12 +0000 (00:26 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Tue, 7 Mar 2017 08:36:19 +0000 (00:36 -0800)
Summary:
Add a few more getters (subject, issuer, notbefore, notafter) to
OpenSSLCertUtils.

Also add an additional API to AsyncSSLSocket to add application-generated
alert strings during the handshake, for e.g., during certificate verification

Reviewed By: knekritz

Differential Revision: D4624754

fbshipit-source-id: f01998b9e0e58b88ece8c6dc51ab590988bf0a8f

folly/io/async/AsyncSSLSocket.cpp
folly/io/async/AsyncSSLSocket.h
folly/ssl/OpenSSLCertUtils.cpp
folly/ssl/OpenSSLCertUtils.h
folly/ssl/test/OpenSSLCertUtilsTest.cpp

index 7b4e16bbcdf5c022794dd19fb78547b5400c246e..a25269d2c55b8536d8ade0e2cc6176d294ae6711 100644 (file)
@@ -1911,6 +1911,14 @@ std::string AsyncSSLSocket::getSSLAlertsReceived() const {
   return ret;
 }
 
+void AsyncSSLSocket::setSSLCertVerificationAlert(std::string alert) {
+  sslVerificationAlert_ = std::move(alert);
+}
+
+std::string AsyncSSLSocket::getSSLCertVerificationAlert() const {
+  return sslVerificationAlert_;
+}
+
 void AsyncSSLSocket::getSSLSharedCiphers(std::string& sharedCiphers) const {
   char ciphersBuffer[1024];
   ciphersBuffer[0] = '\0';
index 99728dddcee12302547fcf4834bf3a5f431b2b97..a8bb1e123aa2a22089b069ec59cb490c9b1c8820 100644 (file)
@@ -597,6 +597,13 @@ class AsyncSSLSocket : public virtual AsyncSocket {
 
   std::string getSSLAlertsReceived() const;
 
+  /*
+   * Save an optional alert message generated during certificate verify
+   */
+  void setSSLCertVerificationAlert(std::string alert);
+
+  std::string getSSLCertVerificationAlert() const;
+
   /**
    * Get the list of shared ciphers between the server and the client.
    * Works well for only SSLv2, not so good for SSLv3 or TLSv1.
@@ -858,6 +865,7 @@ class AsyncSSLSocket : public virtual AsyncSocket {
   std::chrono::milliseconds totalConnectTimeout_{0};
 
   std::unique_ptr<IOBuf> preReceivedData_;
+  std::string sslVerificationAlert_;
 };
 
 } // namespace
index ae619048de93b2c2b18b684fc53329c5661c1477..d06cdac756b0645e71bb619cfd407fd69b39a8dc 100644 (file)
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 #include <folly/ssl/OpenSSLCertUtils.h>
+#include <folly/String.h>
+#include <folly/io/async/ssl/OpenSSLPtrTypes.h>
 
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
@@ -80,5 +82,99 @@ std::vector<std::string> OpenSSLCertUtils::getSubjectAltNames(X509& x509) {
   }
   return ret;
 }
+
+Optional<std::string> OpenSSLCertUtils::getSubject(X509& x509) {
+  auto subject = X509_get_subject_name(&x509);
+  if (!subject) {
+    return none;
+  }
+
+  auto bio = BioUniquePtr(BIO_new(BIO_s_mem()));
+  if (bio == nullptr) {
+    throw std::runtime_error("Cannot allocate bio");
+  }
+  if (X509_NAME_print_ex(bio.get(), subject, 0, XN_FLAG_ONELINE) <= 0) {
+    return none;
+  }
+
+  char* bioData = nullptr;
+  size_t bioLen = BIO_get_mem_data(bio.get(), &bioData);
+  return std::string(bioData, bioLen);
 }
+
+Optional<std::string> OpenSSLCertUtils::getIssuer(X509& x509) {
+  auto issuer = X509_get_issuer_name(&x509);
+  if (!issuer) {
+    return none;
+  }
+
+  auto bio = BioUniquePtr(BIO_new(BIO_s_mem()));
+  if (bio == nullptr) {
+    throw std::runtime_error("Cannot allocate bio");
+  }
+
+  if (X509_NAME_print_ex(bio.get(), issuer, 0, XN_FLAG_ONELINE) <= 0) {
+    return none;
+  }
+
+  char* bioData = nullptr;
+  size_t bioLen = BIO_get_mem_data(bio.get(), &bioData);
+  return std::string(bioData, bioLen);
 }
+
+folly::Optional<std::string> OpenSSLCertUtils::toString(X509& x509) {
+  auto in = BioUniquePtr(BIO_new(BIO_s_mem()));
+  if (in == nullptr) {
+    throw std::runtime_error("Cannot allocate bio");
+  }
+
+  if (X509_print_ex(
+          in.get(),
+          &x509,
+          XN_FLAG_ONELINE,
+          X509_FLAG_NO_HEADER | /* A few bytes of cert and data */
+              X509_FLAG_NO_PUBKEY | /* Public key */
+              X509_FLAG_NO_IDS | /* Issuer/subject IDs */
+              X509_FLAG_NO_AUX | /* Auxiliary info? */
+              X509_FLAG_NO_SIGDUMP | /* Prints the signature */
+              X509_FLAG_NO_SIGNAME /* Signature algorithms */
+          ) > 0) {
+    char* bioData = nullptr;
+    size_t bioLen = BIO_get_mem_data(in.get(), &bioData);
+    return std::string(bioData, bioLen);
+  } else {
+    return none;
+  }
+}
+
+std::string OpenSSLCertUtils::getNotAfterTime(X509& x509) {
+  return getDateTimeStr(X509_get_notAfter(&x509));
+}
+
+std::string OpenSSLCertUtils::getNotBeforeTime(X509& x509) {
+  return getDateTimeStr(X509_get_notBefore(&x509));
+}
+
+std::string OpenSSLCertUtils::getDateTimeStr(const ASN1_TIME* time) {
+  if (!time) {
+    return "";
+  }
+
+  std::array<char, 32> buf;
+
+  auto bio = BioUniquePtr(BIO_new(BIO_s_mem()));
+  if (bio == nullptr) {
+    throw std::runtime_error("Cannot allocate bio");
+  }
+
+  if (ASN1_TIME_print(bio.get(), time) <= 0) {
+    throw std::runtime_error("Cannot print ASN1_TIME");
+  }
+
+  char* bioData = nullptr;
+  size_t bioLen = BIO_get_mem_data(bio.get(), &bioData);
+  return std::string(bioData, bioLen);
+}
+
+} // ssl
+} // folly
index 82e30ffb46446d02286f8fac2b82f64ec988e037..c6c3803b58da7ae8bd86a7956d9a74b1af45ab62 100644 (file)
@@ -31,6 +31,38 @@ class OpenSSLCertUtils {
   static Optional<std::string> getCommonName(X509& x509);
 
   static std::vector<std::string> getSubjectAltNames(X509& x509);
+
+  /*
+   * Return the subject name, if any, from the cert
+   * @param x509    Reference to an X509
+   * @return a folly::Optional<std::string>, or folly::none
+   */
+  static Optional<std::string> getSubject(X509& x509);
+
+  /*
+   * Return the issuer name, if any, from the cert
+   * @param x509    Reference to an X509
+   * @return a folly::Optional<std::string>, or folly::none
+   */
+  static Optional<std::string> getIssuer(X509& x509);
+
+  /*
+   * Get a string representation of the not-before time on the certificate
+   */
+  static std::string getNotBeforeTime(X509& x509);
+
+  /*
+   * Get a string representation of the not-after (expiration) time
+   */
+  static std::string getNotAfterTime(X509& x509);
+
+  /*
+   * Summarize the CN, Subject, Issuer, Validity, and extensions as a string
+   */
+  static folly::Optional<std::string> toString(X509& x509);
+
+ private:
+  static std::string getDateTimeStr(const ASN1_TIME* time);
 };
 }
 }
index e41d0b67cd24fc0e12a6fb1e2b25cb0d226e8abd..629c15a2282c5c8349a3f0fe469c19f97183fefa 100644 (file)
@@ -105,3 +105,55 @@ TEST(OpenSSLCertUtilsTest, TestX509Sans) {
   EXPECT_EQ(altNames[0], "anotherexample.com");
   EXPECT_EQ(altNames[1], "*.thirdexample.com");
 }
+
+TEST(OpenSSLCertUtilsTest, TestX509IssuerAndSubject) {
+  OpenSSL_add_all_algorithms();
+
+  auto x509 = readCertFromData(kTestCertWithSan);
+  EXPECT_NE(x509, nullptr);
+  auto issuer = folly::ssl::OpenSSLCertUtils::getIssuer(*x509);
+  EXPECT_EQ(
+      issuer.value(),
+      "C = US, ST = CA, O = Asox, CN = Asox Certification Authority");
+  auto subj = folly::ssl::OpenSSLCertUtils::getSubject(*x509);
+  EXPECT_EQ(subj.value(), "C = US, O = Asox, CN = 127.0.0.1");
+}
+
+TEST(OpenSSLCertUtilsTest, TestX509Dates) {
+  OpenSSL_add_all_algorithms();
+
+  auto x509 = readCertFromData(kTestCertWithSan);
+  EXPECT_NE(x509, nullptr);
+  auto notBefore = folly::ssl::OpenSSLCertUtils::getNotBeforeTime(*x509);
+  EXPECT_EQ(notBefore, "Feb 13 23:21:03 2017 GMT");
+  auto notAfter = folly::ssl::OpenSSLCertUtils::getNotAfterTime(*x509);
+  EXPECT_EQ(notAfter, "Jul  1 23:21:03 2044 GMT");
+}
+
+TEST(OpenSSLCertUtilsTest, TestX509Summary) {
+  OpenSSL_add_all_algorithms();
+
+  auto x509 = readCertFromData(kTestCertWithSan);
+  EXPECT_NE(x509, nullptr);
+  auto summary = folly::ssl::OpenSSLCertUtils::toString(*x509);
+  EXPECT_EQ(
+      summary.value(),
+      "        Version: 3 (0x2)\n        Serial Number: 2 (0x2)\n"
+      "        Issuer: C = US, ST = CA, O = Asox, CN = Asox Certification Authority\n"
+      "        Validity\n            Not Before: Feb 13 23:21:03 2017 GMT\n"
+      "            Not After : Jul  1 23:21:03 2044 GMT\n"
+      "        Subject: C = US, O = Asox, CN = 127.0.0.1\n"
+      "        X509v3 extensions:\n"
+      "            X509v3 Basic Constraints: \n"
+      "                CA:FALSE\n"
+      "            Netscape Comment: \n"
+      "                OpenSSL Generated Certificate\n"
+      "            X509v3 Subject Key Identifier: \n"
+      "                71:D6:49:9D:64:47:D7:1E:65:8B:1E:94:83:23:42:E1:F2:19:9F:C3\n"
+      "            X509v3 Authority Key Identifier: \n"
+      "                keyid:17:DF:29:09:29:BF:7B:9F:1A:7F:E9:46:49:C8:3B:ED:B3:B9:E8:7B\n\n"
+      "            X509v3 Subject Alternative Name: \n"
+      "                DNS:anotherexample.com, DNS:*.thirdexample.com\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");
+}