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

Side by Side Diff: net/android/keystore_unittest.cc

Issue 2291213002: Remove ENGINE indirection from Android SSLPrivateKey. (Closed)
Patch Set: Fix WebView build Created 4 years, 3 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) 2013 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 <openssl/bn.h>
6 #include <openssl/dsa.h>
7 #include <openssl/ecdsa.h>
8 #include <openssl/err.h>
9 #include <openssl/evp.h>
10 #include <openssl/pem.h>
11 #include <openssl/rsa.h>
12
13 #include "base/android/build_info.h"
14 #include "base/android/jni_android.h"
15 #include "base/android/jni_array.h"
16 #include "base/android/scoped_java_ref.h"
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/compiler_specific.h"
20 #include "base/files/file_path.h"
21 #include "base/files/file_util.h"
22 #include "base/files/scoped_file.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "crypto/auto_cbb.h"
26 #include "crypto/openssl_util.h"
27 #include "net/android/keystore.h"
28 #include "net/android/keystore_openssl.h"
29 #include "net/ssl/scoped_openssl_types.h"
30 #include "net/test/jni/AndroidKeyStoreTestUtil_jni.h"
31 #include "net/test/test_data_directory.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 // Technical note:
35 //
36 // This source file not only checks that signing with
37 // RawSignDigestWithPrivateKey() works correctly, it also verifies that
38 // the generated signature matches 100% of what OpenSSL generates when
39 // calling RSA_sign(NID_md5_sha1,...), DSA_sign(0, ...) or
40 // ECDSA_sign(0, ...).
41 //
42 // That's crucial to ensure that this function can later be used to
43 // implement client certificate support. More specifically, that it is
44 // possible to create a custom EVP_PKEY that uses
45 // RawSignDigestWithPrivateKey() internally to perform RSA/DSA/ECDSA
46 // signing, as invoked by the OpenSSL code at
47 // openssl/ssl/s3_clnt.c:ssl3_send_client_verify().
48 //
49 // For more details, read the comments in AndroidKeyStore.java.
50 //
51 // Finally, it also checks that using the EVP_PKEY generated with
52 // GetOpenSSLPrivateKeyWrapper() works correctly.
53
54 namespace net {
55 namespace android {
56
57 namespace {
58
59 typedef base::android::ScopedJavaLocalRef<jobject> ScopedJava;
60
61 // Returns true if running on an Android version older than 4.2
62 bool IsOnAndroidOlderThan_4_2(void) {
63 const int kAndroid42ApiLevel = 17;
64 int level = base::android::BuildInfo::GetInstance()->sdk_int();
65 return level < kAndroid42ApiLevel;
66 }
67
68 // Implements the callback expected by ERR_print_errors_cb().
69 // used by GetOpenSSLErrorString below.
70 int openssl_print_error_callback(const char* msg, size_t msglen, void* u) {
71 std::string* result = reinterpret_cast<std::string*>(u);
72 result->append(msg, msglen);
73 return 1;
74 }
75
76 // Retrieves the OpenSSL error as a string
77 std::string GetOpenSSLErrorString(void) {
78 std::string result;
79 ERR_print_errors_cb(openssl_print_error_callback, &result);
80 return result;
81 }
82
83 // Resize a string to |size| bytes of data, then return its data buffer
84 // address cast as an 'unsigned char*', as expected by OpenSSL functions.
85 // |str| the target string.
86 // |size| the number of bytes to write into the string.
87 // Return the string's new buffer in memory, as an 'unsigned char*'
88 // pointer.
89 unsigned char* OpenSSLWriteInto(std::string* str, size_t size) {
90 return reinterpret_cast<unsigned char*>(base::WriteInto(str, size + 1));
91 }
92
93 // Load a given private key file into an EVP_PKEY.
94 // |filename| is the key file path.
95 // Returns a new EVP_PKEY on success, NULL on failure.
96 EVP_PKEY* ImportPrivateKeyFile(const char* filename) {
97 // Load file in memory.
98 base::FilePath certs_dir = GetTestCertsDirectory();
99 base::FilePath file_path = certs_dir.AppendASCII(filename);
100 base::ScopedFILE handle(base::OpenFile(file_path, "rb"));
101 if (!handle.get()) {
102 LOG(ERROR) << "Could not open private key file: " << filename;
103 return NULL;
104 }
105 // Assume it is PEM_encoded. Load it as an EVP_PKEY.
106 EVP_PKEY* pkey = PEM_read_PrivateKey(handle.get(), NULL, NULL, NULL);
107 if (!pkey) {
108 LOG(ERROR) << "Could not load public key file: " << filename
109 << ", " << GetOpenSSLErrorString();
110 return NULL;
111 }
112 return pkey;
113 }
114
115 // Convert a private key into its PKCS#8 encoded representation.
116 // |pkey| is the EVP_PKEY handle for the private key.
117 // |pkcs8| will receive the PKCS#8 bytes.
118 // Returns true on success, false otherwise.
119 bool GetPrivateKeyPkcs8Bytes(const crypto::ScopedEVP_PKEY& pkey,
120 std::string* pkcs8) {
121 uint8_t* der;
122 size_t der_len;
123 crypto::AutoCBB cbb;
124 if (!CBB_init(cbb.get(), 0) ||
125 !EVP_marshal_private_key(cbb.get(), pkey.get()) ||
126 !CBB_finish(cbb.get(), &der, &der_len)) {
127 return false;
128 }
129 pkcs8->assign(reinterpret_cast<const char*>(der), der_len);
130 OPENSSL_free(der);
131 return true;
132 }
133
134 bool ImportPrivateKeyFileAsPkcs8(const char* filename,
135 std::string* pkcs8) {
136 crypto::ScopedEVP_PKEY pkey(ImportPrivateKeyFile(filename));
137 if (!pkey.get())
138 return false;
139 return GetPrivateKeyPkcs8Bytes(pkey, pkcs8);
140 }
141
142 // Same as ImportPrivateKey, but for public ones.
143 EVP_PKEY* ImportPublicKeyFile(const char* filename) {
144 // Load file as PEM data.
145 base::FilePath certs_dir = GetTestCertsDirectory();
146 base::FilePath file_path = certs_dir.AppendASCII(filename);
147 base::ScopedFILE handle(base::OpenFile(file_path, "rb"));
148 if (!handle.get()) {
149 LOG(ERROR) << "Could not open public key file: " << filename;
150 return NULL;
151 }
152 EVP_PKEY* pkey = PEM_read_PUBKEY(handle.get(), NULL, NULL, NULL);
153 if (!pkey) {
154 LOG(ERROR) << "Could not load public key file: " << filename
155 << ", " << GetOpenSSLErrorString();
156 return NULL;
157 }
158 return pkey;
159 }
160
161 // Retrieve a JNI local ref from encoded PKCS#8 data.
162 ScopedJava GetPKCS8PrivateKeyJava(PrivateKeyType key_type,
163 const std::string& pkcs8_key) {
164 JNIEnv* env = base::android::AttachCurrentThread();
165 base::android::ScopedJavaLocalRef<jbyteArray> bytes(
166 base::android::ToJavaByteArray(
167 env, reinterpret_cast<const uint8_t*>(pkcs8_key.data()),
168 pkcs8_key.size()));
169
170 ScopedJava key(Java_AndroidKeyStoreTestUtil_createPrivateKeyFromPKCS8(
171 env, key_type, bytes));
172
173 return key;
174 }
175
176 const char kTestRsaKeyFile[] = "android-test-key-rsa.pem";
177
178 // The RSA test hash must be 36 bytes exactly.
179 const char kTestRsaHash[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
180
181 // Retrieve a JNI local ref for our test RSA key.
182 ScopedJava GetRSATestKeyJava() {
183 std::string key;
184 if (!ImportPrivateKeyFileAsPkcs8(kTestRsaKeyFile, &key))
185 return ScopedJava();
186 return GetPKCS8PrivateKeyJava(PRIVATE_KEY_TYPE_RSA, key);
187 }
188
189 const char kTestEcdsaKeyFile[] = "android-test-key-ecdsa.pem";
190 const char kTestEcdsaPublicKeyFile[] = "android-test-key-ecdsa-public.pem";
191
192 // The test hash for ECDSA keys must be 20 bytes exactly.
193 const char kTestEcdsaHash[] = "0123456789ABCDEFGHIJ";
194
195 // Retrieve a JNI local ref for our test ECDSA key.
196 ScopedJava GetECDSATestKeyJava() {
197 std::string key;
198 if (!ImportPrivateKeyFileAsPkcs8(kTestEcdsaKeyFile, &key))
199 return ScopedJava();
200 return GetPKCS8PrivateKeyJava(PRIVATE_KEY_TYPE_ECDSA, key);
201 }
202
203 // Call this function to verify that one message signed with our
204 // test ECDSA private key is correct. Since ECDSA signing introduces
205 // random elements in the signature, it is not possible to compare
206 // signature bits directly. However, one can use the public key
207 // to do the check.
208 bool VerifyTestECDSASignature(const base::StringPiece& message,
209 const base::StringPiece& signature) {
210 crypto::ScopedEVP_PKEY pkey(ImportPublicKeyFile(kTestEcdsaPublicKeyFile));
211 if (!pkey.get())
212 return false;
213 crypto::ScopedEC_KEY pub_key(EVP_PKEY_get1_EC_KEY(pkey.get()));
214 if (!pub_key.get()) {
215 LOG(ERROR) << "Could not get ECDSA public key: "
216 << GetOpenSSLErrorString();
217 return false;
218 }
219
220 const unsigned char* digest =
221 reinterpret_cast<const unsigned char*>(message.data());
222 int digest_len = static_cast<int>(message.size());
223 const unsigned char* sigbuf =
224 reinterpret_cast<const unsigned char*>(signature.data());
225 int siglen = static_cast<int>(signature.size());
226
227 int ret = ECDSA_verify(
228 0, digest, digest_len, sigbuf, siglen, pub_key.get());
229 if (ret != 1) {
230 LOG(ERROR) << "ECDSA_verify() failed: " << GetOpenSSLErrorString();
231 return false;
232 }
233 return true;
234 }
235
236 // Sign a message with OpenSSL, return the result as a string.
237 // |message| is the message to be signed.
238 // |openssl_key| is an OpenSSL EVP_PKEY to use.
239 // |result| receives the result.
240 // Returns true on success, false otherwise.
241 bool SignWithOpenSSL(const base::StringPiece& message,
242 EVP_PKEY* openssl_key,
243 std::string* result) {
244 const unsigned char* digest =
245 reinterpret_cast<const unsigned char*>(message.data());
246 unsigned int digest_len = static_cast<unsigned int>(message.size());
247 std::string signature;
248 size_t signature_size;
249 size_t max_signature_size;
250 int key_type = EVP_PKEY_id(openssl_key);
251 switch (key_type) {
252 case EVP_PKEY_RSA:
253 {
254 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(openssl_key));
255 if (!rsa.get()) {
256 LOG(ERROR) << "Could not get RSA from EVP_PKEY: "
257 << GetOpenSSLErrorString();
258 return false;
259 }
260 // With RSA, the signature will always be RSA_size() bytes.
261 max_signature_size = static_cast<size_t>(RSA_size(rsa.get()));
262 unsigned char* p = OpenSSLWriteInto(&signature,
263 max_signature_size);
264 unsigned int p_len = 0;
265 int ret = RSA_sign(
266 NID_md5_sha1, digest, digest_len, p, &p_len, rsa.get());
267 if (ret != 1) {
268 LOG(ERROR) << "RSA_sign() failed: " << GetOpenSSLErrorString();
269 return false;
270 }
271 signature_size = static_cast<size_t>(p_len);
272 break;
273 }
274 case EVP_PKEY_EC:
275 {
276 crypto::ScopedEC_KEY ecdsa(EVP_PKEY_get1_EC_KEY(openssl_key));
277 if (!ecdsa.get()) {
278 LOG(ERROR) << "Could not get EC_KEY from EVP_PKEY: "
279 << GetOpenSSLErrorString();
280 return false;
281 }
282 // Note, the actual signature can be smaller than ECDSA_size()
283 max_signature_size = ECDSA_size(ecdsa.get());
284 unsigned char* p = OpenSSLWriteInto(&signature,
285 max_signature_size);
286 unsigned int p_len = 0;
287 // Note: first parameter is ignored by function.
288 int ret = ECDSA_sign(
289 0, digest, digest_len, p, &p_len, ecdsa.get());
290 if (ret != 1) {
291 LOG(ERROR) << "ECDSA_sign() fialed: " << GetOpenSSLErrorString();
292 return false;
293 }
294 signature_size = static_cast<size_t>(p_len);
295 break;
296 }
297 default:
298 LOG(WARNING) << "Invalid OpenSSL key type: " << key_type;
299 return false;
300 }
301
302 if (signature_size == 0) {
303 LOG(ERROR) << "Signature is empty!";
304 return false;
305 }
306 if (signature_size > max_signature_size) {
307 LOG(ERROR) << "Signature size mismatch, actual " << signature_size
308 << ", expected <= " << max_signature_size;
309 return false;
310 }
311 signature.resize(signature_size);
312 result->swap(signature);
313 return true;
314 }
315
316 // Check that a generated signature for a given message matches
317 // OpenSSL output byte-by-byte.
318 // |message| is the input message.
319 // |signature| is the generated signature for the message.
320 // |openssl_key| is a raw EVP_PKEY for the same private key than the
321 // one which was used to generate the signature.
322 // Returns true on success, false otherwise.
323 bool CompareSignatureWithOpenSSL(const base::StringPiece& message,
324 const base::StringPiece& signature,
325 EVP_PKEY* openssl_key) {
326 std::string openssl_signature;
327 SignWithOpenSSL(message, openssl_key, &openssl_signature);
328
329 if (signature.size() != openssl_signature.size()) {
330 LOG(ERROR) << "Signature size mismatch, actual "
331 << signature.size() << ", expected "
332 << openssl_signature.size();
333 return false;
334 }
335 for (size_t n = 0; n < signature.size(); ++n) {
336 if (openssl_signature[n] != signature[n]) {
337 LOG(ERROR) << "Signature byte mismatch at index " << n
338 << "actual " << signature[n] << ", expected "
339 << openssl_signature[n];
340 LOG(ERROR) << "Actual signature : "
341 << base::HexEncode(signature.data(), signature.size());
342 LOG(ERROR) << "Expected signature: "
343 << base::HexEncode(openssl_signature.data(),
344 openssl_signature.size());
345 return false;
346 }
347 }
348 return true;
349 }
350
351 // Sign a message with our platform API.
352 //
353 // |android_key| is a JNI reference to the platform PrivateKey object.
354 // |openssl_key| is a pointer to an OpenSSL key object for the exact
355 // same key content.
356 // |message| is a message.
357 // |result| will receive the result.
358 void DoKeySigning(jobject android_key,
359 EVP_PKEY* openssl_key,
360 const base::StringPiece& message,
361 std::string* result) {
362 // First, get the platform signature.
363 std::vector<uint8_t> android_signature;
364 ASSERT_TRUE(
365 RawSignDigestWithPrivateKey(android_key,
366 message,
367 &android_signature));
368
369 result->assign(
370 reinterpret_cast<const char*>(&android_signature[0]),
371 android_signature.size());
372 }
373
374 // Sign a message with our OpenSSL EVP_PKEY wrapper around platform
375 // APIS.
376 //
377 // |android_key| is a JNI reference to the platform PrivateKey object.
378 // |openssl_key| is a pointer to an OpenSSL key object for the exact
379 // same key content.
380 // |message| is a message.
381 // |result| will receive the result.
382 void DoKeySigningWithWrapper(EVP_PKEY* wrapper_key,
383 EVP_PKEY* openssl_key,
384 const base::StringPiece& message,
385 std::string* result) {
386 // First, get the platform signature.
387 std::string wrapper_signature;
388 SignWithOpenSSL(message, wrapper_key, &wrapper_signature);
389 ASSERT_NE(0U, wrapper_signature.size());
390
391 result->assign(
392 reinterpret_cast<const char*>(&wrapper_signature[0]),
393 wrapper_signature.size());
394 }
395
396 } // namespace
397
398 TEST(AndroidKeyStore, GetRSAKeyModulus) {
399 crypto::OpenSSLErrStackTracer err_trace(FROM_HERE);
400
401 // Load the test RSA key.
402 crypto::ScopedEVP_PKEY pkey(ImportPrivateKeyFile(kTestRsaKeyFile));
403 ASSERT_TRUE(pkey.get());
404
405 // Convert it to encoded PKCS#8 bytes.
406 std::string pkcs8_data;
407 ASSERT_TRUE(GetPrivateKeyPkcs8Bytes(pkey, &pkcs8_data));
408
409 // Create platform PrivateKey object from it.
410 ScopedJava key_java = GetPKCS8PrivateKeyJava(PRIVATE_KEY_TYPE_RSA,
411 pkcs8_data);
412 ASSERT_FALSE(key_java.is_null());
413
414 // Retrieve the corresponding modulus through JNI
415 std::vector<uint8_t> modulus_java;
416 ASSERT_TRUE(GetRSAKeyModulus(key_java.obj(), &modulus_java));
417
418 // Create an OpenSSL BIGNUM from it.
419 crypto::ScopedBIGNUM bn(
420 BN_bin2bn(reinterpret_cast<const unsigned char*>(&modulus_java[0]),
421 static_cast<int>(modulus_java.size()),
422 NULL));
423 ASSERT_TRUE(bn.get());
424
425 // Compare it to the one in the RSA key, they must be identical.
426 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey.get()));
427 ASSERT_TRUE(rsa.get()) << GetOpenSSLErrorString();
428
429 ASSERT_EQ(0, BN_cmp(bn.get(), rsa.get()->n));
430 }
431
432 TEST(AndroidKeyStore,GetPrivateKeyTypeRSA) {
433 crypto::OpenSSLErrStackTracer err_trace(FROM_HERE);
434
435 ScopedJava rsa_key = GetRSATestKeyJava();
436 ASSERT_FALSE(rsa_key.is_null());
437 EXPECT_EQ(PRIVATE_KEY_TYPE_RSA,
438 GetPrivateKeyType(rsa_key.obj()));
439 }
440
441 TEST(AndroidKeyStore,SignWithPrivateKeyRSA) {
442 ScopedJava rsa_key = GetRSATestKeyJava();
443 ASSERT_FALSE(rsa_key.is_null());
444
445 if (IsOnAndroidOlderThan_4_2()) {
446 LOG(INFO) << "This test can't run on Android < 4.2";
447 return;
448 }
449
450 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestRsaKeyFile));
451 ASSERT_TRUE(openssl_key.get());
452
453 std::string message = kTestRsaHash;
454 ASSERT_EQ(36U, message.size());
455
456 std::string signature;
457 DoKeySigning(rsa_key.obj(), openssl_key.get(), message, &signature);
458 ASSERT_TRUE(
459 CompareSignatureWithOpenSSL(message, signature, openssl_key.get()));
460 // All good.
461 }
462
463 TEST(AndroidKeyStore,SignWithWrapperKeyRSA) {
464 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
465
466 ScopedJava rsa_key = GetRSATestKeyJava();
467 ASSERT_FALSE(rsa_key.is_null());
468
469 crypto::ScopedEVP_PKEY wrapper_key(
470 GetOpenSSLPrivateKeyWrapper(rsa_key.obj()));
471 ASSERT_TRUE(wrapper_key.get() != NULL);
472
473 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestRsaKeyFile));
474 ASSERT_TRUE(openssl_key.get());
475
476 // Check that RSA_size() works properly on the wrapper key.
477 EXPECT_EQ(EVP_PKEY_size(openssl_key.get()),
478 EVP_PKEY_size(wrapper_key.get()));
479
480 // Message size must be 36 for RSA_sign(NID_md5_sha1,...) to return
481 // without an error.
482 std::string message = kTestRsaHash;
483 ASSERT_EQ(36U, message.size());
484
485 std::string signature;
486 DoKeySigningWithWrapper(wrapper_key.get(),
487 openssl_key.get(),
488 message,
489 &signature);
490 ASSERT_TRUE(
491 CompareSignatureWithOpenSSL(message, signature, openssl_key.get()));
492 }
493
494 TEST(AndroidKeyStore,GetPrivateKeyTypeECDSA) {
495 crypto::OpenSSLErrStackTracer err_trace(FROM_HERE);
496
497 ScopedJava ecdsa_key = GetECDSATestKeyJava();
498 ASSERT_FALSE(ecdsa_key.is_null());
499 EXPECT_EQ(PRIVATE_KEY_TYPE_ECDSA,
500 GetPrivateKeyType(ecdsa_key.obj()));
501 }
502
503 TEST(AndroidKeyStore,SignWithPrivateKeyECDSA) {
504 ScopedJava ecdsa_key = GetECDSATestKeyJava();
505 ASSERT_FALSE(ecdsa_key.is_null());
506
507 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestEcdsaKeyFile));
508 ASSERT_TRUE(openssl_key.get());
509
510 std::string message = kTestEcdsaHash;
511 std::string signature;
512 DoKeySigning(ecdsa_key.obj(), openssl_key.get(), message, &signature);
513 ASSERT_TRUE(VerifyTestECDSASignature(message, signature));
514 }
515
516 TEST(AndroidKeyStore, SignWithWrapperKeyECDSA) {
517 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
518
519 ScopedJava ecdsa_key = GetECDSATestKeyJava();
520 ASSERT_FALSE(ecdsa_key.is_null());
521
522 crypto::ScopedEVP_PKEY wrapper_key(
523 GetOpenSSLPrivateKeyWrapper(ecdsa_key.obj()));
524 ASSERT_TRUE(wrapper_key.get());
525
526 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestEcdsaKeyFile));
527 ASSERT_TRUE(openssl_key.get());
528
529 // Check that ECDSA size works correctly on the wrapper.
530 EXPECT_EQ(EVP_PKEY_size(openssl_key.get()),
531 EVP_PKEY_size(wrapper_key.get()));
532
533 std::string message = kTestEcdsaHash;
534 std::string signature;
535 DoKeySigningWithWrapper(wrapper_key.get(),
536 openssl_key.get(),
537 message,
538 &signature);
539 ASSERT_TRUE(VerifyTestECDSASignature(message, signature));
540 }
541
542 } // namespace android
543 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698