Home | History | Annotate | Download | only in cert
      1 // Copyright (c) 2012 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/cert/x509_util.h"
      6 #include "net/cert/x509_util_nss.h"
      7 
      8 #include <cert.h>  // Must be included before certdb.h
      9 #include <certdb.h>
     10 #include <cryptohi.h>
     11 #include <nss.h>
     12 #include <pk11pub.h>
     13 #include <prerror.h>
     14 #include <secder.h>
     15 #include <secmod.h>
     16 #include <secport.h>
     17 
     18 #include "base/debug/leak_annotations.h"
     19 #include "base/logging.h"
     20 #include "base/memory/scoped_ptr.h"
     21 #include "base/memory/singleton.h"
     22 #include "base/pickle.h"
     23 #include "base/strings/stringprintf.h"
     24 #include "crypto/ec_private_key.h"
     25 #include "crypto/nss_util.h"
     26 #include "crypto/nss_util_internal.h"
     27 #include "crypto/rsa_private_key.h"
     28 #include "crypto/scoped_nss_types.h"
     29 #include "crypto/third_party/nss/chromium-nss.h"
     30 #include "net/cert/x509_certificate.h"
     31 
     32 namespace net {
     33 
     34 namespace {
     35 
     36 class DomainBoundCertOIDWrapper {
     37  public:
     38   static DomainBoundCertOIDWrapper* GetInstance() {
     39     // Instantiated as a leaky singleton to allow the singleton to be
     40     // constructed on a worker thead that is not joined when a process
     41     // shuts down.
     42     return Singleton<DomainBoundCertOIDWrapper,
     43                      LeakySingletonTraits<DomainBoundCertOIDWrapper> >::get();
     44   }
     45 
     46   SECOidTag domain_bound_cert_oid_tag() const {
     47     return domain_bound_cert_oid_tag_;
     48   }
     49 
     50  private:
     51   friend struct DefaultSingletonTraits<DomainBoundCertOIDWrapper>;
     52 
     53   DomainBoundCertOIDWrapper();
     54 
     55   SECOidTag domain_bound_cert_oid_tag_;
     56 
     57   DISALLOW_COPY_AND_ASSIGN(DomainBoundCertOIDWrapper);
     58 };
     59 
     60 DomainBoundCertOIDWrapper::DomainBoundCertOIDWrapper()
     61     : domain_bound_cert_oid_tag_(SEC_OID_UNKNOWN) {
     62   // 1.3.6.1.4.1.11129.2.1.6
     63   // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
     64   //  certificateExtensions.originBoundCertificate)
     65   static const uint8 kObCertOID[] = {
     66     0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x06
     67   };
     68   SECOidData oid_data;
     69   memset(&oid_data, 0, sizeof(oid_data));
     70   oid_data.oid.data = const_cast<uint8*>(kObCertOID);
     71   oid_data.oid.len = sizeof(kObCertOID);
     72   oid_data.offset = SEC_OID_UNKNOWN;
     73   oid_data.desc = "Origin Bound Certificate";
     74   oid_data.mechanism = CKM_INVALID_MECHANISM;
     75   oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
     76   domain_bound_cert_oid_tag_ = SECOID_AddEntry(&oid_data);
     77   if (domain_bound_cert_oid_tag_ == SEC_OID_UNKNOWN)
     78     LOG(ERROR) << "OB_CERT OID tag creation failed";
     79 }
     80 
     81 // Creates a Certificate object that may be passed to the SignCertificate
     82 // method to generate an X509 certificate.
     83 // Returns NULL if an error is encountered in the certificate creation
     84 // process.
     85 // Caller responsible for freeing returned certificate object.
     86 CERTCertificate* CreateCertificate(
     87     SECKEYPublicKey* public_key,
     88     const std::string& subject,
     89     uint32 serial_number,
     90     base::Time not_valid_before,
     91     base::Time not_valid_after) {
     92   // Create info about public key.
     93   CERTSubjectPublicKeyInfo* spki =
     94       SECKEY_CreateSubjectPublicKeyInfo(public_key);
     95   if (!spki)
     96     return NULL;
     97 
     98   // Create the certificate request.
     99   CERTName* subject_name =
    100       CERT_AsciiToName(const_cast<char*>(subject.c_str()));
    101   CERTCertificateRequest* cert_request =
    102       CERT_CreateCertificateRequest(subject_name, spki, NULL);
    103   SECKEY_DestroySubjectPublicKeyInfo(spki);
    104 
    105   if (!cert_request) {
    106     PRErrorCode prerr = PR_GetError();
    107     LOG(ERROR) << "Failed to create certificate request: " << prerr;
    108     CERT_DestroyName(subject_name);
    109     return NULL;
    110   }
    111 
    112   CERTValidity* validity = CERT_CreateValidity(
    113       crypto::BaseTimeToPRTime(not_valid_before),
    114       crypto::BaseTimeToPRTime(not_valid_after));
    115   if (!validity) {
    116     PRErrorCode prerr = PR_GetError();
    117     LOG(ERROR) << "Failed to create certificate validity object: " << prerr;
    118     CERT_DestroyName(subject_name);
    119     CERT_DestroyCertificateRequest(cert_request);
    120     return NULL;
    121   }
    122   CERTCertificate* cert = CERT_CreateCertificate(serial_number, subject_name,
    123                                                  validity, cert_request);
    124   if (!cert) {
    125     PRErrorCode prerr = PR_GetError();
    126     LOG(ERROR) << "Failed to create certificate: " << prerr;
    127   }
    128 
    129   // Cleanup for resources used to generate the cert.
    130   CERT_DestroyName(subject_name);
    131   CERT_DestroyValidity(validity);
    132   CERT_DestroyCertificateRequest(cert_request);
    133 
    134   return cert;
    135 }
    136 
    137 // Signs a certificate object, with |key| generating a new X509Certificate
    138 // and destroying the passed certificate object (even when NULL is returned).
    139 // The logic of this method references SignCert() in NSS utility certutil:
    140 // http://mxr.mozilla.org/security/ident?i=SignCert.
    141 // Returns true on success or false if an error is encountered in the
    142 // certificate signing process.
    143 bool SignCertificate(
    144     CERTCertificate* cert,
    145     SECKEYPrivateKey* key) {
    146   // |arena| is used to encode the cert.
    147   PLArenaPool* arena = cert->arena;
    148   SECOidTag algo_id = SEC_GetSignatureAlgorithmOidTag(key->keyType,
    149                                                       SEC_OID_SHA1);
    150   if (algo_id == SEC_OID_UNKNOWN)
    151     return false;
    152 
    153   SECStatus rv = SECOID_SetAlgorithmID(arena, &cert->signature, algo_id, 0);
    154   if (rv != SECSuccess)
    155     return false;
    156 
    157   // Generate a cert of version 3.
    158   *(cert->version.data) = 2;
    159   cert->version.len = 1;
    160 
    161   SECItem der = { siBuffer, NULL, 0 };
    162 
    163   // Use ASN1 DER to encode the cert.
    164   void* encode_result = SEC_ASN1EncodeItem(
    165       NULL, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
    166   if (!encode_result)
    167     return false;
    168 
    169   // Allocate space to contain the signed cert.
    170   SECItem result = { siBuffer, NULL, 0 };
    171 
    172   // Sign the ASN1 encoded cert and save it to |result|.
    173   rv = DerSignData(arena, &result, &der, key, algo_id);
    174   PORT_Free(der.data);
    175   if (rv != SECSuccess) {
    176     DLOG(ERROR) << "DerSignData: " << PORT_GetError();
    177     return false;
    178   }
    179 
    180   // Save the signed result to the cert.
    181   cert->derCert = result;
    182 
    183   return true;
    184 }
    185 
    186 #if defined(USE_NSS) || defined(OS_IOS)
    187 // Callback for CERT_DecodeCertPackage(), used in
    188 // CreateOSCertHandlesFromBytes().
    189 SECStatus PR_CALLBACK CollectCertsCallback(void* arg,
    190                                            SECItem** certs,
    191                                            int num_certs) {
    192   X509Certificate::OSCertHandles* results =
    193       reinterpret_cast<X509Certificate::OSCertHandles*>(arg);
    194 
    195   for (int i = 0; i < num_certs; ++i) {
    196     X509Certificate::OSCertHandle handle =
    197         X509Certificate::CreateOSCertHandleFromBytes(
    198             reinterpret_cast<char*>(certs[i]->data), certs[i]->len);
    199     if (handle)
    200       results->push_back(handle);
    201   }
    202 
    203   return SECSuccess;
    204 }
    205 
    206 typedef scoped_ptr_malloc<
    207     CERTName,
    208     crypto::NSSDestroyer<CERTName, CERT_DestroyName> > ScopedCERTName;
    209 
    210 // Create a new CERTName object from its encoded representation.
    211 // |arena| is the allocation pool to use.
    212 // |data| points to a DER-encoded X.509 DistinguishedName.
    213 // Return a new CERTName pointer on success, or NULL.
    214 CERTName* CreateCertNameFromEncoded(PLArenaPool* arena,
    215                                     const base::StringPiece& data) {
    216   if (!arena)
    217     return NULL;
    218 
    219   ScopedCERTName name(PORT_ArenaZNew(arena, CERTName));
    220   if (!name.get())
    221     return NULL;
    222 
    223   SECItem item;
    224   item.len = static_cast<unsigned int>(data.length());
    225   item.data = reinterpret_cast<unsigned char*>(
    226       const_cast<char*>(data.data()));
    227 
    228   SECStatus rv = SEC_ASN1DecodeItem(
    229       arena, name.get(), SEC_ASN1_GET(CERT_NameTemplate), &item);
    230   if (rv != SECSuccess)
    231     return NULL;
    232 
    233   return name.release();
    234 }
    235 
    236 #endif  // defined(USE_NSS) || defined(OS_IOS)
    237 
    238 }  // namespace
    239 
    240 namespace x509_util {
    241 
    242 bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
    243                           const std::string& subject,
    244                           uint32 serial_number,
    245                           base::Time not_valid_before,
    246                           base::Time not_valid_after,
    247                           std::string* der_cert) {
    248   DCHECK(key);
    249   CERTCertificate* cert = CreateCertificate(key->public_key(),
    250                                             subject,
    251                                             serial_number,
    252                                             not_valid_before,
    253                                             not_valid_after);
    254   if (!cert)
    255     return false;
    256 
    257   if (!SignCertificate(cert, key->key())) {
    258     CERT_DestroyCertificate(cert);
    259     return false;
    260   }
    261 
    262   der_cert->assign(reinterpret_cast<char*>(cert->derCert.data),
    263                    cert->derCert.len);
    264   CERT_DestroyCertificate(cert);
    265   return true;
    266 }
    267 
    268 bool IsSupportedValidityRange(base::Time not_valid_before,
    269                               base::Time not_valid_after) {
    270   CERTValidity* validity = CERT_CreateValidity(
    271       crypto::BaseTimeToPRTime(not_valid_before),
    272       crypto::BaseTimeToPRTime(not_valid_after));
    273 
    274   if (!validity)
    275     return false;
    276 
    277   CERT_DestroyValidity(validity);
    278   return true;
    279 }
    280 
    281 bool CreateDomainBoundCertEC(crypto::ECPrivateKey* key,
    282                              const std::string& domain,
    283                              uint32 serial_number,
    284                              base::Time not_valid_before,
    285                              base::Time not_valid_after,
    286                              std::string* der_cert) {
    287   DCHECK(key);
    288 
    289   CERTCertificate* cert = CreateCertificate(key->public_key(),
    290                                             "CN=anonymous.invalid",
    291                                             serial_number,
    292                                             not_valid_before,
    293                                             not_valid_after);
    294 
    295   if (!cert)
    296     return false;
    297 
    298   // Create opaque handle used to add extensions later.
    299   void* cert_handle;
    300   if ((cert_handle = CERT_StartCertExtensions(cert)) == NULL) {
    301     LOG(ERROR) << "Unable to get opaque handle for adding extensions";
    302     CERT_DestroyCertificate(cert);
    303     return false;
    304   }
    305 
    306   // Create SECItem for IA5String encoding.
    307   SECItem domain_string_item = {
    308     siAsciiString,
    309     (unsigned char*)domain.data(),
    310     static_cast<unsigned>(domain.size())
    311   };
    312 
    313   // IA5Encode and arena allocate SECItem
    314   SECItem* asn1_domain_string = SEC_ASN1EncodeItem(
    315       cert->arena, NULL, &domain_string_item,
    316       SEC_ASN1_GET(SEC_IA5StringTemplate));
    317   if (asn1_domain_string == NULL) {
    318     LOG(ERROR) << "Unable to get ASN1 encoding for domain in domain_bound_cert"
    319                   " extension";
    320     CERT_DestroyCertificate(cert);
    321     return false;
    322   }
    323 
    324   // Add the extension to the opaque handle
    325   if (CERT_AddExtension(
    326       cert_handle,
    327       DomainBoundCertOIDWrapper::GetInstance()->domain_bound_cert_oid_tag(),
    328       asn1_domain_string,
    329       PR_TRUE,
    330       PR_TRUE) != SECSuccess){
    331     LOG(ERROR) << "Unable to add domain bound cert extension to opaque handle";
    332     CERT_DestroyCertificate(cert);
    333     return false;
    334   }
    335 
    336   // Copy extension into x509 cert
    337   if (CERT_FinishExtensions(cert_handle) != SECSuccess){
    338     LOG(ERROR) << "Unable to copy extension to X509 cert";
    339     CERT_DestroyCertificate(cert);
    340     return false;
    341   }
    342 
    343   if (!SignCertificate(cert, key->key())) {
    344     CERT_DestroyCertificate(cert);
    345     return false;
    346   }
    347 
    348   DCHECK(cert->derCert.len);
    349   // XXX copied from X509Certificate::GetDEREncoded
    350   der_cert->clear();
    351   der_cert->append(reinterpret_cast<char*>(cert->derCert.data),
    352                    cert->derCert.len);
    353   CERT_DestroyCertificate(cert);
    354   return true;
    355 }
    356 
    357 #if defined(USE_NSS) || defined(OS_IOS)
    358 void ParsePrincipal(CERTName* name, CertPrincipal* principal) {
    359 // Starting in NSS 3.15, CERTGetNameFunc takes a const CERTName* argument.
    360 #if NSS_VMINOR >= 15
    361   typedef char* (*CERTGetNameFunc)(const CERTName* name);
    362 #else
    363   typedef char* (*CERTGetNameFunc)(CERTName* name);
    364 #endif
    365 
    366   // TODO(jcampan): add business_category and serial_number.
    367   // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and
    368   // CERT_GetDomainComponentName functions, but they return only the most
    369   // general (the first) RDN.  NSS doesn't have a function for the street
    370   // address.
    371   static const SECOidTag kOIDs[] = {
    372     SEC_OID_AVA_STREET_ADDRESS,
    373     SEC_OID_AVA_ORGANIZATION_NAME,
    374     SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
    375     SEC_OID_AVA_DC };
    376 
    377   std::vector<std::string>* values[] = {
    378     &principal->street_addresses,
    379     &principal->organization_names,
    380     &principal->organization_unit_names,
    381     &principal->domain_components };
    382   DCHECK_EQ(arraysize(kOIDs), arraysize(values));
    383 
    384   CERTRDN** rdns = name->rdns;
    385   for (size_t rdn = 0; rdns[rdn]; ++rdn) {
    386     CERTAVA** avas = rdns[rdn]->avas;
    387     for (size_t pair = 0; avas[pair] != 0; ++pair) {
    388       SECOidTag tag = CERT_GetAVATag(avas[pair]);
    389       for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
    390         if (kOIDs[oid] == tag) {
    391           SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
    392           if (!decode_item)
    393             break;
    394           // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
    395           std::string value(reinterpret_cast<char*>(decode_item->data),
    396                             decode_item->len);
    397           values[oid]->push_back(value);
    398           SECITEM_FreeItem(decode_item, PR_TRUE);
    399           break;
    400         }
    401       }
    402     }
    403   }
    404 
    405   // Get CN, L, S, and C.
    406   CERTGetNameFunc get_name_funcs[4] = {
    407     CERT_GetCommonName, CERT_GetLocalityName,
    408     CERT_GetStateName, CERT_GetCountryName };
    409   std::string* single_values[4] = {
    410     &principal->common_name, &principal->locality_name,
    411     &principal->state_or_province_name, &principal->country_name };
    412   for (size_t i = 0; i < arraysize(get_name_funcs); ++i) {
    413     char* value = get_name_funcs[i](name);
    414     if (value) {
    415       single_values[i]->assign(value);
    416       PORT_Free(value);
    417     }
    418   }
    419 }
    420 
    421 void ParseDate(const SECItem* der_date, base::Time* result) {
    422   PRTime prtime;
    423   SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date);
    424   DCHECK_EQ(SECSuccess, rv);
    425   *result = crypto::PRTimeToBaseTime(prtime);
    426 }
    427 
    428 std::string ParseSerialNumber(const CERTCertificate* certificate) {
    429   return std::string(reinterpret_cast<char*>(certificate->serialNumber.data),
    430                      certificate->serialNumber.len);
    431 }
    432 
    433 void GetSubjectAltName(CERTCertificate* cert_handle,
    434                        std::vector<std::string>* dns_names,
    435                        std::vector<std::string>* ip_addrs) {
    436   if (dns_names)
    437     dns_names->clear();
    438   if (ip_addrs)
    439     ip_addrs->clear();
    440 
    441   SECItem alt_name;
    442   SECStatus rv = CERT_FindCertExtension(cert_handle,
    443                                         SEC_OID_X509_SUBJECT_ALT_NAME,
    444                                         &alt_name);
    445   if (rv != SECSuccess)
    446     return;
    447 
    448   PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    449   DCHECK(arena != NULL);
    450 
    451   CERTGeneralName* alt_name_list;
    452   alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name);
    453   SECITEM_FreeItem(&alt_name, PR_FALSE);
    454 
    455   CERTGeneralName* name = alt_name_list;
    456   while (name) {
    457     // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
    458     // respectively, both of which can be byte copied from
    459     // SECItemType::data into the appropriate output vector.
    460     if (dns_names && name->type == certDNSName) {
    461       dns_names->push_back(std::string(
    462           reinterpret_cast<char*>(name->name.other.data),
    463           name->name.other.len));
    464     } else if (ip_addrs && name->type == certIPAddress) {
    465       ip_addrs->push_back(std::string(
    466           reinterpret_cast<char*>(name->name.other.data),
    467           name->name.other.len));
    468     }
    469     name = CERT_GetNextGeneralName(name);
    470     if (name == alt_name_list)
    471       break;
    472   }
    473   PORT_FreeArena(arena, PR_FALSE);
    474 }
    475 
    476 X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes(
    477     const char* data,
    478     int length,
    479     X509Certificate::Format format) {
    480   X509Certificate::OSCertHandles results;
    481   if (length < 0)
    482     return results;
    483 
    484   crypto::EnsureNSSInit();
    485 
    486   if (!NSS_IsInitialized())
    487     return results;
    488 
    489   switch (format) {
    490     case X509Certificate::FORMAT_SINGLE_CERTIFICATE: {
    491       X509Certificate::OSCertHandle handle =
    492           X509Certificate::CreateOSCertHandleFromBytes(data, length);
    493       if (handle)
    494         results.push_back(handle);
    495       break;
    496     }
    497     case X509Certificate::FORMAT_PKCS7: {
    498       // Make a copy since CERT_DecodeCertPackage may modify it
    499       std::vector<char> data_copy(data, data + length);
    500 
    501       SECStatus result = CERT_DecodeCertPackage(&data_copy[0],
    502           length, CollectCertsCallback, &results);
    503       if (result != SECSuccess)
    504         results.clear();
    505       break;
    506     }
    507     default:
    508       NOTREACHED() << "Certificate format " << format << " unimplemented";
    509       break;
    510   }
    511 
    512   return results;
    513 }
    514 
    515 X509Certificate::OSCertHandle ReadOSCertHandleFromPickle(
    516     PickleIterator* pickle_iter) {
    517   const char* data;
    518   int length;
    519   if (!pickle_iter->ReadData(&data, &length))
    520     return NULL;
    521 
    522   return X509Certificate::CreateOSCertHandleFromBytes(data, length);
    523 }
    524 
    525 void GetPublicKeyInfo(CERTCertificate* handle,
    526                       size_t* size_bits,
    527                       X509Certificate::PublicKeyType* type) {
    528   // Since we might fail, set the output parameters to default values first.
    529   *type = X509Certificate::kPublicKeyTypeUnknown;
    530   *size_bits = 0;
    531 
    532   crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(handle));
    533   if (!key.get())
    534     return;
    535 
    536   *size_bits = SECKEY_PublicKeyStrengthInBits(key.get());
    537 
    538   switch (key->keyType) {
    539     case rsaKey:
    540       *type = X509Certificate::kPublicKeyTypeRSA;
    541       break;
    542     case dsaKey:
    543       *type = X509Certificate::kPublicKeyTypeDSA;
    544       break;
    545     case dhKey:
    546       *type = X509Certificate::kPublicKeyTypeDH;
    547       break;
    548     case ecKey:
    549       *type = X509Certificate::kPublicKeyTypeECDSA;
    550       break;
    551     default:
    552       *type = X509Certificate::kPublicKeyTypeUnknown;
    553       *size_bits = 0;
    554       break;
    555   }
    556 }
    557 
    558 bool GetIssuersFromEncodedList(
    559     const std::vector<std::string>& encoded_issuers,
    560     PLArenaPool* arena,
    561     std::vector<CERTName*>* out) {
    562   std::vector<CERTName*> result;
    563   for (size_t n = 0; n < encoded_issuers.size(); ++n) {
    564     CERTName* name = CreateCertNameFromEncoded(arena, encoded_issuers[n]);
    565     if (name != NULL)
    566       result.push_back(name);
    567   }
    568 
    569   if (result.size() == encoded_issuers.size()) {
    570     out->swap(result);
    571     return true;
    572   }
    573 
    574   for (size_t n = 0; n < result.size(); ++n)
    575     CERT_DestroyName(result[n]);
    576   return false;
    577 }
    578 
    579 
    580 bool IsCertificateIssuedBy(const std::vector<CERTCertificate*>& cert_chain,
    581                            const std::vector<CERTName*>& valid_issuers) {
    582   for (size_t n = 0; n < cert_chain.size(); ++n) {
    583     CERTName* cert_issuer = &cert_chain[n]->issuer;
    584     for (size_t i = 0; i < valid_issuers.size(); ++i) {
    585       if (CERT_CompareName(valid_issuers[i], cert_issuer) == SECEqual)
    586         return true;
    587     }
    588   }
    589   return false;
    590 }
    591 
    592 std::string GetUniqueNicknameForSlot(const std::string& nickname,
    593                                      const SECItem* subject,
    594                                      PK11SlotInfo* slot) {
    595   int index = 2;
    596   std::string new_name = nickname;
    597   std::string temp_nickname = new_name;
    598   std::string token_name;
    599 
    600   if (!slot)
    601     return new_name;
    602 
    603   if (!PK11_IsInternalKeySlot(slot)) {
    604     token_name.assign(PK11_GetTokenName(slot));
    605     token_name.append(":");
    606 
    607     temp_nickname = token_name + new_name;
    608   }
    609 
    610   while (SEC_CertNicknameConflict(temp_nickname.c_str(),
    611                                   const_cast<SECItem*>(subject),
    612                                   CERT_GetDefaultCertDB())) {
    613     base::SStringPrintf(&new_name, "%s #%d", nickname.c_str(), index++);
    614     temp_nickname = token_name + new_name;
    615   }
    616 
    617   return new_name;
    618 }
    619 
    620 #endif  // defined(USE_NSS) || defined(OS_IOS)
    621 
    622 } // namespace x509_util
    623 
    624 } // namespace net
    625