Add certificate gen/set functionality to bring Android closer to JS API

The JS API supports two operations which have never been implemented in
the Android counterpart:
 - generate a new certificate
 - use this certificate when creating a new PeerConnection

Both functions are illustrated in the generateCertificate example code:
 - https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/generateCertificate

Currently, on Android, a new certificate is automatically generated for
every PeerConnection with no programmatic way to set a specific
certificate.

A twin of this feature is already underway for iOS here:
 - https://webrtc-review.googlesource.com/c/src/+/87303

Work sponsored by |pipe|

Bug: webrtc:9546
Change-Id: Iac221517df3ae380aef83c18c9e59b028d709a4f
Reviewed-on: https://webrtc-review.googlesource.com/c/89980
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25090}
This commit is contained in:
Michael Iedema
2018-10-09 15:30:01 +02:00
committed by Commit Bot
parent dcc023816e
commit 0213786b39
9 changed files with 309 additions and 11 deletions

View File

@ -295,6 +295,7 @@ if (is_android) {
"api/org/webrtc/PeerConnection.java", "api/org/webrtc/PeerConnection.java",
"api/org/webrtc/PeerConnectionDependencies.java", "api/org/webrtc/PeerConnectionDependencies.java",
"api/org/webrtc/PeerConnectionFactory.java", "api/org/webrtc/PeerConnectionFactory.java",
"api/org/webrtc/RtcCertificatePem.java",
"api/org/webrtc/RTCStats.java", "api/org/webrtc/RTCStats.java",
"api/org/webrtc/RTCStatsCollectorCallback.java", "api/org/webrtc/RTCStatsCollectorCallback.java",
"api/org/webrtc/RTCStatsReport.java", "api/org/webrtc/RTCStatsReport.java",
@ -625,6 +626,8 @@ if (is_android) {
"src/jni/pc/peerconnection.h", "src/jni/pc/peerconnection.h",
"src/jni/pc/peerconnectionfactory.cc", "src/jni/pc/peerconnectionfactory.cc",
"src/jni/pc/peerconnectionfactory.h", "src/jni/pc/peerconnectionfactory.h",
"src/jni/pc/rtccertificate.cc",
"src/jni/pc/rtccertificate.h",
"src/jni/pc/rtcstatscollectorcallbackwrapper.cc", "src/jni/pc/rtcstatscollectorcallbackwrapper.cc",
"src/jni/pc/rtcstatscollectorcallbackwrapper.h", "src/jni/pc/rtcstatscollectorcallbackwrapper.h",
"src/jni/pc/rtpparameters.cc", "src/jni/pc/rtpparameters.cc",
@ -1180,6 +1183,7 @@ if (is_android) {
"api/org/webrtc/RTCStats.java", "api/org/webrtc/RTCStats.java",
"api/org/webrtc/RTCStatsCollectorCallback.java", "api/org/webrtc/RTCStatsCollectorCallback.java",
"api/org/webrtc/RTCStatsReport.java", "api/org/webrtc/RTCStatsReport.java",
"api/org/webrtc/RtcCertificatePem.java",
"api/org/webrtc/RtpParameters.java", "api/org/webrtc/RtpParameters.java",
"api/org/webrtc/RtpReceiver.java", "api/org/webrtc/RtpReceiver.java",
"api/org/webrtc/RtpSender.java", "api/org/webrtc/RtpSender.java",
@ -1272,6 +1276,7 @@ if (is_android) {
"instrumentationtests/src/org/webrtc/PeerConnectionFactoryTest.java", "instrumentationtests/src/org/webrtc/PeerConnectionFactoryTest.java",
"instrumentationtests/src/org/webrtc/PeerConnectionTest.java", "instrumentationtests/src/org/webrtc/PeerConnectionTest.java",
"instrumentationtests/src/org/webrtc/RendererCommonTest.java", "instrumentationtests/src/org/webrtc/RendererCommonTest.java",
"instrumentationtests/src/org/webrtc/RtcCertificatePemTest.java",
"instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java", "instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java",
"instrumentationtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java", "instrumentationtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java",
"instrumentationtests/src/org/webrtc/TestConstants.java", "instrumentationtests/src/org/webrtc/TestConstants.java",
@ -1296,6 +1301,7 @@ if (is_android) {
"//rtc_base:base_java", "//rtc_base:base_java",
"//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java", "//third_party/android_support_test_runner:runner_java",
"//third_party/google-truth:google_truth_java",
"//third_party/junit", "//third_party/junit",
] ]

View File

@ -385,6 +385,7 @@ public class PeerConnection {
public IceTransportsType iceTransportsType; public IceTransportsType iceTransportsType;
public List<IceServer> iceServers; public List<IceServer> iceServers;
public BundlePolicy bundlePolicy; public BundlePolicy bundlePolicy;
@Nullable public RtcCertificatePem certificate;
public RtcpMuxPolicy rtcpMuxPolicy; public RtcpMuxPolicy rtcpMuxPolicy;
public TcpCandidatePolicy tcpCandidatePolicy; public TcpCandidatePolicy tcpCandidatePolicy;
public CandidateNetworkPolicy candidateNetworkPolicy; public CandidateNetworkPolicy candidateNetworkPolicy;
@ -517,6 +518,12 @@ public class PeerConnection {
return bundlePolicy; return bundlePolicy;
} }
@Nullable
@CalledByNative("RTCConfiguration")
RtcCertificatePem getCertificate() {
return certificate;
}
@CalledByNative("RTCConfiguration") @CalledByNative("RTCConfiguration")
RtcpMuxPolicy getRtcpMuxPolicy() { RtcpMuxPolicy getRtcpMuxPolicy() {
return rtcpMuxPolicy; return rtcpMuxPolicy;
@ -721,6 +728,10 @@ public class PeerConnection {
return nativeGetRemoteDescription(); return nativeGetRemoteDescription();
} }
public RtcCertificatePem getCertificate() {
return nativeGetCertificate();
}
public DataChannel createDataChannel(String label, DataChannel.Init init) { public DataChannel createDataChannel(String label, DataChannel.Init init) {
return nativeCreateDataChannel(label, init); return nativeCreateDataChannel(label, init);
} }
@ -1107,6 +1118,7 @@ public class PeerConnection {
private native long nativeGetNativePeerConnection(); private native long nativeGetNativePeerConnection();
private native SessionDescription nativeGetLocalDescription(); private native SessionDescription nativeGetLocalDescription();
private native SessionDescription nativeGetRemoteDescription(); private native SessionDescription nativeGetRemoteDescription();
private native RtcCertificatePem nativeGetCertificate();
private native DataChannel nativeCreateDataChannel(String label, DataChannel.Init init); private native DataChannel nativeCreateDataChannel(String label, DataChannel.Init init);
private native void nativeCreateOffer(SdpObserver observer, MediaConstraints constraints); private native void nativeCreateOffer(SdpObserver observer, MediaConstraints constraints);
private native void nativeCreateAnswer(SdpObserver observer, MediaConstraints constraints); private native void nativeCreateAnswer(SdpObserver observer, MediaConstraints constraints);

View File

@ -0,0 +1,73 @@
/*
* 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;
/**
* Easily storable/serializable version of a native C++ RTCCertificatePEM.
*/
public class RtcCertificatePem {
/** PEM string representation of the private key. */
public final String privateKey;
/** PEM string representation of the certificate. */
public final String certificate;
/** Default expiration time of 30 days. */
private static final long DEFAULT_EXPIRY = 60 * 60 * 24 * 30;
/** Instantiate an RtcCertificatePem object from stored strings. */
@CalledByNative
public RtcCertificatePem(String privateKey, String certificate) {
this.privateKey = privateKey;
this.certificate = certificate;
}
@CalledByNative
String getPrivateKey() {
return privateKey;
}
@CalledByNative
String getCertificate() {
return certificate;
}
/**
* Generate a new RtcCertificatePem with the default settings of KeyType = ECDSA and
* expires = 30 days.
*/
public static RtcCertificatePem generateCertificate() {
return nativeGenerateCertificate(PeerConnection.KeyType.ECDSA, DEFAULT_EXPIRY);
}
/**
* Generate a new RtcCertificatePem with a custom KeyType and the default setting of
* expires = 30 days.
*/
public static RtcCertificatePem generateCertificate(PeerConnection.KeyType keyType) {
return nativeGenerateCertificate(keyType, DEFAULT_EXPIRY);
}
/**
* Generate a new RtcCertificatePem with a custom expires and the default setting of
* KeyType = ECDSA.
*/
public static RtcCertificatePem generateCertificate(long expires) {
return nativeGenerateCertificate(PeerConnection.KeyType.ECDSA, expires);
}
/** Generate a new RtcCertificatePem with a custom KeyType and a custom expires. */
public static RtcCertificatePem generateCertificate(
PeerConnection.KeyType keyType, long expires) {
return nativeGenerateCertificate(keyType, expires);
}
private static native RtcCertificatePem nativeGenerateCertificate(
PeerConnection.KeyType keyType, long expires);
}

View File

@ -11,6 +11,7 @@
package org.webrtc; package org.webrtc;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -663,6 +664,24 @@ public class PeerConnectionTest {
assertNotNull(offeringPC); assertNotNull(offeringPC);
} }
@Test
@SmallTest
public void testCreationWithCertificate() throws Exception {
PeerConnectionFactory factory = PeerConnectionFactory.builder().createPeerConnectionFactory();
PeerConnection.RTCConfiguration config = new PeerConnection.RTCConfiguration(Arrays.asList());
// Test certificate.
RtcCertificatePem originalCert = RtcCertificatePem.generateCertificate();
config.certificate = originalCert;
ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer");
PeerConnection offeringPC = factory.createPeerConnection(config, offeringExpectations);
RtcCertificatePem restoredCert = offeringPC.getCertificate();
assertEquals(originalCert.privateKey, restoredCert.privateKey);
assertEquals(originalCert.certificate, restoredCert.certificate);
}
@Test @Test
@MediumTest @MediumTest
public void testCompleteSession() throws Exception { public void testCompleteSession() throws Exception {

View File

@ -0,0 +1,73 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import android.support.test.filters.SmallTest;
import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
import org.webrtc.RtcCertificatePem;
import org.webrtc.PeerConnection.KeyType;
/** Tests for RtcCertificatePem.java. */
@RunWith(BaseJUnit4ClassRunner.class)
public class RtcCertificatePemTest {
@Before
public void setUp() {
System.loadLibrary(TestConstants.NATIVE_LIBRARY);
}
@Test
@SmallTest
public void testConstructor() {
RtcCertificatePem original = RtcCertificatePem.generateCertificate();
RtcCertificatePem recreated = new RtcCertificatePem(original.privateKey, original.certificate);
assertThat(original.privateKey).isEqualTo(recreated.privateKey);
assertThat(original.certificate).isEqualTo(recreated.certificate);
}
@Test
@SmallTest
public void testGenerateCertificateDefaults() {
RtcCertificatePem rtcCertificate = RtcCertificatePem.generateCertificate();
assertThat(rtcCertificate.privateKey).isNotEmpty();
assertThat(rtcCertificate.certificate).isNotEmpty();
}
@Test
@SmallTest
public void testGenerateCertificateCustomKeyTypeDefaultExpires() {
RtcCertificatePem rtcCertificate =
RtcCertificatePem.generateCertificate(PeerConnection.KeyType.RSA);
assertThat(rtcCertificate.privateKey).isNotEmpty();
assertThat(rtcCertificate.certificate).isNotEmpty();
}
@Test
@SmallTest
public void testGenerateCertificateCustomExpiresDefaultKeyType() {
RtcCertificatePem rtcCertificate = RtcCertificatePem.generateCertificate(60 * 60 * 24);
assertThat(rtcCertificate.privateKey).isNotEmpty();
assertThat(rtcCertificate.certificate).isNotEmpty();
}
@Test
@SmallTest
public void testGenerateCertificateCustomKeyTypeAndExpires() {
RtcCertificatePem rtcCertificate =
RtcCertificatePem.generateCertificate(PeerConnection.KeyType.RSA, 60 * 60 * 24);
assertThat(rtcCertificate.privateKey).isNotEmpty();
assertThat(rtcCertificate.certificate).isNotEmpty();
}
}

View File

@ -47,6 +47,7 @@
#include "sdk/android/src/jni/pc/icecandidate.h" #include "sdk/android/src/jni/pc/icecandidate.h"
#include "sdk/android/src/jni/pc/mediaconstraints.h" #include "sdk/android/src/jni/pc/mediaconstraints.h"
#include "sdk/android/src/jni/pc/mediastreamtrack.h" #include "sdk/android/src/jni/pc/mediastreamtrack.h"
#include "sdk/android/src/jni/pc/rtccertificate.h"
#include "sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h" #include "sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h"
#include "sdk/android/src/jni/pc/rtpsender.h" #include "sdk/android/src/jni/pc/rtpsender.h"
#include "sdk/android/src/jni/pc/sdpobserver.h" #include "sdk/android/src/jni/pc/sdpobserver.h"
@ -129,6 +130,8 @@ void JavaToNativeRTCConfiguration(
Java_RTCConfiguration_getBundlePolicy(jni, j_rtc_config); Java_RTCConfiguration_getBundlePolicy(jni, j_rtc_config);
ScopedJavaLocalRef<jobject> j_rtcp_mux_policy = ScopedJavaLocalRef<jobject> j_rtcp_mux_policy =
Java_RTCConfiguration_getRtcpMuxPolicy(jni, j_rtc_config); Java_RTCConfiguration_getRtcpMuxPolicy(jni, j_rtc_config);
ScopedJavaLocalRef<jobject> j_rtc_certificate =
Java_RTCConfiguration_getCertificate(jni, j_rtc_config);
ScopedJavaLocalRef<jobject> j_tcp_candidate_policy = ScopedJavaLocalRef<jobject> j_tcp_candidate_policy =
Java_RTCConfiguration_getTcpCandidatePolicy(jni, j_rtc_config); Java_RTCConfiguration_getTcpCandidatePolicy(jni, j_rtc_config);
ScopedJavaLocalRef<jobject> j_candidate_network_policy = ScopedJavaLocalRef<jobject> j_candidate_network_policy =
@ -148,6 +151,13 @@ void JavaToNativeRTCConfiguration(
rtc_config->bundle_policy = JavaToNativeBundlePolicy(jni, j_bundle_policy); rtc_config->bundle_policy = JavaToNativeBundlePolicy(jni, j_bundle_policy);
rtc_config->rtcp_mux_policy = rtc_config->rtcp_mux_policy =
JavaToNativeRtcpMuxPolicy(jni, j_rtcp_mux_policy); JavaToNativeRtcpMuxPolicy(jni, j_rtcp_mux_policy);
if (!j_rtc_certificate.is_null()) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::FromPEM(
JavaToNativeRTCCertificatePEM(jni, j_rtc_certificate));
RTC_CHECK(certificate != nullptr) << "supplied certificate is malformed.";
rtc_config->certificates.push_back(certificate);
}
rtc_config->tcp_candidate_policy = rtc_config->tcp_candidate_policy =
JavaToNativeTcpCandidatePolicy(jni, j_tcp_candidate_policy); JavaToNativeTcpCandidatePolicy(jni, j_tcp_candidate_policy);
rtc_config->candidate_network_policy = rtc_config->candidate_network_policy =
@ -429,6 +439,16 @@ static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetRemoteDescription(
return sdp ? NativeToJavaSessionDescription(jni, sdp) : nullptr; return sdp ? NativeToJavaSessionDescription(jni, sdp) : nullptr;
} }
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_GetCertificate(
JNIEnv* jni,
const JavaParamRef<jobject>& j_pc) {
const PeerConnectionInterface::RTCConfiguration rtc_config =
ExtractNativePC(jni, j_pc)->GetConfiguration();
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc_config.certificates[0];
return NativeToJavaRTCCertificatePEM(jni, certificate->ToPEM());
}
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_CreateDataChannel( static ScopedJavaLocalRef<jobject> JNI_PeerConnection_CreateDataChannel(
JNIEnv* jni, JNIEnv* jni,
const JavaParamRef<jobject>& j_pc, const JavaParamRef<jobject>& j_pc,

View File

@ -396,6 +396,7 @@ static jlong JNI_PeerConnectionFactory_CreatePeerConnection(
PeerConnectionInterface::RTCConfigurationType::kAggressive); PeerConnectionInterface::RTCConfigurationType::kAggressive);
JavaToNativeRTCConfiguration(jni, j_rtc_config, &rtc_config); JavaToNativeRTCConfiguration(jni, j_rtc_config, &rtc_config);
if (rtc_config.certificates.empty()) {
// Generate non-default certificate. // Generate non-default certificate.
rtc::KeyType key_type = GetRtcConfigKeyType(jni, j_rtc_config); rtc::KeyType key_type = GetRtcConfigKeyType(jni, j_rtc_config);
if (key_type != rtc::KT_DEFAULT) { if (key_type != rtc::KT_DEFAULT) {
@ -409,6 +410,7 @@ static jlong JNI_PeerConnectionFactory_CreatePeerConnection(
} }
rtc_config.certificates.push_back(certificate); rtc_config.certificates.push_back(certificate);
} }
}
std::unique_ptr<MediaConstraintsInterface> constraints; std::unique_ptr<MediaConstraintsInterface> constraints;
if (!j_constraints.is_null()) { if (!j_constraints.is_null()) {

View File

@ -0,0 +1,60 @@
/*
* 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/rtccertificate.h"
#include "sdk/android/src/jni/pc/icecandidate.h"
#include "rtc_base/refcount.h"
#include "rtc_base/rtccertificate.h"
#include "rtc_base/rtccertificategenerator.h"
#include "sdk/android/generated_peerconnection_jni/jni/RtcCertificatePem_jni.h"
#include "sdk/android/native_api/jni/java_types.h"
#include "sdk/android/src/jni/jni_helpers.h"
namespace webrtc {
namespace jni {
rtc::RTCCertificatePEM JavaToNativeRTCCertificatePEM(
JNIEnv* jni,
const JavaRef<jobject>& j_rtc_certificate) {
ScopedJavaLocalRef<jstring> privatekey_field =
Java_RtcCertificatePem_getPrivateKey(jni, j_rtc_certificate);
ScopedJavaLocalRef<jstring> certificate_field =
Java_RtcCertificatePem_getCertificate(jni, j_rtc_certificate);
return rtc::RTCCertificatePEM(JavaToNativeString(jni, privatekey_field),
JavaToNativeString(jni, certificate_field));
}
ScopedJavaLocalRef<jobject> NativeToJavaRTCCertificatePEM(
JNIEnv* jni,
const rtc::RTCCertificatePEM& certificate) {
return Java_RtcCertificatePem_Constructor(
jni, NativeToJavaString(jni, certificate.private_key()),
NativeToJavaString(jni, certificate.certificate()));
}
static ScopedJavaLocalRef<jobject> JNI_RtcCertificatePem_GenerateCertificate(
JNIEnv* jni,
const JavaParamRef<jclass>&,
const JavaParamRef<jobject>& j_key_type,
jlong j_expires) {
rtc::KeyType key_type = JavaToNativeKeyType(jni, j_key_type);
uint64_t expires = (uint64_t)j_expires;
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificateGenerator::GenerateCertificate(
rtc::KeyParams(key_type), expires);
rtc::RTCCertificatePEM pem = certificate->ToPEM();
return Java_RtcCertificatePem_Constructor(
jni, NativeToJavaString(jni, pem.private_key()),
NativeToJavaString(jni, pem.certificate()));
}
} // namespace jni
} // namespace webrtc

View File

@ -0,0 +1,33 @@
/*
* 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_RTCCERTIFICATE_H_
#define SDK_ANDROID_SRC_JNI_PC_RTCCERTIFICATE_H_
#include "rtc_base/refcount.h"
#include "rtc_base/rtccertificate.h"
#include "sdk/android/native_api/jni/java_types.h"
#include "sdk/android/src/jni/jni_helpers.h"
namespace webrtc {
namespace jni {
rtc::RTCCertificatePEM JavaToNativeRTCCertificatePEM(
JNIEnv* jni,
const JavaRef<jobject>& j_rtc_certificate);
ScopedJavaLocalRef<jobject> NativeToJavaRTCCertificatePEM(
JNIEnv* env,
const rtc::RTCCertificatePEM& certificate);
} // namespace jni
} // namespace webrtc
#endif // SDK_ANDROID_SRC_JNI_PC_RTCCERTIFICATE_H_