Commit 2081b775 authored by Anirudh Ramachandran's avatar Anirudh Ramachandran Committed by Facebook Github Bot

Add a few more methods to OpenSSLCertUtils

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
parent 4b62278a
......@@ -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';
......
......@@ -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
......@@ -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
......@@ -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);
};
}
}
......@@ -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");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment