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}
This commit is contained in:
Michael Iedema
2018-07-05 15:28:24 +02:00
committed by Commit Bot
parent b336c2784f
commit ccee56beee
8 changed files with 236 additions and 7 deletions

View File

@ -36,6 +36,7 @@ Manish Jethani <manish.jethani@gmail.com>
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

View File

@ -533,8 +533,14 @@ static int const kKbpsMultiplier = 1000;
// 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];

View File

@ -631,6 +631,7 @@ if (is_ios || is_mac) {
"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 @@ if (is_ios || is_mac) {
"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 @@ if (is_ios || is_mac) {
testonly = true
sources = [
"objc/Framework/UnitTests/RTCCertificateTest.mm",
"objc/Framework/UnitTests/RTCConfigurationTest.mm",
"objc/Framework/UnitTests/RTCDataChannelConfigurationTest.mm",
"objc/Framework/UnitTests/RTCIceCandidateTest.mm",

View File

@ -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

View File

@ -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,6 +177,21 @@
_iceBackupCandidatePairPingInterval;
rtc::KeyType keyType =
[[self class] nativeEncryptionKeyTypeForKeyType:_keyType];
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) {
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 =
@ -179,6 +203,7 @@
}
nativeConfig->certificates.push_back(certificate);
}
}
nativeConfig->ice_candidate_pool_size = _iceCandidatePoolSize;
nativeConfig->prune_turn_ports = _shouldPruneTurnPorts ? true : false;
nativeConfig->presume_writable_when_fully_relayed =

View File

@ -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

View File

@ -10,6 +10,7 @@
#import <Foundation/Foundation.h>
#import <WebRTC/RTCCertificate.h>
#import <WebRTC/RTCMacros.h>
@class RTCIceServer;
@ -67,13 +68,15 @@ typedef NS_ENUM(NSInteger, RTCSdpSemantics) {
};
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;

View File

@ -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];
}