Ben Copsey

Allow adding extra client certificates via the clientCertificates array

Added a test for client certificates (iOS only)
Attempt to fix, YET AGAIN, building for 10.6 and that stupid NSXMLParserDelegate
@@ -335,6 +335,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -335,6 +335,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
335 335
336 // If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate 336 // If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate
337 SecIdentityRef clientCertificateIdentity; 337 SecIdentityRef clientCertificateIdentity;
  338 + NSArray *clientCertificates;
338 339
339 // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings 340 // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings
340 NSString *proxyHost; 341 NSString *proxyHost;
@@ -741,7 +742,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -741,7 +742,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
741 // Hides the network activity spinner thing on iOS 742 // Hides the network activity spinner thing on iOS
742 + (void)hideNetworkActivityIndicator; 743 + (void)hideNetworkActivityIndicator;
743 744
744 -  
745 #pragma mark miscellany 745 #pragma mark miscellany
746 746
747 // Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true 747 // Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true
@@ -854,4 +854,5 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -854,4 +854,5 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
854 @property (assign) ASICacheStoragePolicy cacheStoragePolicy; 854 @property (assign) ASICacheStoragePolicy cacheStoragePolicy;
855 @property (assign, readonly) BOOL didUseCachedResponse; 855 @property (assign, readonly) BOOL didUseCachedResponse;
856 @property (assign) NSTimeInterval secondsToCache; 856 @property (assign) NSTimeInterval secondsToCache;
  857 +@property (retain) NSArray *clientCertificates;
857 @end 858 @end
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 23
24 24
25 // Automatically set on build 25 // Automatically set on build
26 -NSString *ASIHTTPRequestVersion = @"v1.7-53 2010-08-18"; 26 +NSString *ASIHTTPRequestVersion = @"v1.7-50 2010-08-18";
27 27
28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
29 29
@@ -318,6 +318,9 @@ static NSOperationQueue *sharedQueue = nil; @@ -318,6 +318,9 @@ static NSOperationQueue *sharedQueue = nil;
318 if (request) { 318 if (request) {
319 CFRelease(request); 319 CFRelease(request);
320 } 320 }
  321 + if (clientCertificateIdentity) {
  322 + CFRelease(clientCertificateIdentity);
  323 + }
321 [self cancelLoad]; 324 [self cancelLoad];
322 [queue release]; 325 [queue release];
323 [userInfo release]; 326 [userInfo release];
@@ -359,11 +362,6 @@ static NSOperationQueue *sharedQueue = nil; @@ -359,11 +362,6 @@ static NSOperationQueue *sharedQueue = nil;
359 [responseStatusMessage release]; 362 [responseStatusMessage release];
360 [connectionInfo release]; 363 [connectionInfo release];
361 [requestID release]; 364 [requestID release];
362 -  
363 - if (clientCertificateIdentity) {  
364 - CFRelease(clientCertificateIdentity);  
365 - }  
366 -  
367 [super dealloc]; 365 [super dealloc];
368 } 366 }
369 367
@@ -946,29 +944,34 @@ static NSOperationQueue *sharedQueue = nil; @@ -946,29 +944,34 @@ static NSOperationQueue *sharedQueue = nil;
946 // 944 //
947 // Handle SSL certificate settings 945 // Handle SSL certificate settings
948 // 946 //
  947 +
949 if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) { 948 if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) {
950 - NSMutableDictionary *sslProperties = [[NSMutableDictionary alloc] initWithCapacity:1]; 949 +
951 - 950 + NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];
  951 +
952 // Tell CFNetwork not to validate SSL certificates 952 // Tell CFNetwork not to validate SSL certificates
953 if (![self validatesSecureCertificate]) { 953 if (![self validatesSecureCertificate]) {
954 [sslProperties setObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]; 954 [sslProperties setObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
955 } 955 }
956 - 956 +
957 // Tell CFNetwork to use a client certificate 957 // Tell CFNetwork to use a client certificate
958 if (clientCertificateIdentity) { 958 if (clientCertificateIdentity) {
959 - CFArrayRef ca = CFArrayCreate(NULL, (const void **)&clientCertificateIdentity, 1, NULL); 959 +
960 - 960 + NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1];
961 - [sslProperties setObject:(NSArray *)ca forKey:(NSString *)kCFStreamSSLCertificates]; 961 +
962 - 962 + // The first object in the array is our SecIdentityRef
963 - CFRelease(ca); 963 + [certificates addObject:(id)clientCertificateIdentity];
  964 +
  965 + // If we've added any additional certificates, add them too
  966 + for (id cert in clientCertificates) {
  967 + [certificates addObject:cert];
  968 + }
  969 + [sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates];
964 } 970 }
965 - 971 +
966 CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties); 972 CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties);
967 -  
968 - [sslProperties release];  
969 } 973 }
970 - 974 +
971 -  
972 975
973 976
974 // 977 //
@@ -4086,8 +4089,6 @@ static NSOperationQueue *sharedQueue = nil; @@ -4086,8 +4089,6 @@ static NSOperationQueue *sharedQueue = nil;
4086 CFRelease(source); 4089 CFRelease(source);
4087 } 4090 }
4088 4091
4089 -  
4090 -  
4091 #pragma mark miscellany 4092 #pragma mark miscellany
4092 4093
4093 // From: http://www.cocoadev.com/index.pl?BaseSixtyFour 4094 // From: http://www.cocoadev.com/index.pl?BaseSixtyFour
@@ -4250,4 +4251,5 @@ static NSOperationQueue *sharedQueue = nil; @@ -4250,4 +4251,5 @@ static NSOperationQueue *sharedQueue = nil;
4250 @synthesize cacheStoragePolicy; 4251 @synthesize cacheStoragePolicy;
4251 @synthesize didUseCachedResponse; 4252 @synthesize didUseCachedResponse;
4252 @synthesize secondsToCache; 4253 @synthesize secondsToCache;
  4254 +@synthesize clientCertificates;
