My favorites | Sign in
Project Hosting will be READ-ONLY Thursday at 3:00pm UTC for up to 3 hours for network maintenance.
Project Home Downloads Wiki Issues Source
Search
for
AdvancedOptions  
Authentication options, handling defined server exceptions, SOAPSecurity, etc.
Featured
Updated Dec 14, 2011 by pmilosev

This page covers some of the more advanced options, not available in versions 0.6 and 0.7-pre1.

Authentication options

In order to provide proper support for advanced authentication, the SSL authentication logic has been removed from the generated code, and delegated to a user-defined delegate. The delegate will have to implement the SSLCredentialsManaging protocol, defined in the generated sources.

Basic Authentication

The generated code, provides a simple implementation of the protocol, which can be used for basic SSL authentication.

To use it, you just need to instantiate the delegate with an username and password, and set the sslManager property of the binding:

...
BasicSSLCredentialsManager *manager = 
    [BasicSSLCredentialsManager managerWithUsername:u.text andPassword:p.text];
       
binding.sslManager = manager;        
...

You can check the implementation of the BasicSSLCredentialsManager here.

Self-signed server certificate

If your application connects to a server which has a self-signed certificate, then simply letting the system to validate the server will not do the job, as the received certificate would come from an unknown certificate authority.
In this case, you need to tell the system which certificate authorities you are expecting, and handle the validation process manually. In order to do this you need a couple of things:

  • The root certificate that have signed the server side certificate.
    One way to get this in your code is by making it part of your application binary, and then generating a NSData instance out of it:
  • - (NSData *)dataCA
    {
        uint8_t bytes[] = {CA_BYTES};
        return [NSData dataWithBytes:bytes length:CA_LENGTH];
    }
  • A NSArray holding the references of all the root certificates you need to support (in this case only one):
  • - (NSArray *) serverAnchors
    {
        static NSArray *anchors = nil;
        if (!anchors) {
            NSData *caData = [self dataCA];
            SecCertificateRef caRef = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) caData);
          
            anchors = [[NSArray arrayWithObjects:(id)caRef,  nil] retain];
            
            if (caRef) {
                CFRelease(caRef);   
            }
        }
        
        return anchors;
    }
  • A proper SSLCredentialsManaging protocol implementation.
    In the code below we give only the most important part of the implementation, which basically tell the system that we want to handle the authentication manually, and then validates the server certificate received:
  • - (BOOL)canAuthenticateForAuthenticationMethod:(NSString *)authMethod
    {
        return [authMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
    }
    - (BOOL)validateResult:(SecTrustResultType)res
    {
        if ((res == kSecTrustResultProceed)                 // trusted certificate
            || ((res == kSecTrustResultConfirm)             // valid but user should be asked for confirmation
                || (res == kSecTrustResultUnspecified)      // valid but user have not specified wheter to trust
                || (res == kSecTrustResultDeny)             // valid but user does not trusts this certificate
                )
            )
        {
            return YES;
        } 
        
        return NO;
    }
    
    - (BOOL)authenticateForChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        if ([challenge previousFailureCount] > 0) {        
            return NO;
        }
        
        NSURLCredential *newCredential = nil;
        NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
        SecurityCenter *securityCenter = [SecurityCenter sharedInstance];
        
        // server authentication - NSURLAuthenticationMethodServerTrust
        if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            SecTrustRef trust = [protectionSpace serverTrust];
            NSArray *anchors = [securityCenter serverAnchors];
            SecTrustSetAnchorCertificates(trust, (CFArrayRef)anchors);
            SecTrustSetAnchorCertificatesOnly(trust, YES);
            
            
            SecTrustResultType res = kSecTrustResultInvalid;
            OSStatus sanityChesk = SecTrustEvaluate(trust, &res);
                   
            if ((sanityChesk == noErr) 
                && [self validateResult:res]) {
                
                newCredential = [NSURLCredential credentialForTrust:trust];
                [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
                
                return YES;
            }
            
            return NO;
        }
        
        [NSException raise:@"Authentication method not supported" format:@"%@ not supported.", [protectionSpace authenticationMethod]];
        return NO;
    }

Client-side certificate authentication

