Commit 68493524 authored by Michael Lee's avatar Michael Lee Committed by Facebook Github Bot

Add getCommonName and getSubjectAltName Cert utility helpers

Summary: `getCommonName` and `getSubjectAltName` are useful shared utility functions.

Reviewed By: anirudhvr

Differential Revision: D4546866

fbshipit-source-id: bcefc1ff3a02df93fbf0cd2d4f24569928bd1753
parent ae910818
...@@ -341,6 +341,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -341,6 +341,7 @@ nobase_follyinclude_HEADERS = \
SpinLock.h \ SpinLock.h \
SpookyHashV1.h \ SpookyHashV1.h \
SpookyHashV2.h \ SpookyHashV2.h \
ssl/OpenSSLCertUtils.h \
ssl/OpenSSLHash.h \ ssl/OpenSSLHash.h \
ssl/OpenSSLVersionFinder.h \ ssl/OpenSSLVersionFinder.h \
ssl/SSLSession.h \ ssl/SSLSession.h \
...@@ -506,6 +507,7 @@ libfolly_la_SOURCES = \ ...@@ -506,6 +507,7 @@ libfolly_la_SOURCES = \
SocketAddress.cpp \ SocketAddress.cpp \
SpookyHashV1.cpp \ SpookyHashV1.cpp \
SpookyHashV2.cpp \ SpookyHashV2.cpp \
ssl/OpenSSLCertUtils.cpp \
ssl/OpenSSLHash.cpp \ ssl/OpenSSLHash.cpp \
ssl/detail/SSLSessionImpl.cpp \ ssl/detail/SSLSessionImpl.cpp \
stats/Instantiations.cpp \ stats/Instantiations.cpp \
......
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/ssl/OpenSSLCertUtils.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <folly/ScopeGuard.h>
namespace folly {
namespace ssl {
Optional<std::string> OpenSSLCertUtils::getCommonName(X509& x509) {
auto subject = X509_get_subject_name(&x509);
if (!subject) {
return none;
}
auto cnLoc = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
if (cnLoc < 0) {
return none;
}
auto cnEntry = X509_NAME_get_entry(subject, cnLoc);
if (!cnEntry) {
return none;
}
auto cnAsn = X509_NAME_ENTRY_get_data(cnEntry);
if (!cnAsn) {
return none;
}
auto cnData = reinterpret_cast<const char*>(ASN1_STRING_data(cnAsn));
auto cnLen = ASN1_STRING_length(cnAsn);
if (!cnData || cnLen <= 0) {
return none;
}
return Optional<std::string>(std::string(cnData, cnLen));
}
std::vector<std::string> OpenSSLCertUtils::getSubjectAltNames(X509& x509) {
auto names = reinterpret_cast<STACK_OF(GENERAL_NAME)*>(
X509_get_ext_d2i(&x509, NID_subject_alt_name, nullptr, nullptr));
if (!names) {
return {};
}
SCOPE_EXIT {
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
};
std::vector<std::string> ret;
auto count = sk_GENERAL_NAME_num(names);
for (int i = 0; i < count; i++) {
auto genName = sk_GENERAL_NAME_value(names, i);
if (!genName || genName->type != GEN_DNS) {
continue;
}
auto nameData =
reinterpret_cast<const char*>(ASN1_STRING_data(genName->d.dNSName));
auto nameLen = ASN1_STRING_length(genName->d.dNSName);
if (!nameData || nameLen <= 0) {
continue;
}
ret.emplace_back(nameData, nameLen);
}
return ret;
}
}
}
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <string>
#include <vector>
#include <openssl/x509.h>
#include <folly/Optional.h>
namespace folly {
namespace ssl {
class OpenSSLCertUtils {
public:
// Note: non-const until OpenSSL 1.1.0
static Optional<std::string> getCommonName(X509& x509);
static std::vector<std::string> getSubjectAltNames(X509& x509);
};
}
}
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/ssl/OpenSSLCertUtils.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <folly/Range.h>
#include <folly/String.h>
#include <folly/io/async/ssl/OpenSSLPtrTypes.h>
#include <folly/portability/GTest.h>
#include <folly/portability/OpenSSL.h>
using namespace testing;
using namespace folly;
const char* kTestCertWithoutSan = "folly/io/async/test/certs/tests-cert.pem";
// Test key
// -----BEGIN EC PRIVATE KEY-----
// MHcCAQEEIBskFwVZ9miFN+SKCFZPe9WEuFGmP+fsecLUnsTN6bOcoAoGCCqGSM49
// AwEHoUQDQgAE7/f4YYOYunAM/VkmjDYDg3AWUgyyTIraWmmQZsnu0bYNV/lLLfNz
// CtHggxGSwEtEe40nNb9C8wQmHUvb7VBBlw==
// -----END EC PRIVATE KEY-----
const std::string kTestCertWithSan = folly::stripLeftMargin(R"(
-----BEGIN CERTIFICATE-----
MIIDXDCCAkSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCQ0ExDTALBgNVBAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwHhcNMTcwMjEzMjMyMTAzWhcNNDQwNzAxMjMyMTAz
WjAwMQswCQYDVQQGEwJVUzENMAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAu
MC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7/f4YYOYunAM/VkmjDYDg3AW
UgyyTIraWmmQZsnu0bYNV/lLLfNzCtHggxGSwEtEe40nNb9C8wQmHUvb7VBBl6OC
ASowggEmMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJh
dGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRx1kmdZEfXHmWLHpSDI0Lh8hmfwzAf
BgNVHSMEGDAWgBQX3ykJKb97nxp/6UZJyDvts7noezAxBgNVHREEKjAoghJhbm90
aGVyZXhhbXBsZS5jb22CEioudGhpcmRleGFtcGxlLmNvbTB4BggrBgEFBQcBAQRs
MGowaAYIKwYBBQUHMAKGXGh0dHBzOi8vcGhhYnJpY2F0b3IuZmIuY29tL2RpZmZ1
c2lvbi9GQkNPREUvYnJvd3NlL21hc3Rlci90aS90ZXN0X2NlcnRzL2NhX2NlcnQu
cGVtP3ZpZXc9cmF3MA0GCSqGSIb3DQEBCwUAA4IBAQCj3FLjLMLudaFDiYo9pAPQ
NBYNpG27aajQCvnEsYaMAGnNBxUUhv/E4xpnJEhatiCJWlPgGebdjXkpXYkLxnFj
38UmpfZbNcvPPKxXmjIlkpYeFwcHTAUpFmMXVHdr8FjkDSN+qWHLllMFNAAqp0U6
4VWjDlq9xCjzNw+8fdcEpwylpPrbNyQHqSO1k+DhM2qPuQfiWPmHe2PbJv8JB3no
HWGi9SNe0FjtJM3066L0Gj8g/bFDo/pnyKguQyGkS7PaepK5/u5Y2fMMBO/m4+U0
b9Yb0TvatsqL688CoZcSn73A0yAjptwbD/4HmcVlG2j/y8eTVpXisugu6Xz+QQGu
-----END CERTIFICATE-----
)");
static folly::ssl::X509UniquePtr readCertFromFile(const std::string& filename) {
folly::ssl::BioUniquePtr bio(BIO_new(BIO_s_file()));
if (!bio) {
throw std::runtime_error("Couldn't create BIO");
}
if (BIO_read_filename(bio.get(), filename.c_str()) != 1) {
throw std::runtime_error("Couldn't read cert file: " + filename);
}
return folly::ssl::X509UniquePtr(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
}
static folly::ssl::X509UniquePtr readCertFromData(
const folly::StringPiece data) {
folly::ssl::BioUniquePtr bio(BIO_new_mem_buf(data.data(), data.size()));
if (!bio) {
throw std::runtime_error("Couldn't create BIO");
}
return folly::ssl::X509UniquePtr(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
}
TEST(OpenSSLCertUtilsTest, TestX509CN) {
OpenSSL_add_all_algorithms();
auto x509 = readCertFromFile(kTestCertWithoutSan);
EXPECT_NE(x509, nullptr);
auto identity = folly::ssl::OpenSSLCertUtils::getCommonName(*x509);
EXPECT_EQ(identity.value(), "Asox Company");
auto sans = folly::ssl::OpenSSLCertUtils::getSubjectAltNames(*x509);
EXPECT_EQ(sans.size(), 0);
}
TEST(OpenSSLCertUtilsTest, TestX509Sans) {
OpenSSL_add_all_algorithms();
auto x509 = readCertFromData(kTestCertWithSan);
EXPECT_NE(x509, nullptr);
auto identity = folly::ssl::OpenSSLCertUtils::getCommonName(*x509);
EXPECT_EQ(identity.value(), "127.0.0.1");
auto altNames = folly::ssl::OpenSSLCertUtils::getSubjectAltNames(*x509);
EXPECT_EQ(altNames.size(), 2);
EXPECT_EQ(altNames[0], "anotherexample.com");
EXPECT_EQ(altNames[1], "*.thirdexample.com");
}
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