iphone programmatically read proxy settings - iphone

I'm looking at adding proxy support to my iphone svn client. When you set up a system wide vpn in the iphone settings you can add a global proxy. Is it possible for external apps to read this information through the api?

Apple has created a sample application for this purpose, called CFProxySupportTool.
CFProxySupportTool shows how to use the CFProxySupport APIs to determine whether a network connection should pass through a proxy; this is useful if you're not using Apple's high-level networking APIs (like CFNetwork and the Foundation URL loading system) but still want to interpret the system-supplied proxy settings.
It's currently available at
https://developer.apple.com/library/mac/#samplecode/CFProxySupportTool/Introduction/Intro.html
The code isn't exactly terse (more than 1000 lines), and is written in plain C. You can also look at the source code of ASIHTTPRequest's startRequest function, which seems to be based on CFProxySupportTool.
Here's a start:
systemProxySettings = [(NSDictionary *) CFNetworkCopySystemProxySettings() autorelease];
proxies = [(NSArray *) CFNetworkCopyProxiesForURL((CFURLRef) URL, (CFDictionaryRef) systemProxySettings) autorelease];
if (!proxies.count)
return;
firstProxySettings = [proxies objectAtIndex:0];
if (nil != (pacScriptURL = [firstProxySettings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]))
{
CFErrorRef cfErrorRef = NULL;
NSError *nsError = nil;
NSString *script;
script = [NSString stringWithContentsOfURL:pacScriptURL
usedEncoding:NULL
error:&nsError];
if (nsError)
return;
proxies = [(NSArray *) CFNetworkCopyProxiesForAutoConfigurationScript((CFStringRef) script, (CFURLRef) URL, &cfErrorRef) autorelease];
if (cfErrorRef || !proxies.count)
return;
firstProxySettings = [proxies objectAtIndex:0];
}
/* Now use `firstProxySettings' */

