Skip to content

Commit

Permalink
Allow adding extra client certificates via the clientCertificates array
Browse files Browse the repository at this point in the history
Added a test for client certificates (iOS only)
Attempt to fix, YET AGAIN, building for 10.6 and that stupid NSXMLParserDelegate
  • Loading branch information
pokeb committed Aug 18, 2010
1 parent 4e354bd commit 68820ae
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 29 deletions.
3 changes: 2 additions & 1 deletion Classes/ASIHTTPRequest.h
Expand Up @@ -335,6 +335,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;

// If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate
SecIdentityRef clientCertificateIdentity;
NSArray *clientCertificates;

// Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings
NSString *proxyHost;
Expand Down Expand Up @@ -741,7 +742,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Hides the network activity spinner thing on iOS
+ (void)hideNetworkActivityIndicator;


#pragma mark miscellany

// Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true
Expand Down Expand Up @@ -854,4 +854,5 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
@property (assign) ASICacheStoragePolicy cacheStoragePolicy;
@property (assign, readonly) BOOL didUseCachedResponse;
@property (assign) NSTimeInterval secondsToCache;
@property (retain) NSArray *clientCertificates;
@end
44 changes: 23 additions & 21 deletions Classes/ASIHTTPRequest.m
Expand Up @@ -23,7 +23,7 @@


// Automatically set on build
NSString *ASIHTTPRequestVersion = @"v1.7-53 2010-08-18";
NSString *ASIHTTPRequestVersion = @"v1.7-50 2010-08-18";

NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";

Expand Down Expand Up @@ -318,6 +318,9 @@ - (void)dealloc
if (request) {
CFRelease(request);
}
if (clientCertificateIdentity) {
CFRelease(clientCertificateIdentity);
}
[self cancelLoad];
[queue release];
[userInfo release];
Expand Down Expand Up @@ -359,11 +362,6 @@ - (void)dealloc
[responseStatusMessage release];
[connectionInfo release];
[requestID release];

if (clientCertificateIdentity) {
CFRelease(clientCertificateIdentity);
}

[super dealloc];
}

Expand Down Expand Up @@ -946,29 +944,34 @@ - (void)startRequest
//
// Handle SSL certificate settings
//

if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) {
NSMutableDictionary *sslProperties = [[NSMutableDictionary alloc] initWithCapacity:1];


NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];

// Tell CFNetwork not to validate SSL certificates
if (![self validatesSecureCertificate]) {
[sslProperties setObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
}

// Tell CFNetwork to use a client certificate
if (clientCertificateIdentity) {
CFArrayRef ca = CFArrayCreate(NULL, (const void **)&clientCertificateIdentity, 1, NULL);

[sslProperties setObject:(NSArray *)ca forKey:(NSString *)kCFStreamSSLCertificates];

CFRelease(ca);

NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1];

// The first object in the array is our SecIdentityRef
[certificates addObject:(id)clientCertificateIdentity];

// If we've added any additional certificates, add them too
for (id cert in clientCertificates) {
[certificates addObject:cert];
}
[sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates];
}

CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties);

[sslProperties release];
}





//
Expand Down Expand Up @@ -4086,8 +4089,6 @@ + (void)runRequests
CFRelease(source);
}



#pragma mark miscellany

// From: http://www.cocoadev.com/index.pl?BaseSixtyFour
Expand Down Expand Up @@ -4250,4 +4251,5 @@ + (NSDate *)dateFromRFC1123String:(NSString *)string
@synthesize cacheStoragePolicy;
@synthesize didUseCachedResponse;
@synthesize secondsToCache;
@synthesize clientCertificates;
@end
Expand Up @@ -6,7 +6,7 @@

#import "ASICloudFilesRequest.h"

#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
#import "ASINSXMLParserCompat.h"
#endif

Expand Down
2 changes: 1 addition & 1 deletion Classes/CloudFiles/ASICloudFilesObjectRequest.h
Expand Up @@ -6,7 +6,7 @@

#import "ASICloudFilesRequest.h"

#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
#import "ASINSXMLParserCompat.h"
#endif

Expand Down
2 changes: 1 addition & 1 deletion Classes/S3/ASINSXMLParserCompat.h
Expand Up @@ -8,7 +8,7 @@
//


#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED <= __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_4_0)
#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_4_0)
@protocol NSXMLParserDelegate

@optional
Expand Down
2 changes: 1 addition & 1 deletion Classes/S3/ASIS3Request.h
Expand Up @@ -11,7 +11,7 @@
#import <Foundation/Foundation.h>
#import "ASIHTTPRequest.h"

#if (!TARGET_OS_IPHONE && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
#if !TARGET_OS_IPHONE || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_4_0)
#import "ASINSXMLParserCompat.h"
#endif

Expand Down
21 changes: 21 additions & 0 deletions Classes/Tests/ClientCertificateTests.h
@@ -0,0 +1,21 @@
//
// ClientCertificateTests.h
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
//
// Created by Ben Copsey on 18/08/2010.
// Copyright 2010 All-Seeing Interactive. All rights reserved.
//

