Adds SSLCertificateVerifier to the Java API.

The native API supports setting an SSLCertificateVerifier that can be used
to provide a custom certificate verifier for incoming SSL certificates. This
change provides this functionality to the Java API so that a Java implementation
can also be provided. It is expected this will only be used in specialized
circumstances and most users will not hit this code path.

Bug: webrtc:9541
Change-Id: Id3c75b8f288333b53edc2959bac533e3ec614978
Reviewed-on: https://webrtc-review.googlesource.com/89500
Commit-Queue: Benjamin Wright <benwright@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24057}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index e6ad0da..fb08dd1 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -492,6 +492,7 @@
     "api/org/webrtc/RtpReceiver.java",
     "api/org/webrtc/RtpSender.java",
     "api/org/webrtc/RtpTransceiver.java",
+    "api/org/webrtc/SSLCertificateVerifier.java",
     "api/org/webrtc/SdpObserver.java",
     "api/org/webrtc/SessionDescription.java",
     "api/org/webrtc/StatsObserver.java",
@@ -578,6 +579,8 @@
     "src/jni/pc/sdpobserver.h",
     "src/jni/pc/sessiondescription.cc",
     "src/jni/pc/sessiondescription.h",
+    "src/jni/pc/sslcertificateverifierwrapper.cc",
+    "src/jni/pc/sslcertificateverifierwrapper.h",
     "src/jni/pc/statsobserver.cc",
     "src/jni/pc/statsobserver.h",
     "src/jni/pc/turncustomizer.cc",
@@ -990,6 +993,7 @@
     "api/org/webrtc/RtpReceiver.java",
     "api/org/webrtc/RtpSender.java",
     "api/org/webrtc/RtpTransceiver.java",
+    "api/org/webrtc/SSLCertificateVerifier.java",
     "api/org/webrtc/SdpObserver.java",
     "api/org/webrtc/SessionDescription.java",
     "api/org/webrtc/StatsObserver.java",
diff --git a/sdk/android/api/org/webrtc/PeerConnectionDependencies.java b/sdk/android/api/org/webrtc/PeerConnectionDependencies.java
index 8ecf0ff..fd765b1 100644
--- a/sdk/android/api/org/webrtc/PeerConnectionDependencies.java
+++ b/sdk/android/api/org/webrtc/PeerConnectionDependencies.java
@@ -20,18 +20,27 @@
  */
 public final class PeerConnectionDependencies {
   // Mandatory dependencies.
-  private PeerConnection.Observer observer;
+  private final PeerConnection.Observer observer;
+
+  // Optional fields.
+  private final SSLCertificateVerifier sslCertificateVerifier;
 
   public static class Builder {
     private PeerConnection.Observer observer;
+    private SSLCertificateVerifier sslCertificateVerifier;
 
     private Builder(PeerConnection.Observer observer) {
       this.observer = observer;
     }
 
+    public Builder setSSLCertificateVerifier(SSLCertificateVerifier sslCertificateVerifier) {
+      this.sslCertificateVerifier = sslCertificateVerifier;
+      return this;
+    }
+
     // Observer is a required dependency and so is forced in the construction of the object.
     public PeerConnectionDependencies createPeerConnectionDependencies() {
-      return new PeerConnectionDependencies(observer);
+      return new PeerConnectionDependencies(observer, sslCertificateVerifier);
     }
   }
 
@@ -43,7 +52,14 @@
     return observer;
   }
 
-  private PeerConnectionDependencies(PeerConnection.Observer observer) {
+  @Nullable
+  SSLCertificateVerifier getSSLCertificateVerifier() {
+    return sslCertificateVerifier;
+  }
+
+  private PeerConnectionDependencies(
+      PeerConnection.Observer observer, SSLCertificateVerifier sslCertificateVerifier) {
     this.observer = observer;
+    this.sslCertificateVerifier = sslCertificateVerifier;
   }
 }
diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
index e4e881f..3a76d7c 100644
--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java
+++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
@@ -329,6 +329,25 @@
   }
 
   /**
+   * Internal helper function to pass the parameters down into the native JNI bridge.
+   */
+  @Nullable
+  PeerConnection createPeerConnectionInternal(PeerConnection.RTCConfiguration rtcConfig,
+      MediaConstraints constraints, PeerConnection.Observer observer,
+      SSLCertificateVerifier sslCertificateVerifier) {
+    long nativeObserver = PeerConnection.createNativePeerConnectionObserver(observer);
+    if (nativeObserver == 0) {
+      return null;
+    }
+    long nativePeerConnection = nativeCreatePeerConnection(
+        nativeFactory, rtcConfig, constraints, nativeObserver, sslCertificateVerifier);
+    if (nativePeerConnection == 0) {
+      return null;
+    }
+    return new PeerConnection(nativePeerConnection);
+  }
+
+  /**
    * Deprecated. PeerConnection constraints are deprecated. Supply values in rtcConfig struct
    * instead and use the method without constraints in the signature.
    */
