OLD | NEW |
| (Empty) |
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_certificate.h" | |
6 | |
7 #include <CommonCrypto/CommonDigest.h> | |
8 #include <CoreServices/CoreServices.h> | |
9 #include <Security/Security.h> | |
10 | |
11 #include <vector> | |
12 | |
13 #include "base/lazy_instance.h" | |
14 #include "base/logging.h" | |
15 #include "base/mac/mac_logging.h" | |
16 #include "base/mac/scoped_cftyperef.h" | |
17 #include "base/memory/singleton.h" | |
18 #include "base/pickle.h" | |
19 #include "base/sha1.h" | |
20 #include "base/strings/string_piece.h" | |
21 #include "base/strings/sys_string_conversions.h" | |
22 #include "base/synchronization/lock.h" | |
23 #include "crypto/cssm_init.h" | |
24 #include "crypto/mac_security_services_lock.h" | |
25 #include "net/cert/x509_util_mac.h" | |
26 | |
27 using base::ScopedCFTypeRef; | |
28 using base::Time; | |
29 | |
30 namespace net { | |
31 | |
32 namespace { | |
33 | |
34 void GetCertDistinguishedName( | |
35 const x509_util::CSSMCachedCertificate& cached_cert, | |
36 const CSSM_OID* oid, | |
37 CertPrincipal* result) { | |
38 x509_util::CSSMFieldValue distinguished_name; | |
39 OSStatus status = cached_cert.GetField(oid, &distinguished_name); | |
40 if (status || !distinguished_name.field()) | |
41 return; | |
42 result->ParseDistinguishedName(distinguished_name.field()->Data, | |
43 distinguished_name.field()->Length); | |
44 } | |
45 | |
46 bool IsCertIssuerInEncodedList(X509Certificate::OSCertHandle cert_handle, | |
47 const std::vector<std::string>& issuers) { | |
48 x509_util::CSSMCachedCertificate cached_cert; | |
49 if (cached_cert.Init(cert_handle) != CSSM_OK) | |
50 return false; | |
51 | |
52 x509_util::CSSMFieldValue distinguished_name; | |
53 OSStatus status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd, | |
54 &distinguished_name); | |
55 if (status || !distinguished_name.field()) | |
56 return false; | |
57 | |
58 base::StringPiece name_piece( | |
59 reinterpret_cast<const char*>(distinguished_name.field()->Data), | |
60 static_cast<size_t>(distinguished_name.field()->Length)); | |
61 | |
62 for (std::vector<std::string>::const_iterator it = issuers.begin(); | |
63 it != issuers.end(); ++it) { | |
64 base::StringPiece issuer_piece(*it); | |
65 if (name_piece == issuer_piece) | |
66 return true; | |
67 } | |
68 | |
69 return false; | |
70 } | |
71 | |
72 void GetCertDateForOID(const x509_util::CSSMCachedCertificate& cached_cert, | |
73 const CSSM_OID* oid, | |
74 Time* result) { | |
75 *result = Time::Time(); | |
76 | |
77 x509_util::CSSMFieldValue field; | |
78 OSStatus status = cached_cert.GetField(oid, &field); | |
79 if (status) | |
80 return; | |
81 | |
82 const CSSM_X509_TIME* x509_time = field.GetAs<CSSM_X509_TIME>(); | |
83 if (x509_time->timeType != BER_TAG_UTC_TIME && | |
84 x509_time->timeType != BER_TAG_GENERALIZED_TIME) { | |
85 LOG(ERROR) << "Unsupported date/time format " | |
86 << x509_time->timeType; | |
87 return; | |
88 } | |
89 | |
90 base::StringPiece time_string( | |
91 reinterpret_cast<const char*>(x509_time->time.Data), | |
92 x509_time->time.Length); | |
93 CertDateFormat format = x509_time->timeType == BER_TAG_UTC_TIME ? | |
94 CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME; | |
95 if (!ParseCertificateDate(time_string, format, result)) | |
96 LOG(ERROR) << "Invalid certificate date/time " << time_string; | |
97 } | |
98 | |
99 std::string GetCertSerialNumber( | |
100 const x509_util::CSSMCachedCertificate& cached_cert) { | |
101 x509_util::CSSMFieldValue serial_number; | |
102 OSStatus status = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, | |
103 &serial_number); | |
104 if (status || !serial_number.field()) | |
105 return std::string(); | |
106 | |
107 return std::string( | |
108 reinterpret_cast<const char*>(serial_number.field()->Data), | |
109 serial_number.field()->Length); | |
110 } | |
111 | |
112 // Returns true if |purpose| is listed as allowed in |usage|. This | |
113 // function also considers the "Any" purpose. If the attribute is | |
114 // present and empty, we return false. | |
115 bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage, | |
116 const CSSM_OID* purpose) { | |
117 for (unsigned p = 0; p < usage->numPurposes; ++p) { | |
118 if (CSSMOIDEqual(&usage->purposes[p], purpose)) | |
119 return true; | |
120 if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ExtendedKeyUsageAny)) | |
121 return true; | |
122 } | |
123 return false; | |
124 } | |
125 | |
126 // Test that a given |cert_handle| is actually a valid X.509 certificate, and | |
127 // return true if it is. | |
128 // | |
129 // On OS X, SecCertificateCreateFromData() does not return any errors if | |
130 // called with invalid data, as long as data is present. The actual decoding | |
131 // of the certificate does not happen until an API that requires a CSSM | |
132 // handle is called. While SecCertificateGetCLHandle is the most likely | |
133 // candidate, as it performs the parsing, it does not check whether the | |
134 // parsing was actually successful. Instead, SecCertificateGetSubject is | |
135 // used (supported since 10.3), as a means to check that the certificate | |
136 // parsed as a valid X.509 certificate. | |
137 bool IsValidOSCertHandle(SecCertificateRef cert_handle) { | |
138 const CSSM_X509_NAME* sanity_check = NULL; | |
139 OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check); | |
140 return status == noErr && sanity_check; | |
141 } | |
142 | |
143 // Parses |data| of length |length|, attempting to decode it as the specified | |
144 // |format|. If |data| is in the specified format, any certificates contained | |
145 // within are stored into |output|. | |
146 void AddCertificatesFromBytes(const char* data, size_t length, | |
147 SecExternalFormat format, | |
148 X509Certificate::OSCertHandles* output) { | |
149 SecExternalFormat input_format = format; | |
150 ScopedCFTypeRef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy( | |
151 kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), length, | |
152 kCFAllocatorNull)); | |
153 | |
154 CFArrayRef items = NULL; | |
155 OSStatus status; | |
156 { | |
157 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); | |
158 status = SecKeychainItemImport(local_data, NULL, &input_format, | |
159 NULL, 0, NULL, NULL, &items); | |
160 } | |
161 | |
162 if (status) { | |
163 OSSTATUS_DLOG(WARNING, status) | |
164 << "Unable to import items from data of length " << length; | |
165 return; | |
166 } | |
167 | |
168 ScopedCFTypeRef<CFArrayRef> scoped_items(items); | |
169 CFTypeID cert_type_id = SecCertificateGetTypeID(); | |
170 | |
171 for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) { | |
172 SecKeychainItemRef item = reinterpret_cast<SecKeychainItemRef>( | |
173 const_cast<void*>(CFArrayGetValueAtIndex(items, i))); | |
174 | |
175 // While inputFormat implies only certificates will be imported, if/when | |
176 // other formats (eg: PKCS#12) are supported, this may also include | |
177 // private keys or other items types, so filter appropriately. | |
178 if (CFGetTypeID(item) == cert_type_id) { | |
179 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(item); | |
180 // OS X ignores |input_format| if it detects that |local_data| is PEM | |
181 // encoded, attempting to decode data based on internal rules for PEM | |
182 // block headers. If a PKCS#7 blob is encoded with a PEM block of | |
183 // CERTIFICATE, OS X 10.5 will return a single, invalid certificate | |
184 // based on the decoded data. If this happens, the certificate should | |
185 // not be included in |output|. Because |output| is empty, | |
186 // CreateCertificateListfromBytes will use PEMTokenizer to decode the | |
187 // data. When called again with the decoded data, OS X will honor | |
188 // |input_format|, causing decode to succeed. On OS X 10.6, the data | |
189 // is properly decoded as a PKCS#7, whether PEM or not, which avoids | |
190 // the need to fallback to internal decoding. | |
191 if (IsValidOSCertHandle(cert)) { | |
192 CFRetain(cert); | |
193 output->push_back(cert); | |
194 } | |
195 } | |
196 } | |
197 } | |
198 | |
199 } // namespace | |
200 | |
201 void X509Certificate::Initialize() { | |
202 x509_util::CSSMCachedCertificate cached_cert; | |
203 if (cached_cert.Init(cert_handle_) == CSSM_OK) { | |
204 GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1SubjectNameStd, | |
205 &subject_); | |
206 GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1IssuerNameStd, | |
207 &issuer_); | |
208 GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotBefore, | |
209 &valid_start_); | |
210 GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotAfter, | |
211 &valid_expiry_); | |
212 serial_number_ = GetCertSerialNumber(cached_cert); | |
213 } | |
214 | |
215 fingerprint_ = CalculateFingerprint(cert_handle_); | |
216 ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_); | |
217 } | |
218 | |
219 bool X509Certificate::IsIssuedByEncoded( | |
220 const std::vector<std::string>& valid_issuers) { | |
221 if (IsCertIssuerInEncodedList(cert_handle_, valid_issuers)) | |
222 return true; | |
223 | |
224 for (OSCertHandles::iterator it = intermediate_ca_certs_.begin(); | |
225 it != intermediate_ca_certs_.end(); ++it) { | |
226 if (IsCertIssuerInEncodedList(*it, valid_issuers)) | |
227 return true; | |
228 } | |
229 return false; | |
230 } | |
231 | |
232 void X509Certificate::GetSubjectAltName( | |
233 std::vector<std::string>* dns_names, | |
234 std::vector<std::string>* ip_addrs) const { | |
235 if (dns_names) | |
236 dns_names->clear(); | |
237 if (ip_addrs) | |
238 ip_addrs->clear(); | |
239 | |
240 x509_util::CSSMCachedCertificate cached_cert; | |
241 OSStatus status = cached_cert.Init(cert_handle_); | |
242 if (status) | |
243 return; | |
244 x509_util::CSSMFieldValue subject_alt_name; | |
245 status = cached_cert.GetField(&CSSMOID_SubjectAltName, &subject_alt_name); | |
246 if (status || !subject_alt_name.field()) | |
247 return; | |
248 const CSSM_X509_EXTENSION* cssm_ext = | |
249 subject_alt_name.GetAs<CSSM_X509_EXTENSION>(); | |
250 if (!cssm_ext || !cssm_ext->value.parsedValue) | |
251 return; | |
252 const CE_GeneralNames* alt_name = | |
253 reinterpret_cast<const CE_GeneralNames*>(cssm_ext->value.parsedValue); | |
254 | |
255 for (size_t name = 0; name < alt_name->numNames; ++name) { | |
256 const CE_GeneralName& name_struct = alt_name->generalName[name]; | |
257 const CSSM_DATA& name_data = name_struct.name; | |
258 // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs | |
259 // respectively, both of which can be byte copied from | |
260 // CSSM_DATA::data into the appropriate output vector. | |
261 if (dns_names && name_struct.nameType == GNT_DNSName) { | |
262 dns_names->push_back(std::string( | |
263 reinterpret_cast<const char*>(name_data.Data), | |
264 name_data.Length)); | |
265 } else if (ip_addrs && name_struct.nameType == GNT_IPAddress) { | |
266 ip_addrs->push_back(std::string( | |
267 reinterpret_cast<const char*>(name_data.Data), | |
268 name_data.Length)); | |
269 } | |
270 } | |
271 } | |
272 | |
273 // static | |
274 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle, | |
275 std::string* encoded) { | |
276 CSSM_DATA der_data; | |
277 if (!cert_handle || SecCertificateGetData(cert_handle, &der_data) != noErr) | |
278 return false; | |
279 encoded->assign(reinterpret_cast<char*>(der_data.Data), | |
280 der_data.Length); | |
281 return true; | |
282 } | |
283 | |
284 // static | |
285 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, | |
286 X509Certificate::OSCertHandle b) { | |
287 DCHECK(a && b); | |
288 if (a == b) | |
289 return true; | |
290 if (CFEqual(a, b)) | |
291 return true; | |
292 CSSM_DATA a_data, b_data; | |
293 return SecCertificateGetData(a, &a_data) == noErr && | |
294 SecCertificateGetData(b, &b_data) == noErr && | |
295 a_data.Length == b_data.Length && | |
296 memcmp(a_data.Data, b_data.Data, a_data.Length) == 0; | |
297 } | |
298 | |
299 // static | |
300 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( | |
301 const char* data, int length) { | |
302 CSSM_DATA cert_data; | |
303 cert_data.Data = const_cast<uint8*>(reinterpret_cast<const uint8*>(data)); | |
304 cert_data.Length = length; | |
305 | |
306 OSCertHandle cert_handle = NULL; | |
307 OSStatus status = SecCertificateCreateFromData(&cert_data, | |
308 CSSM_CERT_X_509v3, | |
309 CSSM_CERT_ENCODING_DER, | |
310 &cert_handle); | |
311 if (status != noErr) | |
312 return NULL; | |
313 if (!IsValidOSCertHandle(cert_handle)) { | |
314 CFRelease(cert_handle); | |
315 return NULL; | |
316 } | |
317 return cert_handle; | |
318 } | |
319 | |
320 // static | |
321 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( | |
322 const char* data, int length, Format format) { | |
323 OSCertHandles results; | |
324 | |
325 switch (format) { | |
326 case FORMAT_SINGLE_CERTIFICATE: { | |
327 OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); | |
328 if (handle) | |
329 results.push_back(handle); | |
330 break; | |
331 } | |
332 case FORMAT_PKCS7: | |
333 AddCertificatesFromBytes(data, length, kSecFormatPKCS7, &results); | |
334 break; | |
335 default: | |
336 NOTREACHED() << "Certificate format " << format << " unimplemented"; | |
337 break; | |
338 } | |
339 | |
340 return results; | |
341 } | |
342 | |
343 // static | |
344 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( | |
345 OSCertHandle handle) { | |
346 if (!handle) | |
347 return NULL; | |
348 return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle))); | |
349 } | |
350 | |
351 // static | |
352 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { | |
353 if (cert_handle) | |
354 CFRelease(cert_handle); | |
355 } | |
356 | |
357 // static | |
358 SHA1HashValue X509Certificate::CalculateFingerprint( | |
359 OSCertHandle cert) { | |
360 SHA1HashValue sha1; | |
361 memset(sha1.data, 0, sizeof(sha1.data)); | |
362 | |
363 CSSM_DATA cert_data; | |
364 OSStatus status = SecCertificateGetData(cert, &cert_data); | |
365 if (status) | |
366 return sha1; | |
367 | |
368 DCHECK(cert_data.Data); | |
369 DCHECK_NE(cert_data.Length, 0U); | |
370 | |
371 CC_SHA1(cert_data.Data, cert_data.Length, sha1.data); | |
372 | |
373 return sha1; | |
374 } | |
375 | |
376 // static | |
377 SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) { | |
378 SHA256HashValue sha256; | |
379 memset(sha256.data, 0, sizeof(sha256.data)); | |
380 | |
381 CSSM_DATA cert_data; | |
382 OSStatus status = SecCertificateGetData(cert, &cert_data); | |
383 if (status) | |
384 return sha256; | |
385 | |
386 DCHECK(cert_data.Data); | |
387 DCHECK_NE(cert_data.Length, 0U); | |
388 | |
389 CC_SHA256(cert_data.Data, cert_data.Length, sha256.data); | |
390 | |
391 return sha256; | |
392 } | |
393 | |
394 // static | |
395 SHA1HashValue X509Certificate::CalculateCAFingerprint( | |
396 const OSCertHandles& intermediates) { | |
397 SHA1HashValue sha1; | |
398 memset(sha1.data, 0, sizeof(sha1.data)); | |
399 | |
400 // The CC_SHA(3cc) man page says all CC_SHA1_xxx routines return 1, so | |
401 // we don't check their return values. | |
402 CC_SHA1_CTX sha1_ctx; | |
403 CC_SHA1_Init(&sha1_ctx); | |
404 CSSM_DATA cert_data; | |
405 for (size_t i = 0; i < intermediates.size(); ++i) { | |
406 OSStatus status = SecCertificateGetData(intermediates[i], &cert_data); | |
407 if (status) | |
408 return sha1; | |
409 CC_SHA1_Update(&sha1_ctx, cert_data.Data, cert_data.Length); | |
410 } | |
411 CC_SHA1_Final(sha1.data, &sha1_ctx); | |
412 | |
413 return sha1; | |
414 } | |
415 | |
416 bool X509Certificate::SupportsSSLClientAuth() const { | |
417 x509_util::CSSMCachedCertificate cached_cert; | |
418 OSStatus status = cached_cert.Init(cert_handle_); | |
419 if (status) | |
420 return false; | |
421 | |
422 // RFC5280 says to take the intersection of the two extensions. | |
423 // | |
424 // Our underlying crypto libraries don't expose | |
425 // ClientCertificateType, so for now we will not support fixed | |
426 // Diffie-Hellman mechanisms. For rsa_sign, we need the | |
427 // digitalSignature bit. | |
428 // | |
429 // In particular, if a key has the nonRepudiation bit and not the | |
430 // digitalSignature one, we will not offer it to the user. | |
431 x509_util::CSSMFieldValue key_usage; | |
432 status = cached_cert.GetField(&CSSMOID_KeyUsage, &key_usage); | |
433 if (status == CSSM_OK && key_usage.field()) { | |
434 const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>(); | |
435 const CE_KeyUsage* key_usage_value = | |
436 reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue); | |
437 if (!((*key_usage_value) & CE_KU_DigitalSignature)) | |
438 return false; | |
439 } | |
440 | |
441 status = cached_cert.GetField(&CSSMOID_ExtendedKeyUsage, &key_usage); | |
442 if (status == CSSM_OK && key_usage.field()) { | |
443 const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>(); | |
444 const CE_ExtendedKeyUsage* ext_key_usage = | |
445 reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue); | |
446 if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth)) | |
447 return false; | |
448 } | |
449 return true; | |
450 } | |
451 | |
452 CFMutableArrayRef X509Certificate::CreateOSCertChainForCert() const { | |
453 CFMutableArrayRef cert_list = | |
454 CFArrayCreateMutable(kCFAllocatorDefault, 0, | |
455 &kCFTypeArrayCallBacks); | |
456 if (!cert_list) | |
457 return NULL; | |
458 | |
459 CFArrayAppendValue(cert_list, os_cert_handle()); | |
460 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) | |
461 CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]); | |
462 | |
463 return cert_list; | |
464 } | |
465 | |
466 // static | |
467 X509Certificate::OSCertHandle | |
468 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) { | |
469 const char* data; | |
470 int length; | |
471 if (!pickle_iter->ReadData(&data, &length)) | |
472 return NULL; | |
473 | |
474 return CreateOSCertHandleFromBytes(data, length); | |
475 } | |
476 | |
477 // static | |
478 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle, | |
479 Pickle* pickle) { | |
480 CSSM_DATA cert_data; | |
481 OSStatus status = SecCertificateGetData(cert_handle, &cert_data); | |
482 if (status) | |
483 return false; | |
484 | |
485 return pickle->WriteData(reinterpret_cast<char*>(cert_data.Data), | |
486 cert_data.Length); | |
487 } | |
488 | |
489 // static | |
490 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle, | |
491 size_t* size_bits, | |
492 PublicKeyType* type) { | |
493 // Since we might fail, set the output parameters to default values first. | |
494 *type = kPublicKeyTypeUnknown; | |
495 *size_bits = 0; | |
496 | |
497 SecKeyRef key; | |
498 OSStatus status = SecCertificateCopyPublicKey(cert_handle, &key); | |
499 if (status) { | |
500 NOTREACHED() << "SecCertificateCopyPublicKey failed: " << status; | |
501 return; | |
502 } | |
503 ScopedCFTypeRef<SecKeyRef> scoped_key(key); | |
504 | |
505 const CSSM_KEY* cssm_key; | |
506 status = SecKeyGetCSSMKey(key, &cssm_key); | |
507 if (status) { | |
508 NOTREACHED() << "SecKeyGetCSSMKey failed: " << status; | |
509 return; | |
510 } | |
511 | |
512 *size_bits = cssm_key->KeyHeader.LogicalKeySizeInBits; | |
513 | |
514 switch (cssm_key->KeyHeader.AlgorithmId) { | |
515 case CSSM_ALGID_RSA: | |
516 *type = kPublicKeyTypeRSA; | |
517 break; | |
518 case CSSM_ALGID_DSA: | |
519 *type = kPublicKeyTypeDSA; | |
520 break; | |
521 case CSSM_ALGID_ECDSA: | |
522 *type = kPublicKeyTypeECDSA; | |
523 break; | |
524 case CSSM_ALGID_DH: | |
525 *type = kPublicKeyTypeDH; | |
526 break; | |
527 default: | |
528 *type = kPublicKeyTypeUnknown; | |
529 *size_bits = 0; | |
530 break; | |
531 } | |
532 } | |
533 | |
534 // static | |
535 bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle) { | |
536 x509_util::CSSMCachedCertificate cached_cert; | |
537 OSStatus status = cached_cert.Init(cert_handle); | |
538 if (status != noErr) | |
539 return false; | |
540 | |
541 x509_util::CSSMFieldValue subject; | |
542 status = cached_cert.GetField(&CSSMOID_X509V1SubjectNameStd, &subject); | |
543 if (status != CSSM_OK || !subject.field()) | |
544 return false; | |
545 | |
546 x509_util::CSSMFieldValue issuer; | |
547 status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd, &issuer); | |
548 if (status != CSSM_OK || !issuer.field()) | |
549 return false; | |
550 | |
551 if (subject.field()->Length != issuer.field()->Length || | |
552 memcmp(subject.field()->Data, issuer.field()->Data, | |
553 issuer.field()->Length) != 0) { | |
554 return false; | |
555 } | |
556 | |
557 CSSM_CL_HANDLE cl_handle = CSSM_INVALID_HANDLE; | |
558 status = SecCertificateGetCLHandle(cert_handle, &cl_handle); | |
559 if (status) | |
560 return false; | |
561 CSSM_DATA cert_data; | |
562 status = SecCertificateGetData(cert_handle, &cert_data); | |
563 if (status) | |
564 return false; | |
565 | |
566 if (CSSM_CL_CertVerify(cl_handle, 0, &cert_data, &cert_data, NULL, 0)) | |
567 return false; | |
568 return true; | |
569 } | |
570 | |
571 } // namespace net | |
OLD | NEW |