// Currently, these tests only work on iOS - it looks like the method for parsing the PKCS12 file would need to be ported

#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import "ASITestCase.h"

@interface ClientCertificateTests : ASITestCase {

}
- (void)testClientCertificate;
+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data;

@end
76 changes: 76 additions & 0 deletions Classes/Tests/ClientCertificateTests.m
@@ -0,0 +1,76 @@
//
// ClientCertificateTests.m
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
//
// Created by Ben Copsey on 18/08/2010.
// Copyright 2010 All-Seeing Interactive. All rights reserved.
//

#import "ClientCertificateTests.h"
#import "ASIHTTPRequest.h"

@implementation ClientCertificateTests

- (void)testClientCertificate
{
// This test will fail the second time it is run, I presume the certificate is being cached somewhere

// This url requires we present a client certificate to connect to it
NSURL *url = [NSURL URLWithString:@"https://clientcertificate.allseeing-i.com:8080/ASIHTTPRequest/tests/first"];

// First, let's attempt to connect to the url without supplying a certificate
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

// We have to turn off validation for these tests, as the server has a self-signed certificate
[request setValidatesSecureCertificate:NO];
[request startSynchronous];

GHAssertNotNil([request error],@"Request succeeded even though we presented no certificate, cannot proceed with test");

// Now, let's grab the certificate (included in the resources of the test app)
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];
[ClientCertificateTests extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];

request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://clientcertificate.allseeing-i.com:8080/ASIHTTPRequest/tests/first"]];

// In this case, we have no need to add extra certificates, just the one inside the indentity will be used
[request setClientCertificateIdentity:identity];
[request setValidatesSecureCertificate:NO];
[request startSynchronous];

// Make sure the request got the correct content
GHAssertNil([request error],@"Request failed with error %@",[request error]);
BOOL success = [[request responseString] isEqualToString:@"This is the expected content for the first string"];
GHAssertTrue(success,@"Request failed to download the correct content");
}

// Based on code from http://developer.apple.com/mac/library/documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html

+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data
{
OSStatus securityError = errSecSuccess;

NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"" forKey:(id)kSecImportExportPassphrase];

CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items);

if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
} else {
NSLog(@"Failed with error code %i",securityError);
return NO;
}
return YES;
}


@end
12 changes: 9 additions & 3 deletions Mac.xcodeproj/project.pbxproj
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
B502441B1025D36B002B13E1 /* ProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B502441A1025D36B002B13E1 /* ProxyTests.m */; };
B50C17DB121C254D0055FCAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50C17DA121C254D0055FCAB /* Security.framework */; };
B50FDEAA115A325E00BBC736 /* GHUnit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B5DB497C115624BF0062DB57 /* GHUnit.framework */; };
B51A1A9511DDF84700ED75CF /* ASIDownloadCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B51A1A9411DDF84700ED75CF /* ASIDownloadCacheTests.m */; };
B522D71F103074AC009A2D22 /* ASIInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B522D71E103074AC009A2D22 /* ASIInputStream.m */; };
Expand Down Expand Up @@ -98,6 +99,8 @@
8D1107320486CEB800E47090 /* Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mac.app; sourceTree = BUILT_PRODUCTS_DIR; };
B50244191025D36B002B13E1 /* ProxyTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProxyTests.h; sourceTree = "<group>"; };
B502441A1025D36B002B13E1 /* ProxyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProxyTests.m; sourceTree = "<group>"; };
B50C17DA121C254D0055FCAB /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
B50C18D1121C2F240055FCAB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
B515507810BD56E800608267 /* strict.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = strict.xcconfig; sourceTree = "<group>"; };
B51791281024BF0F00583567 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
B51A1A9311DDF84700ED75CF /* ASIDownloadCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDownloadCacheTests.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -192,6 +195,7 @@
B5DB49CA115627300062DB57 /* GHUnit.framework in Frameworks */,
B55B60140F7659A30064029C /* libz.1.2.3.dylib in Frameworks */,
B522DE601031BB03009A2D22 /* SystemConfiguration.framework in Frameworks */,
B50C17DB121C254D0055FCAB /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -237,6 +241,8 @@
B55B5EB70F7658200064029C /* libz.1.2.3.dylib */,
B53FADE70FF38B2A002E2DE6 /* SystemConfiguration.framework */,
B51791281024BF0F00583567 /* Security.framework */,
B50C17DA121C254D0055FCAB /* Security.framework */,
B50C18D1121C2F240055FCAB /* Foundation.framework */,
);
name = Mac;
sourceTree = "<group>";
Expand Down Expand Up @@ -615,7 +621,7 @@
);
PREBINDING = NO;
PRODUCT_NAME = "Unit Tests (GHUnit)";
SDKROOT = macosx10.5;
SDKROOT = macosx10.6;
};
name = Debug;
};
Expand All @@ -636,7 +642,7 @@
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
INFOPLIST_FILE = "Mac Sample/Tests-Info.plist";
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.5;
MACOSX_DEPLOYMENT_TARGET = 10.6;
OTHER_LDFLAGS = (
"-framework",
Foundation,
Expand All @@ -645,7 +651,7 @@
);
PREBINDING = NO;
PRODUCT_NAME = "Unit Tests (GHUnit)";
SDKROOT = macosx10.5;
SDKROOT = macosx10.6;
ZERO_LINK = NO;
};
name = Release;
Expand Down
Binary file added iPhone Sample/Resources/client.p12
Binary file not shown.
14 changes: 14 additions & 0 deletions iPhone.xcodeproj/project.pbxproj
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
B50C1823121C26DB0055FCAB /* ClientCertificateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B50C1822121C26DB0055FCAB /* ClientCertificateTests.m */; };
B50C182C121C26FA0055FCAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B50C182B121C26FA0055FCAB /* Security.framework */; };
B50C1848121C27510055FCAB /* client.p12 in Resources */ = {isa = PBXBuildFile; fileRef = B50C1847121C27510055FCAB /* client.p12 */; };
B51791A31024C3E800583567 /* AuthenticationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B51791A21024C3E800583567 /* AuthenticationViewController.m */; };
B51A1A9B11DDF85100ED75CF /* ASIDownloadCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B51A1A7A11DDF7BF00ED75CF /* ASIDownloadCacheTests.m */; };
B522DACE1030B2AB009A2D22 /* ASIInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B522DACD1030B2AB009A2D22 /* ASIInputStream.m */; };
Expand Down Expand Up @@ -122,6 +125,10 @@