@@ -336,16 +355,8 @@
   @Deprecated
   public PeerConnection createPeerConnection(PeerConnection.RTCConfiguration rtcConfig,
       MediaConstraints constraints, PeerConnection.Observer observer) {
-    long nativeObserver = PeerConnection.createNativePeerConnectionObserver(observer);
-    if (nativeObserver == 0) {
-      return null;
-    }
-    long nativePeerConnection =
-        nativeCreatePeerConnection(nativeFactory, rtcConfig, constraints, nativeObserver);
-    if (nativePeerConnection == 0) {
-      return null;
-    }
-    return new PeerConnection(nativePeerConnection);
+    return createPeerConnectionInternal(
+        rtcConfig, constraints, observer, /* sslCertificateVerifier= */ null);
   }
 
   /**
@@ -376,7 +387,8 @@
   @Nullable
   public PeerConnection createPeerConnection(
       PeerConnection.RTCConfiguration rtcConfig, PeerConnectionDependencies dependencies) {
-    return createPeerConnection(rtcConfig, null /* constraints */, dependencies.getObserver());
+    return createPeerConnectionInternal(rtcConfig, null /* constraints */,
+        dependencies.getObserver(), dependencies.getSSLCertificateVerifier());
   }
 
   public MediaStream createLocalMediaStream(String label) {
@@ -514,7 +526,8 @@
       VideoDecoderFactory decoderFactory, long nativeAudioProcessor,
       long nativeFecControllerFactory);
   private static native long nativeCreatePeerConnection(long factory,
-      PeerConnection.RTCConfiguration rtcConfig, MediaConstraints constraints, long nativeObserver);
+      PeerConnection.RTCConfiguration rtcConfig, MediaConstraints constraints, long nativeObserver,
+      SSLCertificateVerifier sslCertificateVerifier);
   private static native long nativeCreateLocalMediaStream(long factory, String label);
   private static native long nativeCreateVideoSource(long factory, boolean is_screencast);
   private static native long nativeCreateVideoTrack(
diff --git a/sdk/android/api/org/webrtc/SSLCertificateVerifier.java b/sdk/android/api/org/webrtc/SSLCertificateVerifier.java
new file mode 100644
index 0000000..461cd3b
--- /dev/null
+++ b/sdk/android/api/org/webrtc/SSLCertificateVerifier.java
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+/**
+ * The SSLCertificateVerifier interface allows API users to provide custom
+ * logic to verify certificates.
+ */
+public interface SSLCertificateVerifier {
+  /**
+   * Implementations of verify allow applications to provide custom logic for
+   * verifying certificates. This is not required by default and should be used
+   * with care.
+   *
+   * @param certificate A byte array containing a DER encoded X509 certificate.
+   * @return True if the certificate is verified and trusted else false.
+   */
+  @CalledByNative boolean verify(byte[] certificate);
+}
diff --git a/sdk/android/src/jni/pc/peerconnectionfactory.cc b/sdk/android/src/jni/pc/peerconnectionfactory.cc
index 889593e..ba1be38 100644
--- a/sdk/android/src/jni/pc/peerconnectionfactory.cc
+++ b/sdk/android/src/jni/pc/peerconnectionfactory.cc
@@ -34,6 +34,7 @@
 #include "sdk/android/src/jni/pc/media.h"
 #include "sdk/android/src/jni/pc/ownedfactoryandthreads.h"
 #include "sdk/android/src/jni/pc/peerconnection.h"
+#include "sdk/android/src/jni/pc/sslcertificateverifierwrapper.h"
 #include "sdk/android/src/jni/pc/video.h"
 #include "system_wrappers/include/field_trial.h"
 // Adding 'nogncheck' to disable the gn include headers check.
@@ -374,7 +375,8 @@
     jlong factory,
     const JavaParamRef<jobject>& j_rtc_config,
     const JavaParamRef<jobject>& j_constraints,
-    jlong observer_p) {
+    jlong observer_p,
+    const JavaParamRef<jobject>& j_sslCertificateVerifier) {
   rtc::scoped_refptr<PeerConnectionFactoryInterface> f(
       reinterpret_cast<PeerConnectionFactoryInterface*>(
           factoryFromJava(factory)));
@@ -404,8 +406,17 @@
     constraints = JavaToNativeMediaConstraints(jni, j_constraints);
     CopyConstraintsIntoRtcConfiguration(constraints.get(), &rtc_config);
   }