Some services (e.g. eBanking) are intended to be consumed only by a defined set of client applications. In this case you might need to use 2-way SSL, which asks the client application to authenticate with proper certificate.
To do this, you would need the following:

  • NSData representation of a PKCS12 key store, holding the certificate and the private key for the client.
    One way to do this is to integrate the key store as part of the application's byte code.
  • - (NSData *)dataPKCS12
    {
        uint8_t bytes[] = {PKCS12_BYTES};
        return [NSData dataWithBytes:bytes length:PKCS12_LENGTH];
    }
  • NSArray, in this case named keystores, which holds all the key/certificate entries in the key store, each represented by a single NSDictionary instance:
  • - (void)importPKCS12
    {
        if (!keystores) {
            NSData *pkcsData = [self dataPKCS12];
            NSString *password = [self passwordPKCS12];
            
            OSStatus sanityChesk = SecPKCS12Import((CFDataRef)pkcsData, 
                                                   (CFDictionaryRef)[NSDictionary dictionaryWithObject:password 
                                                                                                forKey:(id)kSecImportExportPassphrase], 
                                                   (CFArrayRef *)&keystores);
            CHECK_CONDITION1(sanityChesk == noErr, @"Error while importing pkcs12 [%d]", sanityChesk);
            [keystores retain];
        }
    }
  • Logic to extract the identity and certificates for the client.
    In this example the clientKeyStore variable points to one of the entries in the keystores variable mentioned above:
  • - (SecIdentityRef)clientIdentity
    {
        return (SecIdentityRef)[clientKeyStore objectForKey:(id)kSecImportItemIdentity];
    }
    
    - (NSArray *)clientCertificates
    {
        return [clientKeyStore objectForKey:(id)kSecImportItemCertChain];
    }
  • A proper SSLCredentialsManaging protocol implementation.
    In the code below we give only the most important part of the implementation, which basically tell the system that we want to handle the authentication manually, and then authenticates the client with a certificate:
  • - (BOOL)canAuthenticateForAuthenticationMethod:(NSString *)authMethod
    {
        return [authMethod isEqualToString:NSURLAuthenticationMethodClientCertificate];
    }
    - (BOOL)authenticateForChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        if ([challenge previousFailureCount] > 0) {        
            return NO;
        }
        
        NSURLCredential *newCredential = nil;
        NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
        SecurityCenter *securityCenter = [SecurityCenter sharedInstance];
        
        // client authentication - NSURLAuthenticationMethodClientCertificate
        if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
           
            NSArray *acceptedIssuers = [protectionSpace distinguishedNames];
            [securityCenter setIssuerDistinguishedNames:acceptedIssuers];
            
            SecIdentityRef identity = (SecIdentityRef)[securityCenter clientIdentity];
            NSArray *certs = [securityCenter clientCertificates];
            
            if (identity && certs) {
                newCredential = [NSURLCredential credentialWithIdentity:identity
                                                           certificates:certs
                                                            persistence:NSURLCredentialPersistenceNone];
                [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
                
                return YES;
            }
            
            return NO;
        }
        
        [NSException raise:@"Authentication method not supported" format:@"%@ not supported.", [protectionSpace authenticationMethod]];
        return NO;
    }

NOTE

Pay some extra attention on how you will make the CAs and the PKCS12 key store available to the application. E.g. A simple hex dump of the application may reveal this data to a potential attacker. Make sure you always use some sort of encryption, or at least byte-masking before integrating the data to your binary.


SOAPSecurity

Let me say this short and clear: SOAPSecurity for iPhone is EXTREMELY difficult to implement.
That said, I will only give an example of what the code generated by wsdl2objc offers. For anything else you are on your own :)

XMLSigning

One of the features covered by the SOAPSecurity extension, is the XMLSigning standard, which can be used to sign portions of the XML request, in order to ensure it is received unmodified by the server. To make things more flexible, the generated code uses a delegate, which if set is called to sign the generated XML regrets before sending it to the server.

Out of the box, the generated code comes with a SOAPSigner implementation which signs the the whole XML body, using the RSA-SHA1 algorithm.

To use this feature you need to:

  • Implement the SOAPSignerDelegate protocol (lets say in a class called SimpleSOAPSignerDelegate), and set an instance of this delegate to the SOAPSigner instance you would use:
  • ...
    soapSigner = [[SOAPSigner alloc] initWithDelegate:[SimpleSOAPSignerDelegate sharedInstance]];
    ...
  • And set the instantiated SOAPSigner as a delegate to the binding:
  • ...
    id binding = [self binding];
    [binding setSoapSigner:soapSigner];
    ...

The SimpleSOAPSignerDelegate should use the iPhone SDK's Security framework to sign the data:

