summaryrefslogtreecommitdiffstats
path: root/net/ssl/openssl_client_key_store.cc
blob: 9ea044e3fde6671e391cf1ad9f6ddecda35b8584 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) 2013 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/ssl/openssl_client_key_store.h"

#include <openssl/evp.h>
#include <openssl/x509.h>

#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "net/cert/x509_certificate.h"

namespace net {

namespace {

typedef OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;

// Increment the reference count of a given EVP_PKEY. This function
// is similar to EVP_PKEY_dup which is not available from the OpenSSL
// version used by Chromium at the moment. Its name is distinct to
// avoid compiler warnings about ambiguous function calls at caller
// sites.
EVP_PKEY* CopyEVP_PKEY(EVP_PKEY* key) {
  if (key)
    CRYPTO_add(&key->references, 1, CRYPTO_LOCK_EVP_PKEY);
  return key;
}

// Return the EVP_PKEY holding the public key of a given certificate.
// |cert| is a certificate.
// Returns a scoped EVP_PKEY for it.
ScopedEVP_PKEY GetOpenSSLPublicKey(const X509Certificate* cert) {
  // X509_PUBKEY_get() increments the reference count of its result.
  // Unlike X509_get_X509_PUBKEY() which simply returns a direct pointer.
  EVP_PKEY* pkey =
      X509_PUBKEY_get(X509_get_X509_PUBKEY(cert->os_cert_handle()));
  if (!pkey)
    LOG(ERROR) << "Can't extract private key from certificate!";
  return ScopedEVP_PKEY(pkey);
}

}  // namespace

OpenSSLClientKeyStore::OpenSSLClientKeyStore() {
}

OpenSSLClientKeyStore::~OpenSSLClientKeyStore() {
}

OpenSSLClientKeyStore::KeyPair::KeyPair(EVP_PKEY* pub_key,
                                        EVP_PKEY* priv_key) {
  public_key = CopyEVP_PKEY(pub_key);
  private_key = CopyEVP_PKEY(priv_key);
}

OpenSSLClientKeyStore::KeyPair::~KeyPair() {
  EVP_PKEY_free(public_key);
  EVP_PKEY_free(private_key);
}

OpenSSLClientKeyStore::KeyPair::KeyPair(const KeyPair& other) {
  public_key = CopyEVP_PKEY(other.public_key);
  private_key = CopyEVP_PKEY(other.private_key);
}

void OpenSSLClientKeyStore::KeyPair::operator=(const KeyPair& other) {
  EVP_PKEY* old_public_key = public_key;
  EVP_PKEY* old_private_key = private_key;
  public_key = CopyEVP_PKEY(other.public_key);
  private_key = CopyEVP_PKEY(other.private_key);
  EVP_PKEY_free(old_private_key);
  EVP_PKEY_free(old_public_key);
}

int OpenSSLClientKeyStore::FindKeyPairIndex(EVP_PKEY* public_key) {
  if (!public_key)
    return -1;
  for (size_t n = 0; n < pairs_.size(); ++n) {
    if (EVP_PKEY_cmp(pairs_[n].public_key, public_key) == 1)
      return static_cast<int>(n);
  }
  return -1;
}

void OpenSSLClientKeyStore::AddKeyPair(EVP_PKEY* pub_key,
                                       EVP_PKEY* private_key) {
  int index = FindKeyPairIndex(pub_key);
  if (index < 0)
    pairs_.push_back(KeyPair(pub_key, private_key));
}

// Common code for OpenSSLClientKeyStore. Shared by all OpenSSL-based
// builds.
bool OpenSSLClientKeyStore::RecordClientCertPrivateKey(
    const X509Certificate* client_cert,
    EVP_PKEY* private_key) {
  // Sanity check.
  if (!client_cert || !private_key)
    return false;

  // Get public key from certificate.
  ScopedEVP_PKEY pub_key(GetOpenSSLPublicKey(client_cert));
  if (!pub_key.get())
    return false;

  AddKeyPair(pub_key.get(), private_key);
  return true;
}

bool OpenSSLClientKeyStore::FetchClientCertPrivateKey(
    const X509Certificate* client_cert,
    ScopedEVP_PKEY* private_key) {
  if (!client_cert)
    return false;

  ScopedEVP_PKEY pub_key(GetOpenSSLPublicKey(client_cert));
  if (!pub_key.get())
    return false;

  int index = FindKeyPairIndex(pub_key.get());
  if (index < 0)
    return false;

  private_key->reset(CopyEVP_PKEY(pairs_[index].private_key));
  return true;
}

void OpenSSLClientKeyStore::Flush() {
  pairs_.clear();
}

OpenSSLClientKeyStore* OpenSSLClientKeyStore::GetInstance() {
  return Singleton<OpenSSLClientKeyStore>::get();
}

}  // namespace net