ttyd uses client cert

Bug: 363235314
Test: terminal app can show ttyd page, but the others cannot access to
the page

Change-Id: I60e4249f0170caa54e6c6777e9fc03cff176541c
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index d91af2f..932ca76 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -9,6 +9,7 @@
         "java/**/*.kt",
     ],
     resource_dirs: ["res"],
+    asset_dirs: ["assets"],
     static_libs: [
         "vm_launcher_lib",
         "androidx-constraintlayout_constraintlayout",
diff --git a/android/TerminalApp/assets/client.p12 b/android/TerminalApp/assets/client.p12
new file mode 100644
index 0000000..f1f5820
--- /dev/null
+++ b/android/TerminalApp/assets/client.p12
Binary files differ
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 1fabf8d..3aca929 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -19,6 +19,7 @@
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.Intent;
+import android.net.http.SslError;
 import android.os.Bundle;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -27,6 +28,8 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
+import android.webkit.ClientCertRequest;
+import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebResourceError;
 import android.webkit.WebResourceRequest;
@@ -44,11 +47,17 @@
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.RandomAccessFile;
 import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.UnknownHostException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
 
 public class MainActivity extends AppCompatActivity
         implements VmLauncherServices.VmLauncherServiceCallback,
@@ -57,6 +66,8 @@
     private static final String TAG = "VmTerminalApp";
     private static final String VM_ADDR = "192.168.0.2";
     private static final int TTYD_PORT = 7681;
+    private X509Certificate[] mCertificates;
+    private PrivateKey mPrivateKey;
     private WebView mWebView;
     private AccessibilityManager mAccessibilityManager;
 
@@ -92,6 +103,7 @@
         mAccessibilityManager.addTouchExplorationStateChangeListener(this);
 
         connectToTerminalService();
+        readClientCertificate();
     }
 
     private URL getTerminalServiceUrl() {
@@ -100,13 +112,35 @@
         String query = needsAccessibility ? "?screenReaderMode=true" : "";
 
         try {
-            return new URL("http", VM_ADDR, TTYD_PORT, file + query);
+            return new URL("https", VM_ADDR, TTYD_PORT, file + query);
         } catch (MalformedURLException e) {
             // this cannot happen
             return null;
         }
     }
 
+    private void readClientCertificate() {
+        // TODO(b/363235314): instead of using the key in asset, it should be generated in runtime
+        // and then provisioned in the vm via virtio-fs
+        try (InputStream keystoreFileStream =
+                getClass().getResourceAsStream("/assets/client.p12")) {
+            KeyStore keyStore = KeyStore.getInstance("PKCS12");
+            String password = "1234";
+            String alias = "1";
+
+            keyStore.load(keystoreFileStream, password != null ? password.toCharArray() : null);
+            Key key = keyStore.getKey(alias, password.toCharArray());
+            if (key instanceof PrivateKey) {
+                mPrivateKey = (PrivateKey) key;
+                Certificate cert = keyStore.getCertificate(alias);
+                mCertificates = new X509Certificate[1];
+                mCertificates[0] = (X509Certificate) cert;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, e.getMessage());
+        }
+    }
+
     private void connectToTerminalService() {
         Log.i(TAG, "URL=" + getTerminalServiceUrl().toString());
         mWebView.setWebViewClient(
@@ -146,6 +180,23 @@
                             view.setVisibility(View.VISIBLE);
                         }
                     }
+
+                    @Override
+                    public void onReceivedClientCertRequest(
+                            WebView view, ClientCertRequest request) {
+                        if (mPrivateKey != null && mCertificates != null) {
+                            request.proceed(mPrivateKey, mCertificates);
+                            return;
+                        }
+                        super.onReceivedClientCertRequest(view, request);
+                    }
+
+                    @Override
+                    public void onReceivedSslError(
+                            WebView view, SslErrorHandler handler, SslError error) {
+                        // ttyd uses self-signed certificate
+                        handler.proceed();
+                    }
                 });
         new Thread(
                         () -> {