- (NSData *)signData:(NSData *)input key:(SecKeyRef)key
{
    CHECK_CONDITION([input length], @"The input length must not 0");
    CHECK_CONDITION(key, @"The private key must not be NULL");
    
    OSStatus sanityCheck = noErr;
	NSData * signedHash = nil;
	
	uint8_t * signature = NULL;
	size_t signatureSize = SecKeyGetBlockSize(key);
	
	// Malloc a buffer to hold signature.
	signature = malloc( signatureSize );
	memset((void *)signature, 0x0, signatureSize);
	
	// Sign the SHA1 hash.
    NSData *checksum = [self checksumSHA1:[input bytes] length:[input length]];
	sanityCheck = SecKeyRawSign(key, 
                                kSecPaddingPKCS1SHA1, 
                                (const uint8_t *)[checksum bytes], 
                                CC_SHA1_DIGEST_LENGTH, 
                                signature, 
                                &signatureSize);
	
	CHECK_CONDITION1(sanityCheck == noErr, @"Problem signing the SHA1 hash, OSStatus == %d.", sanityCheck );
	
	// Build up signed SHA1 blob.
	signedHash = [NSData dataWithBytes:(const void *)signature length:(NSUInteger)signatureSize];
	if (signature) free(signature);
	
	return signedHash;   
}

What is happening behind the scene is:

  • The proper security header would be added to the XML.
    This header specifies all the algorithms used (e.g. for signing and xml canonicalization), and contains the actual signature at the end.
  • The XML is transformed into a canonical form.
    This is needed as the XML doesn't mind if it has some extra space or a new line between the tags. However all of this changes the byte representation of the XML and of course the final signature. So the canonicalization transforms the XML into a standard form which will ensure the same byte representation on both client and server side.
  • The bytes of the transformed XML are given for signing.
  • And finally the signature is included into the XML header.

NOTE

Third party C libraries exist that can do this with small or no effort by you. However the Apple's terms and conditions forbid any third-party encryption methods to be used (at least without some special permission).


Comment by cchie...@gmail.com, Mar 26, 2011

Hi, the binding generated by the app does not contain a "authenticationProperties" property. Can you kindly enlighten me on how to set the credentials in this case?

Thanks jx

Comment by maxi...@gmail.com, Mar 29, 2011

Hi, I noticed the same issue: no authenticationProperties property in the current version. Could you please let me know how to proceed? Thanks in advanca /lm

Comment by cchie...@gmail.com, Apr 1, 2011

Just realized the versions listed in the download tab are no longer valid. You'll have to checkout and build directly from trunk to make sense of what the tutorials are saying. Anyway I encountered this weird error. There is this statement in the codes ==> "parameters.soapSigner" which result in a build error. I can't seem to find where the "parameters" variable is ever declared.

Comment by project member pmilosev, Apr 12, 2011

Hi all

Sorry for my late reply, but I was expecting an email notification if any comment is posted, which did not happen. Regarding the authentication properties, yes this is something new and is part of the trunk. In the older versions you only have the opportunity of using basic authentication, by specifying the username and the password directly to the binding.

It's strange that you get an build error for the soap signer as I have tried building the project after merging my branch with the trunk and had no issues. I'll check this as soon as possible.

sorry for the troubles

Comment by lailo...@gmail.com, May 18, 2011

There is an issue that NSURLCredentialPersistenceSession is not used by default in the current code since it is added to the wrong dictionary.

Comment by lailo...@gmail.com, May 18, 2011

Another thing to note (and test) is that NSURLCredentialPersistenceNone does not seem to work.

I've debugged in the background, but as soon as a connection authenticates, it will use those same credentials on subsequent calls even though you specify different credentials. I think it is a bug on Apple's side.

Try it, in a single program call the function twice... once with correct account details, and once without (both with NSURLCredentialPersistenceNone) .. both calls will succeed.

Comment by ronaldwi...@gmail.com, Aug 30, 2011

Hi All, I was wondering if any of the commiters can do another build 0.7 pre 2 ? which contains the authenticationProperties feature? I've gotten latest from trunk but no longer have Mac OSX 10.5 SDK since I've upgraded to XCode4. Can anyone help out? Cheers

Comment by eli.h...@gmail.com, Sep 9, 2011

Hi everyone,

I am trying to send the following xmlstring (expected as a string by my web service): <data><macaddress></macaddress></data> but the request sent has the string reformatted to html equivalents such as &amp;lt;data&amp;gt;&amp;lt;mac&amp;gt;/mac&amp;gt/data&amp;gt;

Does anyone know how to resolve this. I would like the xmlstring to stay the same.

Thanks

Comment by pcord...@gmail.com, Yesterday (28 hours ago)

on the line NSData checksum = checksumSHA1:[input bytes? length:length?]; where is the checksumSHA1 implementation?


Sign in to add a comment
Powered by Google Project Hosting