Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(213)

Side by Side Diff: net/socket/ssl_client_socket_openssl_unittest.cc

Issue 12220104: Wire up SSL client authentication for OpenSSL/Android through the net/ stack (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: remove chrome/browser changes + fix linux_redux build Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/socket/ssl_client_socket.h"
6
7 #include <errno.h>
8 #include <string.h>
9
10 #include <openssl/bn.h>
11 #include <openssl/evp.h>
12 #include <openssl/pem.h>
13 #include <openssl/rsa.h>
14
15 #include "base/file_util.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_handle.h"
18 #include "base/values.h"
19 #include "crypto/openssl_util.h"
20 #include "net/base/address_list.h"
21 #include "net/base/cert_test_util.h"
22 #include "net/base/host_resolver.h"
23 #include "net/base/io_buffer.h"
24 #include "net/base/mock_cert_verifier.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/net_log.h"
27 #include "net/base/net_log_unittest.h"
28 #include "net/base/openssl_private_key_store.h"
29 #include "net/base/ssl_cert_request_info.h"
30 #include "net/base/ssl_config_service.h"
31 #include "net/base/test_completion_callback.h"
32 #include "net/base/test_data_directory.h"
33 #include "net/base/test_root_certs.h"
34 #include "net/socket/client_socket_factory.h"
35 #include "net/socket/client_socket_handle.h"
36 #include "net/socket/socket_test_util.h"
37 #include "net/socket/tcp_client_socket.h"
38 #include "net/test/test_server.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40 #include "testing/platform_test.h"
41
42 //-----------------------------------------------------------------------------
43
44 namespace {
45
46 typedef net::OpenSSLPrivateKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
47
48 typedef crypto::ScopedOpenSSL<RSA, RSA_free> ScopedRSA;
49 typedef crypto::ScopedOpenSSL<BIGNUM, BN_free> ScopedBIGNUM;
Ryan Sleevi 2013/02/13 23:25:55 scoped_ptr<> ?
digit1 2013/02/14 06:23:50 I don't really see the point given that it require
Ryan Sleevi 2013/02/14 07:15:00 crypto/openssl_util -> crypto/scoped_openssl_types
50
51 // The following is needed to construct paths to certificates passed as
52 // |client_authorities| in server SSLOptions. Current implementation of
53 // RemoteTestServer (used on Android) expects relative paths, as opposed to
54 // LocalTestServer, which expects absolute paths (what to fix?).
55 base::FilePath CertDirectory() {
56 #ifdef OS_ANDROID
57 return net::GetTestCertsDirectoryRelative();
58 #else
59 return net::GetTestCertsDirectory();
60 #endif
61 }
Ryan Sleevi 2013/02/13 23:25:55 This shouldn't be in a unittest file like this.
digit1 2013/02/14 06:23:50 Can you clarify? This comes straight from net/sock
Ryan Sleevi 2013/02/14 07:15:00 Then we should refactor the logic in test_data_dir
digit1 2013/02/14 08:24:39 I see, I'll do that then.
62
63 // Loads a PEM-encoded private key file into a scoped EVP_PKEY object.
64 // |filepath| is the private key file path.
65 // |*pkey| is reset to the new EVP_PKEY on success, untouched otherwise.
66 // Returns true on success, false on failure.
67 bool LoadPrivateKeyOpenSSL(
68 const base::FilePath& filepath,
69 net::OpenSSLPrivateKeyStore::ScopedEVP_PKEY* pkey) {
70 ScopedStdioHandle file(file_util::OpenFile(filepath, "rb"));
Ryan Sleevi 2013/02/13 23:25:55 Use FileUtil::ReadFileToString, rather than a Scop
digit1 2013/02/14 06:23:50 I'm surprised by this request, given that last wee
Ryan Sleevi 2013/02/14 07:15:00 I seem to recall that you were using the _BIO vari
digit1 2013/02/14 08:24:39 No, I was using a memory BIO to pass the data read
Ryan Sleevi 2013/02/14 08:51:15 Bah! I think at the time I wrote that comment I wa
71 if (!file.get()) {
72 LOG(ERROR) << "Could not open private key file: "
73 << filepath.value() << ": " << strerror(errno);
74 return false;
75 }
76 EVP_PKEY* result = PEM_read_PrivateKey(file.get(), NULL, NULL, NULL);
77 if (result == NULL) {
78 LOG(ERROR) << "Could not read private key file: "
79 << filepath.value();
80 return false;
81 }
82 pkey->reset(result);
83 return true;
84 }
85
86 // By default, OpenSSL checks that the private key and the certificate
87 // match. If not, it will reject them immediately, resulting in a
88 // net::ERR_SSL_PROTOCOL_ERROR, and no client certificate being sent
89 // to the server.
90 //
91 // This function create a new random RSA-based private key that can
92 // bypass this check, by setting the RSA_METHOD_FLAG_NO_CHECK in its
93 // RSA_METHOD.
94 //
95 // |num_bits| is the key length in bits.
96 // |*pkey| is reset to the new key value on success, untouched otherwise.
97 // Returns true on success.
98 bool CreateRandomPrivateKey(
Ryan Sleevi 2013/02/13 23:25:55 This is not something we want to do in tests, sinc
digit1 2013/02/14 06:23:50 I'm not sure to understand what you mean here. Can
Ryan Sleevi 2013/02/14 07:15:00 We don't want to generate random keys in tests. Ev
digit1 2013/02/14 08:24:39 I see, I didn't realize that. Thanks for the clari
Ryan Sleevi 2013/02/14 08:51:15 We're effectively a forked version of TLSlite. I'v
99 int num_bits,
100 net::OpenSSLPrivateKeyStore::ScopedEVP_PKEY* pkey) {
101 // Ensure the initialization of a custom RSA_METHOD that has the
102 // RSA_METHOD_FLAG_NO_CHECK flag set.
103 static int s_rsa_method_init;
104 static RSA_METHOD s_rsa_method;
105
106 if (!s_rsa_method_init) {
107 s_rsa_method = *RSA_get_default_method();
108 s_rsa_method.flags |= RSA_METHOD_FLAG_NO_CHECK;
109 s_rsa_method_init = 1;
110 }
111
112 // Create a new RSA key with the new method.
113 ScopedRSA rsa(RSA_new());
114 RSA_set_method(rsa.get(), &s_rsa_method);
115
116 // Create exponent as a BIGNUM, this copies the code in
117 // RSA_generate_key(), which can't be used here because it creates
118 // a new RSA key.
119 const unsigned long exponent = 65537;
120 ScopedBIGNUM bn_exponent(BN_new());
121 for (int i = 0; i < static_cast<int>(8*sizeof(unsigned long)); ++i) {
122 if (exponent & (1UL << i))
123 BN_set_bit(bn_exponent.get(), i);
124 }
125
126 if (!RSA_generate_key_ex(rsa.get(), num_bits, bn_exponent.get(), NULL)) {
127 LOG(ERROR) << "Can't generate RSA private key";
128 return false;
129 }
130
131 // Generate EVP_PKEY wrapper
132 ScopedEVP_PKEY result(EVP_PKEY_new());
133 if (!EVP_PKEY_set1_RSA(result.get(), rsa.get())) {
134 LOG(ERROR) << "Could not create EVP_PKEY wrapper for RSA private key";
135 return false;
136 }
137 rsa.release();
138
139 pkey->swap(result);
140 return true;
141 }
142
143 // LogContainsSSLConnectEndEvent returns true if the given index in the given
144 // log is an SSL connect end event. The NSS sockets will cork in an attempt to
145 // merge the first application data record with the Finished message when false
146 // starting. However, in order to avoid the server timing out the handshake,
147 // they'll give up waiting for application data and send the Finished after a
148 // timeout. This means that an SSL connect end event may appear as a socket
149 // write.
150 bool LogContainsSSLConnectEndEvent(
151 const net::CapturingNetLog::CapturedEntryList& log, int i) {
152 return net::LogContainsEndEvent(log, i, net::NetLog::TYPE_SSL_CONNECT) ||
153 net::LogContainsEvent(log, i, net::NetLog::TYPE_SOCKET_BYTES_SENT,
154 net::NetLog::PHASE_NONE);
155 };
156
157 } // namespace
Ryan Sleevi 2013/02/13 23:25:55 Put the entire file into namespace net { namespace
digit1 2013/02/14 06:23:50 This would result in a link error, because there a
Ryan Sleevi 2013/02/14 07:15:00 Not if you put them in the unnamed namespace, like
digit1 2013/02/14 08:24:39 turns out that using 'static' is enough to put the
digit1 2013/02/14 08:24:39 Oh, I realize I misread your initial comment. will
158
159 //-----------------------------------------------------------------------------
Ryan Sleevi 2013/02/13 23:25:55 Remove this
digit1 2013/02/14 06:23:50 Again, this came from net/socket/ssl_client_socket
Ryan Sleevi 2013/02/14 07:15:00 Same cleanup I'm working on there, trying to avoid
160
161 const net::SSLConfig kDefaultSSLConfig;
162
163 class SSLClientSocketOpenSSLClientAuthTest : public PlatformTest {
164 public:
165 SSLClientSocketOpenSSLClientAuthTest()
166 : socket_factory_(net::ClientSocketFactory::GetDefaultFactory()),
167 cert_verifier_(new net::MockCertVerifier) {
168 cert_verifier_->set_default_result(net::OK);
169 context_.cert_verifier = cert_verifier_.get();
170 key_store_ = net::OpenSSLPrivateKeyStore::GetInstance();
171 }
172
173 protected:
174 virtual ~SSLClientSocketOpenSSLClientAuthTest() {
175 key_store_->Flush();
176 }
177
178 virtual void TearDown() {
179 // if (sock_.get() && sock_->IsConnected())
180 // sock_->Disconnect();
181 }
182
183 net::SSLClientSocket* CreateSSLClientSocket(
184 net::StreamSocket* transport_socket,
185 const net::HostPortPair& host_and_port,
186 const net::SSLConfig& ssl_config) {
187 return socket_factory_->CreateSSLClientSocket(transport_socket,
188 host_and_port,
189 ssl_config,
190 context_);
191 }
192
193 // Connect to a HTTPS test server.
194 bool ConnectToTestServer(net::TestServer::SSLOptions& ssl_options) {
195 test_server_.reset(new net::TestServer(net::TestServer::TYPE_HTTPS,
196 ssl_options,
197 base::FilePath()));
198 if (!test_server_.get()) {
199 LOG(ERROR) << "Could not create new TestServer";
200 return false;
201 }
202 if (!test_server_->Start()) {
203 LOG(ERROR) << "Could not start TestServer";
204 return false;
205 }
206
207 if (!test_server_->GetAddressList(&addr_)) {
208 LOG(ERROR) << "Could not get TestServer address list";
209 return false;
210 }
211
212 transport_.reset(new net::TCPClientSocket(
213 addr_, &log_, net::NetLog::Source()));
214 int rv = transport_->Connect(callback_.callback());
215 if (rv == net::ERR_IO_PENDING)
216 rv = callback_.WaitForResult();
217 if (rv != net::OK) {
218 LOG(ERROR) << "Could not connect to TestServer";
219 return false;
220 }
221 return true;
222 }
223
224 bool RecordPrivateKey(net::SSLConfig& ssl_config,
225 EVP_PKEY* private_key) {
226 return key_store_->RecordClientCertPrivateKey(
227 ssl_config.client_cert.get(), private_key);
228 }
229
230 bool CreateAndConnectSSLClientSocket(net::SSLConfig& ssl_config,
231 int* result) {
232 sock_.reset(CreateSSLClientSocket(transport_.release(),
233 test_server_->host_port_pair(),
234 ssl_config));
235
236 if (sock_->IsConnected()) {
237 LOG(ERROR) << "SSL Socket prematurely connected";
238 return false;
239 }
240
241 int rv = sock_->Connect(callback_.callback());
242
243 net::CapturingNetLog::CapturedEntryList entries;
244 log_.GetEntries(&entries);
245 if (!net::LogContainsBeginEvent(
246 entries, 5, net::NetLog::TYPE_SSL_CONNECT)) {
247 LOG(ERROR) << "SSL connection not started in logs";
248 return false;
249 }
250 if (rv == net::ERR_IO_PENDING)
251 rv = callback_.WaitForResult();
252
253 *result = rv;
254 return true;
255 }
256
257
258 bool CheckSSLClientSocketSentCert() {
259 net::CapturingNetLog::CapturedEntryList entries;
260 log_.GetEntries(&entries);
261 if (!LogContainsSSLConnectEndEvent(entries, -1)) {
262 LOG(ERROR) << "!LogContainsSSLConnectEndEvent()";
263 return false;
264 }
265
266 // Check that the client certificate was sent.
267 net::SSLInfo ssl_info;
268 sock_->GetSSLInfo(&ssl_info);
269 return ssl_info.client_cert_sent;
270 }
271
272 net::ClientSocketFactory* socket_factory_;
273 scoped_ptr<net::MockCertVerifier> cert_verifier_;
274 net::SSLClientSocketContext context_;
275 net::OpenSSLPrivateKeyStore* key_store_;
276 scoped_ptr<net::TestServer> test_server_;
277 net::AddressList addr_;
278 net::TestCompletionCallback callback_;
279 net::CapturingNetLog log_;
280 scoped_ptr<net::StreamSocket> transport_;
281 scoped_ptr<net::SSLClientSocket> sock_;
282 };
283
284 //-----------------------------------------------------------------------------
Ryan Sleevi 2013/02/13 23:25:55 Remove this
digit1 2013/02/14 08:24:39 Done.
285
286 // Connect to a server requesting client authentication, do not send
287 // any client certificates. It should refuse the connection.
288 TEST_F(SSLClientSocketOpenSSLClientAuthTest, NoCert) {
289 net::TestServer::SSLOptions ssl_options;
290 ssl_options.request_client_certificate = true;
291
292 ASSERT_TRUE(ConnectToTestServer(ssl_options));
293
294 base::FilePath certs_dir = net::GetTestCertsDirectory();
295 net::SSLConfig ssl_config = kDefaultSSLConfig;
296
297 int rv;
298 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
299
300 EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv);
301 EXPECT_FALSE(sock_->IsConnected());
302 }
303
304 // Connect to a server requesting client authentication, and send it
305 // an empty certificate. It should refuse the connection.
306 TEST_F(SSLClientSocketOpenSSLClientAuthTest, SendEmptyCert) {
307 net::TestServer::SSLOptions ssl_options;
308 ssl_options.request_client_certificate = true;
309
310 ASSERT_TRUE(ConnectToTestServer(ssl_options));
311
312 base::FilePath certs_dir = net::GetTestCertsDirectory();
313 net::SSLConfig ssl_config = kDefaultSSLConfig;
314 ssl_config.send_client_cert = true;
315 ssl_config.client_cert = NULL;
316
317 int rv;
318 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
319
320 EXPECT_EQ(net::OK, rv);
321 EXPECT_TRUE(sock_->IsConnected());
322 }
323
324 // Connect to a server requesting client authentication. Send it a
325 // matching certificate. It should allow the connection.
326 TEST_F(SSLClientSocketOpenSSLClientAuthTest, SendGoodCert) {
327 net::TestServer::SSLOptions ssl_options;
328 ssl_options.request_client_certificate = true;
329 ssl_options.client_authorities.push_back(
330 CertDirectory().AppendASCII("client_1_root.pem"));
331
332 ASSERT_TRUE(ConnectToTestServer(ssl_options));
333
334 base::FilePath certs_dir = net::GetTestCertsDirectory();
335 net::SSLConfig ssl_config = kDefaultSSLConfig;
336 ssl_config.send_client_cert = true;
337 ssl_config.client_cert = net::ImportCertFromFile(certs_dir,
338 "client_1.pem");
339
340 // This is required to ensure that signing works with the client
341 // certificate's private key.
342 net::OpenSSLPrivateKeyStore::ScopedEVP_PKEY client_private_key;
343 ASSERT_TRUE(LoadPrivateKeyOpenSSL(certs_dir.AppendASCII("client_1.key"),
344 &client_private_key));
345 EXPECT_TRUE(RecordPrivateKey(ssl_config, client_private_key.get()));
346
347 int rv;
348 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
349
350 EXPECT_EQ(net::OK, rv);
351 EXPECT_TRUE(sock_->IsConnected());
352
353 EXPECT_TRUE(CheckSSLClientSocketSentCert());
354
355 sock_->Disconnect();
356 EXPECT_FALSE(sock_->IsConnected());
357 }
358
359 // Connect to a server requesting client authentication. Send it a
360 // non-matching certificate. It should not allow the connection.
361 // NOTE: Disabled because our TestServer never verifies that the client
362 // certificate matches the required CA authorities. Thus is always
363 // accepts the connection.
364 TEST_F(SSLClientSocketOpenSSLClientAuthTest, DISABLED_SendBadCert) {
365 net::TestServer::SSLOptions ssl_options;
366 ssl_options.request_client_certificate = true;
367 ssl_options.client_authorities.push_back(
368 CertDirectory().AppendASCII("client_1_root.pem"));
369
370 ASSERT_TRUE(ConnectToTestServer(ssl_options));
371
372 base::FilePath certs_dir = net::GetTestCertsDirectory();
373 net::SSLConfig ssl_config = kDefaultSSLConfig;
374 ssl_config.send_client_cert = true;
375 ssl_config.client_cert = net::ImportCertFromFile(certs_dir,
376 "client_2.pem");
377
378 net::OpenSSLPrivateKeyStore::ScopedEVP_PKEY client_private_key;
379 ASSERT_TRUE(LoadPrivateKeyOpenSSL(certs_dir.AppendASCII("client_2.key"),
380 &client_private_key));
381 EXPECT_TRUE(RecordPrivateKey(ssl_config, client_private_key.get()));
382
383 int rv;
384 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
385
386 EXPECT_EQ(net::ERR_BAD_SSL_CLIENT_AUTH_CERT, rv);
387 EXPECT_FALSE(sock_->IsConnected());
388
389 EXPECT_TRUE(CheckSSLClientSocketSentCert());
390 }
391
392 // Connect to a server requesting client authentication. Send it a
393 // matching certificate, but do not sign with the right private key.
394 // It should not allow the connection.
Ryan Sleevi 2013/02/13 23:25:55 What is this really testing? BadSignature is a ser
digit1 2013/02/14 06:23:50 Ok, I'll remove this test, this gets rid of the pr
395 TEST_F(SSLClientSocketOpenSSLClientAuthTest, SendBadSignature) {
396 net::TestServer::SSLOptions ssl_options;
397 ssl_options.request_client_certificate = true;
398 ssl_options.client_authorities.push_back(
399 CertDirectory().AppendASCII("client_1_root.pem"));
400
401 ASSERT_TRUE(ConnectToTestServer(ssl_options));
402
403 base::FilePath certs_dir = net::GetTestCertsDirectory();
404 net::SSLConfig ssl_config = kDefaultSSLConfig;
405 ssl_config.send_client_cert = true;
406 ssl_config.client_cert = net::ImportCertFromFile(certs_dir,
407 "client_1.pem");
408
409 // Instead of the matching private key, generate a new random one.
410 // This one is specially crafted to bypass OpenSSL checks.
411 // IMPORTANT: Use a key size that matches the certificate public key's,
412 // otherwise the TestServer will abort violently with an exception
413 // during the verification phase.
414 ScopedEVP_PKEY client_private_key;
415 ASSERT_TRUE(CreateRandomPrivateKey(2048, &client_private_key));
416 EXPECT_TRUE(RecordPrivateKey(ssl_config, client_private_key.get()));
417
418 int rv;
419 ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
420
421 EXPECT_FALSE(sock_->IsConnected());
422 EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv);
423 EXPECT_TRUE(CheckSSLClientSocketSentCert());
424 }
OLDNEW
« net/data/ssl/certificates/client_2_root.pem ('K') | « net/socket/ssl_client_socket_openssl.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698