4253 @end 4255 @end
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 6
7 #import "ASICloudFilesRequest.h" 7 #import "ASICloudFilesRequest.h"
8 8
9 -#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0) 9 +#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
10 #import "ASINSXMLParserCompat.h" 10 #import "ASINSXMLParserCompat.h"
11 #endif 11 #endif
12 12
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 6
7 #import "ASICloudFilesRequest.h" 7 #import "ASICloudFilesRequest.h"
8 8
9 -#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0) 9 +#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
10 #import "ASINSXMLParserCompat.h" 10 #import "ASINSXMLParserCompat.h"
11 #endif 11 #endif
12 12
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 // 8 //
9 9
10 10
11 -#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED <= __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_4_0) 11 +#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_4_0)
12 @protocol NSXMLParserDelegate 12 @protocol NSXMLParserDelegate
13 13
14 @optional 14 @optional
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 #import <Foundation/Foundation.h> 11 #import <Foundation/Foundation.h>
12 #import "ASIHTTPRequest.h" 12 #import "ASIHTTPRequest.h"
13 13
14 -#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0) 14 +#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
15 #import "ASINSXMLParserCompat.h" 15 #import "ASINSXMLParserCompat.h"
16 #endif 16 #endif
17 17
  1 +//
  2 +// ClientCertificateTests.h
  3 +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4 +//
  5 +// Created by Ben Copsey on 18/08/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +
  9 +// Currently, these tests only work on iOS - it looks like the method for parsing the PKCS12 file would need to be ported
  10 +
  11 +#import <Foundation/Foundation.h>
  12 +#import <Security/Security.h>
  13 +#import "ASITestCase.h"
  14 +
  15 +@interface ClientCertificateTests : ASITestCase {
  16 +
  17 +}
  18 +- (void)testClientCertificate;
  19 ++ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data;
  20 +
  21 +@end
  1 +//
  2 +// ClientCertificateTests.m
  3 +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4 +//
  5 +// Created by Ben Copsey on 18/08/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +
  9 +#import "ClientCertificateTests.h"
  10 +#import "ASIHTTPRequest.h"
  11 +
  12 +@implementation ClientCertificateTests
  13 +
  14 +- (void)testClientCertificate
  15 +{
  16 + // This test will fail the second time it is run, I presume the certificate is being cached somewhere
  17 +
  18 + // This url requires we present a client certificate to connect to it
  19 + NSURL *url = [NSURL URLWithString:@"https://clientcertificate.allseeing-i.com:8080/ASIHTTPRequest/tests/first"];
  20 +
  21 + // First, let's attempt to connect to the url without supplying a certificate
  22 + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
  23 +
  24 + // We have to turn off validation for these tests, as the server has a self-signed certificate
  25 + [request setValidatesSecureCertificate:NO];
  26 + [request startSynchronous];
  27 +
  28 + GHAssertNotNil([request error],@"Request succeeded even though we presented no certificate, cannot proceed with test");
  29 +
  30 + // Now, let's grab the certificate (included in the resources of the test app)
  31 + SecIdentityRef identity = NULL;
  32 + SecTrustRef trust = NULL;
  33 + NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];
  34 + [ClientCertificateTests extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
  35 +
  36 + request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://clientcertificate.allseeing-i.com:8080/ASIHTTPRequest/tests/first"]];
  37 +
  38 + // In this case, we have no need to add extra certificates, just the one inside the indentity will be used
  39 + [request setClientCertificateIdentity:identity];
  40 + [request setValidatesSecureCertificate:NO];
  41 + [request startSynchronous];
  42 +
  43 + // Make sure the request got the correct content
  44 + GHAssertNil([request error],@"Request failed with error %@",[request error]);
  45 + BOOL success = [[request responseString] isEqualToString:@"This is the expected content for the first string"];
  46 + GHAssertTrue(success,@"Request failed to download the correct content");
  47 +}
  48 +
  49 +// Based on code from http://developer.apple.com/mac/library/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html
  50 +
  51 ++ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data
  52 +{
  53 + OSStatus securityError = errSecSuccess;
  54 +
  55 + NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"" forKey:(id)kSecImportExportPassphrase];
  56 +
  57 + CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  58 + securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items);
  59 +
  60 + if (securityError == 0) {
  61 + CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
  62 + const void *tempIdentity = NULL;
  63 + tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
  64 + *outIdentity = (SecIdentityRef)tempIdentity;
  65 + const void *tempTrust = NULL;
  66 + tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
  67 + *outTrust = (SecTrustRef)tempTrust;
  68 + } else {
  69 + NSLog(@"Failed with error code %i",securityError);
  70 + return NO;
  71 + }
  72 + return YES;
  73 +}
  74 +
  75 +
  76 +@end
This diff was suppressed by a .gitattributes entry.
No preview for this file type
This diff was suppressed by a .gitattributes entry.