From 303d7d45b88c2fefcffbc5b24a267c6fd159c7a0 Mon Sep 17 00:00:00 2001 From: svaldez Date: Fri, 25 Mar 2016 13:37:13 -0700 Subject: Adding iOS OpenSSL Implementation This adds the OpenSSL-specific implementations for iOS, using SecTrustEvaluate in order to determine the validity of the certificate chain. (Relanding without nist-pkits dependency) TBR=rsleevi@chromium.org,davidben@chromium.org BUG=591545 Review URL: https://codereview.chromium.org/1833183002 Cr-Commit-Position: refs/heads/master@{#383348} --- net/BUILD.gn | 12 + net/cert/cert_verify_proc.cc | 4 + net/cert/cert_verify_proc_ios.cc | 239 ++++++++++++++++ net/cert/cert_verify_proc_ios.h | 36 +++ net/cert/x509_certificate_openssl_ios.cc | 475 +++++++++++++++++++++++++++++++ net/net.gyp | 7 + net/net.gypi | 3 + net/net_common.gypi | 12 + 8 files changed, 788 insertions(+) create mode 100644 net/cert/cert_verify_proc_ios.cc create mode 100644 net/cert/cert_verify_proc_ios.h create mode 100644 net/cert/x509_certificate_openssl_ios.cc (limited to 'net') diff --git a/net/BUILD.gn b/net/BUILD.gn index b6390a7..6fd7fc7 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn @@ -320,6 +320,14 @@ if (!is_nacl) { ] } + if (is_ios && use_nss_verifier) { + net_shared_sources -= [ + "cert/cert_verify_proc_ios.cc", + "cert/cert_verify_proc_ios.h", + "cert/x509_certificate_openssl_ios.cc", + ] + } + if (is_chromecast && use_nss_certs) { net_shared_sources += [ "ssl/ssl_platform_key_chromecast.cc" ] net_shared_sources -= [ "ssl/ssl_platform_key_nss.cc" ] @@ -376,6 +384,10 @@ if (!is_nacl) { ] } + if (is_ios && !use_nss_verifier) { + net_shared_sources += [ "cert/test_root_certs_mac.cc" ] + } + if (is_ios || is_mac) { net_shared_sources += gypi_values.net_base_mac_ios_sources } diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc index eba84e6..9c8267d 100644 --- a/net/cert/cert_verify_proc.cc +++ b/net/cert/cert_verify_proc.cc @@ -32,6 +32,8 @@ #include "net/cert/cert_verify_proc_openssl.h" #elif defined(OS_ANDROID) #include "net/cert/cert_verify_proc_android.h" +#elif defined(OS_IOS) +#include "net/cert/cert_verify_proc_ios.h" #elif defined(OS_MACOSX) #include "net/cert/cert_verify_proc_mac.h" #elif defined(OS_WIN) @@ -209,6 +211,8 @@ CertVerifyProc* CertVerifyProc::CreateDefault() { return new CertVerifyProcOpenSSL(); #elif defined(OS_ANDROID) return new CertVerifyProcAndroid(); +#elif defined(OS_IOS) + return new CertVerifyProcIOS(); #elif defined(OS_MACOSX) return new CertVerifyProcMac(); #elif defined(OS_WIN) diff --git a/net/cert/cert_verify_proc_ios.cc b/net/cert/cert_verify_proc_ios.cc new file mode 100644 index 0000000..5f2884f --- /dev/null +++ b/net/cert/cert_verify_proc_ios.cc @@ -0,0 +1,239 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/cert/cert_verify_proc_ios.h" + +#include +#include + +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" +#include "crypto/sha2.h" +#include "net/base/net_errors.h" +#include "net/cert/asn1_util.h" +#include "net/cert/cert_verify_result.h" +#include "net/cert/test_root_certs.h" +#include "net/cert/x509_certificate.h" +#include "net/ssl/openssl_ssl_util.h" + +using base::ScopedCFTypeRef; + +namespace net { + +namespace { + +int NetErrorFromOSStatus(OSStatus status) { + switch (status) { + case noErr: + return OK; + case errSecNotAvailable: + return ERR_NOT_IMPLEMENTED; + case errSecAuthFailed: + return ERR_ACCESS_DENIED; + default: + return ERR_FAILED; + } +} + +// Creates a series of SecPolicyRefs to be added to a SecTrustRef used to +// validate a certificate for an SSL server. |hostname| contains the name of +// the SSL server that the certificate should be verified against. If +// successful, returns noErr, and stores the resultant array of SecPolicyRefs +// in |policies|. +OSStatus CreateTrustPolicies(ScopedCFTypeRef* policies) { + ScopedCFTypeRef local_policies( + CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); + if (!local_policies) + return errSecAllocate; + + SecPolicyRef ssl_policy = SecPolicyCreateBasicX509(); + CFArrayAppendValue(local_policies, ssl_policy); + CFRelease(ssl_policy); + ssl_policy = SecPolicyCreateSSL(true, nullptr); + CFArrayAppendValue(local_policies, ssl_policy); + CFRelease(ssl_policy); + + policies->reset(local_policies.release()); + return noErr; +} + +// Builds and evaluates a SecTrustRef for the certificate chain contained +// in |cert_array|, using the verification policies in |trust_policies|. On +// success, returns OK, and updates |trust_ref| and |trust_result|. On failure, +// no output parameters are modified. +// +// Note: An OK return does not mean that |cert_array| is trusted, merely that +// verification was performed successfully. +int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array, + CFArrayRef trust_policies, + ScopedCFTypeRef* trust_ref, + ScopedCFTypeRef* verified_chain, + SecTrustResultType* trust_result) { + SecTrustRef tmp_trust = nullptr; + OSStatus status = + SecTrustCreateWithCertificates(cert_array, trust_policies, &tmp_trust); + if (status) + return NetErrorFromOSStatus(status); + ScopedCFTypeRef scoped_tmp_trust(tmp_trust); + + if (TestRootCerts::HasInstance()) { + status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust); + if (status) + return NetErrorFromOSStatus(status); + } + + SecTrustResultType tmp_trust_result; + status = SecTrustEvaluate(tmp_trust, &tmp_trust_result); + if (status) + return NetErrorFromOSStatus(status); + + ScopedCFTypeRef tmp_verified_chain( + CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); + const CFIndex chain_length = SecTrustGetCertificateCount(tmp_trust); + for (CFIndex i = 0; i < chain_length; ++i) { + SecCertificateRef chain_cert = SecTrustGetCertificateAtIndex(tmp_trust, i); + CFArrayAppendValue(tmp_verified_chain, chain_cert); + } + + trust_ref->swap(scoped_tmp_trust); + *trust_result = tmp_trust_result; + verified_chain->reset(tmp_verified_chain.release()); + return OK; +} + +void GetCertChainInfo(CFArrayRef cert_chain, CertVerifyResult* verify_result) { + DCHECK_LT(0, CFArrayGetCount(cert_chain)); + + verify_result->has_md2 = false; + verify_result->has_md4 = false; + verify_result->has_md5 = false; + verify_result->has_sha1 = false; + verify_result->has_sha1_leaf = false; + + SecCertificateRef verified_cert = nullptr; + std::vector verified_chain; + for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) { + SecCertificateRef chain_cert = reinterpret_cast( + const_cast(CFArrayGetValueAtIndex(cert_chain, i))); + if (i == 0) { + verified_cert = chain_cert; + } else { + verified_chain.push_back(chain_cert); + } + + std::string der_bytes; + if (!X509Certificate::GetDEREncoded(chain_cert, &der_bytes)) + return; + const uint8_t* bytes = reinterpret_cast(der_bytes.data()); + ScopedX509 x509_cert(d2i_X509(NULL, &bytes, der_bytes.size())); + + base::StringPiece spki_bytes; + if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) + continue; + + HashValue sha1(HASH_VALUE_SHA1); + CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data()); + verify_result->public_key_hashes.push_back(sha1); + + HashValue sha256(HASH_VALUE_SHA256); + CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data()); + verify_result->public_key_hashes.push_back(sha256); + + int sig_alg = OBJ_obj2nid(x509_cert->sig_alg->algorithm); + if (sig_alg == NID_md2WithRSAEncryption) { + verify_result->has_md2 = true; + } else if (sig_alg == NID_md4WithRSAEncryption) { + verify_result->has_md4 = true; + } else if (sig_alg == NID_md5WithRSAEncryption || + sig_alg == NID_md5WithRSA) { + verify_result->has_md5 = true; + } else if (sig_alg == NID_sha1WithRSAEncryption || + sig_alg == NID_dsaWithSHA || sig_alg == NID_dsaWithSHA1 || + sig_alg == NID_dsaWithSHA1_2 || sig_alg == NID_sha1WithRSA || + sig_alg == NID_ecdsa_with_SHA1) { + verify_result->has_sha1 = true; + if (i == 0) + verify_result->has_sha1_leaf = true; + } + } + if (!verified_cert) { + NOTREACHED(); + return; + } + + verify_result->verified_cert = + X509Certificate::CreateFromHandle(verified_cert, verified_chain); +} + +} // namespace + +CertVerifyProcIOS::CertVerifyProcIOS() {} + +CertVerifyProcIOS::~CertVerifyProcIOS() {} + +bool CertVerifyProcIOS::SupportsAdditionalTrustAnchors() const { + return false; +} + +bool CertVerifyProcIOS::SupportsOCSPStapling() const { + return false; +} + +int CertVerifyProcIOS::VerifyInternal( + X509Certificate* cert, + const std::string& hostname, + const std::string& ocsp_response, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) { + ScopedCFTypeRef trust_policies; + OSStatus status = CreateTrustPolicies(&trust_policies); + if (status) + return NetErrorFromOSStatus(status); + + ScopedCFTypeRef cert_array( + cert->CreateOSCertChainForCert()); + ScopedCFTypeRef trust_ref; + SecTrustResultType trust_result = kSecTrustResultDeny; + ScopedCFTypeRef final_chain; + + status = BuildAndEvaluateSecTrustRef(cert_array, trust_policies, &trust_ref, + &final_chain, &trust_result); + if (status) + return NetErrorFromOSStatus(status); + + if (CFArrayGetCount(final_chain) == 0) + return ERR_FAILED; + + GetCertChainInfo(final_chain, verify_result); + + // TODO(sleevi): Support CRLSet revocation. + // TODO(svaldez): Add specific error codes for trust errors resulting from + // expired/not-yet-valid certs. + switch (trust_result) { + case kSecTrustResultUnspecified: + case kSecTrustResultProceed: + break; + case kSecTrustResultDeny: + verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID; + default: + verify_result->cert_status |= CERT_STATUS_INVALID; + } + + // Perform hostname verification independent of SecTrustEvaluate. + if (!verify_result->verified_cert->VerifyNameMatch( + hostname, &verify_result->common_name_fallback_used)) { + verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; + } + + verify_result->is_issued_by_known_root = false; + + if (IsCertStatusError(verify_result->cert_status)) + return MapCertStatusToNetError(verify_result->cert_status); + + return OK; +} + +} // namespace net diff --git a/net/cert/cert_verify_proc_ios.h b/net/cert/cert_verify_proc_ios.h new file mode 100644 index 0000000..e965a48 --- /dev/null +++ b/net/cert/cert_verify_proc_ios.h @@ -0,0 +1,36 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_CERT_CERT_VERIFY_PROC_IOS_H_ +#define NET_CERT_CERT_VERIFY_PROC_IOS_H_ + +#include "net/cert/cert_verify_proc.h" + +namespace net { + +// Performs certificate path construction and validation using iOS's +// Security.framework. +class CertVerifyProcIOS : public CertVerifyProc { + public: + CertVerifyProcIOS(); + + bool SupportsAdditionalTrustAnchors() const override; + bool SupportsOCSPStapling() const override; + + protected: + ~CertVerifyProcIOS() override; + + private: + int VerifyInternal(X509Certificate* cert, + const std::string& hostname, + const std::string& ocsp_response, + int flags, + CRLSet* crl_set, + const CertificateList& additional_trust_anchors, + CertVerifyResult* verify_result) override; +}; + +} // namespace net + +#endif // NET_CERT_CERT_VERIFY_PROC_IOS_H_ diff --git a/net/cert/x509_certificate_openssl_ios.cc b/net/cert/x509_certificate_openssl_ios.cc new file mode 100644 index 0000000..49a88d7 --- /dev/null +++ b/net/cert/x509_certificate_openssl_ios.cc @@ -0,0 +1,475 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/cert/x509_certificate.h" + +#include +#include + +#include +#include + +#include "base/mac/scoped_cftyperef.h" +#include "base/pickle.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "crypto/openssl_util.h" +#include "crypto/scoped_openssl_types.h" +#include "net/base/ip_address_number.h" +#include "net/cert/x509_util_openssl.h" +#include "net/ssl/openssl_ssl_util.h" + +using base::ScopedCFTypeRef; + +namespace net { + +namespace { + +using ScopedGENERAL_NAMES = + crypto::ScopedOpenSSL; + +// Returns true if a given |cert_handle| is actually a valid X.509 certificate +// handle. +// +// SecCertificateCreateFromData() does not always force the immediate parsing of +// the certificate, and as such, may return a SecCertificateRef for an +// invalid/unparsable certificate. Force parsing to occur to ensure that the +// SecCertificateRef is correct. On later versions where +// SecCertificateCreateFromData() immediately parses, rather than lazily, this +// call is cheap, as the subject is cached. +bool IsValidOSCertHandle(SecCertificateRef cert_handle) { + ScopedCFTypeRef sanity_check( + SecCertificateCopySubjectSummary(cert_handle)); + return sanity_check != nullptr; +} + +void CreateOSCertHandlesFromPKCS7Bytes( + const char* data, + size_t length, + X509Certificate::OSCertHandles* handles) { + crypto::EnsureOpenSSLInit(); + crypto::OpenSSLErrStackTracer err_cleaner(FROM_HERE); + + CBS der_data; + CBS_init(&der_data, reinterpret_cast(data), length); + STACK_OF(X509)* certs = sk_X509_new_null(); + + if (PKCS7_get_certificates(certs, &der_data)) { + for (size_t i = 0; i < sk_X509_num(certs); ++i) { + X509* x509_cert = sk_X509_value(certs, i); + base::StringPiece der; + if (!x509_util::GetDER(x509_cert, &der)) + return; + handles->push_back(X509Certificate::CreateOSCertHandleFromBytes( + der.data(), der.length())); + } + } + sk_X509_pop_free(certs, X509_free); +} + +void ParsePrincipalValues(X509_NAME* name, + int nid, + std::vector* fields) { + for (int index = -1; + (index = X509_NAME_get_index_by_NID(name, nid, index)) != -1;) { + std::string field; + if (!x509_util::ParsePrincipalValueByIndex(name, index, &field)) + break; + fields->push_back(field); + } +} + +void ParsePrincipal(X509Certificate::OSCertHandle os_cert, + X509_NAME* x509_name, + CertPrincipal* principal) { + if (!x509_name) + return; + + ParsePrincipalValues(x509_name, NID_streetAddress, + &principal->street_addresses); + ParsePrincipalValues(x509_name, NID_organizationName, + &principal->organization_names); + ParsePrincipalValues(x509_name, NID_organizationalUnitName, + &principal->organization_unit_names); + ParsePrincipalValues(x509_name, NID_domainComponent, + &principal->domain_components); + + x509_util::ParsePrincipalValueByNID(x509_name, NID_commonName, + &principal->common_name); + x509_util::ParsePrincipalValueByNID(x509_name, NID_localityName, + &principal->locality_name); + x509_util::ParsePrincipalValueByNID(x509_name, NID_stateOrProvinceName, + &principal->state_or_province_name); + x509_util::ParsePrincipalValueByNID(x509_name, NID_countryName, + &principal->country_name); +} + +void ParseSubjectAltName(X509Certificate::OSCertHandle os_cert, + std::vector* dns_names, + std::vector* ip_addresses) { + DCHECK(dns_names || ip_addresses); + ScopedX509 cert = OSCertHandleToOpenSSL(os_cert); + if (!cert.get()) + return; + int index = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1); + X509_EXTENSION* alt_name_ext = X509_get_ext(cert.get(), index); + if (!alt_name_ext) + return; + + ScopedGENERAL_NAMES alt_names( + reinterpret_cast(X509V3_EXT_d2i(alt_name_ext))); + if (!alt_names.get()) + return; + + for (size_t i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) { + const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i); + if (name->type == GEN_DNS && dns_names) { + const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName); + if (!dns_name) + continue; + int dns_name_len = ASN1_STRING_length(name->d.dNSName); + dns_names->push_back( + std::string(reinterpret_cast(dns_name), dns_name_len)); + } else if (name->type == GEN_IPADD && ip_addresses) { + const unsigned char* ip_addr = name->d.iPAddress->data; + if (!ip_addr) + continue; + int ip_addr_len = name->d.iPAddress->length; + if (ip_addr_len != static_cast(kIPv4AddressSize) && + ip_addr_len != static_cast(kIPv6AddressSize)) { + // http://www.ietf.org/rfc/rfc3280.txt requires subjectAltName iPAddress + // to have 4 or 16 bytes, whereas in a name constraint it includes a + // net mask hence 8 or 32 bytes. Logging to help diagnose any mixup. + LOG(WARNING) << "Bad sized IP Address in cert: " << ip_addr_len; + continue; + } + ip_addresses->push_back( + std::string(reinterpret_cast(ip_addr), ip_addr_len)); + } + } +} + +// Used to free a list of X509_NAMEs and the objects it points to. +void sk_X509_NAME_free_all(STACK_OF(X509_NAME) * sk) { + sk_X509_NAME_pop_free(sk, X509_NAME_free); +} + +} // namespace + +// static +X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( + OSCertHandle handle) { + if (!handle) + return nullptr; + return reinterpret_cast(const_cast(CFRetain(handle))); +} + +// static +void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { + if (cert_handle) + CFRelease(cert_handle); +} + +void X509Certificate::Initialize() { + crypto::EnsureOpenSSLInit(); + fingerprint_ = CalculateFingerprint(cert_handle_); + ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_); + ScopedX509 x509_cert = OSCertHandleToOpenSSL(cert_handle_); + if (!x509_cert) + return; + ASN1_INTEGER* serial_num = X509_get_serialNumber(x509_cert.get()); + if (serial_num) { + // ASN1_INTEGERS represent the decoded number, in a format internal to + // OpenSSL. Most notably, this may have leading zeroes stripped off for + // numbers whose first byte is >= 0x80. Thus, it is necessary to + // re-encoded the integer back into DER, which is what the interface + // of X509Certificate exposes, to ensure callers get the proper (DER) + // value. + int bytes_required = i2c_ASN1_INTEGER(serial_num, nullptr); + unsigned char* buffer = reinterpret_cast( + base::WriteInto(&serial_number_, bytes_required + 1)); + int bytes_written = i2c_ASN1_INTEGER(serial_num, &buffer); + DCHECK_EQ(static_cast(bytes_written), serial_number_.size()); + } + + ParsePrincipal(cert_handle_, X509_get_subject_name(x509_cert.get()), + &subject_); + ParsePrincipal(cert_handle_, X509_get_issuer_name(x509_cert.get()), &issuer_); + x509_util::ParseDate(X509_get_notBefore(x509_cert.get()), &valid_start_); + x509_util::ParseDate(X509_get_notAfter(x509_cert.get()), &valid_expiry_); +} + +// static +SHA1HashValue X509Certificate::CalculateFingerprint(OSCertHandle cert) { + SHA1HashValue sha1; + memset(sha1.data, 0, sizeof(sha1.data)); + + ScopedCFTypeRef cert_data(SecCertificateCopyData(cert)); + if (!cert_data) + return sha1; + DCHECK(CFDataGetBytePtr(cert_data)); + DCHECK_NE(0, CFDataGetLength(cert_data)); + CC_SHA1(CFDataGetBytePtr(cert_data), CFDataGetLength(cert_data), sha1.data); + + return sha1; +} + +// static +SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) { + SHA256HashValue sha256; + memset(sha256.data, 0, sizeof(sha256.data)); + + ScopedCFTypeRef cert_data(SecCertificateCopyData(cert)); + if (!cert_data) + return sha256; + DCHECK(CFDataGetBytePtr(cert_data)); + DCHECK_NE(0, CFDataGetLength(cert_data)); + CC_SHA256(CFDataGetBytePtr(cert_data), CFDataGetLength(cert_data), + sha256.data); + + return sha256; +} + +// static +SHA1HashValue X509Certificate::CalculateCAFingerprint( + const OSCertHandles& intermediates) { + SHA1HashValue sha1; + memset(sha1.data, 0, sizeof(sha1.data)); + + CC_SHA1_CTX sha1_ctx; + CC_SHA1_Init(&sha1_ctx); + for (size_t i = 0; i < intermediates.size(); ++i) { + ScopedCFTypeRef cert_data( + SecCertificateCopyData(intermediates[i])); + if (!cert_data) + return sha1; + CC_SHA1_Update(&sha1_ctx, CFDataGetBytePtr(cert_data), + CFDataGetLength(cert_data)); + } + CC_SHA1_Final(sha1.data, &sha1_ctx); + return sha1; +} + +// static +X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( + const char* data, + size_t length) { + ScopedCFTypeRef cert_data(CFDataCreateWithBytesNoCopy( + kCFAllocatorDefault, reinterpret_cast(data), + base::checked_cast(length), kCFAllocatorNull)); + if (!cert_data) + return nullptr; + OSCertHandle cert_handle = SecCertificateCreateWithData(nullptr, cert_data); + if (!cert_handle) + return nullptr; + if (!IsValidOSCertHandle(cert_handle)) { + CFRelease(cert_handle); + return nullptr; + } + return cert_handle; +} + +// static +X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( + const char* data, + size_t length, + Format format) { + OSCertHandles results; + + switch (format) { + case FORMAT_SINGLE_CERTIFICATE: { + OSCertHandle handle = + X509Certificate::CreateOSCertHandleFromBytes(data, length); + if (handle) + results.push_back(handle); + break; + } + case FORMAT_PKCS7: { + CreateOSCertHandlesFromPKCS7Bytes(data, length, &results); + break; + } + default: { + NOTREACHED() << "Certificate format " << format << " unimplemented"; + break; + } + } + + return results; +} + +void X509Certificate::GetSubjectAltName( + std::vector* dns_names, + std::vector* ip_addrs) const { + if (dns_names) + dns_names->clear(); + if (ip_addrs) + ip_addrs->clear(); + + ParseSubjectAltName(cert_handle_, dns_names, ip_addrs); +} + +// static +bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle, + std::string* encoded) { + base::StringPiece der; + if (!cert_handle) + return false; + ScopedCFTypeRef der_data(SecCertificateCopyData(cert_handle)); + if (!der_data) + return false; + encoded->assign(reinterpret_cast(CFDataGetBytePtr(der_data)), + CFDataGetLength(der_data)); + return true; +} + +// static +bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, + X509Certificate::OSCertHandle b) { + DCHECK(a && b); + return CFEqual(a, b); +} + +// static +X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle( + base::PickleIterator* pickle_iter) { + const char* data; + int length; + if (!pickle_iter->ReadData(&data, &length)) + return nullptr; + + return X509Certificate::CreateOSCertHandleFromBytes(data, length); +} + +// static +bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle, + base::Pickle* pickle) { + ScopedCFTypeRef cert_data(SecCertificateCopyData(cert_handle)); + if (!cert_data) + return false; + + return pickle->WriteData( + reinterpret_cast(CFDataGetBytePtr(cert_data)), + CFDataGetLength(cert_data)); +} + +// static +void X509Certificate::GetPublicKeyInfo(OSCertHandle os_cert, + size_t* size_bits, + PublicKeyType* type) { + *type = kPublicKeyTypeUnknown; + *size_bits = 0; + ScopedX509 cert = OSCertHandleToOpenSSL(os_cert); + if (!cert) + return; + crypto::ScopedEVP_PKEY scoped_key(X509_get_pubkey(cert.get())); + if (!scoped_key) + return; + + EVP_PKEY* key = scoped_key.get(); + + switch (key->type) { + case EVP_PKEY_RSA: + *type = kPublicKeyTypeRSA; + break; + case EVP_PKEY_DSA: + *type = kPublicKeyTypeDSA; + break; + case EVP_PKEY_EC: + *type = kPublicKeyTypeECDSA; + break; + case EVP_PKEY_DH: + *type = kPublicKeyTypeDH; + break; + } + *size_bits = EVP_PKEY_bits(key); +} + +bool X509Certificate::SupportsSSLClientAuth() const { + return false; +} + +CFMutableArrayRef X509Certificate::CreateOSCertChainForCert() const { + CFMutableArrayRef cert_list = + CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if (!cert_list) + return nullptr; + + CFArrayAppendValue(cert_list, os_cert_handle()); + for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) + CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]); + + return cert_list; +} + +bool X509Certificate::IsIssuedByEncoded( + const std::vector& valid_issuers) { + if (valid_issuers.empty()) + return false; + + // Convert to a temporary list of X509_NAME objects. + // It will own the objects it points to. + crypto::ScopedOpenSSL + issuer_names(sk_X509_NAME_new_null()); + if (!issuer_names) + return false; + + for (std::vector::const_iterator it = valid_issuers.begin(); + it != valid_issuers.end(); ++it) { + const unsigned char* p = reinterpret_cast(it->data()); + long len = static_cast(it->length()); + X509_NAME* ca_name = d2i_X509_NAME(nullptr, &p, len); + if (ca_name == nullptr) + return false; + sk_X509_NAME_push(issuer_names.get(), ca_name); + } + + // Create a temporary list of X509_NAME objects corresponding + // to the certificate chain. It doesn't own the object it points to. + std::vector cert_names; + ScopedX509 x509_cert = OSCertHandleToOpenSSL(cert_handle_); + if (!x509_cert) + return false; + X509_NAME* issuer = X509_get_issuer_name(x509_cert.get()); + if (issuer == nullptr) + return false; + + cert_names.push_back(issuer); + for (OSCertHandles::iterator it = intermediate_ca_certs_.begin(); + it != intermediate_ca_certs_.end(); ++it) { + ScopedX509 intermediate_cert = OSCertHandleToOpenSSL(*it); + if (!intermediate_cert) + return false; + issuer = X509_get_issuer_name(intermediate_cert.get()); + if (issuer == nullptr) + return false; + cert_names.push_back(issuer); + } + + // and 'cert_names'. + for (size_t n = 0; n < cert_names.size(); ++n) { + for (size_t m = 0; m < sk_X509_NAME_num(issuer_names.get()); ++m) { + X509_NAME* issuer = sk_X509_NAME_value(issuer_names.get(), m); + if (X509_NAME_cmp(issuer, cert_names[n]) == 0) { + return true; + } + } + } + + return false; +} + +// static +bool X509Certificate::IsSelfSigned(OSCertHandle os_cert) { + ScopedX509 cert = OSCertHandleToOpenSSL(os_cert); + if (!cert) + return false; + crypto::ScopedEVP_PKEY scoped_key(X509_get_pubkey(cert.get())); + if (!scoped_key) + return false; + + // NOTE: X509_verify() returns 1 in case of success, 0 or -1 on error. + return X509_verify(cert.get(), scoped_key.get()) == 1; +} + +} // namespace net diff --git a/net/net.gyp b/net/net.gyp index 1af2162..e0df0b3 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -240,6 +240,13 @@ 'cert/x509_util_ios.h', ], }], + [ 'use_nss_verifier == 1 and OS == "ios"', { + 'sources!': [ + 'cert/cert_verify_proc_ios.cc', + 'cert/cert_verify_proc_ios.h', + 'cert/x509_certificate_openssl_ios.cc', + ], + }], [ 'use_openssl==1', { 'sources!': [ 'quic/test_tools/crypto_test_utils_nss.cc', diff --git a/net/net.gypi b/net/net.gypi index a224d4b..f946a5b 100644 --- a/net/net.gypi +++ b/net/net.gypi @@ -570,6 +570,8 @@ 'cert/cert_verify_proc.h', 'cert/cert_verify_proc_android.cc', 'cert/cert_verify_proc_android.h', + 'cert/cert_verify_proc_ios.cc', + 'cert/cert_verify_proc_ios.h', 'cert/cert_verify_proc_mac.cc', 'cert/cert_verify_proc_mac.h', 'cert/cert_verify_proc_nss.cc', @@ -629,6 +631,7 @@ 'cert/x509_certificate_ios.cc', 'cert/x509_certificate_mac.cc', 'cert/x509_certificate_nss.cc', + 'cert/x509_certificate_openssl_ios.cc', 'cert/x509_certificate_win.cc', 'cert/x509_util_android.cc', 'cert/x509_util_android.h', diff --git a/net/net_common.gypi b/net/net_common.gypi index 9bd4aac..330c617 100644 --- a/net/net_common.gypi +++ b/net/net_common.gypi @@ -309,6 +309,13 @@ 'cert/x509_util_ios.h', ], }], + [ 'OS == "ios" and use_nss_verifier == 1', { + 'sources!': [ + 'cert/cert_verify_proc_ios.cc', + 'cert/cert_verify_proc_ios.h', + 'cert/x509_certificate_openssl_ios.cc', + ], + }], [ 'enable_websockets == 1', { 'sources': ['<@(net_websockets_sources)'] }], @@ -426,5 +433,10 @@ ['include', '^proxy/proxy_server_mac\\.cc$'], ], }], + ['OS == "ios" and <(use_nss_verifier) == 0', { + 'sources/': [ + ['include', '^cert/test_root_certs_mac\\.cc$'], + ], + }], ], } -- cgit v1.1