-  rtc::scoped_refptr<PeerConnectionInterface> pc(
-      f->CreatePeerConnection(rtc_config, nullptr, nullptr, observer.get()));
+
+  PeerConnectionDependencies peer_connection_dependencies(observer.get());
+  if (!j_sslCertificateVerifier.is_null()) {
+    peer_connection_dependencies.tls_cert_verifier =
+        absl::make_unique<SSLCertificateVerifierWrapper>(
+            jni, j_sslCertificateVerifier);
+  }
+
+  rtc::scoped_refptr<PeerConnectionInterface> pc(f->CreatePeerConnection(
+      rtc_config, std::move(peer_connection_dependencies)));
+
   return jlongFromPointer(
       new OwnedPeerConnection(pc, std::move(observer), std::move(constraints)));
 }
diff --git a/sdk/android/src/jni/pc/sslcertificateverifierwrapper.cc b/sdk/android/src/jni/pc/sslcertificateverifierwrapper.cc
new file mode 100644
index 0000000..2ff9a46
--- /dev/null
+++ b/sdk/android/src/jni/pc/sslcertificateverifierwrapper.cc
@@ -0,0 +1,44 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "sdk/android/src/jni/pc/sslcertificateverifierwrapper.h"
+#include "sdk/android/generated_peerconnection_jni/jni/SSLCertificateVerifier_jni.h"
+#include "sdk/android/native_api/jni/class_loader.h"
+#include "sdk/android/native_api/jni/java_types.h"
+
+namespace webrtc {
+namespace jni {
+
+SSLCertificateVerifierWrapper::SSLCertificateVerifierWrapper(
+    JNIEnv* jni,
+    const JavaRef<jobject>& ssl_certificate_verifier)
+    : ssl_certificate_verifier_(jni, ssl_certificate_verifier) {}
+
+SSLCertificateVerifierWrapper::~SSLCertificateVerifierWrapper() = default;
+
+bool SSLCertificateVerifierWrapper::Verify(
+    const rtc::SSLCertificate& certificate) {
+  JNIEnv* jni = AttachCurrentThreadIfNeeded();
+
+  // Serialize the der encoding of the cert into a jbyteArray
+  rtc::Buffer cert_der_buffer;
+  certificate.ToDER(&cert_der_buffer);
+  ScopedJavaLocalRef<jbyteArray> jni_buffer(
+      jni, jni->NewByteArray(cert_der_buffer.size()));
+  jni->SetByteArrayRegion(
+      jni_buffer.obj(), 0, cert_der_buffer.size(),
+      reinterpret_cast<const jbyte*>(cert_der_buffer.data()));
+
+  return Java_SSLCertificateVerifier_verify(jni, ssl_certificate_verifier_,
+                                            jni_buffer);
+}
+
+}  // namespace jni
+}  // namespace webrtc
diff --git a/sdk/android/src/jni/pc/sslcertificateverifierwrapper.h b/sdk/android/src/jni/pc/sslcertificateverifierwrapper.h
new file mode 100644
index 0000000..719fa44
--- /dev/null
+++ b/sdk/android/src/jni/pc/sslcertificateverifierwrapper.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef SDK_ANDROID_SRC_JNI_PC_SSLCERTIFICATEVERIFIERWRAPPER_H_
+#define SDK_ANDROID_SRC_JNI_PC_SSLCERTIFICATEVERIFIERWRAPPER_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "rtc_base/sslcertificate.h"
+#include "sdk/android/src/jni/jni_helpers.h"
+
+namespace webrtc {
+namespace jni {
+
+// Wrapper for Java SSLCertifiacteVerifier class. Delegates method calls through
+// JNI and wraps the encoder inside SSLCertificateVerifierWrapper.
+class SSLCertificateVerifierWrapper : public rtc::SSLCertificateVerifier {
+ public:
+  SSLCertificateVerifierWrapper(
+      JNIEnv* jni,
+      const JavaRef<jobject>& ssl_certificate_verifier);
+  ~SSLCertificateVerifierWrapper() override;
+
+  bool Verify(const rtc::SSLCertificate& certificate) override;
+
+ private:
+  const ScopedJavaGlobalRef<jobject> ssl_certificate_verifier_;
+};
+
+}  // namespace jni
+}  // namespace webrtc
+
+#endif  // SDK_ANDROID_SRC_JNI_PC_SSLCERTIFICATEVERIFIERWRAPPER_H_