/* Begin PBXFileReference section */
1D6058910D05DD3D006BFB54 /* ASIHTTPRequest iPhone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ASIHTTPRequest iPhone.app"; sourceTree = BUILT_PRODUCTS_DIR; };
B50C1821121C26DB0055FCAB /* ClientCertificateTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClientCertificateTests.h; sourceTree = "<group>"; };
B50C1822121C26DB0055FCAB /* ClientCertificateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ClientCertificateTests.m; sourceTree = "<group>"; };
B50C182B121C26FA0055FCAB /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
B50C1847121C27510055FCAB /* client.p12 */ = {isa = PBXFileReference; lastKnownFileType = file; name = client.p12; path = "iPhone Sample/Resources/client.p12"; sourceTree = "<group>"; };
B51791A11024C3E800583567 /* AuthenticationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AuthenticationViewController.h; path = "iPhone Sample/AuthenticationViewController.h"; sourceTree = "<group>"; };
B51791A21024C3E800583567 /* AuthenticationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AuthenticationViewController.m; path = "iPhone Sample/AuthenticationViewController.m"; sourceTree = "<group>"; };
B51A1A7911DDF7BF00ED75CF /* ASIDownloadCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDownloadCacheTests.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -280,6 +287,7 @@
B576D4D111C7CCBC0059B815 /* CoreGraphics.framework in Frameworks */,
B576D51411C7CE6D0059B815 /* Foundation.framework in Frameworks */,
B576D53E11C7CF350059B815 /* libz.1.2.3.dylib in Frameworks */,
B50C182C121C26FA0055FCAB /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -360,6 +368,7 @@
B576D53D11C7CF350059B815 /* libz.1.2.3.dylib */,
B552511311D20FC200F9B170 /* CoreGraphics.framework */,
B55251A011D2174C00F9B170 /* CoreGraphics.framework */,
B50C182B121C26FA0055FCAB /* Security.framework */,
);
name = iPhone;
sourceTree = "<group>";
Expand Down Expand Up @@ -395,6 +404,7 @@
B576D76511C7F4D90059B815 /* iPhoneMainWindow.xib */,
B576D76311C7F4D40059B815 /* iPadMainWindow.xib */,
B523254111CA01F1006C6E5A /* Sample.xib */,
B50C1847121C27510055FCAB /* client.p12 */,
B56529A2101C8EDA000499CF /* iphone-tests-icon.png */,
B55B60730F765A990064029C /* iphone-icon.png */,
B55B60CB0F765BB10064029C /* Tests-Info.plist */,
Expand Down Expand Up @@ -458,6 +468,8 @@
B5254FF71025F9BF00CF7BC4 /* ProxyTests.m */,
B52D4A2D10DA4ED5008E8365 /* PerformanceTests.h */,
B52D4A2E10DA4ED5008E8365 /* PerformanceTests.m */,
B50C1821121C26DB0055FCAB /* ClientCertificateTests.h */,
B50C1822121C26DB0055FCAB /* ClientCertificateTests.m */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -626,6 +638,7 @@
buildActionMask = 2147483647;
files = (
B56529A3101C8EDA000499CF /* iphone-tests-icon.png in Resources */,
B50C1848121C27510055FCAB /* client.p12 in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -744,6 +757,7 @@
B55252B411D22E2200F9B170 /* Reachability.m in Sources */,
B5FE752711DBBA6400F898C8 /* ASIDownloadCache.m in Sources */,
B51A1A9B11DDF85100ED75CF /* ASIDownloadCacheTests.m in Sources */,
B50C1823121C26DB0055FCAB /* ClientCertificateTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

0 comments on commit 68820ae

Please sign in to comment.