OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/cert/x509_util_nss.h" | |
6 | |
7 #include <cert.h> // Must be included before certdb.h | 5 #include <cert.h> // Must be included before certdb.h |
8 #include <certdb.h> | 6 #include <certdb.h> |
9 #include <cryptohi.h> | 7 #include <cryptohi.h> |
10 #include <nss.h> | 8 #include <nss.h> |
11 #include <pk11pub.h> | 9 #include <pk11pub.h> |
12 #include <prerror.h> | 10 #include <prerror.h> |
13 #include <secder.h> | 11 #include <secder.h> |
14 #include <secmod.h> | 12 #include <secmod.h> |
15 #include <secport.h> | 13 #include <secport.h> |
16 | 14 |
17 #include <memory> | 15 #include <memory> |
18 | 16 |
19 #include "base/debug/leak_annotations.h" | 17 #include "base/debug/leak_annotations.h" |
20 #include "base/logging.h" | 18 #include "base/logging.h" |
21 #include "base/memory/singleton.h" | 19 #include "base/memory/singleton.h" |
| 20 #include "base/numerics/safe_conversions.h" |
22 #include "base/pickle.h" | 21 #include "base/pickle.h" |
23 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
24 #include "crypto/ec_private_key.h" | 23 #include "crypto/ec_private_key.h" |
25 #include "crypto/nss_util.h" | 24 #include "crypto/nss_util.h" |
26 #include "crypto/nss_util_internal.h" | 25 #include "crypto/nss_util_internal.h" |
27 #include "crypto/rsa_private_key.h" | 26 #include "crypto/rsa_private_key.h" |
28 #include "crypto/scoped_nss_types.h" | 27 #include "crypto/scoped_nss_types.h" |
29 #include "crypto/third_party/nss/chromium-nss.h" | 28 #include "crypto/third_party/nss/chromium-nss.h" |
30 #include "net/cert/x509_certificate.h" | 29 #include "net/cert/x509_certificate.h" |
31 #include "net/cert/x509_util.h" | 30 #include "net/cert/x509_util.h" |
| 31 #include "net/cert/x509_util_nss.h" |
32 | 32 |
33 namespace net { | 33 namespace net { |
34 | 34 |
35 namespace { | 35 namespace { |
36 | 36 |
37 // Creates a Certificate object that may be passed to the SignCertificate | 37 // Microsoft User Principal Name: 1.3.6.1.4.1.311.20.2.3 |
38 // method to generate an X509 certificate. | 38 const uint8_t kUpnOid[] = {0x2b, 0x6, 0x1, 0x4, 0x1, |
39 // Returns NULL if an error is encountered in the certificate creation | 39 0x82, 0x37, 0x14, 0x2, 0x3}; |
40 // process. | 40 |
41 // Caller responsible for freeing returned certificate object. | 41 // Callback for CERT_DecodeCertPackage(), used in |
42 CERTCertificate* CreateCertificate(SECKEYPublicKey* public_key, | 42 // CreateOSCertHandlesFromBytes(). |
43 const std::string& subject, | 43 SECStatus PR_CALLBACK CollectCertsCallback(void* arg, |
44 uint32_t serial_number, | 44 SECItem** certs, |
45 base::Time not_valid_before, | 45 int num_certs) { |
46 base::Time not_valid_after) { | 46 X509Certificate::OSCertHandles* results = |
47 // Create info about public key. | 47 reinterpret_cast<X509Certificate::OSCertHandles*>(arg); |
48 CERTSubjectPublicKeyInfo* spki = | 48 |
49 SECKEY_CreateSubjectPublicKeyInfo(public_key); | 49 for (int i = 0; i < num_certs; ++i) { |
50 if (!spki) | 50 X509Certificate::OSCertHandle handle = |
| 51 X509Certificate::CreateOSCertHandleFromBytes( |
| 52 reinterpret_cast<char*>(certs[i]->data), certs[i]->len); |
| 53 if (handle) |
| 54 results->push_back(handle); |
| 55 } |
| 56 |
| 57 return SECSuccess; |
| 58 } |
| 59 |
| 60 typedef std::unique_ptr<CERTName, |
| 61 crypto::NSSDestroyer<CERTName, CERT_DestroyName>> |
| 62 ScopedCERTName; |
| 63 |
| 64 // Create a new CERTName object from its encoded representation. |
| 65 // |arena| is the allocation pool to use. |
| 66 // |data| points to a DER-encoded X.509 DistinguishedName. |
| 67 // Return a new CERTName pointer on success, or NULL. |
| 68 CERTName* CreateCertNameFromEncoded(PLArenaPool* arena, |
| 69 const base::StringPiece& data) { |
| 70 if (!arena) |
51 return NULL; | 71 return NULL; |
52 | 72 |
53 // Create the certificate request. | 73 ScopedCERTName name(PORT_ArenaZNew(arena, CERTName)); |
54 CERTName* subject_name = | 74 if (!name.get()) |
55 CERT_AsciiToName(const_cast<char*>(subject.c_str())); | |
56 CERTCertificateRequest* cert_request = | |
57 CERT_CreateCertificateRequest(subject_name, spki, NULL); | |
58 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
59 | |
60 if (!cert_request) { | |
61 PRErrorCode prerr = PR_GetError(); | |
62 LOG(ERROR) << "Failed to create certificate request: " << prerr; | |
63 CERT_DestroyName(subject_name); | |
64 return NULL; | 75 return NULL; |
65 } | 76 |
66 | 77 SECItem item; |
67 CERTValidity* validity = CERT_CreateValidity( | 78 item.len = static_cast<unsigned int>(data.length()); |
68 crypto::BaseTimeToPRTime(not_valid_before), | 79 item.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())); |
69 crypto::BaseTimeToPRTime(not_valid_after)); | 80 |
70 if (!validity) { | 81 SECStatus rv = SEC_ASN1DecodeItem(arena, name.get(), |
71 PRErrorCode prerr = PR_GetError(); | 82 SEC_ASN1_GET(CERT_NameTemplate), &item); |
72 LOG(ERROR) << "Failed to create certificate validity object: " << prerr; | 83 if (rv != SECSuccess) |
73 CERT_DestroyName(subject_name); | |
74 CERT_DestroyCertificateRequest(cert_request); | |
75 return NULL; | 84 return NULL; |
76 } | 85 |
77 CERTCertificate* cert = CERT_CreateCertificate(serial_number, subject_name, | 86 return name.release(); |
78 validity, cert_request); | 87 } |
79 if (!cert) { | 88 |
80 PRErrorCode prerr = PR_GetError(); | 89 } // namespace |
81 LOG(ERROR) << "Failed to create certificate: " << prerr; | 90 |
82 } | 91 namespace x509_util { |
83 | 92 |
84 // Cleanup for resources used to generate the cert. | 93 void ParsePrincipal(CERTName* name, CertPrincipal* principal) { |
85 CERT_DestroyName(subject_name); | 94 // Starting in NSS 3.15, CERTGetNameFunc takes a const CERTName* argument. |
86 CERT_DestroyValidity(validity); | 95 #if NSS_VMINOR >= 15 |
87 CERT_DestroyCertificateRequest(cert_request); | 96 typedef char* (*CERTGetNameFunc)(const CERTName* name); |
88 | 97 #else |
89 return cert; | 98 typedef char* (*CERTGetNameFunc)(CERTName * name); |
90 } | 99 #endif |
91 | 100 |
92 SECOidTag ToSECOid(x509_util::DigestAlgorithm alg) { | 101 // TODO(jcampan): add business_category and serial_number. |
93 switch (alg) { | 102 // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and |
94 case x509_util::DIGEST_SHA1: | 103 // CERT_GetDomainComponentName functions, but they return only the most |
95 return SEC_OID_SHA1; | 104 // general (the first) RDN. NSS doesn't have a function for the street |
96 case x509_util::DIGEST_SHA256: | 105 // address. |
97 return SEC_OID_SHA256; | 106 static const SECOidTag kOIDs[] = { |
98 } | 107 SEC_OID_AVA_STREET_ADDRESS, SEC_OID_AVA_ORGANIZATION_NAME, |
99 return SEC_OID_UNKNOWN; | 108 SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, SEC_OID_AVA_DC}; |
100 } | 109 |
101 | 110 std::vector<std::string>* values[] = { |
102 // Signs a certificate object, with |key| generating a new X509Certificate | 111 &principal->street_addresses, &principal->organization_names, |
103 // and destroying the passed certificate object (even when NULL is returned). | 112 &principal->organization_unit_names, &principal->domain_components}; |
104 // The logic of this method references SignCert() in NSS utility certutil: | 113 DCHECK_EQ(arraysize(kOIDs), arraysize(values)); |
105 // http://mxr.mozilla.org/security/ident?i=SignCert. | 114 |
106 // Returns true on success or false if an error is encountered in the | 115 CERTRDN** rdns = name->rdns; |
107 // certificate signing process. | 116 for (size_t rdn = 0; rdns[rdn]; ++rdn) { |
108 bool SignCertificate( | 117 CERTAVA** avas = rdns[rdn]->avas; |
109 CERTCertificate* cert, | 118 for (size_t pair = 0; avas[pair] != 0; ++pair) { |
110 SECKEYPrivateKey* key, | 119 SECOidTag tag = CERT_GetAVATag(avas[pair]); |
111 SECOidTag hash_algorithm) { | 120 for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { |
112 // |arena| is used to encode the cert. | 121 if (kOIDs[oid] == tag) { |
113 PLArenaPool* arena = cert->arena; | 122 SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value); |
114 SECOidTag algo_id = SEC_GetSignatureAlgorithmOidTag(key->keyType, | 123 if (!decode_item) |
115 hash_algorithm); | 124 break; |
116 if (algo_id == SEC_OID_UNKNOWN) | 125 // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote. |
117 return false; | 126 std::string value(reinterpret_cast<char*>(decode_item->data), |
118 | 127 decode_item->len); |
119 SECStatus rv = SECOID_SetAlgorithmID(arena, &cert->signature, algo_id, 0); | 128 values[oid]->push_back(value); |
| 129 SECITEM_FreeItem(decode_item, PR_TRUE); |
| 130 break; |
| 131 } |
| 132 } |
| 133 } |
| 134 } |
| 135 |
| 136 // Get CN, L, S, and C. |
| 137 CERTGetNameFunc get_name_funcs[4] = {CERT_GetCommonName, CERT_GetLocalityName, |
| 138 CERT_GetStateName, CERT_GetCountryName}; |
| 139 std::string* single_values[4] = { |
| 140 &principal->common_name, &principal->locality_name, |
| 141 &principal->state_or_province_name, &principal->country_name}; |
| 142 for (size_t i = 0; i < arraysize(get_name_funcs); ++i) { |
| 143 char* value = get_name_funcs[i](name); |
| 144 if (value) { |
| 145 single_values[i]->assign(value); |
| 146 PORT_Free(value); |
| 147 } |
| 148 } |
| 149 } |
| 150 |
| 151 void ParseDate(const SECItem* der_date, base::Time* result) { |
| 152 PRTime prtime; |
| 153 SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date); |
| 154 DCHECK_EQ(SECSuccess, rv); |
| 155 *result = crypto::PRTimeToBaseTime(prtime); |
| 156 } |
| 157 |
| 158 std::string ParseSerialNumber(const CERTCertificate* certificate) { |
| 159 return std::string(reinterpret_cast<char*>(certificate->serialNumber.data), |
| 160 certificate->serialNumber.len); |
| 161 } |
| 162 |
| 163 void GetSubjectAltName(CERTCertificate* cert_handle, |
| 164 std::vector<std::string>* dns_names, |
| 165 std::vector<std::string>* ip_addrs) { |
| 166 if (dns_names) |
| 167 dns_names->clear(); |
| 168 if (ip_addrs) |
| 169 ip_addrs->clear(); |
| 170 |
| 171 SECItem alt_name; |
| 172 SECStatus rv = CERT_FindCertExtension( |
| 173 cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, &alt_name); |
120 if (rv != SECSuccess) | 174 if (rv != SECSuccess) |
121 return false; | 175 return; |
122 | 176 |
123 // Generate a cert of version 3. | 177 PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
124 *(cert->version.data) = 2; | 178 DCHECK(arena != NULL); |
125 cert->version.len = 1; | 179 |
126 | 180 CERTGeneralName* alt_name_list; |
127 SECItem der = { siBuffer, NULL, 0 }; | 181 alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name); |
128 | 182 SECITEM_FreeItem(&alt_name, PR_FALSE); |
129 // Use ASN1 DER to encode the cert. | 183 |
130 void* encode_result = SEC_ASN1EncodeItem( | 184 CERTGeneralName* name = alt_name_list; |
131 NULL, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate)); | 185 while (name) { |
132 if (!encode_result) | 186 // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs |
133 return false; | 187 // respectively, both of which can be byte copied from |
134 | 188 // SECItemType::data into the appropriate output vector. |
135 // Allocate space to contain the signed cert. | 189 if (dns_names && name->type == certDNSName) { |
136 SECItem result = { siBuffer, NULL, 0 }; | 190 dns_names->push_back( |
137 | 191 std::string(reinterpret_cast<char*>(name->name.other.data), |
138 // Sign the ASN1 encoded cert and save it to |result|. | 192 name->name.other.len)); |
139 rv = DerSignData(arena, &result, &der, key, algo_id); | 193 } else if (ip_addrs && name->type == certIPAddress) { |
140 PORT_Free(der.data); | 194 ip_addrs->push_back( |
141 if (rv != SECSuccess) { | 195 std::string(reinterpret_cast<char*>(name->name.other.data), |
142 DLOG(ERROR) << "DerSignData: " << PORT_GetError(); | 196 name->name.other.len)); |
143 return false; | 197 } |
144 } | 198 name = CERT_GetNextGeneralName(name); |
145 | 199 if (name == alt_name_list) |
146 // Save the signed result to the cert. | 200 break; |
147 cert->derCert = result; | 201 } |
148 | 202 PORT_FreeArena(arena, PR_FALSE); |
149 return true; | 203 } |
150 } | 204 |
151 | 205 void GetRFC822SubjectAltNames(CERTCertificate* cert_handle, |
152 } // namespace | 206 std::vector<std::string>* names) { |
153 | 207 crypto::ScopedSECItem alt_name(SECITEM_AllocItem(NULL, NULL, 0)); |
154 namespace x509_util { | 208 DCHECK(alt_name.get()); |
155 | 209 |
156 bool CreateSelfSignedCert(crypto::RSAPrivateKey* key, | 210 names->clear(); |
157 DigestAlgorithm alg, | 211 SECStatus rv = CERT_FindCertExtension( |
158 const std::string& subject, | 212 cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, alt_name.get()); |
159 uint32_t serial_number, | 213 if (rv != SECSuccess) |
160 base::Time not_valid_before, | 214 return; |
161 base::Time not_valid_after, | 215 |
162 std::string* der_cert) { | 216 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
163 DCHECK(key); | 217 DCHECK(arena.get()); |
164 DCHECK(!strncmp(subject.c_str(), "CN=", 3U)); | 218 |
165 CERTCertificate* cert = CreateCertificate(key->public_key(), | 219 CERTGeneralName* alt_name_list; |
166 subject, | 220 alt_name_list = CERT_DecodeAltNameExtension(arena.get(), alt_name.get()); |
167 serial_number, | 221 |
168 not_valid_before, | 222 CERTGeneralName* name = alt_name_list; |
169 not_valid_after); | 223 while (name) { |
170 if (!cert) | 224 if (name->type == certRFC822Name) { |
171 return false; | 225 names->push_back( |
172 | 226 std::string(reinterpret_cast<char*>(name->name.other.data), |
173 if (!SignCertificate(cert, key->key(), ToSECOid(alg))) { | 227 name->name.other.len)); |
174 CERT_DestroyCertificate(cert); | 228 } |
175 return false; | 229 name = CERT_GetNextGeneralName(name); |
176 } | 230 if (name == alt_name_list) |
177 | 231 break; |
178 der_cert->assign(reinterpret_cast<char*>(cert->derCert.data), | 232 } |
179 cert->derCert.len); | 233 } |
180 CERT_DestroyCertificate(cert); | 234 |
181 return true; | 235 void GetUPNSubjectAltNames(CERTCertificate* cert_handle, |
182 } | 236 std::vector<std::string>* names) { |
183 | 237 crypto::ScopedSECItem alt_name(SECITEM_AllocItem(NULL, NULL, 0)); |
184 bool GetTLSServerEndPointChannelBinding(const X509Certificate& certificate, | 238 DCHECK(alt_name.get()); |
185 std::string* token) { | 239 |
186 NOTIMPLEMENTED(); | 240 names->clear(); |
| 241 SECStatus rv = CERT_FindCertExtension( |
| 242 cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, alt_name.get()); |
| 243 if (rv != SECSuccess) |
| 244 return; |
| 245 |
| 246 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
| 247 DCHECK(arena.get()); |
| 248 |
| 249 CERTGeneralName* alt_name_list; |
| 250 alt_name_list = CERT_DecodeAltNameExtension(arena.get(), alt_name.get()); |
| 251 |
| 252 CERTGeneralName* name = alt_name_list; |
| 253 while (name) { |
| 254 if (name->type == certOtherName) { |
| 255 OtherName* on = &name->name.OthName; |
| 256 if (on->oid.len == sizeof(kUpnOid) && |
| 257 memcmp(on->oid.data, kUpnOid, sizeof(kUpnOid)) == 0) { |
| 258 SECItem decoded; |
| 259 if (SEC_QuickDERDecodeItem(arena.get(), &decoded, |
| 260 SEC_ASN1_GET(SEC_UTF8StringTemplate), |
| 261 &name->name.OthName.name) == SECSuccess) { |
| 262 names->push_back( |
| 263 std::string(reinterpret_cast<char*>(decoded.data), decoded.len)); |
| 264 } |
| 265 } |
| 266 } |
| 267 name = CERT_GetNextGeneralName(name); |
| 268 if (name == alt_name_list) |
| 269 break; |
| 270 } |
| 271 } |
| 272 |
| 273 X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes( |
| 274 const char* data, |
| 275 size_t length, |
| 276 X509Certificate::Format format) { |
| 277 X509Certificate::OSCertHandles results; |
| 278 |
| 279 crypto::EnsureNSSInit(); |
| 280 |
| 281 if (!NSS_IsInitialized()) |
| 282 return results; |
| 283 |
| 284 switch (format) { |
| 285 case X509Certificate::FORMAT_SINGLE_CERTIFICATE: { |
| 286 X509Certificate::OSCertHandle handle = |
| 287 X509Certificate::CreateOSCertHandleFromBytes(data, length); |
| 288 if (handle) |
| 289 results.push_back(handle); |
| 290 break; |
| 291 } |
| 292 case X509Certificate::FORMAT_PKCS7: { |
| 293 // Make a copy since CERT_DecodeCertPackage may modify it |
| 294 std::vector<char> data_copy(data, data + length); |
| 295 |
| 296 SECStatus result = CERT_DecodeCertPackage( |
| 297 data_copy.data(), base::checked_cast<int>(data_copy.size()), |
| 298 CollectCertsCallback, &results); |
| 299 if (result != SECSuccess) |
| 300 results.clear(); |
| 301 break; |
| 302 } |
| 303 default: |
| 304 NOTREACHED() << "Certificate format " << format << " unimplemented"; |
| 305 break; |
| 306 } |
| 307 |
| 308 return results; |
| 309 } |
| 310 |
| 311 X509Certificate::OSCertHandle ReadOSCertHandleFromPickle( |
| 312 base::PickleIterator* pickle_iter) { |
| 313 const char* data; |
| 314 int length; |
| 315 if (!pickle_iter->ReadData(&data, &length)) |
| 316 return NULL; |
| 317 |
| 318 return X509Certificate::CreateOSCertHandleFromBytes(data, length); |
| 319 } |
| 320 |
| 321 void GetPublicKeyInfo(CERTCertificate* handle, |
| 322 size_t* size_bits, |
| 323 X509Certificate::PublicKeyType* type) { |
| 324 // Since we might fail, set the output parameters to default values first. |
| 325 *type = X509Certificate::kPublicKeyTypeUnknown; |
| 326 *size_bits = 0; |
| 327 |
| 328 crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(handle)); |
| 329 if (!key.get()) |
| 330 return; |
| 331 |
| 332 *size_bits = SECKEY_PublicKeyStrengthInBits(key.get()); |
| 333 |
| 334 switch (key->keyType) { |
| 335 case rsaKey: |
| 336 *type = X509Certificate::kPublicKeyTypeRSA; |
| 337 break; |
| 338 case dsaKey: |
| 339 *type = X509Certificate::kPublicKeyTypeDSA; |
| 340 break; |
| 341 case dhKey: |
| 342 *type = X509Certificate::kPublicKeyTypeDH; |
| 343 break; |
| 344 case ecKey: |
| 345 *type = X509Certificate::kPublicKeyTypeECDSA; |
| 346 break; |
| 347 default: |
| 348 *type = X509Certificate::kPublicKeyTypeUnknown; |
| 349 *size_bits = 0; |
| 350 break; |
| 351 } |
| 352 } |
| 353 |
| 354 bool GetIssuersFromEncodedList(const std::vector<std::string>& encoded_issuers, |
| 355 PLArenaPool* arena, |
| 356 std::vector<CERTName*>* out) { |
| 357 std::vector<CERTName*> result; |
| 358 for (size_t n = 0; n < encoded_issuers.size(); ++n) { |
| 359 CERTName* name = CreateCertNameFromEncoded(arena, encoded_issuers[n]); |
| 360 if (name != NULL) |
| 361 result.push_back(name); |
| 362 } |
| 363 |
| 364 if (result.size() == encoded_issuers.size()) { |
| 365 out->swap(result); |
| 366 return true; |
| 367 } |
| 368 |
| 369 for (size_t n = 0; n < result.size(); ++n) |
| 370 CERT_DestroyName(result[n]); |
187 return false; | 371 return false; |
188 } | 372 } |
189 | 373 |
190 } // namespace x509_util | 374 bool IsCertificateIssuedBy(const std::vector<CERTCertificate*>& cert_chain, |
191 | 375 const std::vector<CERTName*>& valid_issuers) { |
192 } // namespace net | 376 for (size_t n = 0; n < cert_chain.size(); ++n) { |
| 377 CERTName* cert_issuer = &cert_chain[n]->issuer; |
| 378 for (size_t i = 0; i < valid_issuers.size(); ++i) { |
| 379 if (CERT_CompareName(valid_issuers[i], cert_issuer) == SECEqual) |
| 380 return true; |
| 381 } |
| 382 } |
| 383 return false; |
| 384 } |
| 385 |
| 386 std::string GetUniqueNicknameForSlot(const std::string& nickname, |
| 387 const SECItem* subject, |
| 388 PK11SlotInfo* slot) { |
| 389 int index = 2; |
| 390 std::string new_name = nickname; |
| 391 std::string temp_nickname = new_name; |
| 392 std::string token_name; |
| 393 |
| 394 if (!slot) |
| 395 return new_name; |
| 396 |
| 397 if (!PK11_IsInternalKeySlot(slot)) { |
| 398 token_name.assign(PK11_GetTokenName(slot)); |
| 399 token_name.append(":"); |
| 400 |
| 401 temp_nickname = token_name + new_name; |
| 402 } |
| 403 |
| 404 while (SEC_CertNicknameConflict(temp_nickname.c_str(), |
| 405 const_cast<SECItem*>(subject), |
| 406 CERT_GetDefaultCertDB())) { |
| 407 base::SStringPrintf(&new_name, "%s #%d", nickname.c_str(), index++); |
| 408 temp_nickname = token_name + new_name; |
| 409 } |
| 410 |
| 411 return new_name; |
| 412 } |
| 413 |
| 414 } // namespace x509_util |
| 415 |
| 416 } // namespace net |
OLD | NEW |