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

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

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

Powered by Google App Engine
This is Rietveld 408576698