|
AdvancedOptions
Authentication options, handling defined server exceptions, SOAPSecurity, etc.
Featured This page covers some of the more advanced options, not available in versions 0.6 and 0.7-pre1. Authentication optionsIn 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 AuthenticationThe 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 certificateIf 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.
- (NSData *)dataCA
{
uint8_t bytes[] = {CA_BYTES};
return [NSData dataWithBytes:bytes length:CA_LENGTH];
}- (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;
}
- (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 authenticationSome 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.
- (NSData *)dataPKCS12
{
uint8_t bytes[] = {PKCS12_BYTES};
return [NSData dataWithBytes:bytes length:PKCS12_LENGTH];
}- (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];
}
}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];
}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;
}NOTEPay 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. SOAPSecurityLet me say this short and clear: SOAPSecurity for iPhone is EXTREMELY difficult to implement. XMLSigningOne 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:
... soapSigner = [[SOAPSigner alloc] initWithDelegate:[SimpleSOAPSignerDelegate sharedInstance]]; ... ... 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:
NOTEThird 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). |
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
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
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.
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
There is an issue that NSURLCredentialPersistenceSession is not used by default in the current code since it is added to the wrong dictionary.
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.
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
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 &lt;data&gt;&lt;mac&gt;/mac&gt/data&gt;
Does anyone know how to resolve this. I would like the xmlstring to stay the same.
Thanks
on the line NSData checksum = checksumSHA1:[input bytes? length:length?]; where is the checksumSHA1 implementation?