Conditional IF/ELSE statements in iOS Constants File - ios

Fellow Coders...
I have server url's set up in my application's global constants file.
I also have a variable called "DebugMode" in my application plist that once switched should change the urls the application will be using.
Constants.h
extern NSString * const LOGIN_URL;
Constants.m
NSString * const LOGIN_URL = #"http://www.url.com";
Anyway I can replicate the following psuedo code below into Objective C?
if([[[[NSBundle mainBundle] infoDictionary] objectForKey:#"DebugMode"] boolValue] == NO)
{
NSString * const LOGIN_URL = #"http://www.production-url.com";
}
else
{
NSString * const LOGIN_URL = #"http://www.qa-url.com";
}

What your asking for isn't exactly possible (at least not in the way your asking for). A constant is setup and established whilst compiling (not strictly true, but for the sake of this explanation, it will do) and thus means that it can not be mutated for any reason at runtime.
The traditional way of changing the values of constants depending on debug and release code is through the preprocessor. Like so:
#if __DEBUG_MODE__ == 1
NSString * const LOGIN_URL = #"http://www.qa-url.com";
#else
NSString * const LOGIN_URL = #"http://www.production-url.com";
#endif
Now __DEBUG_MODE__ needs to be defined before it can do anything, and there are a few ways you could do this. You could add the following line to you prefix header file (.pch)
#define __DEBUG_MODE__ 1 // Change to 0 to disable debug mode.
or add the compiler flag -M__DEBUG_MODE__=1 to the file you wish to effect. This means that whenever __DEBUG_MODE__ is set with a value of 1, the compiler will use your debug constant, and when it has a value of 0 the compiler will use the production constant.
This also has the benefit of keeping debug and production code separate (you should avoid having both in your binary as it can open a whole world of problems and security issues).
Hope this helps.

Whenever I've had a situation like this I've just created a class method in my constants file:
+ (NSString *)loginURL {
if([[[[NSBundle mainBundle] infoDictionary] objectForKey:#"DebugMode"] boolValue] == NO){
return #"http://www.production-url.com";
}
else {
return #"http://www.qa-url.com";
}
}
It also makes it more clear in your code that as the loginURL string is coming via a method, it may be dependent on a run time condition:
NSURL *loginURL = [NSURL URLWithString:[Constants loginURL]];

Related

Delay in loading encrypted local storage on iOS using WL.EncryptedCache.open

We are using WL.EncryptedCache.open to open the locat storage. This at times takes 4-8 seconds to get the encryption key. Please suggest on how to resolve this performance issue ?
We are using Worklight 6.0
You can generate the secure token locally by overwriting the secure random call: WL.EncryptedCache.secureRandom = function(callback){callback(Math.random()+"")}. That way you don't have to hit the server to get it. It will be significantly less secure and I do not recommend it. Beyond that and running the application on a faster device, there's nothing you can do. Generating a secure key is an expensive operation.
Alternatively, you can replace Math.random above with a cordova plugin exec call that gets a cryptographically secure random string using SecRandomCopyBytes. Some example code:
int bytes = 32;
uint8_t randBytes[bytes];
int rc = SecRandomCopyBytes(kSecRandomDefault, (size_t)bytes, randBytes);
if (rc != 0) {
//handle failure
}
NSMutableString* hexEncoded = [NSMutableString new];
for (int i = 0; i < bytes; i++) {
[hexEncoded appendString:[NSString stringWithFormat:#"%02x", randBytes[i]]];
}
NSString* randomStr = [NSString stringWithFormat:#"%#", hexEncoded];
There are getting started modules that explain how to write cordova plugins for iOS here.

Detecting if app is ad hoc

I have this URL in my server that is used for testing and another one that is used for production.
At some point in my code I did this:
#ifdef DEBUG
static NSString * const url = "http://sandbox.myserver.com";
#else
static NSString * const url = "http://live.myserver.com";
#endif
this works fine when I am debugging the app on Xcode but if I send ad hoc versions of my app to beta testers it will fail. Ad hoc apps will use the production URL instead of the sandbox one.
What is the correct way of doing this test?
in short:
var data = String($.NSString.stringWithContentsOfFileEncodingError($.NSBundle.mainBundle.pathForResourceOfType('embedded', 'mobileprovision'), $.NSISOLatin1StringEncoding, null));
data = data.slice(data.indexOf('<plist'));
data = data.slice(0, data.indexOf('</plist>')+8);
data = $.NSString.stringWithString(data).dataUsingEncoding($.NSISOLatin1StringEncoding);
var mobileProvision = $.NSPropertyListSerialization.propertyListWithDataOptionsFormatError(data, $.NSPropertyListImmutable, null, null);
if (mobileProvision.valueForKey('ProvisionedDevices')) {
res.debug = 'adhoc';
} else {
res.debug = false;
}

Can i change the server url with sudzc?

I am trying to generate source code from a wsdl file for iOS. I've stumbled upon a couple of tools and so far wsclient++ and sudzc at least seem to work. But I need to send requests to different servers with the same soap interface, depending on the state of the iOS app.
In the source code generated by wsclient I can set the server URL via
MyWebService* ws = [MyWebService service];
// // set base url for entire application
[SoapWebService setGlobalBaseUrl: #"http://domain.com"];
NSError* error = nil;
Result* rs = [ws callMethod: p1 param2:p2 error:&error];
Which would me allow me to do something like
if(condition1) [SoapWebService setGlobalBaseUrl: #"http://betaserver.com"];
if(condition2) [SoapWebService setGlobalBaseUrl: #"http://developserver.com"];
if(condition3) [SoapWebService setGlobalBaseUrl: #"http://liveserver.com"];
Is there a way to archive something similar with the source code generated by sudzc?
As long as the soap is the same response you shouldn't have a problem using your code. There is a file that stores the server address. The code generated by sudzc can be modified to any address. I actually created a dynamic way of hitting servers. I will find the file and code I used to do this.
You can search the project for your domain you used for sudzc.
I'm not in front of a mac right now, but I will update later.
UPDATE:
Ok, so I created a settings tab and allowed the user to input a specific ip address if necessary. It saves the IP address in a dictionary and then this file retrieves it from the dictionary. I left some of my original comments and added some in the code so you can see both ways. If it confuses you let me know and I'll edit again. In my sudzc generated code I modified the file to this:
/*
wsUpdateQOH.m
The implementation classes and methods for the wsUpdateQOH web service.
Generated by SudzC.com
*/
#import "wsUpdateQOH.h"
#import "Soap.h"
#import "Settings.h"
#define URL #"http://%#/webServiceAddress/updateqoh.asmx"
/* Implementation of the service */
#implementation wsUpdateQOH
- (id) init
{
if(self = [super init])
{
// take out hard coded address and add variable to have a dynamic IP #"http://www.site.com/webServiceAddress/updateqoh.asmx"
// here is the dictionary return and format of the url string
NSString *savedValue = [[NSUserDefaults standardUserDefaults] stringForKey:#"serverIP"];
self.serviceUrl = [[NSString alloc] initWithFormat:URL, savedValue];
// uncomment for a hard coded address self.serviceUrl = #"http://%#/webServiceAddress/updateqoh.asmx";
self.namespace = #"http://tempuri.org/webServiceAddress/UpdateQOH";
self.headers = nil;
self.logging = NO;
}
return self;
}
- (id) initWithUsername: (NSString*) username andPassword: (NSString*) password {
if(self = [super initWithUsername:username andPassword:password]) {
}
return self;
}
+ (wsUpdateQOH*) service {
return [wsUpdateQOH serviceWithUsername:nil andPassword:nil];
}
+ (wsUpdateQOH*) serviceWithUsername: (NSString*) username andPassword: (NSString*) password {
return [[[wsUpdateQOH alloc] initWithUsername:username andPassword:password] autorelease];
}
// *** Below here is the soap actions ***

best and secured way of storing paypal api credentials

I'm creating an iPhone application that integrates the PayPal API for iOS. The API requires the API credentials when sending a request. I read the PayPal API and it says
Never send Express Checkout requests from your mobile application directly to PayPal. The requests require your PayPal API credentials. Placing your credentials on mobile devices exposes you and PayPal to unacceptable security risks. Send Express Checkout requests only from secure servers.
My question is, what is the best way of storing the API credentials so as to decrease the possibilities for my credentials being exposed or hacked? Is attaching the credentials to an iPhone build risky? Why or how? Is storing these credentials on a secure server reliable enough?
EDIT: how can keychain access api on iOS can help me with this?
Putting the API keys in your app is completely insecure. Via a verity of techniques, anyone who can download the app or gets their hands on a phone with the app on it can simply read the API key. This holds even if you do what #MatthiasBauch suggested and download the secret later. It also holds even if you do what #Rexeisen suggested and obfuscate the string.
You best bet is to user apple's built in subscription services to handle payments ( which may not be applicable and they take a cut but is likely more secure than what you can do on the phone)
In the likely even that you don't want to or can't do that, give each instance of the app a unique id that they register when they download with a server you control. That sever than has the paypal credentials and will make api calls on their behalf . This way, if any given phone is stolen/ has its api key to your server read, you can simply revoke that key and your paypal API keys are still safe. Important caveat: until you actually revoke that app's key, anyone who has it can still use it to make what ever calls your server supports. This could be a very bad thing.
It is risky as you can run the Strings utility on just about any app (try it, it's kinda scary) and get the strings from the code. Generally I'd recommend packaging the secrets in the app, but leave them on a secure server elsewhere. If you must put it in the app, one thing you can do is obfuscate the strings so it's not obvious.
NSString *secret = kTwitterClientSecret;
NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
NSString *key = #"Twitter";
[secretData obfuscateOrDeobfuscateWithKey:key];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [NSString stringWithFormat:#"%#/%#-%#", documentsPath, key, #"output"];
[secretData writeToFile:path atomically:NO];
NSLog(#"Wrote obfuscated data to: %#", documentsPath);
Where obfuscateOrDeobfuscateWithKey is a category on NSData
// Inspiration from: http://iosdevelopertips.com/cocoa/obfuscation-encryption-of-string-nsstring.html
- (void)obfuscateOrDeobfuscateWithKey:(NSString *)key
{
// Get pointer to data to obfuscate
char *dataPtr = (char *) [self bytes];
// Get pointer to key data
char *keyData = (char *) [[key dataUsingEncoding:NSUTF8StringEncoding] bytes];
// Points to each char in sequence in the key
char *keyPtr = keyData;
int keyIndex = 0;
// For each character in data, xor with current value in key
for (int x = 0; x < [self length]; x++) {
// Replace current character in data with current character xor'd with current key value.
// Bump each pointer to the next character.
*dataPtr = *dataPtr ^ *keyPtr;
dataPtr++;
keyPtr++;
// If at end of key data, reset count and set key pointer back to start of key value
if (++keyIndex == [key length]) {
keyIndex = 0, keyPtr = keyData;
}
}
}
Then you can declare a constant to be something like
static unsigned char const kTwitterClientSecret[] = {
0x00, 0x00, 0x00, ... etc ...
};
static unsigned int const kTwitterClientSecret_len = LENGTH;
Then to get the string back you can do
[NSString deobfuscatedStringWithBytes:kTwitterClientSecret length:kTwitterClientSecret_len key:#"Twitter"];
Where this is a category on NSString
+ (NSString *)deobfuscatedStringWithBytes:(const void *)bytes length:(NSUInteger)length key:(NSString *)key
{
NSData *deobfuscatedData = [NSData dataWithBytes:bytes length:length];
[deobfuscatedData obfuscateOrDeobfuscateWithKey:key];
return [[NSString alloc] initWithData:deobfuscatedData encoding:NSUTF8StringEncoding];
}
This will do very simple obfuscation and will not show up in strings.

Detecting CA:TRUE in x509 Basic Contraints

What is the proper way to check the basic constraints of a certificate ? Below is the code I am currently using to mimic the icons show in the Keychain (the reason for below is that while we have a
SFChooseIdentityPanel * identityPanel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
the equivalent for selecting a CA or a Host/Leaf cert does not exist. And that is useful when setting up/locking down SSL connections.
Unfortunately - I cannot find the OID strings in the header files to cleanly extract the CA:TRUE or false form a cert (or am using the API in the wrong way).
So the questions are
How do I cleanly check for CA:TRUE - while below works - I can imagine that it would get foiled by a malformed cert with text strings in the right places.
Secondly - I am using a heuristic of Issuer==Subject to detect self signed. Is there a cleaner way to do this ?
Finally - from trial and error - below seems to mimic apple her choices in the keychain - but the docs are rather hard to understand. Does kSecTrustResultProceed really mean that the user has set an override and kSecTrustResultUnspecified that in fact the trust is specified by the system basic trust ? While it 'work's - I cannot quite understand the exact interpretation of the docs.
Thanks a lot. Code below.
Dw.
#implementation NSImage (CertificateSelectionPanelExtensions)
+(NSImage *)iconForCertificate:(SecCertificateRef)certificateRef small:(BOOL)isSmall
{
BOOL isCA = FALSE, isInvalid = TRUE, isUserTrust = FALSE;
NSString * issuer = nil, * subject = nil;
const void *keys[] = { kSecOIDX509V1SubjectName, kSecOIDX509V1IssuerName, kSecOIDExtendedKeyUsage, kSecOIDBasicConstraints };
CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks);
CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection, NULL);
CFArrayRef values;
CFDictionaryRef dict;
dict = CFDictionaryGetValue(vals, kSecOIDBasicConstraints );
values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
if (values) {
for(int i = 0; i < CFArrayGetCount(values); i++) {
CFDictionaryRef subDict = CFArrayGetValueAtIndex(values, i);
// We cannot find OID defines for the CA - so rely on the lower libraries to give us a string
// of sorts. Not a good idea - as now this code can be foiled by a actual string.
//
NSString *k = [NSString stringWithFormat:#"%#", CFDictionaryGetValue(subDict, kSecPropertyKeyLabel)];
NSString *v = [NSString stringWithFormat:#"%#", CFDictionaryGetValue(subDict, kSecPropertyKeyValue)];
if ([#"Certificate Authority" isEqualToString:k] && [#"Yes" isEqualToString:v]) {
isCA = TRUE;
}
}
};
// Fall back on a simple self-sign check if there where no kSecOIDBasicConstraints.
// set on the cert. Note that it is a DN is equal check - in some cases
// doing a 509v3 Subject/Authority Key Identifier may be better ?? XXXX
//
if (!isCA && !values) {
dict = CFDictionaryGetValue(vals, kSecOIDX509V1SubjectName);
values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
subject = [NSString stringWithFormat:#"%#", values];
dict = CFDictionaryGetValue(vals, kSecOIDX509V1IssuerName);
values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
issuer = [NSString stringWithFormat:#"%#", values];
// Crap way of secondgessing CA ness.
if ([issuer isEqualToString:subject])
isCA = TRUE;
};
SecPolicyRef policy = SecPolicyCreateBasicX509(); // SecPolicyCreateSSL(YES,nil);
CFArrayRef chain = CFArrayCreate(NULL, (const void**)(&certificateRef), 1, NULL);
SecTrustRef trustRef;
SecTrustCreateWithCertificates(chain, policy, &trustRef);
SecTrustResultType result;
SecTrustEvaluate (trustRef, &result);
if(result == kSecTrustResultProceed) {
isUserTrust = TRUE;
isInvalid = FALSE;
} else
if (result == kSecTrustResultUnspecified)
isInvalid = FALSE;
CFRelease(trustRef);
CFRelease(chain);
// Images as per /System/Library/Frameworks/SecurityInterface.framework/Versions/A/Resources
// Cert <Small | Large> <Personal | Root> [_Invalid | _UserTrust ]
//
return [NSImage imageNamed:[NSString stringWithFormat:#"Cert%#%#%#",
isSmall ? #"Small" : #"Large",
isCA ? #"Root" : #"Personal",
isInvalid ? #"_Invalid" : (isUserTrust ? #"_UserTrust" : #"")]];
}
#end
The basicConstraints extension is defined in X.509 as follows:
basicConstraints EXTENSION ::= {
SYNTAX BasicConstraintsSyntax
IDENTIFIED BY id-ce-basicConstraints }
BasicConstraintsSyntax ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL }
This, in turn, is encoded according to the Distinguished Encoding Rules (X.690). The individual parts of the BasicConstraintsSyntax sequence do not have their own OIDs.
I wouldn’t claim to be an expert, but I don’t think there is a problem with the test for the cA flag that you are doing. I don’t think the [NSString stringWithFormat:#"%#", ...] part is necessary, mind.
As for checking for self-signed certificates, it seems not unreasonable to test the subject and issuer names; obviously that really tells you whether the certificate claims to be self-signed, and to actually test that it is you’d need to check the signature yourself (whether that’s something you want to do, I don’t know). FYI, on the topic of the key identifiers, according to RFC3280, in the specific case of a self-signed certificate, the authority key identifier can be omitted, so a certificate without an authority key identifier might be an indication that the certificate was self-signed, but it’s entirely possible for someone to deliberately issue a malformed certificate without authority key identifier.
The final question appears to be cleared up by looking at the docs, which indicate that the values mean roughly what you say.
The other thing worth saying is that there is code out there that can help with this kind of thing; for instance, Jens Alfke’s MYCrypto library
Edit (8th Feb 2012)
According to the X.509 Style Guide, it’s possible for the subject name to be empty, in which case you would need to look at the subjectAltName extension instead. It’s very unlikely that this would break a test for self-signed certificates that relied on comparing the subject and issuer names — it seems unreasonable to issue a certificate to yourself providing a DN for the issuer name but then leaving the subject name empty. However, it’s worth bearing in mind.

Resources