diff --git a/Tests/AFNetworking Tests.xcodeproj/project.pbxproj b/Tests/AFNetworking Tests.xcodeproj/project.pbxproj index 4e64c3d..f4c273d 100644 --- a/Tests/AFNetworking Tests.xcodeproj/project.pbxproj +++ b/Tests/AFNetworking Tests.xcodeproj/project.pbxproj @@ -27,6 +27,10 @@ 29A9CE2217456336002360C8 /* AFJSONRequestOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */; }; A70F4A96175A529400386DF5 /* root_certificate.cer in Resources */ = {isa = PBXBuildFile; fileRef = A70F4A95175A529400386DF5 /* root_certificate.cer */; }; A70F4A97175A529400386DF5 /* root_certificate.cer in Resources */ = {isa = PBXBuildFile; fileRef = A70F4A95175A529400386DF5 /* root_certificate.cer */; }; + A70F4A9E175A726B00386DF5 /* ca.cer in Resources */ = {isa = PBXBuildFile; fileRef = A70F4A9C175A726B00386DF5 /* ca.cer */; }; + A70F4A9F175A726B00386DF5 /* ca.cer in Resources */ = {isa = PBXBuildFile; fileRef = A70F4A9C175A726B00386DF5 /* ca.cer */; }; + A70F4AA0175A726B00386DF5 /* derived.cert in Resources */ = {isa = PBXBuildFile; fileRef = A70F4A9D175A726B00386DF5 /* derived.cert */; }; + A70F4AA1175A726B00386DF5 /* derived.cert in Resources */ = {isa = PBXBuildFile; fileRef = A70F4A9D175A726B00386DF5 /* derived.cert */; }; A7DC62A617592E4200EBEC2F /* AFTestURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DC62A517592E4200EBEC2F /* AFTestURLProtocol.m */; }; A7DC62A717592E4200EBEC2F /* AFTestURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DC62A517592E4200EBEC2F /* AFTestURLProtocol.m */; }; A7DC62A917592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DC62A817592E4800EBEC2F /* AFURLConnectionOperationTests.m */; }; @@ -81,6 +85,8 @@ 55E73C267F33406A9F92476C /* libPods-ios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 96A923755B00464187DEDBAF /* libPods-osx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-osx.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A70F4A95175A529400386DF5 /* root_certificate.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = root_certificate.cer; path = Resources/root_certificate.cer; sourceTree = ""; }; + A70F4A9C175A726B00386DF5 /* ca.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = ca.cer; path = Resources/ca.cer; sourceTree = ""; }; + A70F4A9D175A726B00386DF5 /* derived.cert */ = {isa = PBXFileReference; lastKnownFileType = file; name = derived.cert; path = Resources/derived.cert; sourceTree = ""; }; A7DC62A417592E4200EBEC2F /* AFTestURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFTestURLProtocol.h; sourceTree = ""; }; A7DC62A517592E4200EBEC2F /* AFTestURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFTestURLProtocol.m; sourceTree = ""; }; A7DC62A817592E4800EBEC2F /* AFURLConnectionOperationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLConnectionOperationTests.m; sourceTree = ""; }; @@ -222,6 +228,8 @@ A70F4A91175A4E0000386DF5 /* Certificates */ = { isa = PBXGroup; children = ( + A70F4A9C175A726B00386DF5 /* ca.cer */, + A70F4A9D175A726B00386DF5 /* derived.cert */, A70F4A95175A529400386DF5 /* root_certificate.cer */, ); name = Certificates; @@ -300,6 +308,8 @@ files = ( F8C6F282174D2C6200B154D5 /* Icon.png in Resources */, A70F4A96175A529400386DF5 /* root_certificate.cer in Resources */, + A70F4A9E175A726B00386DF5 /* ca.cer in Resources */, + A70F4AA0175A726B00386DF5 /* derived.cert in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -309,6 +319,8 @@ files = ( F8C6F283174D2C6200B154D5 /* Icon.png in Resources */, A70F4A97175A529400386DF5 /* root_certificate.cer in Resources */, + A70F4A9F175A726B00386DF5 /* ca.cer in Resources */, + A70F4AA1175A726B00386DF5 /* derived.cert in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/AFURLConnectionOperationTests.m b/Tests/AFURLConnectionOperationTests.m index d575339..44cc410 100644 --- a/Tests/AFURLConnectionOperationTests.m +++ b/Tests/AFURLConnectionOperationTests.m @@ -180,4 +180,130 @@ expect(useCredentialInvoked).will.beTruthy(); } +- (void)testThatAFURLConnectionOperationTrustsPublicKeysOfDerivedCertificates { + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/path" relativeToURL:self.baseURL]]; + AFURLConnectionOperation *operation = [[AFURLConnectionOperation alloc] initWithRequest:request]; + operation.SSLPinningMode = AFSSLPinningModePublicKey; + + __block BOOL useCredentialInvoked = NO; + + NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:request.URL.host port:request.URL.port.integerValue protocol:request.URL.scheme realm:nil authenticationMethod:NSURLAuthenticationMethodServerTrust]; + + NSData *caCertificateData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"ca" ofType:@"cer"]]; + NSParameterAssert(caCertificateData); + + SecCertificateRef caCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCertificateData); + NSParameterAssert(caCertificate); + + NSData *hostCertificateData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"derived" ofType:@"cert"]]; + NSParameterAssert(hostCertificateData); + + SecCertificateRef hostCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCertificateData); + NSParameterAssert(hostCertificate); + + SecCertificateRef allowedCertificates[] = {caCertificate, hostCertificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 2, NULL); + + SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecTrustRef trust = NULL; + OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &trust); + NSAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status); + + SecTrustResultType result; + status = SecTrustEvaluate(trust, &result); + NSAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status); + + id mockedProtectionSpace = [OCMockObject partialMockForObject:protectionSpace]; + + [[[mockedProtectionSpace stub] andDo:^(NSInvocation *invocation) { + [invocation setReturnValue:(void *)&trust]; + }] serverTrust]; + + AFTestURLProtocol *protocol = [[AFTestURLProtocol alloc] initWithRequest:request cachedResponse:nil client:nil]; + id mockedProtocol = [OCMockObject partialMockForObject:protocol]; + + void(^useCredential)(NSInvocation *invocation) = ^(NSInvocation *invocation) { + useCredentialInvoked = YES; + }; + + [[[mockedProtocol stub] andDo:useCredential] useCredential:OCMOCK_ANY forAuthenticationChallenge:OCMOCK_ANY]; + + NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:trust]; + NSURLAuthenticationChallenge *authenticationChallenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:protectionSpace proposedCredential:credential previousFailureCount:0 failureResponse:nil error:nil sender:mockedProtocol]; + [protocol.client URLProtocol:mockedProtocol didReceiveAuthenticationChallenge:authenticationChallenge]; + + [operation connection:nil willSendRequestForAuthenticationChallenge:authenticationChallenge]; + + CFRelease(trust); + CFRelease(policy); + CFRelease(certificates); + CFRelease(caCertificate); + CFRelease(hostCertificate); + + expect(useCredentialInvoked).will.beTruthy(); +} + +- (void)testThatAFURLConnectionOperationTrustsDerivedCertificates { + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/path" relativeToURL:self.baseURL]]; + AFURLConnectionOperation *operation = [[AFURLConnectionOperation alloc] initWithRequest:request]; + operation.SSLPinningMode = AFSSLPinningModeCertificate; + + __block BOOL useCredentialInvoked = NO; + + NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:request.URL.host port:request.URL.port.integerValue protocol:request.URL.scheme realm:nil authenticationMethod:NSURLAuthenticationMethodServerTrust]; + + NSData *caCertificateData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"ca" ofType:@"cer"]]; + NSParameterAssert(caCertificateData); + + SecCertificateRef caCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCertificateData); + NSParameterAssert(caCertificate); + + NSData *hostCertificateData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"derived" ofType:@"cert"]]; + NSParameterAssert(hostCertificateData); + + SecCertificateRef hostCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCertificateData); + NSParameterAssert(hostCertificate); + + SecCertificateRef allowedCertificates[] = {caCertificate, hostCertificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 2, NULL); + + SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecTrustRef trust = NULL; + OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &trust); + NSAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status); + + SecTrustResultType result; + status = SecTrustEvaluate(trust, &result); + NSAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status); + + id mockedProtectionSpace = [OCMockObject partialMockForObject:protectionSpace]; + + [[[mockedProtectionSpace stub] andDo:^(NSInvocation *invocation) { + [invocation setReturnValue:(void *)&trust]; + }] serverTrust]; + + AFTestURLProtocol *protocol = [[AFTestURLProtocol alloc] initWithRequest:request cachedResponse:nil client:nil]; + id mockedProtocol = [OCMockObject partialMockForObject:protocol]; + + void(^useCredential)(NSInvocation *invocation) = ^(NSInvocation *invocation) { + useCredentialInvoked = YES; + }; + + [[[mockedProtocol stub] andDo:useCredential] useCredential:OCMOCK_ANY forAuthenticationChallenge:OCMOCK_ANY]; + + NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:trust]; + NSURLAuthenticationChallenge *authenticationChallenge = [[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:protectionSpace proposedCredential:credential previousFailureCount:0 failureResponse:nil error:nil sender:mockedProtocol]; + [protocol.client URLProtocol:mockedProtocol didReceiveAuthenticationChallenge:authenticationChallenge]; + + [operation connection:nil willSendRequestForAuthenticationChallenge:authenticationChallenge]; + + CFRelease(trust); + CFRelease(policy); + CFRelease(certificates); + CFRelease(caCertificate); + CFRelease(hostCertificate); + + expect(useCredentialInvoked).will.beTruthy(); +} + @end diff --git a/Tests/Resources/ca.cer b/Tests/Resources/ca.cer new file mode 100644 index 0000000..b04dc96 Binary files /dev/null and b/Tests/Resources/ca.cer differ diff --git a/Tests/Resources/derived.cert b/Tests/Resources/derived.cert new file mode 100644 index 0000000..46bf343 Binary files /dev/null and b/Tests/Resources/derived.cert differ