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: components/cronet/android/api/src/org/chromium/net/CronetEngine.java

Issue 1407263010: [Cronet] Public key pinning for Java API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Small changes and rebase Created 5 years 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.net; 5 package org.chromium.net;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.support.annotation.IntDef; 8 import android.support.annotation.IntDef;
9 import android.util.Base64;
9 import android.util.Log; 10 import android.util.Log;
10 11
11 import org.json.JSONArray; 12 import org.json.JSONArray;
12 import org.json.JSONException; 13 import org.json.JSONException;
13 import org.json.JSONObject; 14 import org.json.JSONObject;
14 15
15 import java.io.File; 16 import java.io.File;
16 import java.lang.annotation.Retention; 17 import java.lang.annotation.Retention;
17 import java.lang.annotation.RetentionPolicy; 18 import java.lang.annotation.RetentionPolicy;
18 import java.lang.reflect.Constructor; 19 import java.lang.reflect.Constructor;
20 import java.net.IDN;
19 import java.net.Proxy; 21 import java.net.Proxy;
20 import java.net.URL; 22 import java.net.URL;
21 import java.net.URLConnection; 23 import java.net.URLConnection;
22 import java.net.URLStreamHandlerFactory; 24 import java.net.URLStreamHandlerFactory;
25 import java.util.Date;
26 import java.util.HashSet;
23 import java.util.List; 27 import java.util.List;
24 import java.util.Map; 28 import java.util.Map;
29 import java.util.Set;
25 import java.util.concurrent.Executor; 30 import java.util.concurrent.Executor;
31 import java.util.regex.Pattern;
26 32
27 /** 33 /**
28 * An engine to process {@link UrlRequest}s, which uses the best HTTP stack 34 * An engine to process {@link UrlRequest}s, which uses the best HTTP stack
29 * available on the current platform. 35 * available on the current platform.
30 */ 36 */
31 public abstract class CronetEngine { 37 public abstract class CronetEngine {
32 /** 38 /**
33 * A builder for {@link CronetEngine}s, which allows runtime configuration o f 39 * A builder for {@link CronetEngine}s, which allows runtime configuration o f
34 * {@code CronetEngine}. Configuration options are set on the builder and 40 * {@code CronetEngine}. Configuration options are set on the builder and
35 * then {@link #build} is called to create the {@code CronetEngine}. 41 * then {@link #build} is called to create the {@code CronetEngine}.
36 */ 42 */
37 public static class Builder { 43 public static class Builder {
44 private static final Pattern INVALID_PKP_HOST_NAME = Pattern.compile("^[ 0-9\\.]*$");
45
38 private final JSONObject mConfig; 46 private final JSONObject mConfig;
39 private final Context mContext; 47 private final Context mContext;
40 48
41 /** 49 /**
42 * Default config enables SPDY, disables QUIC, SDCH and HTTP cache. 50 * Default config enables SPDY, disables QUIC, SDCH and HTTP cache.
43 * @param context Android {@link Context} for engine to use. 51 * @param context Android {@link Context} for engine to use.
44 */ 52 */
45 public Builder(Context context) { 53 public Builder(Context context) {
46 mConfig = new JSONObject(); 54 mConfig = new JSONObject();
47 mContext = context; 55 mContext = context;
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after
296 hint.put(CronetEngineBuilderList.QUIC_HINT_PORT, port); 304 hint.put(CronetEngineBuilderList.QUIC_HINT_PORT, port);
297 hint.put(CronetEngineBuilderList.QUIC_HINT_ALT_PORT, alternatePo rt); 305 hint.put(CronetEngineBuilderList.QUIC_HINT_ALT_PORT, alternatePo rt);
298 quicHints.put(hint); 306 quicHints.put(hint);
299 } catch (JSONException e) { 307 } catch (JSONException e) {
300 // Intentionally do nothing. 308 // Intentionally do nothing.
301 } 309 }
302 return this; 310 return this;
303 } 311 }
304 312
305 /** 313 /**
314 * <p>
315 * Pins a set of public keys for a given host. By pinning a set of publi c keys,
316 * {@code pinsSha256}, communication with {@code hostName} is required t o
317 * authenticate with a certificate with a public key from the set of pin ned ones.
318 * An app can pin the public key of the root certificate, any of the int ermediate
319 * certificates or the end-entry certificate. Authentication will fail a nd secure
320 * communication will not be established if none of the public keys is p resent in the
321 * host's certificate chain, even if the host attempts to authenticate w ith a
322 * certificate allowed by the device's trusted store of certificates.
323 * </p>
324 * <p>
325 * Calling this method multiple times with the same host name overrides the previously
326 * set pins for the host.
327 * </p>
328 * <p>
329 * More information about the public key pinning can be found in
330 * <a href="https://tools.ietf.org/html/rfc7469">RFC 7469</a>.
331 * </p>
332 *
333 * @param hostName name of the host to which the public keys should be p inned. A host that
334 * consists only of digits and the dot character is trea ted as invalid.
335 * @param pinsSha256 a set of pins. Each pin is the SHA-256 cryptographi c
336 * hash of the DER-encoded ASN.1 representation of the Subject Public
337 * Key Info (SPKI) of the host's X.509 certificate. Us e
338 * {@link java.security.cert.Certificate#getPublicKey( )
339 * Certificate.getPublicKey()} and
340 * {@link java.security.Key#getEncoded() Key.getEncode d()}
341 * to obtain DER-encoded ASN.1 representation of the S PKI.
342 * Although, the method does not mandate the presence of the backup pin
343 * that can be used if the control of the primary priv ate key has been
344 * lost, it is highly recommended to supply one.
345 * @param includeSubdomains indicates whether the pinning policy should be applied to
346 * subdomains of {@code hostName}.
347 * @param expirationDate specifies the expiration date for the pins.
348 * @return the builder to facilitate chaining.
349 * @throws NullPointerException if any of the input parameters are {@cod e null}.
350 * @throws IllegalArgumentException if the given host name is invalid or {@code pinsSha256}
351 * contains a byte array that does not represent a valid
352 * SHA-256 hash.
353 */
354 public Builder addPublicKeyPins(String hostName, Set<byte[]> pinsSha256,
355 boolean includeSubdomains, Date expirationDate) {
356 if (hostName == null) {
357 throw new NullPointerException("The hostname cannot be null");
358 }
359 if (pinsSha256 == null) {
360 throw new NullPointerException("The set of SHA256 pins cannot be null");
361 }
362 if (expirationDate == null) {
363 throw new NullPointerException("The pin expiration date cannot b e null");
364 }
365 String idnHostName = validateHostNameForPinningAndConvert(hostName);
366 try {
367 // Add PKP_LIST JSON array element if it is not present.
368 JSONArray pkpList = mConfig.optJSONArray(CronetEngineBuilderList .PKP_LIST);
369 if (pkpList == null) {
370 pkpList = new JSONArray();
371 mConfig.put(CronetEngineBuilderList.PKP_LIST, pkpList);
372 }
373
374 // Convert the pin to BASE64 encoding. The hash set will elimina te duplications.
375 Set<String> hashes = new HashSet<>(pinsSha256.size());
376 for (byte[] pinSha256 : pinsSha256) {
377 hashes.add(convertSha256ToBase64WithPrefix(pinSha256));
378 }
379
380 // Add new element to PKP_LIST JSON array.
381 JSONObject pkp = new JSONObject();
382 pkp.put(CronetEngineBuilderList.PKP_HOST, idnHostName);
383 pkp.put(CronetEngineBuilderList.PKP_PIN_HASHES, new JSONArray(ha shes));
384 pkp.put(CronetEngineBuilderList.PKP_INCLUDE_SUBDOMAINS, includeS ubdomains);
385 // The expiration time is passed as a double, in seconds since J anuary 1, 1970.
386 pkp.put(CronetEngineBuilderList.PKP_EXPIRATION_DATE,
387 (double) expirationDate.getTime() / 1000);
388 pkpList.put(pkp);
389 } catch (JSONException e) {
390 // This exception should never happen.
391 throw new RuntimeException(
392 "Failed to add pubic key pins with the given arguments", e);
393 }
394 return this;
395 }
396
397 /**
398 * Converts a given SHA256 array of bytes to BASE64 encoded string and p repends
399 * {@code sha256/} prefix to it. The format corresponds to the format th at is expected by
400 * {@code net::HashValue} class.
401 *
402 * @param sha256 SHA256 bytes to convert to BASE64.
403 * @return the BASE64 encoded SHA256 with the prefix.
404 * @throws IllegalArgumentException if the provided pin is invalid.
405 */
406 private static String convertSha256ToBase64WithPrefix(byte[] sha256) {
407 if (sha256 == null || sha256.length != 32) {
408 throw new IllegalArgumentException("Public key pin is invalid");
409 }
410 return "sha256/" + Base64.encodeToString(sha256, Base64.NO_WRAP);
411 }
412
413 /**
414 * Checks whether a given string represents a valid host name for PKP an d converts it
415 * to ASCII Compatible Encoding representation according to RFC 1122, RF C 1123 and
416 * RFC 3490. This method is more restrictive than required by RFC 7469. Thus, a host
417 * that contains digits and the dot character only is considered invalid .
418 *
419 * Note: Currently Cronet doesn't have native implementation of host nam e validation that
420 * can be used. There is code that parses a provided URL but doesn 't ensure its
421 * correctness. The implementation relies on {@code getaddrinfo} f unction.
422 *
423 * @param hostName host name to check and convert.
424 * @return true if the string is a valid host name.
425 * @throws IllegalArgumentException if the the given string does not rep resent a valid
426 * hostname.
427 */
428 private static String validateHostNameForPinningAndConvert(String hostNa me)
429 throws IllegalArgumentException {
430 if (INVALID_PKP_HOST_NAME.matcher(hostName).matches()) {
431 throw new IllegalArgumentException("Hostname " + hostName + " is illegal."
432 + " A hostname should not consist of digits and/or dots only.");
433 }
434 try {
435 return IDN.toASCII(hostName, IDN.USE_STD3_ASCII_RULES);
436 } catch (IllegalArgumentException ex) {
437 throw new IllegalArgumentException("Hostname " + hostName + " is illegal."
438 + " The name of the host does not comply with RFC 1122 a nd RFC 1123.");
439 }
440 }
441
442 /**
306 * Sets experimental options to be used in Cronet. 443 * Sets experimental options to be used in Cronet.
307 * 444 *
308 * @param options JSON formatted experimental options. 445 * @param options JSON formatted experimental options.
309 * @return the builder to facilitate chaining. 446 * @return the builder to facilitate chaining.
310 */ 447 */
311 public Builder setExperimentalOptions(String options) { 448 public Builder setExperimentalOptions(String options) {
312 return putString(CronetEngineBuilderList.EXPERIMENTAL_OPTIONS, optio ns); 449 return putString(CronetEngineBuilderList.EXPERIMENTAL_OPTIONS, optio ns);
313 } 450 }
314 451
315 /** 452 /**
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after
690 cronetEngine = possibleEngine; 827 cronetEngine = possibleEngine;
691 } 828 }
692 } catch (ClassNotFoundException e) { 829 } catch (ClassNotFoundException e) {
693 // Leave as null. 830 // Leave as null.
694 } catch (Exception e) { 831 } catch (Exception e) {
695 throw new IllegalStateException("Cannot instantiate: " + CRONET_URL_ REQUEST_CONTEXT, e); 832 throw new IllegalStateException("Cannot instantiate: " + CRONET_URL_ REQUEST_CONTEXT, e);
696 } 833 }
697 return cronetEngine; 834 return cronetEngine;
698 } 835 }
699 } 836 }
OLDNEW
« no previous file with comments | « components/cronet/android/api/package-list ('k') | components/cronet/android/cronet_url_request_context_adapter.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698