| // Copyright 2016 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. |
| |
| package org.chromium.android_webview; |
| |
| import android.net.Uri; |
| import android.webkit.ValueCallback; |
| |
| import org.chromium.base.Log; |
| import org.chromium.base.annotations.CalledByNative; |
| import org.chromium.base.annotations.JNINamespace; |
| |
| import java.security.KeyFactory; |
| import java.security.KeyPair; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PrivateKey; |
| import java.security.PublicKey; |
| import java.security.spec.InvalidKeySpecException; |
| import java.security.spec.PKCS8EncodedKeySpec; |
| import java.security.spec.X509EncodedKeySpec; |
| |
| /** |
| * AwTokenBindingManager manages the token binding protocol. |
| * |
| * see https://tools.ietf.org/html/draft-ietf-tokbind-protocol-03 |
| * |
| * The token binding protocol can be enabled for browser context |
| * separately. However all webviews share the same browser context and do not |
| * expose the browser context to the embedder app. As such, there is no way to |
| * enable token binding manager for individual webviews. |
| */ |
| @JNINamespace("android_webview") |
| public final class AwTokenBindingManager { |
| private static final String TAG = "TokenBindingManager"; |
| private static final String ELLIPTIC_CURVE = "EC"; |
| |
| public void enableTokenBinding() { |
| nativeEnableTokenBinding(); |
| } |
| |
| public void getKey(Uri origin, String[] spec, ValueCallback<KeyPair> callback) { |
| if (callback == null) { |
| throw new IllegalArgumentException("callback can't be null"); |
| } |
| nativeGetTokenBindingKey(origin.getHost(), callback); |
| } |
| |
| public void deleteKey(Uri origin, ValueCallback<Boolean> callback) { |
| if (origin == null) { |
| throw new IllegalArgumentException("origin can't be null"); |
| } |
| // null callback is allowed |
| nativeDeleteTokenBindingKey(origin.getHost(), callback); |
| } |
| |
| public void deleteAllKeys(ValueCallback<Boolean> callback) { |
| // null callback is allowed |
| nativeDeleteAllTokenBindingKeys(callback); |
| } |
| |
| @CalledByNative |
| private static void onKeyReady( |
| ValueCallback<KeyPair> callback, byte[] privateKeyBytes, byte[] publicKeyBytes) { |
| if (privateKeyBytes == null || publicKeyBytes == null) { |
| callback.onReceiveValue(null); |
| return; |
| } |
| KeyPair keyPair = null; |
| try { |
| KeyFactory factory = KeyFactory.getInstance(ELLIPTIC_CURVE); |
| PrivateKey privateKey = |
| factory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); |
| PublicKey publicKey = factory.generatePublic(new X509EncodedKeySpec(publicKeyBytes)); |
| keyPair = new KeyPair(publicKey, privateKey); |
| } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { |
| Log.e(TAG, "Failed converting key ", ex); |
| } |
| callback.onReceiveValue(keyPair); |
| } |
| |
| @CalledByNative |
| private static void onDeletionComplete(ValueCallback<Boolean> callback) { |
| // At present, the native deletion complete callback always succeeds. |
| callback.onReceiveValue(true); |
| } |
| |
| private native void nativeEnableTokenBinding(); |
| private native void nativeGetTokenBindingKey(String host, ValueCallback<KeyPair> callback); |
| private native void nativeDeleteTokenBindingKey(String host, ValueCallback<Boolean> callback); |
| private native void nativeDeleteAllTokenBindingKeys(ValueCallback<Boolean> callback); |
| } |