blob: 584b974ff2e3f91219ca125dee00bda91e5d1432 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
//
// ISPCertificatePinning.m
// SSLCertificatePinning
//
// Created by Alban Diquet on 1/14/14.
// Copyright (c) 2014 iSEC Partners. All rights reserved.
//
#import "ISPCertificatePinning.h"
// All the pinned certificate are stored in this plist on the filesystem
#define PINNED_KEYS_FILE_PATH "~/Library/SSLPins.plist"
@implementation ISPCertificatePinning
+ (BOOL)setupSSLPinsUsingDictionnary:(NSDictionary*)domainsAndCertificates {
if (domainsAndCertificates == nil) {
return NO;
}
// Serialize the dictionary to a plist
NSError *error;
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:domainsAndCertificates
format:NSPropertyListXMLFormat_v1_0
options:0
error:&error];
if (plistData == nil) {
NSLog(@"Error serializing plist: %@", error);
return NO;
}
// Write the plist to a pre-defined location on the filesystem
NSError *writeError;
if ([plistData writeToFile:[@PINNED_KEYS_FILE_PATH stringByExpandingTildeInPath]
options:NSDataWritingAtomic
error:&writeError] == NO) {
NSLog(@"Error saving plist to the filesystem: %@", writeError);
return NO;
}
return YES;
}
+ (BOOL)verifyPinnedCertificateForTrust:(SecTrustRef)trust andDomain:(NSString*)domain {
if ((trust == NULL) || (domain == nil)) {
return NO;
}
// Deserialize the plist that contains our SSL pins
NSDictionary *SSLPinsDict = [NSDictionary dictionaryWithContentsOfFile:[@PINNED_KEYS_FILE_PATH stringByExpandingTildeInPath]];
if (SSLPinsDict == nil) {
NSLog(@"Error accessing the SSL Pins plist at %@", @PINNED_KEYS_FILE_PATH);
return NO;
}
// Do we have certificates pinned for this domain ?
NSArray *trustedCertificates = [SSLPinsDict objectForKey:domain];
if ((trustedCertificates == nil) || ([trustedCertificates count] < 1)) {
return NO;
}
// For each pinned certificate, check if it is part of the server's cert trust chain
// We only need one of the pinned certificates to be in the server's trust chain
for (NSData *pinnedCertificate in trustedCertificates) {
// Check each certificate in the server's trust chain (the trust object)
// Unfortunately the anchor/CA certificate cannot be accessed this way
CFIndex certsNb = SecTrustGetCertificateCount(trust);
for(int i=0;i<certsNb;i++) {
// Extract the certificate
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, i);
NSData* DERCertificate = (__bridge NSData*) SecCertificateCopyData(certificate);
// Compare the two certificates
if ([pinnedCertificate isEqualToData:DERCertificate]) {
return YES;
}
}
// Check the anchor/CA certificate separately
SecCertificateRef anchorCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(pinnedCertificate));
if (anchorCertificate == NULL) {
break;
}
NSArray *anchorArray = [NSArray arrayWithObject:(__bridge id)(anchorCertificate)];
if (SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)(anchorArray)) != 0) {
CFRelease(anchorCertificate);
break;
}
SecTrustResultType trustResult;
SecTrustEvaluate(trust, &trustResult);
if (trustResult == kSecTrustResultUnspecified) {
// The anchor certificate was pinned
CFRelease(anchorCertificate);
return YES;
}
CFRelease(anchorCertificate);
}
// If we get here, we didn't find any matching certificate in the chain
return NO;
}
@end
|