A Swift 4 version (special thanks to mortehu for providing the initial example).
//Shown this way for clarity, you may not want to waste cycles in your production code
if let url = URL(string: "https://someurloutthere.com") {
let systemProxySettings = CFNetworkCopySystemProxySettings()?.takeUnretainedValue() ?? [:] as CFDictionary
let proxiesForTargetUrl = CFNetworkCopyProxiesForURL(url as CFURL, systemProxySettings).takeUnretainedValue() as? [[AnyHashable: Any]] ?? []
for proxy in proxiesForTargetUrl {
print("Proxy: \(String(describing: proxy))")
//Print the proxy type
print("Proxy Type: \(String(describing: proxy[kCFProxyTypeKey]))")
//There different proxy value keys depending on the type, this is an example of getting the proxy config script if the type is kCFProxyTypeAutoConfigurationURL. If the proxy type were kCFProxyTypeSOCKS you would want to access the SOCKS property keys to see/get the SOCKS proxy values
print("Proxy Autoconfig script URL: \(String(describing: proxy[kCFProxyAutoConfigurationURLKey]))"
}
}

Have you investigated using something like ASIHttpRequest, see the section in the how to document describing proxy support.
At the very least the source code should contain some helpful guidance.

Take a look at the CFProxySupport API, in particular CFNetworkCopyProxiesForURL() will let you read the proxies that are needed to get to a particular URL.

Related

How to pin the Public key of a certificate on iOS

While improving the security of an iOS application that we are developing, we found the need to PIN (the entire or parts of) the SSL certificate of server to prevent man-in-the-middle attacks.
Even though there are various approaches to do this, when you searching for thisI only found examples for pinning the entire certificate. Such practice poses a problem: As soon as the certificate is updated, your application will not be able to connect anymore.
If you choose to pin the public key instead of the entire certificate you will find yourself (I believe) in an equally secure situation, while being more resilient to certificate updates in the server.
But how do you do this?
In case you are in need of knowing how to extract this information from the certificate in your iOS code, here you have one way to do it.
First of all add the security framework.
#import <Security/Security.h>
The add the openssl libraries. You can download them from https://github.com/st3fan/ios-openssl
#import <openssl/x509.h>
The NSURLConnectionDelegate Protocol allows you to decide whether the connection should be able to respond to a protection space. In a nutshell, this is when you can have a look at the certificate that is coming from the server, and decide to allow the connection to proceed or to cancel. What you want to do here is compare the certificates public key with the one you've pinned. Now the question is, how do you get such public key? Have a look at the following code:
First get the certificate in X509 format (you will need the ssl libraries for this)
const unsigned char *certificateDataBytes = (const unsigned char *)[serverCertificateData bytes];
X509 *certificateX509 = d2i_X509(NULL, &certificateDataBytes, [serverCertificateData length]);
Now we will prepare to read the public key data
ASN1_BIT_STRING *pubKey2 = X509_get0_pubkey_bitstr(certificateX509);
NSString *publicKeyString = [[NSString alloc] init];
At this point you can iterate through the pubKey2 string and extract the bytes in HEX format into a string with the following loop
for (int i = 0; i < pubKey2->length; i++)
{
NSString *aString = [NSString stringWithFormat:#"%02x", pubKey2->data[i]];
publicKeyString = [publicKeyString stringByAppendingString:aString];
}
Print the public key to see it
NSLog(#"%#", publicKeyString);
The complete code
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
const unsigned char *certificateDataBytes = (const unsigned char *)[serverCertificateData bytes];
X509 *certificateX509 = d2i_X509(NULL, &certificateDataBytes, [serverCertificateData length]);
ASN1_BIT_STRING *pubKey2 = X509_get0_pubkey_bitstr(certificateX509);
NSString *publicKeyString = [[NSString alloc] init];
for (int i = 0; i < pubKey2->length; i++)
{
NSString *aString = [NSString stringWithFormat:#"%02x", pubKey2->data[i]];
publicKeyString = [publicKeyString stringByAppendingString:aString];
}
if ([publicKeyString isEqual:myPinnedPublicKeyString]){
NSLog(#"YES THEY ARE EQUAL, PROCEED");
return YES;
}else{
NSLog(#"Security Breach");
[connection cancel];
return NO;
}
}
As far as I can tell you cannot easily create the expected public key directly in iOS, you need to do it via a certificate.
So the steps needed are similar to pinning the certificate, but additionally you need to extract the public key from the actual certificate, and from a reference certificate (the expected public key).
What you need to do is:
Use a NSURLConnectionDelegate to retrieve the data, and implement willSendRequestForAuthenticationChallenge.
Include a reference certificate in DER format. In the example I've used a simple resource file.
Extract the public key presented by the server
Extract the public key from your reference certificate
Compare the two
If they match, continue with the regular checks (hostname, certificate signing, etc)
If they don't match, fail.
Some example code:
(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
// get the public key offered by the server
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecKeyRef actualKey = SecTrustCopyPublicKey(serverTrust);
// load the reference certificate
NSString *certFile = [[NSBundle mainBundle] pathForResource:#"ref-cert" ofType:#"der"];
NSData* certData = [NSData dataWithContentsOfFile:certFile];
SecCertificateRef expectedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
// extract the expected public key
SecKeyRef expectedKey = NULL;
SecCertificateRef certRefs[1] = { expectedCertificate };
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, (void *) certRefs, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef expTrust = NULL;
OSStatus status = SecTrustCreateWithCertificates(certArray, policy, &expTrust);
if (status == errSecSuccess) {
expectedKey = SecTrustCopyPublicKey(expTrust);
}
CFRelease(expTrust);
CFRelease(policy);
CFRelease(certArray);
// check a match
if (actualKey != NULL && expectedKey != NULL && [(__bridge id) actualKey isEqual:(__bridge id)expectedKey]) {
// public keys match, continue with other checks
[challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
} else {
// public keys do not match
[challenge.sender cancelAuthenticationChallenge:challenge];
}
if(actualKey) {
CFRelease(actualKey);
}
if(expectedKey) {
CFRelease(expectedKey);
}
}
Disclaimer: this is example code only, and not thoroughly tested.
For a full implementation start with the certificate pinning example by OWASP.
And remember that certificate pinning can always be avoided using SSL Kill Switch and similar tools.
You can do public key SSL pinning using the SecTrustCopyPublicKey function of the Security.framework. See an example at connection:willSendRequestForAuthenticationChallenge: of the AFNetworking project.
If you need openSSL for iOS, use https://gist.github.com/foozmeat/5154962 It's based on st3fan/ios-openssl, which currently doesn't work.
You could use the PhoneGap (Build) plugin mentioned here: http://www.x-services.nl/certificate-pinning-plugin-for-phonegap-to-prevent-man-in-the-middle-attacks/734
The plugin supports multiple certificates, so the server and client don't need to be updated at the same time. If your fingerprint changes every (say) 2 year, then implement a mechanism for forcing the clients to update (add a version to your app and create a 'minimalRequiredVersion' API method on the server. Tell the client to update if the app version is too low (f.i. when the new certificate is activate).
If you use AFNetworking (more specifically, AFSecurityPolicy), and you choose the mode AFSSLPinningModePublicKey, it doesn't matter if your certificates change or not, as long as the public keys stay the same. Yes, it is true that AFSecurityPolicy doesn't provide a method for you to directly set your public keys; you can only set your certificates by calling setPinnedCertificates. However, if you look at the implementation of setPinnedCertificates, you'll see that the framework is extracting the public keys from the certificates and then comparing the keys.
In short, pass in the certificates, and don't worry about them changing in the future. The framework only cares about the public keys in those certificates.
The following code works for me.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
[manager.securityPolicy setPinnedCertificates:myCertificate];
...for pinning the entire certificate. Such practice poses a problem...
Also, Google changes the certificate monthly (or so) but retains or re-certifies the public. So certificate pinning will result in a lot of spurious warnings, while public key pinning will pass key continuity tests.
I believe Google does it to keep CRLs, OCSP and Revocation Lists manageable, and I expect others will do it also. For my sites, I usually re-certify the keys so folks to ensure key continuity.
But how do you do this?
Certificate and Public Key Pinning. The article discusses the practice and offers sample code for OpenSSL, Android, iOS, and .Net. There is at least one problem with iOS incumbent to the framework discussed at iOS: Provide Meaningful Error from NSUrlConnection didReceiveAuthenticationChallenge (Certificate Failure).
Also, Peter Gutmann has a great treatment of key continuity and pinning in his book Engineering Security.
If you use AFNetworking, use AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];

What is the most efficient way to sync Core Data with JSON API

What would be the best way to sync my Core Data schema with a remote API serving JSON? At the moment I'm looping through each dictionary in the JSON response checking Core Data to see if the API ID exists.
This works great, but all thats left to do now is delete any local objects that aren't on the server. Here is an example of my JSON data:
[
{
"id":1234,
"name":"My first object",
"description":"This is a long string with lots of information"
},
{
"id":1235,
"name":"My second object",
"description":"This is a long string with lots of information"
}
]
Currently the only way I can think of accomplishing this is something like the following:
NSArray *jsonData = // Convert json data to array using NSJSONSerialization
NSInteger fetchedCount = _fetchedResultsController.fetchedObjects.count;
if (fetchedCount != jsonData.count) {
for (int i = 0; i < fetchedCount; i++) {
NSManagedObject *object = [_fetchedResultsController objectAtIndexPath: [NSIndexPath indexPathForItem:i
inSection:0]];
NSNumber *idNumber = object.apiID;
BOOL shouldDelete = YES;
for (NSDictionary *jsonDict in jsonData) {
if ([jsonDict objectForKey:#"id"] == idNumber) {
shouldDelete = NO;
}
}
if (shouldDelete) {
// Delete object.
}
}
}
I think that will be massively inefficient if the JSON array contains a lot of objects.
This could be ok, but I think you should apply the Find-or-Create pattern suggested in Apple doc. See here for a deep explanation Efficiently Importing Data (In particular see Implementing Find-or-Create Efficiently).
The overall idea is quite simple. Having two arrays of objects (the one you retrieve from Core Data and the one you retrieve from the service) that are ordered (by apiID and id resp.).
Obviously if there are a lot of data, I really suggest to perform operations in background. Remember that each thread needs to rely on its NSManagedObjectContext. Otherwise take advantage of new queue mechanism provided by iOS 5 API.
For the sake of completeness, I also suggest to read RayWenderlich tutorial How To Synchronize Core Data with a Web Service Part 1 and 2. It's very interesting.
Hope that helps.

How do I use code generated from WSDL2OBJC

hi everyone i am new to iphone development and started with some sample application.
In sample application i am using webservices..i went through this tutorial http://code.google.com/p/wsdl2objc/wiki/UsageInstructions and understood about wsdl2objc..
but this tutorial is too short so can anyone suggest similar like tutorial or examples so tat is still more clear...
thank u
All depends on your web service class name etc as wsdl2objc makes up alot of your NSObjects and methods based upon this, however,
suggesting that you are using soap 1.2 bindings and your web service was called 'SimpleService', the following would call a web method named 'MobileTestService and return back the integer value from the xml generated.
-(NSString *)returnThatStringFromWebServiceResult {
SimpleServiceSoap12Binding * binding = [SimpleService SimpleServiceSoap12Binding];
binding.logXMLInOut = YES; // shows all in the console log.
SimpleService_concat * testParams = [[SimpleService_concat new]autorelease];
testParams.s1 = someTextField.text; // parameters all become properties of this testParams object
testParams.s2 = anotherTextField.text;
SimpleServiceSoap12BindingResponse * response= [binding SimpleService_concatUsingParameters:testParams];
[response self]; // removes compile error
NSArray * responseBodyParts = response.bodyParts;
NSError * responseError = response.error;
if (responseError!=NULL) {
return #""; // if error from ws use [responeError code]; for http err code
}
for (id bodyPart in responseBodyParts)
{
if ([bodyPart isKindOfClass:[SimpleService_concat_Response class]])
{
SimpleService_concatResponse* body = (SimpleService_concatResponse*) bodyPart;
return body.SimpleService_concatResult; // if you are returning a string from your WS then this value will be a string,
}
}
}

Alternatives to NSHost in iPhone app

I'm currently using this code
NSHost *host = [NSHost hostWithAddress:hostname];
if (host == nil) {
host = [NSHost hostWithName:hostname];
if (host == nil) {
[self setMessage:#"Invalid IP address or hostname:"];
return;
}
}
to retrive my IP Address for a networking app I'm working on, however I'm aware that NSHost is a private API that will be rejected. Can anyone help me with working this code to produce the same results without using NSHost? I'm not really sure where to start.
EDIT:
Following suggestions that seem damn near perfect below I've added this code into my app in the place of the code above
Boolean result;
CFHostRef hostRef;
CFArrayRef addresses;
NSString *hostname = #"www.apple.com";
hostRef = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)hostname);
if (hostRef) {
result = CFHostStartInfoResolution(hostRef, kCFHostAddresses, NULL); // pass an error instead of NULL here to find out why it failed
if (result == TRUE) {
addresses = CFHostGetAddressing(hostRef, &result);
}
}
if (result == TRUE) {
NSLog(#"Resolved");
} else {
NSLog(#"Not resolved");
}
I've removed the 4th line (as I have this information from elsewhere already) but I get errors being based around CFHostRef being undeclared. How would I resolve that? It seems to be my only big hurdle, as other errors are only based upon the lack of being able to see hostRef after that.
EDIT: Scratch that I also get kCFHostAddresses undeclared.
You can use CFHost to achieve the same. On the top of the CFHost Reference is a cookbook recipe for making the lookup.
The following code does very, very basic synchronous resolution (as yours above would with NSHost). Note that you don't want to do this since it can render your app unresponsive because it doesn't return until it's resolved or the timeout hits.
Use asynchronous lookup instead (CFHostSetClient and CFHostScheduleWithRunLoop as described in the CFHost documentation above). Also, depending on what you're planning to do, you may want to look into using the reachability APIs. Check out the WWDC sessions on networking available on the iPhone developer website.
Boolean result;
CFHostRef hostRef;
CFArrayRef addresses;
NSString *hostname = #"www.apple.com";
hostRef = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)hostname);
if (hostRef) {
result = CFHostStartInfoResolution(hostRef, kCFHostAddresses, NULL); // pass an error instead of NULL here to find out why it failed
if (result == TRUE) {
addresses = CFHostGetAddressing(hostRef, &result);
}
}
if (result == TRUE) {
NSLog(#"Resolved");
} else {
NSLog(#"Not resolved");
}
// Don't forget to release hostRef when you're done with it
Look at this: http://blog.zachwaugh.com/post/309927273/programmatically-retrieving-ip-address-of-iphone
http://developer.apple.com/iphone/library/qa/qa2009/qa1652.html
Got a great little answer through the Developer Support system, this worked perfectly.

How to use Three20 framework to upload multiple images and handle JSON response?

So I have an iPhone application that needs to:
Post several strings and up to 5 images (stored in memory) to a RoR web application
Parse the JSON returned that will include several strings and an array of URLs (each representing the location of where the uploaded images can be found on the website).
QUESTIONS:
Can this be done with Three20 (would be nice since I'm using it for other things)? And if so, how?
If it can't be done with Three20 ... how would it be accomplished using ASIHttpRequest? Or maybe something baked into the SDK if that is a better option?
Thanks much
Unfortunately there isn't a whole lot of tutorials and good documentation for three20 out there on the web ... so here is how I finally got things working:
- (void) sendToWebsite {
NSString* url = [[NSString stringWithFormat:kRequestURLPath, self.entityId] stringByAppendingString:#".json"] ;
// Prep. the request
TTURLRequest* request = [TTURLRequest requestWithURL: url delegate: self];
request.httpMethod = #"POST";
request.cachePolicy = TTURLRequestCachePolicyNoCache;
// Response will be JSON ... BUT WHY DO I NEED TO DO THIS HERE???
request.response = [[[TTURLJSONResponse alloc] init] autorelease];
// Set a header value
[request setValue:[[UIDevice currentDevice] uniqueIdentifier] forHTTPHeaderField:#"Device-UID"];
// Post a string
[request.parameters setObject:self.entity_title forKey:#"entity_title"];
// Post some images
for (int i = 0; i < [self.photos count]; i++) {
// IS IT POSSIBLE TO ADD A PARAM NAME SO I CAN LOOK FOR THE SAME NAME
// IN THE WEB APPLICATION REGARDLESS OF FILENAME???
[request addFile:UIImagePNGRepresentation([self.winnerImages objectAtIndex:i])
mimeType:#"image/png"
fileName:[NSString stringWithFormat:#"photo_%i.png", i]];
}
// You rails guys will know what this is for
[request.parameters setObject:#"put" forKey:#"_method"];
// Send the request
[request sendSynchronously];
}
Things I still don't understand (or find problematic):
For a posted file, how can I include both a param name AND a filename?
What is the purpose of setting request.response = to whatever? I don't get that.
Answering #2:
You need to supply the handler for the response before you send your request, the TTURLJSONResponse is not the actual response, but it's responsible for handling the response. This is where you'd process the response for your strings and array of URLs.
It's really a protocol called TTURLResponse that defines the following method for implementation:
/**
* Processes the data from a successful request and determines if it is valid.
*
* If the data is not valid, return an error. The data will not be cached if there is an error.
*
* #param request The request this response is bound to.
* #param response The response object, useful for getting the status code.
* #param data The data received from the TTURLRequest.
* #return NSError if there was an error parsing the data. nil otherwise.
*
* #required
*/
- (NSError*)request:(TTURLRequest*)request
processResponse:(NSHTTPURLResponse*)response
data:(id)data;
You chose TTURLJSONResponse as your handler, which is a straight-forward implementation to look at for help on writing your own.