diff options
author | rsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-06 18:22:09 +0000 |
---|---|---|
committer | rsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-06 18:22:09 +0000 |
commit | 130686ac6398e7d53a238f2588f44dd926f90933 (patch) | |
tree | ec7d482c645e36174c20bfb2a0cdde0a26a93f6d /net/base | |
parent | 1fa3a98a1eb48c7f96e5a1d710a9b4ca296b6ad1 (diff) | |
download | chromium_src-130686ac6398e7d53a238f2588f44dd926f90933.zip chromium_src-130686ac6398e7d53a238f2588f44dd926f90933.tar.gz chromium_src-130686ac6398e7d53a238f2588f44dd926f90933.tar.bz2 |
Sort client certificates by preference before prompting the user
When determining the list of available client certificates, order the returned
client certificates based on heuristics to determine preference. Certificates
are sorted based on validity, including that they're still valid, that
certificates with longer expirations are preferred, and that, barring those,
certificates that are newer are preferred.
BUG=89267
TEST=net_unittests --gtest_filter=X509UtilTest*
Review URL: https://chromiumcodereview.appspot.com/11275167
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166231 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/x509_util.cc | 48 | ||||
-rw-r--r-- | net/base/x509_util.h | 19 | ||||
-rw-r--r-- | net/base/x509_util_unittest.cc | 54 |
3 files changed, 121 insertions, 0 deletions
diff --git a/net/base/x509_util.cc b/net/base/x509_util.cc new file mode 100644 index 0000000..246c173 --- /dev/null +++ b/net/base/x509_util.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2012 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/base/x509_util.h" + +#include "base/time.h" +#include "net/base/x509_certificate.h" + +namespace net { + +namespace x509_util { + +bool ClientCertSorter::operator()( + const scoped_refptr<X509Certificate>& a, + const scoped_refptr<X509Certificate>& b) const { + // Certificates that are null are sorted last. + if (!a.get() || !b.get()) + return a.get() && !b.get(); + + // Certificates that are expired/not-yet-valid are sorted last. + base::Time now = base::Time::Now(); + bool a_is_valid = now >= a->valid_start() && now <= a->valid_expiry(); + bool b_is_valid = now >= b->valid_start() && now <= b->valid_expiry(); + if (a_is_valid && !b_is_valid) + return true; + + // Certificates with longer expirations appear as higher priority (less + // than) certificates with shorter expirations. + if (a->valid_expiry() != b->valid_expiry()) + return a->valid_expiry() > b->valid_expiry(); + + // If the expiration dates are equivalent, certificates that were issued + // more recently should be prioritized over older certificates. + if (a->valid_start() != b->valid_start()) + return a->valid_start() > b->valid_start(); + + // Otherwise, prefer client certificates with shorter chains. + const X509Certificate::OSCertHandles& a_intermediates = + a->GetIntermediateCertificates(); + const X509Certificate::OSCertHandles& b_intermediates = + b->GetIntermediateCertificates(); + return a_intermediates.size() < b_intermediates.size(); +} + +} // namespace x509_util + +} // namespace net diff --git a/net/base/x509_util.h b/net/base/x509_util.h index 1257d46..a800fdf 100644 --- a/net/base/x509_util.h +++ b/net/base/x509_util.h @@ -7,6 +7,7 @@ #include <string> +#include "base/memory/ref_counted.h" #include "base/time.h" #include "net/base/net_export.h" @@ -16,6 +17,8 @@ class ECPrivateKey; namespace net { +class X509Certificate; + namespace x509_util { // Returns true if the times can be used to create an X.509 certificate. @@ -41,6 +44,22 @@ bool NET_EXPORT_PRIVATE CreateDomainBoundCertEC( base::Time not_valid_after, std::string* der_cert); +// Comparator for use in STL algorithms that will sort client certificates by +// order of preference. +// Returns true if |a| is more preferable than |b|, allowing it to be used +// with any algorithm that compares according to strict weak ordering. +// +// Criteria include: +// - Prefer certificates that have a longer validity period (later +// expiration dates) +// - If equal, prefer certificates that were issued more recently +// - If equal, prefer shorter chains (if available) +struct NET_EXPORT_PRIVATE ClientCertSorter { + bool operator()( + const scoped_refptr<X509Certificate>& a, + const scoped_refptr<X509Certificate>& b) const; +}; + } // namespace x509_util } // namespace net diff --git a/net/base/x509_util_unittest.cc b/net/base/x509_util_unittest.cc new file mode 100644 index 0000000..34c1781 --- /dev/null +++ b/net/base/x509_util_unittest.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2012 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/base/x509_util.h" + +#include <algorithm> + +#include "base/time.h" +#include "net/base/x509_certificate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace x509_util { + +TEST(X509UtilTest, SortClientCertificates) { + CertificateList certs; + + const base::Time now = base::Time::Now(); + const base::TimeDelta five_days = base::TimeDelta::FromDays(5); + + certs.push_back(scoped_refptr<X509Certificate>(NULL)); + certs.push_back(new X509Certificate( + "expired", "expired", + base::Time::UnixEpoch(), base::Time::UnixEpoch())); + certs.push_back(new X509Certificate( + "not yet valid", "not yet valid", + base::Time::Max(), base::Time::Max())); + certs.push_back(new X509Certificate( + "older cert", "older cert", + now - five_days, now + five_days)); + certs.push_back(scoped_refptr<X509Certificate>(NULL)); + certs.push_back(new X509Certificate( + "newer cert", "newer cert", + now - base::TimeDelta::FromDays(3), now + five_days)); + + std::sort(certs.begin(), certs.end(), ClientCertSorter()); + + ASSERT_TRUE(certs[0].get()); + EXPECT_EQ("newer cert", certs[0]->subject().common_name); + ASSERT_TRUE(certs[1].get()); + EXPECT_EQ("older cert", certs[1]->subject().common_name); + ASSERT_TRUE(certs[2].get()); + EXPECT_EQ("not yet valid", certs[2]->subject().common_name); + ASSERT_TRUE(certs[3].get()); + EXPECT_EQ("expired", certs[3]->subject().common_name); + ASSERT_FALSE(certs[4].get()); + ASSERT_FALSE(certs[5].get()); +} + +} // namespace x509_util + +} // namespace net |