proxygen
OpenSSLCertUtils.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
17 
18 #include <folly/FileUtil.h>
19 #include <folly/ScopeGuard.h>
20 #include <folly/String.h>
22 
23 namespace folly {
24 namespace ssl {
25 
26 namespace {
27 std::string getOpenSSLErrorString(unsigned long err) {
28  std::array<char, 256> errBuff;
29  ERR_error_string_n(err, errBuff.data(), errBuff.size());
30  return std::string(errBuff.data());
31 }
32 } // namespace
33 
35  auto subject = X509_get_subject_name(&x509);
36  if (!subject) {
37  return none;
38  }
39 
40  auto cnLoc = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
41  if (cnLoc < 0) {
42  return none;
43  }
44 
45  auto cnEntry = X509_NAME_get_entry(subject, cnLoc);
46  if (!cnEntry) {
47  return none;
48  }
49 
50  auto cnAsn = X509_NAME_ENTRY_get_data(cnEntry);
51  if (!cnAsn) {
52  return none;
53  }
54 
55  auto cnData = reinterpret_cast<const char*>(ASN1_STRING_get0_data(cnAsn));
56  auto cnLen = ASN1_STRING_length(cnAsn);
57  if (!cnData || cnLen <= 0) {
58  return none;
59  }
60 
61  return Optional<std::string>(std::string(cnData, cnLen));
62 }
63 
64 std::vector<std::string> OpenSSLCertUtils::getSubjectAltNames(X509& x509) {
65  auto names = reinterpret_cast<STACK_OF(GENERAL_NAME)*>(
66  X509_get_ext_d2i(&x509, NID_subject_alt_name, nullptr, nullptr));
67  if (!names) {
68  return {};
69  }
70  SCOPE_EXIT {
71  sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
72  };
73 
74  std::vector<std::string> ret;
75  auto count = sk_GENERAL_NAME_num(names);
76  for (int i = 0; i < count; i++) {
77  auto genName = sk_GENERAL_NAME_value(names, i);
78  if (!genName || genName->type != GEN_DNS) {
79  continue;
80  }
81  auto nameData = reinterpret_cast<const char*>(
82  ASN1_STRING_get0_data(genName->d.dNSName));
83  auto nameLen = ASN1_STRING_length(genName->d.dNSName);
84  if (!nameData || nameLen <= 0) {
85  continue;
86  }
87  ret.emplace_back(nameData, nameLen);
88  }
89  return ret;
90 }
91 
93  auto subject = X509_get_subject_name(&x509);
94  if (!subject) {
95  return none;
96  }
97 
98  auto bio = BioUniquePtr(BIO_new(BIO_s_mem()));
99  if (bio == nullptr) {
100  throw std::runtime_error("Cannot allocate bio");
101  }
102  if (X509_NAME_print_ex(bio.get(), subject, 0, XN_FLAG_ONELINE) <= 0) {
103  return none;
104  }
105 
106  char* bioData = nullptr;
107  size_t bioLen = BIO_get_mem_data(bio.get(), &bioData);
108  return std::string(bioData, bioLen);
109 }
110 
112  auto issuer = X509_get_issuer_name(&x509);
113  if (!issuer) {
114  return none;
115  }
116 
117  auto bio = BioUniquePtr(BIO_new(BIO_s_mem()));
118  if (bio == nullptr) {
119  throw std::runtime_error("Cannot allocate bio");
120  }
121 
122  if (X509_NAME_print_ex(bio.get(), issuer, 0, XN_FLAG_ONELINE) <= 0) {
123  return none;
124  }
125 
126  char* bioData = nullptr;
127  size_t bioLen = BIO_get_mem_data(bio.get(), &bioData);
128  return std::string(bioData, bioLen);
129 }
130 
132  auto in = BioUniquePtr(BIO_new(BIO_s_mem()));
133  if (in == nullptr) {
134  throw std::runtime_error("Cannot allocate bio");
135  }
136 
137  int flags = 0;
138 
139  flags |= X509_FLAG_NO_HEADER | /* A few bytes of cert and data */
140  X509_FLAG_NO_PUBKEY | /* Public key */
141  X509_FLAG_NO_AUX | /* Auxiliary info? */
142  X509_FLAG_NO_SIGDUMP | /* Prints the signature */
143  X509_FLAG_NO_SIGNAME; /* Signature algorithms */
144 
145 #ifdef X509_FLAG_NO_IDS
146  flags |= X509_FLAG_NO_IDS; /* Issuer/subject IDs */
147 #endif
148 
149  if (X509_print_ex(in.get(), &x509, XN_FLAG_ONELINE, flags) > 0) {
150  char* bioData = nullptr;
151  size_t bioLen = BIO_get_mem_data(in.get(), &bioData);
152  return std::string(bioData, bioLen);
153  } else {
154  return none;
155  }
156 }
157 
159  return getDateTimeStr(X509_get_notAfter(&x509));
160 }
161 
163  return getDateTimeStr(X509_get_notBefore(&x509));
164 }
165 
167  if (!time) {
168  return "";
169  }
170 
171  auto bio = BioUniquePtr(BIO_new(BIO_s_mem()));
172  if (bio == nullptr) {
173  throw std::runtime_error("Cannot allocate bio");
174  }
175 
176  if (ASN1_TIME_print(bio.get(), time) <= 0) {
177  throw std::runtime_error("Cannot print ASN1_TIME");
178  }
179 
180  char* bioData = nullptr;
181  size_t bioLen = BIO_get_mem_data(bio.get(), &bioData);
182  return std::string(bioData, bioLen);
183 }
184 
186  auto begin = range.data();
187  X509UniquePtr cert(d2i_X509(nullptr, &begin, range.size()));
188  if (!cert) {
189  throw std::runtime_error("could not read cert");
190  }
191  return cert;
192 }
193 
194 std::unique_ptr<IOBuf> OpenSSLCertUtils::derEncode(X509& x509) {
195  auto len = i2d_X509(&x509, nullptr);
196  if (len < 0) {
197  throw std::runtime_error("Error computing length");
198  }
199  auto buf = IOBuf::create(len);
200  auto dataPtr = buf->writableData();
201  len = i2d_X509(&x509, &dataPtr);
202  if (len < 0) {
203  throw std::runtime_error("Error converting cert to DER");
204  }
205  buf->append(len);
206  return buf;
207 }
208 
209 std::vector<X509UniquePtr> OpenSSLCertUtils::readCertsFromBuffer(
210  ByteRange range) {
211  BioUniquePtr b(
212  BIO_new_mem_buf(const_cast<unsigned char*>(range.data()), range.size()));
213  if (!b) {
214  throw std::runtime_error("failed to create BIO");
215  }
216  std::vector<X509UniquePtr> certs;
217  ERR_clear_error();
218  while (true) {
219  X509UniquePtr x509(PEM_read_bio_X509(b.get(), nullptr, nullptr, nullptr));
220  if (x509) {
221  certs.push_back(std::move(x509));
222  continue;
223  }
224  auto err = ERR_get_error();
225  ERR_clear_error();
226  if (BIO_eof(b.get()) && ERR_GET_LIB(err) == ERR_LIB_PEM &&
227  ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
228  // Reach end of buffer.
229  break;
230  }
231  throw std::runtime_error(folly::to<std::string>(
232  "Unable to parse cert ",
233  certs.size(),
234  ": ",
235  getOpenSSLErrorString(err)));
236  }
237  return certs;
238 }
239 
240 std::array<uint8_t, SHA_DIGEST_LENGTH> OpenSSLCertUtils::getDigestSha1(
241  X509& x509) {
242  unsigned int len;
243  std::array<uint8_t, SHA_DIGEST_LENGTH> md;
244  int rc = X509_digest(&x509, EVP_sha1(), md.data(), &len);
245 
246  if (rc <= 0) {
247  throw std::runtime_error("Could not calculate SHA1 digest for cert");
248  }
249  return md;
250 }
251 
252 std::array<uint8_t, SHA256_DIGEST_LENGTH> OpenSSLCertUtils::getDigestSha256(
253  X509& x509) {
254  unsigned int len;
255  std::array<uint8_t, SHA256_DIGEST_LENGTH> md;
256  int rc = X509_digest(&x509, EVP_sha256(), md.data(), &len);
257 
258  if (rc <= 0) {
259  throw std::runtime_error("Could not calculate SHA256 digest for cert");
260  }
261  return md;
262 }
263 
265  std::string certData;
266  if (!folly::readFile(caFile.c_str(), certData)) {
267  throw std::runtime_error(
268  folly::to<std::string>("Could not read store file: ", caFile));
269  }
270  return readStoreFromBuffer(folly::StringPiece(certData));
271 }
272 
274  auto certs = readCertsFromBuffer(certRange);
275  ERR_clear_error();
276  folly::ssl::X509StoreUniquePtr store(X509_STORE_new());
277  for (auto& caCert : certs) {
278  if (X509_STORE_add_cert(store.get(), caCert.get()) != 1) {
279  auto err = ERR_get_error();
280  if (ERR_GET_LIB(err) != ERR_LIB_X509 ||
281  ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
282  throw std::runtime_error(folly::to<std::string>(
283  "Could not insert CA certificate into store: ",
284  getOpenSSLErrorString(err)));
285  }
286  }
287  }
288  return store;
289 }
290 } // namespace ssl
291 } // namespace folly
bool readFile(int fd, Container &out, size_t num_bytes=std::numeric_limits< size_t >::max())
Definition: FileUtil.h:125
flags
Definition: http_parser.h:127
static folly::Optional< std::string > toString(X509 &x509)
std::unique_ptr< X509, X509Deleter > X509UniquePtr
static std::unique_ptr< IOBuf > create(std::size_t capacity)
Definition: IOBuf.cpp:229
char b
std::unique_ptr< BIO, BioDeleter > BioUniquePtr
static std::array< uint8_t, SHA_DIGEST_LENGTH > getDigestSha1(X509 &x509)
static std::array< uint8_t, SHA256_DIGEST_LENGTH > getDigestSha256(X509 &x509)
static Optional< std::string > getSubject(X509 &x509)
STACK_OF(X509_OBJECT)*X509_STORE_get0_objects(X509_STORE *store)
Definition: OpenSSL.cpp:305
static X509StoreUniquePtr readStoreFromBuffer(ByteRange range)
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
static X509UniquePtr derDecode(ByteRange)
constexpr size_type size() const
Definition: Range.h:431
auto begin(TestAdlIterable &instance)
Definition: ForeachTest.cpp:56
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
Definition: OpenSSL.cpp:199
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static std::vector< std::string > getSubjectAltNames(X509 &x509)
static X509StoreUniquePtr readStoreFromFile(std::string caFile)
static Optional< std::string > getIssuer(X509 &x509)
static std::string getNotAfterTime(X509 &x509)
constexpr Iter data() const
Definition: Range.h:446
std::unique_ptr< X509_STORE, X509StoreDeleter > X509StoreUniquePtr
constexpr Range< Iter > range(Iter first, Iter last)
Definition: Range.h:1114
static Optional< std::string > getCommonName(X509 &x509)
int * count
static std::string getDateTimeStr(const ASN1_TIME *time)
const char * string
Definition: Conv.cpp:212
Container::value_type * dataPtr(Container &cont)
Definition: RangeTest.cpp:1082
static std::vector< X509UniquePtr > readCertsFromBuffer(ByteRange range)
static std::unique_ptr< IOBuf > derEncode(X509 &)
std::chrono::nanoseconds time()
constexpr None none
Definition: Optional.h:87
static std::string getNotBeforeTime(X509 &x509)