Add certificate generate/set functionality to bring iOS closer to JS API
The JS API supports two operations which have never been implemented in
the iOS 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 iOS, a new certificate is automatically generated for
every PeerConnection with no programmatic way to set a specific
certificate.
Work sponsored by |pipe|
Bug: webrtc:9498
Change-Id: Ic1936c3de8b8bd18aef67c784727b72f90e7157c
Reviewed-on: https://webrtc-review.googlesource.com/87303
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24276}
diff --git a/AUTHORS b/AUTHORS
index 69b5e2f..41833d0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -36,6 +36,7 @@
Martin Storsjo <martin@martin.st>
Matthias Liebig <matthias.gcode@gmail.com>
Maxim Potapov <vopatop.skam@gmail.com>
+Michael Iedema <michael@kapsulate.com>
Mike Gilbert <floppymaster@gmail.com>
Mo Zanaty <mzanaty@cisco.com>
Pali Rohar
diff --git a/examples/objc/AppRTCMobile/ARDAppClient.m b/examples/objc/AppRTCMobile/ARDAppClient.m
index e4dbad4..465ce8c 100644
--- a/examples/objc/AppRTCMobile/ARDAppClient.m
+++ b/examples/objc/AppRTCMobile/ARDAppClient.m
@@ -533,8 +533,14 @@
// Create peer connection.
RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
RTCConfiguration *config = [[RTCConfiguration alloc] init];
+ RTCCertificate *pcert = [RTCCertificate generateCertificateWithParams:@{
+ @"expires" : @100000,
+ @"name" : @"RSASSA-PKCS1-v1_5"
+ }];
config.iceServers = _iceServers;
config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
+ config.certificate = pcert;
+
_peerConnection = [_factory peerConnectionWithConfiguration:config
constraints:constraints
delegate:self];
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 7c95f0f..4abea8c 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -631,6 +631,7 @@
"objc/Framework/Classes/PeerConnection/RTCAudioSource.mm",
"objc/Framework/Classes/PeerConnection/RTCAudioTrack+Private.h",
"objc/Framework/Classes/PeerConnection/RTCAudioTrack.mm",
+ "objc/Framework/Classes/PeerConnection/RTCCertificate.mm",
"objc/Framework/Classes/PeerConnection/RTCConfiguration+Native.h",
"objc/Framework/Classes/PeerConnection/RTCConfiguration+Private.h",
"objc/Framework/Classes/PeerConnection/RTCConfiguration.mm",
@@ -693,6 +694,7 @@
"objc/Framework/Classes/PeerConnection/RTCVideoTrack.mm",
"objc/Framework/Headers/WebRTC/RTCAudioSource.h",
"objc/Framework/Headers/WebRTC/RTCAudioTrack.h",
+ "objc/Framework/Headers/WebRTC/RTCCertificate.h",
"objc/Framework/Headers/WebRTC/RTCConfiguration.h",
"objc/Framework/Headers/WebRTC/RTCDataChannel.h",
"objc/Framework/Headers/WebRTC/RTCDataChannelConfiguration.h",
@@ -868,6 +870,7 @@
testonly = true
sources = [
+ "objc/Framework/UnitTests/RTCCertificateTest.mm",
"objc/Framework/UnitTests/RTCConfigurationTest.mm",
"objc/Framework/UnitTests/RTCDataChannelConfigurationTest.mm",
"objc/Framework/UnitTests/RTCIceCandidateTest.mm",
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCCertificate.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCCertificate.mm
new file mode 100644
index 0000000..34fa837
--- /dev/null
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCCertificate.mm
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#import "WebRTC/RTCCertificate.h"
+#import "WebRTC/RTCLogging.h"
+
+#include "rtc_base/logging.h"
+#include "rtc_base/rtccertificategenerator.h"
+#include "rtc_base/sslidentity.h"
+
+@implementation RTCCertificate
+
+@synthesize private_key = _private_key;
+@synthesize certificate = _certificate;
+
+- (id)copyWithZone:(NSZone *)zone {
+ id copy = [[[self class] alloc] initWithPrivateKey:[self.private_key copyWithZone:zone]
+ certificate:[self.certificate copyWithZone:zone]];
+ return copy;
+}
+
+- (instancetype)initWithPrivateKey:(NSString *)private_key certificate:(NSString *)certificate {
+ if (self = [super init]) {
+ _private_key = [private_key copy];
+ _certificate = [certificate copy];
+ }
+ return self;
+}
+
++ (nullable RTCCertificate *)generateCertificateWithParams:(NSDictionary *)params {
+ rtc::KeyType keyType = rtc::KT_ECDSA;
+ NSString *keyTypeString = [params valueForKey:@"name"];
+ if (keyTypeString && [keyTypeString isEqualToString:@"RSASSA-PKCS1-v1_5"]) {
+ keyType = rtc::KT_RSA;
+ }
+
+ NSNumber *expires = [params valueForKey:@"expires"];
+ rtc::scoped_refptr<rtc::RTCCertificate> cc_certificate = nullptr;
+ if (expires != nil) {
+ uint64_t expirationTimestamp = [expires unsignedLongLongValue];
+ cc_certificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType),
+ expirationTimestamp);
+ } else {
+ cc_certificate =
+ rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType), absl::nullopt);
+ }
+ if (!cc_certificate) {
+ RTCLogError(@"Failed to generate certificate.");
+ return nullptr;
+ }
+ // grab PEMs and create an NS RTCCerticicate
+ rtc::RTCCertificatePEM pem = cc_certificate->ToPEM();
+ std::string pem_private_key = pem.private_key();
+ std::string pem_certificate = pem.certificate();
+ RTC_LOG(LS_INFO) << "CERT PEM ";
+ RTC_LOG(LS_INFO) << pem_certificate;
+
+ RTCCertificate *cert = [[RTCCertificate alloc] initWithPrivateKey:@(pem_private_key.c_str())
+ certificate:@(pem_certificate.c_str())];
+ return cert;
+}
+
+@end
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm
index a357085..1748377 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm
@@ -23,6 +23,7 @@
@implementation RTCConfiguration
@synthesize iceServers = _iceServers;
+@synthesize certificate = _certificate;
@synthesize iceTransportPolicy = _iceTransportPolicy;
@synthesize bundlePolicy = _bundlePolicy;
@synthesize rtcpMuxPolicy = _rtcpMuxPolicy;
@@ -63,6 +64,14 @@
[iceServers addObject:iceServer];
}
_iceServers = iceServers;
+ if (!config.certificates.empty()) {
+ rtc::scoped_refptr<rtc::RTCCertificate> native_cert;
+ native_cert = config.certificates[0];
+ rtc::RTCCertificatePEM native_pem = native_cert->ToPEM();
+ _certificate =
+ [[RTCCertificate alloc] initWithPrivateKey:@(native_pem.private_key().c_str())
+ certificate:@(native_pem.certificate().c_str())];
+ }
_iceTransportPolicy =
[[self class] transportPolicyForTransportsType:config.type];
_bundlePolicy =
@@ -168,16 +177,32 @@
_iceBackupCandidatePairPingInterval;
rtc::KeyType keyType =
[[self class] nativeEncryptionKeyTypeForKeyType:_keyType];
- // Generate non-default certificate.
- if (keyType != rtc::KT_DEFAULT) {
- rtc::scoped_refptr<rtc::RTCCertificate> certificate =
- rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType),
- absl::optional<uint64_t>());
+ if (_certificate != nullptr) {
+ // if offered a pemcert use it...
+ RTC_LOG(LS_INFO) << "Have configured cert - using it.";
+ std::string pem_private_key = [[_certificate private_key] UTF8String];
+ std::string pem_certificate = [[_certificate certificate] UTF8String];
+ rtc::RTCCertificatePEM pem = rtc::RTCCertificatePEM(pem_private_key, pem_certificate);
+ rtc::scoped_refptr<rtc::RTCCertificate> certificate = rtc::RTCCertificate::FromPEM(pem);
+ RTC_LOG(LS_INFO) << "Created cert from PEM strings.";
if (!certificate) {
- RTCLogError(@"Failed to generate certificate.");
+ RTC_LOG(LS_ERROR) << "Failed to generate certificate from PEM.";
return nullptr;
}
nativeConfig->certificates.push_back(certificate);
+ } else {
+ RTC_LOG(LS_INFO) << "Don't have configured cert.";
+ // Generate non-default certificate.
+ if (keyType != rtc::KT_DEFAULT) {
+ rtc::scoped_refptr<rtc::RTCCertificate> certificate =
+ rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType),
+ absl::optional<uint64_t>());
+ if (!certificate) {
+ RTCLogError(@"Failed to generate certificate.");
+ return nullptr;
+ }
+ nativeConfig->certificates.push_back(certificate);
+ }
}
nativeConfig->ice_candidate_pool_size = _iceCandidatePoolSize;
nativeConfig->prune_turn_ports = _shouldPruneTurnPorts ? true : false;
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCCertificate.h b/sdk/objc/Framework/Headers/WebRTC/RTCCertificate.h
new file mode 100644
index 0000000..6b4ea43
--- /dev/null
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCCertificate.h
@@ -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.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import <WebRTC/RTCMacros.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+RTC_EXPORT
+@interface RTCCertificate : NSObject <NSCopying>
+
+/** Private key in PEM. */
+@property(nonatomic, readonly, copy) NSString *private_key;
+
+/** Public key in an x509 cert encoded in PEM. */
+@property(nonatomic, readonly, copy) NSString *certificate;
+
+/**
+ * Initialize an RTCCertificate with PEM strings for private_key and certificate.
+ */
+- (instancetype)initWithPrivateKey:(NSString *)private_key
+ certificate:(NSString *)certificate NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/** Generate a new certificate for 're' use.
+ *
+ * Optional dictionary of parameters. Defaults to KeyType ECDSA if none are
+ * provided.
+ * - name: "ECDSA" or "RSASSA-PKCS1-v1_5"
+ */
++ (nullable RTCCertificate *)generateCertificateWithParams:(NSDictionary *)params;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h b/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h
index d274530..11ea6a3 100644
--- a/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h
@@ -10,6 +10,7 @@
#import <Foundation/Foundation.h>
+#import <WebRTC/RTCCertificate.h>
#import <WebRTC/RTCMacros.h>
@class RTCIceServer;
@@ -67,13 +68,15 @@
};
NS_ASSUME_NONNULL_BEGIN
-
RTC_EXPORT
@interface RTCConfiguration : NSObject
/** An array of Ice Servers available to be used by ICE. */
@property(nonatomic, copy) NSArray<RTCIceServer *> *iceServers;
+/** An RTCCertificate for 're' use. */
+@property(nonatomic, nullable) RTCCertificate *certificate;
+
/** Which candidates the ICE agent is allowed to use. The W3C calls it
* |iceTransportPolicy|, while in C++ it is called |type|. */
@property(nonatomic, assign) RTCIceTransportPolicy iceTransportPolicy;
diff --git a/sdk/objc/Framework/UnitTests/RTCCertificateTest.mm b/sdk/objc/Framework/UnitTests/RTCCertificateTest.mm
new file mode 100644
index 0000000..64269f7
--- /dev/null
+++ b/sdk/objc/Framework/UnitTests/RTCCertificateTest.mm
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#import <Foundation/Foundation.h>
+
+#include <vector>
+
+#include "rtc_base/gunit.h"
+
+#import "NSString+StdString.h"
+#import "RTCConfiguration+Private.h"
+#import "WebRTC/RTCConfiguration.h"
+#import "WebRTC/RTCIceServer.h"
+#import "WebRTC/RTCMediaConstraints.h"
+#import "WebRTC/RTCPeerConnection.h"
+#import "WebRTC/RTCPeerConnectionFactory.h"
+
+@interface RTCCertificateTest : NSObject
+- (void)testCertificateIsUsedInConfig;
+@end
+
+@implementation RTCCertificateTest
+
+- (void)testCertificateIsUsedInConfig {
+ RTCConfiguration *originalConfig = [[RTCConfiguration alloc] init];
+
+ NSArray *urlStrings = @[ @"stun:stun1.example.net" ];
+ RTCIceServer *server = [[RTCIceServer alloc] initWithURLStrings:urlStrings];
+ originalConfig.iceServers = @[ server ];
+
+ // Generate a new certificate.
+ RTCCertificate *originalCertificate = [RTCCertificate generateCertificateWithParams:@{
+ @"expires" : @100000,
+ @"name" : @"RSASSA-PKCS1-v1_5"
+ }];
+
+ // Store certificate in configuration.
+ originalConfig.certificate = originalCertificate;
+
+ RTCMediaConstraints *contraints =
+ [[RTCMediaConstraints alloc] initWithMandatoryConstraints:@{} optionalConstraints:nil];
+ RTCPeerConnectionFactory *factory = [[RTCPeerConnectionFactory alloc] init];
+
+ // Create PeerConnection with this certificate.
+ RTCPeerConnection *peerConnection =
+ [factory peerConnectionWithConfiguration:originalConfig constraints:contraints delegate:nil];
+
+ // Retrieve certificate from the configuration.
+ RTCConfiguration *retrievedConfig = peerConnection.configuration;
+
+ // Extract PEM strings from original certificate.
+ std::string originalPrivateKeyField = [[originalCertificate private_key] UTF8String];
+ std::string originalCertificateField = [[originalCertificate certificate] UTF8String];
+
+ // Extract PEM strings from certificate retrieved from configuration.
+ RTCCertificate *retrievedCertificate = retrievedConfig.certificate;
+ std::string retrievedPrivateKeyField = [[retrievedCertificate private_key] UTF8String];
+ std::string retrievedCertificateField = [[retrievedCertificate certificate] UTF8String];
+
+ // Check that the original certificate and retrieved certificate match.
+ EXPECT_EQ(originalPrivateKeyField, retrievedPrivateKeyField);
+ EXPECT_EQ(retrievedCertificateField, retrievedCertificateField);
+}
+
+@end
+
+TEST(CertificateTest, CertificateIsUsedInConfig) {
+ RTCCertificateTest *test = [[RTCCertificateTest alloc] init];
+ [test testCertificateIsUsedInConfig];
+}