summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Lee <randall.lee@mapbox.com>2018-05-07 17:57:42 -0400
committerFabian Guerra Soto <fabian.guerra@mapbox.com>2018-06-22 13:07:14 -0700
commit1642dd9b89c46ce01f62c40f3cd16c8999d0e508 (patch)
treea9647da03c71550a15997fc8f517b62e88abc330
parent4b2b97f18c87bc7f1f1003ad6c145e61a614ccdb (diff)
downloadqtlocation-mapboxgl-1642dd9b89c46ce01f62c40f3cd16c8999d0e508.tar.gz
[iOS] - Update telemetry certificate pinning (#11845)
* Update telemetry certificate pinning * Load both CN certificates * [ios] Use China events endpoint with China API endpoint * Update CHANGELOG.md # Conflicts: # platform/ios/CHANGELOG.md # platform/ios/ios.xcodeproj/project.pbxproj # platform/ios/src/MGLAPIClient.m
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.h6
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.m3
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj42
-rw-r--r--platform/ios/resources/api_mapbox_cn-digicert_2018.derbin0 -> 1704 bytes
-rw-r--r--platform/ios/resources/api_mapbox_cn-geotrust_2018.derbin0 -> 1578 bytes
-rw-r--r--platform/ios/src/MGLAPIClient.m218
6 files changed, 268 insertions, 1 deletions
diff --git a/platform/darwin/src/MGLNetworkConfiguration.h b/platform/darwin/src/MGLNetworkConfiguration.h
index 2db46d78c5..87b4cacb99 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.h
+++ b/platform/darwin/src/MGLNetworkConfiguration.h
@@ -2,6 +2,12 @@
NS_ASSUME_NONNULL_BEGIN
+/// The default base URL for Mapbox APIs other than the telemetry API.
+extern NSString * const MGLDefaultMapboxAPIBaseURL;
+
+/// The PRC base URL for Mapbox APIs other than the telemetry API.
+extern NSString * const MGLChinaMapboxAPIBaseURL;
+
/**
The MGLNetworkConfiguration object provides a global way to set a base API URL for
retrieval of map data, styles, and other resources.
diff --git a/platform/darwin/src/MGLNetworkConfiguration.m b/platform/darwin/src/MGLNetworkConfiguration.m
index 4cfa405a1a..5be92c10b8 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.m
+++ b/platform/darwin/src/MGLNetworkConfiguration.m
@@ -1,5 +1,8 @@
#import "MGLNetworkConfiguration.h"
+NSString * const MGLDefaultMapboxAPIBaseURL = @"https://api.mapbox.com";
+NSString * const MGLChinaMapboxAPIBaseURL = @"https://api.mapbox.cn";
+
@implementation MGLNetworkConfiguration
+ (void)load {
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 46c10dab89..ea7869a383 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -37,6 +37,8 @@
1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; };
1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; };
1FC4817D2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; };
+ 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; };
+ 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; };
1FC4817F2098CD80000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; };
30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; };
30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; };
@@ -359,6 +361,10 @@
96E5170320005A6800A02306 /* FABKitProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */; };
96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848891CBB037E00AB86E3 /* SMCalloutView.h */; };
96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
+ AC0C15F3209D0E6900B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */; };
+ AC0C15F4209D0E7000B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */; };
+ AC0C15F5209D0E7200B65675 /* api_mapbox_cn-digicert_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */; };
+ AC0C15F6209D0E7300B65675 /* api_mapbox_cn-digicert_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */; };
AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; };
@@ -957,6 +963,8 @@
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXEmbeddedMapViewController.h; sourceTree = "<group>"; };
632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXEmbeddedMapViewController.m; sourceTree = "<group>"; };
6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; };
+ 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolyline+MGLAdditions.h"; sourceTree = "<group>"; };
+ 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolyline+MGLAdditions.m"; sourceTree = "<group>"; };
8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; };
8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; };
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; };
@@ -994,6 +1002,8 @@
96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = "<group>"; };
+ AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_cn-geotrust_2018.der"; sourceTree = "<group>"; };
+ AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_cn-digicert_2018.der"; sourceTree = "<group>"; };
AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = "<group>"; };
AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = "<group>"; };
CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewIntegrationTest.m; sourceTree = "<group>"; };
@@ -1812,8 +1822,8 @@
4031ACFD1E9FD26900A3EA26 /* Test Helpers */,
409F43FB1E9E77D10048729D /* Swift Integration */,
357579811D502AD4000B822E /* Styling */,
- 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */,
DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */,
+ 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */,
DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */,
35D9DDE11DA25EEC00DAAD69 /* MGLCodingTests.m */,
DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */,
@@ -1929,6 +1939,13 @@
DA89339F1CCC951200E68420 /* Localizable.strings */,
DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */,
DA8933EF1CCD387900E68420 /* strip-frameworks.sh */,
+ 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */,
+ AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */,
+ AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */,
+ 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */,
+ 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */,
+ 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */,
+ 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */,
);
name = "Kit Resources";
path = resources;
@@ -1956,6 +1973,16 @@
path = ../vendor/SMCalloutView;
sourceTree = "<group>";
};
+ DA8848911CBB049300AB86E3 /* reachability */ = {
+ isa = PBXGroup;
+ children = (
+ DA88488D1CBB047F00AB86E3 /* reachability.h */,
+ DA88488F1CBB048E00AB86E3 /* reachability.m */,
+ );
+ name = reachability;
+ path = ..;
+ sourceTree = "<group>";
+ };
DA8933B91CCD2C6700E68420 /* Foundation Resources */ = {
isa = PBXGroup;
children = (
@@ -2769,10 +2796,17 @@
files = (
DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */,
DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */,
+ AC0C15F5209D0E7200B65675 /* api_mapbox_cn-digicert_2018.der in Resources */,
960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */,
DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */,
DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */,
DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */,
+ 40EA6BC11EF4599600FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */,
+ 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */,
+ AC0C15F3209D0E6900B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */,
+ 408982EA1DEE208B00754016 /* api_mapbox_com-digicert_2016.der in Resources */,
+ 40EA6BC31EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */,
+ 408982EB1DEE209100754016 /* api_mapbox_com-geotrust_2016.der in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2780,11 +2814,17 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ AC0C15F4209D0E7000B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */,
DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */,
DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */,
960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */,
DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */,
DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */,
+ 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */,
+ 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */,
+ 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */,
+ AC0C15F6209D0E7300B65675 /* api_mapbox_cn-digicert_2018.der in Resources */,
+ 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/platform/ios/resources/api_mapbox_cn-digicert_2018.der b/platform/ios/resources/api_mapbox_cn-digicert_2018.der
new file mode 100644
index 0000000000..e458713337
--- /dev/null
+++ b/platform/ios/resources/api_mapbox_cn-digicert_2018.der
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_cn-geotrust_2018.der b/platform/ios/resources/api_mapbox_cn-geotrust_2018.der
new file mode 100644
index 0000000000..e3d4b222ae
--- /dev/null
+++ b/platform/ios/resources/api_mapbox_cn-geotrust_2018.der
Binary files differ
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
new file mode 100644
index 0000000000..68e78835c3
--- /dev/null
+++ b/platform/ios/src/MGLAPIClient.m
@@ -0,0 +1,218 @@
+#import "MGLAPIClient.h"
+#import "NSBundle+MGLAdditions.h"
+#import "NSData+MGLAdditions.h"
+#import "MGLAccountManager.h"
+#import "MGLNetworkConfiguration.h"
+
+static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS";
+static NSString * const MGLAPIClientBaseURL = @"https://events.mapbox.com";
+static NSString * const MGLAPIClientChinaBaseURL = @"https://events.mapbox.cn";
+static NSString * const MGLAPIClientEventsPath = @"events/v2";
+
+static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent";
+static NSString * const MGLAPIClientHeaderFieldContentTypeKey = @"Content-Type";
+static NSString * const MGLAPIClientHeaderFieldContentTypeValue = @"application/json";
+static NSString * const MGLAPIClientHeaderFieldContentEncodingKey = @"Content-Encoding";
+static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
+
+@interface MGLAPIClient ()
+
+@property (nonatomic, copy) NSURLSession *session;
+@property (nonatomic, copy) NSURL *baseURL;
+@property (nonatomic, copy) NSData *digicertCert_2016;
+@property (nonatomic, copy) NSData *geoTrustCert_2016;
+@property (nonatomic, copy) NSData *digicertCert_2017;
+@property (nonatomic, copy) NSData *geoTrustCert_2017;
+@property (nonatomic, copy) NSData *digicertCert_cn_2018;
+@property (nonatomic, copy) NSData *geoTrustCert_cn_2018;
+@property (nonatomic, copy) NSData *testServerCert;
+@property (nonatomic, copy) NSString *userAgent;
+@property (nonatomic) BOOL usesTestServer;
+
+@end
+
+@implementation MGLAPIClient
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
+ delegate:self delegateQueue:nil];
+ [self loadCertificates];
+ [self setupBaseURL];
+ [self setupUserAgent];
+ }
+ return self;
+}
+
+#pragma mark Public API
+
+- (void)postEvents:(nonnull NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
+ __block NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:[self requestForEvents:events]
+ completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+ NSError *statusError = nil;
+ if (httpResponse.statusCode >= 400) {
+ NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_DESC", nil, nil, @"The session data task failed. Original request was: %@", nil), dataTask.originalRequest];
+ NSString *reason = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_REASON", nil, nil, @"The status code was %ld", nil), (long)httpResponse.statusCode];
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description,
+ NSLocalizedFailureReasonErrorKey: reason};
+ statusError = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
+ }
+ if (completionHandler) {
+ error = error ?: statusError;
+ completionHandler(error);
+ }
+ dataTask = nil;
+ }];
+ [dataTask resume];
+}
+
+- (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
+ [self postEvents:@[event] completionHandler:completionHandler];
+}
+
+#pragma mark Utilities
+
+- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
+ NSString *path = [NSString stringWithFormat:@"%@?access_token=%@", MGLAPIClientEventsPath, [MGLAccountManager accessToken]];
+ NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
+ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
+ [request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey];
+ [request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey];
+ [request setHTTPMethod:MGLAPIClientHTTPMethodPost];
+
+ NSData *jsonData = [self serializedDataForEvents:events];
+
+ // Compressing less than 3 events can have a negative impact on the size.
+ if (events.count > 2) {
+ NSData *compressedData = [jsonData mgl_compressedData];
+ [request setValue:@"deflate" forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
+ [request setHTTPBody:compressedData];
+ }
+
+ // Set JSON data if events.count were less than 3 or something went wrong with compressing HTTP body data.
+ if (!request.HTTPBody) {
+ [request setValue:nil forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
+ [request setHTTPBody:jsonData];
+ }
+
+ return [request copy];
+}
+
+- (void)setupBaseURL {
+ NSString *testServerURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"MGLTelemetryTestServerURL"];
+ NSURL *testServerURL = [NSURL URLWithString:testServerURLString];
+ if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) {
+ self.baseURL = testServerURL;
+ self.usesTestServer = YES;
+ } else if ([[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxAPIBaseURL"] isEqualToString:MGLChinaMapboxAPIBaseURL]) {
+ self.baseURL = [NSURL URLWithString:MGLAPIClientChinaBaseURL];
+ } else {
+ self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL];
+ }
+}
+
+- (void)loadCertificates {
+ NSData *certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2016"];
+ self.geoTrustCert_2016 = certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2016"];
+ self.digicertCert_2016 = certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2017"];
+ self.geoTrustCert_2017 = certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2017"];
+ self.digicertCert_2017 = certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_cn-geotrust_2018"];
+ self.geoTrustCert_cn_2018 = certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_cn-digicert_2018"];
+ self.digicertCert_cn_2018 = certificate;
+ [self loadCertificate:&certificate withResource:@"api_mapbox_staging"];
+ self.testServerCert = certificate;
+}
+
+- (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource {
+ NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle];
+ NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der"];
+ if (cerPath != nil) {
+ *certificate = [NSData dataWithContentsOfFile:cerPath];
+ }
+}
+
+- (void)setupUserAgent {
+ NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
+ NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
+ NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
+ NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
+ NSString *sdkVersion = semanticVersion ?: shortVersion;
+ _userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@/%@", appName, appVersion, appBuildNumber, MGLAPIClientUserAgentBase, sdkVersion];
+}
+
+#pragma mark - JSON Serialization
+
+- (NSData *)serializedDataForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
+ return [NSJSONSerialization dataWithJSONObject:events options:0 error:nil];
+}
+
+#pragma mark NSURLSessionDelegate
+
+- (BOOL)evaluateCertificateWithCertificateData:(NSData *)certificateData keyCount:(CFIndex)keyCount serverTrust:(SecTrustRef)serverTrust challenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
+ for (int lc = 0; lc < keyCount; lc++) {
+ SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
+ NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
+ if ([remoteCertificateData isEqualToData:certificateData]) {
+ completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
+
+ if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
+ SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
+ SecTrustResultType trustResult;
+
+ // Validate the certificate chain with the device's trust store anyway this *might* use revocation checking
+ SecTrustEvaluate(serverTrust, &trustResult);
+
+ BOOL found = NO; // For clarity; we start in a state where the challange has not been completed and no certificate has been found
+
+ if (trustResult == kSecTrustResultUnspecified) {
+ // Look for a pinned certificate in the server's certificate chain
+ CFIndex numKeys = SecTrustGetCertificateCount(serverTrust);
+
+ // Check certs in the following order: digicert 2016, digicert 2017, digicert CN 2018, geotrust 2016, geotrust 2017, geotrust CN 2018
+ found = [self evaluateCertificateWithCertificateData:self.digicertCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ if (!found) {
+ found = [self evaluateCertificateWithCertificateData:self.digicertCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ }
+ if (!found) {
+ found = [self evaluateCertificateWithCertificateData:self.digicertCert_cn_2018 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ }
+ if (!found) {
+ found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ }
+ if (!found) {
+ found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ }
+ if (!found) {
+ found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_cn_2018 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ }
+
+ // If challenge can't be completed with any of the above certs, then try the test server if the app is configured to use the test server
+ if (!found && _usesTestServer) {
+ found = [self evaluateCertificateWithCertificateData:self.testServerCert keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
+ }
+ }
+
+ if (!found) {
+ // No certificate was found so cancel the connection.
+ completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ }
+ }
+}
+
+@end