HMAC-SHA1 for iOS to match Yahoo's OAuth API call - ios

I know HMAC-SHA1 functions are easily available on SO, but I have tried all of them to generate an OAuth signature but with no success.
Since the code sample given by yahoo
is written in Java I am not sure If I am following the same HMAC-SHA1 algorithm.
Here is the method which I use to generate it:
- (NSString *)generateOAuthHeader
{
NSString *apiURL = #"https://weather-ydn-yql.media.yahoo.com/forecastrss";
NSString *oauth_consumer_key = #"dj0yJmk9V004dENIbkd6dXh3JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTg0";
NSString *consumerSecret = #"9b54fad8d2bccedaa17eddfe342a0178ee72eb34";
NSString *oauth_nonce = #"840eee23-f521-4d52-bca9-3a715894f22";
NSString *oauth_signature_method = #"HMAC-SHA1";
NSString *oauth_timestamp = [NSString stringWithFormat:#"%.0f", [[NSDate date] timeIntervalSince1970]];
NSString *oauth_version = #"1.0";
NSString *encodedApiURL = urlformdata_encode(apiURL);
NSString *parameters = NSString *parameters = [NSString stringWithFormat:#"oauth_consumer_key=%#&oauth_nonce=%#&oauth_signature_method=%#&oauth_timestamp=%#&oauth_version=%#&lat=%f&lon=%f&format=json", oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version, 30.707640, 76.703553, nil];
NSString *encodedParameters = urlformdata_encode(parameters);
NSString *signature = [NSString stringWithFormat:#"GET&%#&%#&", encodedApiURL, encodedParameters];
signature = [self hmacsha1:signature secret:consumerSecret];
NSString *authorizationHeader = [NSString stringWithFormat:#"OAuth oauth_consumer_key=\"%#\", oauth_nonce=\"%#\", oauth_signature_method=\"%#\", oauth_timestamp=\"%#\", oauth_version=\"%#\", oauth_signature=\"%#\"", oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version, signature, nil];
return authorizationHeader;
}
But I always end up having a 401 error meaning the signature is not correct.
I created a public repo in objective-c so any one can try it out, it is available here: https://github.com/userException/yahooOAuthiOS

The one minute detail which is not mentioned on Yahoo's page is you have to append "&" to the consumer secret while creating HMA-SHA1 encrypted string. Because of this the HMAC-SHA1 string was not what the yahoo's server was comparing it with.
I have committed my changes in the same repo mentioned in the question, if you need to have the Swift/Objective-C version of it.

Related

Objective-C: JWT - Encode with RS256 using string data of a private key

I'm using this library to encode a RS256 token https://github.com/yourkarma/JWT
In the docs there is an example that deals with RS256 encoding
NSDictionary *payload = #{#"payload" : #"hidden_information"};
NSString *algorithmName = #"RS256";
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"secret_key" ofType:#"p12"];
NSData *privateKeySecretData = [NSData dataWithContentsOfFile:filePath];
NSString *passphraseForPrivateKey = #"secret";
JWTBuilder *builder = [JWTBuilder encodePayload:payload].secretData(privateKeySecretData).privateKeyCertificatePassphrase(passphraseForPrivateKey).algorithmName(algorithmName);
NSString *token = builder.encode;
Now, since I only have a string of a private key similar like this:
"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtN7LQq7l9a9....SUug==\n-----END RSA PRIVATE KEY-----\n"
I'm wondering how I can implement that? Obviously I don't need a passphrase.
Thanks in advance
It turned out it wasn't that hard, with a help of a workmate ;-)
Here's the solution:
NSDictionary *payload = #{#"payload" : #"hidden_information"};
id <JWTAlgorithmDataHolderProtocol> holder = [JWTAlgorithmRSFamilyDataHolder new]
.keyExtractorType([JWTCryptoKeyExtractor privateKeyWithPEMBase64].type)
.privateKeyCertificatePassphrase(nil)
.algorithmName(JWTAlgorithmNameRS256)
.secret(<string of the private key as above>);
JWTCodingResultType *result = [JWTEncodingBuilder encodePayload:payload]
.addHolder(holder)
.result;
NSString *token = result.successResult.encoded;
NSError *error = result.errorResult.error;

String encode in objective c

I am very new to Objective-C.
I want to get the encoded content for a NSString. In java I can do that as follows,
String str = "https://www.google.co.in/#q=ios+sqlite+crud+example";
String encodedParam = URLEncoder.encode(str, "UTF-8");
I am using http://www.tutorialspoint.com/compile_objective-c_online.php to test the codes posted in stackoverflow. There is no solution yet. I know its trivial one. Struggling to find a way though.
tried with following function, and it says following error while compile,
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
CFStringConvertNSStringEncodingToEncoding(encoding));
}
Error,
sh-4.3$ gcc `gnustep-config --objc-flags` -L/usr/GNUstep/System/Library/Libraries -lgnustep-base -lobjc *.m -o main
main.m: In function 'main':
main.m:7:14: error: 'urlEncodeUsingEncoding' undeclared (first use in this function)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
^
main.m:7:14: note: each undeclared identifier is reported only once for each function it appears in
main.m:7:36: error: expected ';' before ':' token
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
Edit as per the answers,
Suggested by Patrick, I used the code as follows,
NSString *storedURL = #"google.com/?search&q=this";
NSString *urlstring = [NSString stringWithFormat:#"http://%#/",storedURL];
NSURL *url = [NSURL URLWithString:urlstring];
NSError *error = nil;
NSStringEncoding encoding;
NSString *my_string = [[NSString alloc] initWithContentsOfURL:url
usedEncoding:&encoding
error:&error];
NSLog (my_string);
Nothing printed in console... Is it my NSLog is right?
Suggested by lightwolf, my code is looks like below,
NSString *str = #"https://www.google.co.in/#q=ios+sqlite+crud+example";
NSString *encodedParam = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog (encodedParam);
it prints the log, but value is same as the str..... not encoded... I want this str as
https%3A%2F%2Fwww.google.co.in%2F%23q%3Dios%2Bsqlite%2Bcrud%2Bexample
If you want to encode a specific range of characters you chould use
NSString *str = #"https://www.google.co.in/#q=ios+sqlite+crud+example";
NSString *encodedParam = [str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];
NSLog (#"%#", encodedParam);
Note the invertedSet; In that way, you are encoding all characters except the set specified (all alphanumeric ones)
The result is
https%3A%2F%2Fwww%2Egoogle%2Eco%2Ein%2F%23q%3Dios%2Bsqlite%2Bcrud%2Bexample
If you want to use a specific set of characters you should use
NSString *str = #"https://www.google.co.in/#q=ios+sqlite+crud+example";
NSCharacterSet* set = [NSCharacterSet characterSetWithCharactersInString:#"!*'();#&=+$,?%#[]"];
NSString *encodedParam = [str stringByAddingPercentEncodingWithAllowedCharacters:[set invertedSet]];
NSLog (#"%#", encodedParam);
In this case I intentionally missed / and : so the result is
https://www.google.co.in/%23q%3Dios%2Bsqlite%2Bcrud%2Bexample
Maybe this is what you want
NSString *str = #"<html><head><title>First</title></head><body><p>Parsed HTML into a doc.</p></body></html>";
NSString *encodedParam = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
You have to encode only the params, not the entire URL of course

Obfuscate iPhone Password

If I need to obfuscate an iPhone password that is hardcode (Oauth Client Identifier and Client Secret), would this be a way to do it?
NSString *a = #"a";
NSString *b = #"b";
NSString *c = #"c";
NSString *d = #"d";
NSString *e = #"e";
NSString *f = #"f";
NSString *g = #"g";
NSString *h = #"h";
NSString *i = #"i";
/* hidden */
NSString *w = #"w";
NSString *x = #"x";
NSString *y = #"y";
NSString *z = #"z";
NSString *pwd = [NSString stringWithFormat:#"%#%#%#%#%#%#%#%#", p,a,s,s,w,o,r,d];
I know obfuscate isn't recommended but after reading this OAuth secrets in mobile apps it seems like the only way.
If you compile with clang -O3, only the letters that are actually used in the password get included in the .o file. You should include some code that pretends to use the rest of the alphabet, such as another call to + stringWithFormat whose results are ignored.
You could always encrypt the text in question offline, store the encrypted version in the app, then at the point where you need it, decrypt it. That way it (at least) isn't in plain text in the app. Even your mechanism above will likely produce a pattern in the binary.

iphone - app crash in a form

In my app, I have a view where user have to fill a form. But, sometime the app crash here, in this function, that simple cacth the value field and built a url to give
-(NSString*)urlToUpload{
NSString *string1 =[[NSString alloc]init];
string1= [NSString stringWithFormat:#"?nombre="];
NSString *string2 = [string1 stringByAppendingString:nameAdded];
//crash here
NSString *string3 = [string2 stringByAppendingString:#"&horario="];
NSString *string4 = [string3 stringByAppendingString:horarioAdded];
NSString *string5 = [string4 stringByAppendingString:#"&info="];
NSString *string6 = [string5 stringByAppendingString:infoAdded];
NSString *string7 = [string6 stringByAppendingString:#"&offerta="];
NSString *string8 = [string7 stringByAppendingString:offertaAdded];
NSString *lat_string = [[[NSString alloc] initWithFormat:#"%f",locationToUpload2.latitude] autorelease];
NSString *lon_string = [[[NSString alloc] initWithFormat:#"%f",locationToUpload2.longitude] autorelease];
NSString *string9 = [string8 stringByAppendingString:#"&latitude="];
NSString *string10 = [string9 stringByAppendingString:lat_string];
NSString *string11 = [string10 stringByAppendingString:#"&longitude="];
NSString *string12 = [string11 stringByAppendingString:lon_string];
NSString *url1 = [NSString stringWithFormat:#"http://myserverside/mysql_up.php"];
NSString *url = [url1 stringByAppendingString:string12];
return url;
}
EDIT:
It seems problem appers on nameAdded when there is a white space into textField(i.e. MisterB not crash, Mister B yes ).
But I am using:
nameAdded =[[nameField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
and NSLOg give of nameAdded is Mister%20B.
The crash still appearing...
Just use a single stringWithFormat::
- (NSString *)urlToUpload {
NSString *url = [NSString stringWithFormat:#"http://myserverside/mysql_up.php?nombre=%#&horario=%#&info=%#&offerta=%#&latitude=%f&longitude=%f",
[nameAdded stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[horarioAdded stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[infoAdded stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[offertaAdded stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
locationToUpload2.latitude, locationToUpload2.longitude];
return url;
}
Make sure the referenced variables are valid.
In your original code there is no need to alloc/init an NSString then assign another string to the same variable. That's a memory leak (string1).
If you really want to structure your code the way you have it, at least use an NSMutableString and append to that one mutable string. Creating over a dozen NSString variables is the wrong way to do it.
Updated: Ensure each of the strings added to the URL are properly escaped.
It looks like nameAdded may be the cause of your problems. Is it nil at that point?
Also
You are allocating a string, setting it to string1 and then immediately setting string1 to the class function stringWithFormat which allocates another string. Also you are using stringWithFormat but you aren't using any format so you could simply use NSString *string1 = #"?nombre=";
Rather than declaring all of those variables you should just use NSMutableString and build it all in one variabl

IOS stringByReplacingOccurrencesOfString coming back null

I've been banging my head against the wall on this, I must be missing something. This code is called just after I base64 encode some encrypted text. At the point where I call NSLog signature is always valid. However sometimes the rest works great and other times removePlus or finalSig come back null. Also I'm using Xcode 4.5, ios6 and I have ARC enabled for the project. I feel like maybe something is getting auto released before I want it to, or something like that. If anyone has any ideas any help is appreciated.
// Get the data out of the context
char *outputBuffer;
long outputLength = BIO_get_mem_data(context, &outputBuffer);
NSMutableString *signature = [[NSMutableString alloc] init];
[signature appendString:[NSMutableString stringWithCString:outputBuffer encoding:NSASCIIStringEncoding]];
NSLog(#"Base64 Pre Encoded: %#", signature);
signature = [[signature stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding] mutableCopy];
NSMutableString *removePlus = [[signature stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"] mutableCopy];
NSString *finalSig = [removePlus stringByReplacingOccurrencesOfString:#"=" withString:#"%3D"];
//now we create the url request portion
NSMutableString *variables = [[NSMutableString alloc] init];
//set the variables we're going
[variables appendString:finalSig];
Try swapping your code for the following, as I imagine it will show you exactly where your problem is:
// Get the data out of the context
char *outputBuffer;
long outputLength = BIO_get_mem_data(context, &outputBuffer);
outputBuffer[outputLength] = '\0';
if(outputLength != strlen(outputBuffer)) NSLog(#"SOMETHING VERY WRONG");
NSString * signature = [NSString stringWithCString:outputBuffer encoding:NSASCIIStringEncoding]];
NSLog(#"Base64 Pre Encoded: %#", signature);
signature = [[signature stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSLog(#"Base64 Post Encoded: %#", signature);
signature = [signature stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
NSLog(#"Base64 Remove '+': %#", signature);
signature = [removePlus stringByReplacingOccurrencesOfString:#"=" withString:#"%3D"];
NSLog(#"Base64 Remove '=': %#", signature);
//now we create the url request portion
NSMutableString *variables = [NSMutableString alloc] initWithString:signature];
…
//set the variables we're going
[variables appendString:finalSig];
Turns out that NSString * signature = [NSString stringWithCString:outputBuffer encoding:NSASCIIStringEncoding]]; requires a null terminated string and my string was not. Anyway this was a more effective way to create the string:
NSData *myRequestData = [ NSData dataWithBytes: [ variables UTF8String ] length: [ variables length ] ];

Resources