1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/base/x509_certificate.h" 6 7 #include <openssl/asn1.h> 8 #include <openssl/crypto.h> 9 #include <openssl/obj_mac.h> 10 #include <openssl/pem.h> 11 #include <openssl/pkcs7.h> 12 #include <openssl/sha.h> 13 #include <openssl/ssl.h> 14 #include <openssl/x509v3.h> 15 16 #include "base/memory/singleton.h" 17 #include "base/pickle.h" 18 #include "base/sha1.h" 19 #include "base/string_number_conversions.h" 20 #include "crypto/openssl_util.h" 21 #include "net/base/asn1_util.h" 22 #include "net/base/cert_status_flags.h" 23 #include "net/base/cert_verify_result.h" 24 #include "net/base/net_errors.h" 25 #include "net/base/x509_openssl_util.h" 26 27 namespace net { 28 29 namespace nxou = net::x509_openssl_util; 30 31 namespace { 32 33 void CreateOSCertHandlesFromPKCS7Bytes( 34 const char* data, int length, 35 X509Certificate::OSCertHandles* handles) { 36 crypto::EnsureOpenSSLInit(); 37 const unsigned char* der_data = reinterpret_cast<const unsigned char*>(data); 38 crypto::ScopedOpenSSL<PKCS7, PKCS7_free> pkcs7_cert( 39 d2i_PKCS7(NULL, &der_data, length)); 40 if (!pkcs7_cert.get()) 41 return; 42 43 STACK_OF(X509)* certs = NULL; 44 int nid = OBJ_obj2nid(pkcs7_cert.get()->type); 45 if (nid == NID_pkcs7_signed) { 46 certs = pkcs7_cert.get()->d.sign->cert; 47 } else if (nid == NID_pkcs7_signedAndEnveloped) { 48 certs = pkcs7_cert.get()->d.signed_and_enveloped->cert; 49 } 50 51 if (certs) { 52 for (int i = 0; i < sk_X509_num(certs); ++i) { 53 X509* x509_cert = 54 X509Certificate::DupOSCertHandle(sk_X509_value(certs, i)); 55 handles->push_back(x509_cert); 56 } 57 } 58 } 59 60 void ParsePrincipalValues(X509_NAME* name, 61 int nid, 62 std::vector<std::string>* fields) { 63 for (int index = -1; 64 (index = X509_NAME_get_index_by_NID(name, nid, index)) != -1;) { 65 std::string field; 66 if (!nxou::ParsePrincipalValueByIndex(name, index, &field)) 67 break; 68 fields->push_back(field); 69 } 70 } 71 72 void ParsePrincipal(X509Certificate::OSCertHandle cert, 73 X509_NAME* x509_name, 74 CertPrincipal* principal) { 75 if (!x509_name) 76 return; 77 78 ParsePrincipalValues(x509_name, NID_streetAddress, 79 &principal->street_addresses); 80 ParsePrincipalValues(x509_name, NID_organizationName, 81 &principal->organization_names); 82 ParsePrincipalValues(x509_name, NID_organizationalUnitName, 83 &principal->organization_unit_names); 84 ParsePrincipalValues(x509_name, NID_domainComponent, 85 &principal->domain_components); 86 87 nxou::ParsePrincipalValueByNID(x509_name, NID_commonName, 88 &principal->common_name); 89 nxou::ParsePrincipalValueByNID(x509_name, NID_localityName, 90 &principal->locality_name); 91 nxou::ParsePrincipalValueByNID(x509_name, NID_stateOrProvinceName, 92 &principal->state_or_province_name); 93 nxou::ParsePrincipalValueByNID(x509_name, NID_countryName, 94 &principal->country_name); 95 } 96 97 void ParseSubjectAltNames(X509Certificate::OSCertHandle cert, 98 std::vector<std::string>* dns_names) { 99 int index = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); 100 X509_EXTENSION* alt_name_ext = X509_get_ext(cert, index); 101 if (!alt_name_ext) 102 return; 103 104 crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free> alt_names( 105 reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext))); 106 if (!alt_names.get()) 107 return; 108 109 for (int i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) { 110 const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i); 111 if (name->type == GEN_DNS) { 112 unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName); 113 if (!dns_name) 114 continue; 115 int dns_name_len = ASN1_STRING_length(name->d.dNSName); 116 dns_names->push_back( 117 std::string(reinterpret_cast<char*>(dns_name), dns_name_len)); 118 } 119 } 120 } 121 122 // Maps X509_STORE_CTX_get_error() return values to our cert status flags. 123 int MapCertErrorToCertStatus(int err) { 124 switch (err) { 125 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: 126 return CERT_STATUS_COMMON_NAME_INVALID; 127 case X509_V_ERR_CERT_NOT_YET_VALID: 128 case X509_V_ERR_CERT_HAS_EXPIRED: 129 case X509_V_ERR_CRL_NOT_YET_VALID: 130 case X509_V_ERR_CRL_HAS_EXPIRED: 131 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: 132 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: 133 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: 134 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: 135 return CERT_STATUS_DATE_INVALID; 136 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: 137 case X509_V_ERR_UNABLE_TO_GET_CRL: 138 case X509_V_ERR_INVALID_CA: 139 case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: 140 case X509_V_ERR_INVALID_NON_CA: 141 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: 142 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: 143 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: 144 return CERT_STATUS_AUTHORITY_INVALID; 145 #if 0 146 // TODO(bulach): what should we map to these status? 147 return CERT_STATUS_NO_REVOCATION_MECHANISM; 148 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; 149 return CERT_STATUS_NOT_IN_DNS; 150 #endif 151 case X509_V_ERR_CERT_REVOKED: 152 return CERT_STATUS_REVOKED; 153 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: 154 return CERT_STATUS_WEAK_SIGNATURE_ALGORITHM; 155 // All these status are mapped to CERT_STATUS_INVALID. 156 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: 157 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: 158 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: 159 case X509_V_ERR_CERT_SIGNATURE_FAILURE: 160 case X509_V_ERR_CRL_SIGNATURE_FAILURE: 161 case X509_V_ERR_OUT_OF_MEM: 162 case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: 163 case X509_V_ERR_CERT_CHAIN_TOO_LONG: 164 case X509_V_ERR_PATH_LENGTH_EXCEEDED: 165 case X509_V_ERR_INVALID_PURPOSE: 166 case X509_V_ERR_CERT_UNTRUSTED: 167 case X509_V_ERR_CERT_REJECTED: 168 case X509_V_ERR_AKID_SKID_MISMATCH: 169 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: 170 case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: 171 case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: 172 case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: 173 case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: 174 case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: 175 case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: 176 case X509_V_ERR_INVALID_EXTENSION: 177 case X509_V_ERR_INVALID_POLICY_EXTENSION: 178 case X509_V_ERR_NO_EXPLICIT_POLICY: 179 case X509_V_ERR_UNNESTED_RESOURCE: 180 case X509_V_ERR_APPLICATION_VERIFICATION: 181 return CERT_STATUS_INVALID; 182 default: 183 NOTREACHED() << "Invalid X509 err " << err; 184 return CERT_STATUS_INVALID; 185 } 186 } 187 188 // sk_X509_free is a function-style macro, so can't be used as a template 189 // param directly. 190 void sk_X509_free_fn(STACK_OF(X509)* st) { 191 sk_X509_free(st); 192 } 193 194 struct DERCache { 195 unsigned char* data; 196 int data_length; 197 }; 198 199 void DERCache_free(void* parent, void* ptr, CRYPTO_EX_DATA* ad, int idx, 200 long argl, void* argp) { 201 DERCache* der_cache = static_cast<DERCache*>(ptr); 202 if (!der_cache) 203 return; 204 if (der_cache->data) 205 OPENSSL_free(der_cache->data); 206 OPENSSL_free(der_cache); 207 } 208 209 class X509InitSingleton { 210 public: 211 static X509InitSingleton* GetInstance() { 212 // We allow the X509 store to leak, because it is used from a non-joinable 213 // worker that is not stopped on shutdown, hence may still be using 214 // OpenSSL library after the AtExit runner has completed. 215 return Singleton<X509InitSingleton, 216 LeakySingletonTraits<X509InitSingleton> >::get(); 217 } 218 int der_cache_ex_index() const { return der_cache_ex_index_; } 219 X509_STORE* store() const { return store_.get(); } 220 221 void ResetCertStore() { 222 store_.reset(X509_STORE_new()); 223 DCHECK(store_.get()); 224 X509_STORE_set_default_paths(store_.get()); 225 // TODO(joth): Enable CRL (see X509_STORE_set_flags(X509_V_FLAG_CRL_CHECK)). 226 } 227 228 private: 229 friend struct DefaultSingletonTraits<X509InitSingleton>; 230 X509InitSingleton() { 231 crypto::EnsureOpenSSLInit(); 232 der_cache_ex_index_ = X509_get_ex_new_index(0, 0, 0, 0, DERCache_free); 233 DCHECK_NE(der_cache_ex_index_, -1); 234 ResetCertStore(); 235 } 236 237 int der_cache_ex_index_; 238 crypto::ScopedOpenSSL<X509_STORE, X509_STORE_free> store_; 239 240 DISALLOW_COPY_AND_ASSIGN(X509InitSingleton); 241 }; 242 243 // Takes ownership of |data| (which must have been allocated by OpenSSL). 244 DERCache* SetDERCache(X509Certificate::OSCertHandle cert, 245 int x509_der_cache_index, 246 unsigned char* data, 247 int data_length) { 248 DERCache* internal_cache = static_cast<DERCache*>( 249 OPENSSL_malloc(sizeof(*internal_cache))); 250 if (!internal_cache) { 251 // We took ownership of |data|, so we must free if we can't add it to 252 // |cert|. 253 OPENSSL_free(data); 254 return NULL; 255 } 256 257 internal_cache->data = data; 258 internal_cache->data_length = data_length; 259 X509_set_ex_data(cert, x509_der_cache_index, internal_cache); 260 return internal_cache; 261 } 262 263 // Returns true if |der_cache| points to valid data, false otherwise. 264 // (note: the DER-encoded data in |der_cache| is owned by |cert|, callers should 265 // not free it). 266 bool GetDERAndCacheIfNeeded(X509Certificate::OSCertHandle cert, 267 DERCache* der_cache) { 268 int x509_der_cache_index = 269 X509InitSingleton::GetInstance()->der_cache_ex_index(); 270 271 // Re-encoding the DER data via i2d_X509 is an expensive operation, but it's 272 // necessary for comparing two certificates. We re-encode at most once per 273 // certificate and cache the data within the X509 cert using X509_set_ex_data. 274 DERCache* internal_cache = static_cast<DERCache*>( 275 X509_get_ex_data(cert, x509_der_cache_index)); 276 if (!internal_cache) { 277 unsigned char* data = NULL; 278 int data_length = i2d_X509(cert, &data); 279 if (data_length <= 0 || !data) 280 return false; 281 internal_cache = SetDERCache(cert, x509_der_cache_index, data, data_length); 282 if (!internal_cache) 283 return false; 284 } 285 *der_cache = *internal_cache; 286 return true; 287 } 288 289 } // namespace 290 291 // static 292 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( 293 OSCertHandle cert_handle) { 294 DCHECK(cert_handle); 295 // Using X509_dup causes the entire certificate to be reparsed. This 296 // conversion, besides being non-trivial, drops any associated 297 // application-specific data set by X509_set_ex_data. Using CRYPTO_add 298 // just bumps up the ref-count for the cert, without causing any allocations 299 // or deallocations. 300 CRYPTO_add(&cert_handle->references, 1, CRYPTO_LOCK_X509); 301 return cert_handle; 302 } 303 304 // static 305 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { 306 // Decrement the ref-count for the cert and, if all references are gone, 307 // free the memory and any application-specific data associated with the 308 // certificate. 309 X509_free(cert_handle); 310 } 311 312 void X509Certificate::Initialize() { 313 crypto::EnsureOpenSSLInit(); 314 fingerprint_ = CalculateFingerprint(cert_handle_); 315 316 ASN1_INTEGER* num = X509_get_serialNumber(cert_handle_); 317 if (num) { 318 serial_number_ = std::string( 319 reinterpret_cast<char*>(num->data), 320 num->length); 321 // Remove leading zeros. 322 while (serial_number_.size() > 1 && serial_number_[0] == 0) 323 serial_number_ = serial_number_.substr(1, serial_number_.size() - 1); 324 } 325 326 ParsePrincipal(cert_handle_, X509_get_subject_name(cert_handle_), &subject_); 327 ParsePrincipal(cert_handle_, X509_get_issuer_name(cert_handle_), &issuer_); 328 nxou::ParseDate(X509_get_notBefore(cert_handle_), &valid_start_); 329 nxou::ParseDate(X509_get_notAfter(cert_handle_), &valid_expiry_); 330 } 331 332 // static 333 void X509Certificate::ResetCertStore() { 334 X509InitSingleton::GetInstance()->ResetCertStore(); 335 } 336 337 SHA1Fingerprint X509Certificate::CalculateFingerprint(OSCertHandle cert) { 338 SHA1Fingerprint sha1; 339 unsigned int sha1_size = static_cast<unsigned int>(sizeof(sha1.data)); 340 int ret = X509_digest(cert, EVP_sha1(), sha1.data, &sha1_size); 341 CHECK(ret); 342 CHECK_EQ(sha1_size, sizeof(sha1.data)); 343 return sha1; 344 } 345 346 // static 347 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( 348 const char* data, int length) { 349 if (length < 0) 350 return NULL; 351 crypto::EnsureOpenSSLInit(); 352 const unsigned char* d2i_data = 353 reinterpret_cast<const unsigned char*>(data); 354 // Don't cache this data via SetDERCache as this wire format may be not be 355 // identical from the i2d_X509 roundtrip. 356 X509* cert = d2i_X509(NULL, &d2i_data, length); 357 return cert; 358 } 359 360 // static 361 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( 362 const char* data, int length, Format format) { 363 OSCertHandles results; 364 if (length < 0) 365 return results; 366 367 switch (format) { 368 case FORMAT_SINGLE_CERTIFICATE: { 369 OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); 370 if (handle) 371 results.push_back(handle); 372 break; 373 } 374 case FORMAT_PKCS7: { 375 CreateOSCertHandlesFromPKCS7Bytes(data, length, &results); 376 break; 377 } 378 default: { 379 NOTREACHED() << "Certificate format " << format << " unimplemented"; 380 break; 381 } 382 } 383 384 return results; 385 } 386 387 // static 388 X509Certificate* X509Certificate::CreateSelfSigned( 389 crypto::RSAPrivateKey* key, 390 const std::string& subject, 391 uint32 serial_number, 392 base::TimeDelta valid_duration) { 393 // TODO(port): Implement. 394 return NULL; 395 } 396 397 void X509Certificate::GetDNSNames(std::vector<std::string>* dns_names) const { 398 dns_names->clear(); 399 400 ParseSubjectAltNames(cert_handle_, dns_names); 401 402 if (dns_names->empty()) 403 dns_names->push_back(subject_.common_name); 404 } 405 406 // static 407 X509_STORE* X509Certificate::cert_store() { 408 return X509InitSingleton::GetInstance()->store(); 409 } 410 411 #ifndef ANDROID 412 int X509Certificate::Verify(const std::string& hostname, 413 int flags, 414 CertVerifyResult* verify_result) const { 415 verify_result->Reset(); 416 417 if (IsBlacklisted()) { 418 verify_result->cert_status |= CERT_STATUS_REVOKED; 419 return ERR_CERT_REVOKED; 420 } 421 422 // TODO(joth): We should fetch the subjectAltNames directly rather than via 423 // GetDNSNames, so we can apply special handling for IP addresses vs DNS 424 // names, etc. See http://crbug.com/62973. 425 std::vector<std::string> cert_names; 426 GetDNSNames(&cert_names); 427 if (!VerifyHostname(hostname, cert_names)) 428 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; 429 430 crypto::ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx( 431 X509_STORE_CTX_new()); 432 433 crypto::ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn> intermediates( 434 sk_X509_new_null()); 435 if (!intermediates.get()) 436 return ERR_OUT_OF_MEMORY; 437 438 for (OSCertHandles::const_iterator it = intermediate_ca_certs_.begin(); 439 it != intermediate_ca_certs_.end(); ++it) { 440 if (!sk_X509_push(intermediates.get(), *it)) 441 return ERR_OUT_OF_MEMORY; 442 } 443 int rv = X509_STORE_CTX_init(ctx.get(), cert_store(), 444 cert_handle_, intermediates.get()); 445 CHECK_EQ(1, rv); 446 447 if (X509_verify_cert(ctx.get()) != 1) { 448 int x509_error = X509_STORE_CTX_get_error(ctx.get()); 449 int cert_status = MapCertErrorToCertStatus(x509_error); 450 LOG(ERROR) << "X509 Verification error " 451 << X509_verify_cert_error_string(x509_error) 452 << " : " << x509_error 453 << " : " << X509_STORE_CTX_get_error_depth(ctx.get()) 454 << " : " << cert_status; 455 verify_result->cert_status |= cert_status; 456 } 457 458 if (IsCertStatusError(verify_result->cert_status)) 459 return MapCertStatusToNetError(verify_result->cert_status); 460 461 STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(ctx.get()); 462 for (int i = 0; i < sk_X509_num(chain); ++i) { 463 X509* cert = sk_X509_value(chain, i); 464 DERCache der_cache; 465 if (!GetDERAndCacheIfNeeded(cert, &der_cache)) 466 continue; 467 468 base::StringPiece der_bytes(reinterpret_cast<const char*>(der_cache.data), 469 der_cache.data_length); 470 base::StringPiece spki_bytes; 471 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) 472 continue; 473 474 SHA1Fingerprint hash; 475 base::SHA1HashBytes(reinterpret_cast<const uint8*>(spki_bytes.data()), 476 spki_bytes.size(), hash.data); 477 verify_result->public_key_hashes.push_back(hash); 478 } 479 480 if (IsPublicKeyBlacklisted(verify_result->public_key_hashes)) { 481 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID; 482 return MapCertStatusToNetError(verify_result->cert_status); 483 } 484 485 // Currently we only ues OpenSSL's default root CA paths, so treat all 486 // correctly verified certs as being from a known root. TODO(joth): if the 487 // motivations described in http://src.chromium.org/viewvc/chrome?view=rev&revision=80778 488 // become an issue on OpenSSL builds, we will need to embed a hardcoded list 489 // of well known root CAs, as per the _mac and _win versions. 490 verify_result->is_issued_by_known_root = true; 491 492 return OK; 493 } 494 495 bool X509Certificate::GetDEREncoded(std::string* encoded) { 496 DERCache der_cache; 497 if (!GetDERAndCacheIfNeeded(cert_handle_, &der_cache)) 498 return false; 499 encoded->assign(reinterpret_cast<const char*>(der_cache.data), 500 der_cache.data_length); 501 return true; 502 } 503 #endif 504 505 // static 506 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, 507 X509Certificate::OSCertHandle b) { 508 DCHECK(a && b); 509 if (a == b) 510 return true; 511 512 // X509_cmp only checks the fingerprint, but we want to compare the whole 513 // DER data. Encoding it from OSCertHandle is an expensive operation, so we 514 // cache the DER (if not already cached via X509_set_ex_data). 515 DERCache der_cache_a, der_cache_b; 516 517 return GetDERAndCacheIfNeeded(a, &der_cache_a) && 518 GetDERAndCacheIfNeeded(b, &der_cache_b) && 519 der_cache_a.data_length == der_cache_b.data_length && 520 memcmp(der_cache_a.data, der_cache_b.data, der_cache_a.data_length) == 0; 521 } 522 523 // static 524 X509Certificate::OSCertHandle 525 X509Certificate::ReadCertHandleFromPickle(const Pickle& pickle, 526 void** pickle_iter) { 527 const char* data; 528 int length; 529 if (!pickle.ReadData(pickle_iter, &data, &length)) 530 return NULL; 531 532 return CreateOSCertHandleFromBytes(data, length); 533 } 534 535 // static 536 bool X509Certificate::WriteCertHandleToPickle(OSCertHandle cert_handle, 537 Pickle* pickle) { 538 DERCache der_cache; 539 if (!GetDERAndCacheIfNeeded(cert_handle, &der_cache)) 540 return false; 541 542 return pickle->WriteData( 543 reinterpret_cast<const char*>(der_cache.data), 544 der_cache.data_length); 545 } 546 547 #if defined(ANDROID) 548 // static 549 std::string X509Certificate::GetDEREncodedBytes(OSCertHandle handle) { 550 DERCache der_cache = {0}; 551 GetDERAndCacheIfNeeded(handle, &der_cache); 552 return std::string(reinterpret_cast<const char*>(der_cache.data), 553 der_cache.data_length); 554 } 555 #endif 556 557 #if defined(ANDROID) 558 void X509Certificate::GetChainDEREncodedBytes( 559 std::vector<std::string>* chain_bytes) const { 560 OSCertHandles cert_handles(intermediate_ca_certs_); 561 // Make sure the peer's own cert is the first in the chain, if it's not 562 // already there. 563 if (cert_handles.empty() || cert_handles[0] != cert_handle_) 564 cert_handles.insert(cert_handles.begin(), cert_handle_); 565 566 chain_bytes->reserve(cert_handles.size()); 567 for (OSCertHandles::const_iterator it = cert_handles.begin(); 568 it != cert_handles.end(); ++it) { 569 DERCache der_cache = {0}; 570 GetDERAndCacheIfNeeded(*it, &der_cache); 571 std::string cert_bytes = std::string( 572 reinterpret_cast<const char*>(der_cache.data), der_cache.data_length); 573 chain_bytes->push_back(cert_bytes); 574 } 575 } 576 #endif 577 578 } // namespace net 579