I am working on an app which needs to send the data on server in MD5 encoded string. In Objective C MD5 string is generated something like:
*68da55b645ef7fef43d4df4910a30a0d*
where as on PHP side the MD5 string is generated something like:
*c16a5320fa475530d9583c34fd356ef5*.
Here both are not generating same so it creates problem.
Below is my code to generate the MD5 string in Objective C.
- (NSString *)md5
{
const char *cStr = [self UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5( cStr, (int)strlen(cStr), result ); // This is the md5 call
return [NSString stringWithFormat:
#"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
In PHP the MD5 string is generated using MD5.min.js
This issue rejects my API calls. Any idea how to make it identical? Ideally it should generate the same on both the sides but it is not generating.
Any help would be appreciated!!!
The problem is the base 64 encoded iv(in PHP) doesn't match when decoded in IOS
The scenario is I am using Blowfish Algorithm CBC mode . Simply server(PHP) generate a random cryptographic IV
and encoded base64 format , send to IOS .
The Problem is here when-time I try to decode , the decoded results are not correct always . Sometime it generates correct and rest of time error.
Here is my PHP code.
public function ivGenerator()
{
$ivSize = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
echo base64_encode($iv);
}
Here is two result set of ivGenerator which i will use later for decoding
bTpkpLxMbNo=
aIvJeujxW7w=
Here is my IOS code .
NSString *ecodedIVString = #"bTpkpLxMbNo=";
NSData *ecodedIVData= [[NSData alloc] initWithBase64EncodedString:ecodedIVString options:0];
NSString *decodeIVString = [[NSString alloc] initWithData:ecodedIVData encoding:NSASCIIStringEncoding];
NSLog(#"decodeIVString %#" , decodeIVString); // m:d¤¼LlÚ //its correct
If I use ,
NSString *ecodedIVString = #"aIvJeujxW7w=";
Then the result is hÉzèñ[¼ , but the actual result should be h‹Ézèñ[¼
I have tried
NSWindowsCP1252StringEncoding , NSISOLatin1StringEncoding , NSUTF8StringEncoding
But all of them are unable to generate accurate result (approx. 10 test I have found it).
Thanks in advance !
You should print out the result in hexadecimals and then perform a comparison.
It seems that the encoding on both platforms is the same (something you should not take for granted) so you get the same characters back. However, it isn't sure that the font or font version you are using shows the characters. It could also be that one platform shows an ‹ character for unknown encoding sequences, and the other doesn't show anything at all.
This brings us to the basic issue: if you decode random byte values then you may encounter byte sequences that do not translate to a character. So comparing binary strings by converting them to a printable string is no good. Instead you need to directly compare the bytes (e.g. by saving the IV to iv.bin files) or by translating them into hexadecimals.
It's extremely likely that the IV's are identical, despite the fact that the printout is different.
I am playing around with signatures for request on the iPhone.
I have a simple PHP script running that will verify incoming JSON data against the signature in the header (all in all it is very similar to two-legged OAuth).
The following Obj-C code generates the signature:
const char *cKey = [kConsumerSecret cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [payload cStringUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Payload: %s", cData);
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *hmac = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *signature = [hmac base64EncodedString];
And the verifying PHP code looks like this:
$originalPayload = '{"name":"Ben’s iPhone"}'; // The message received from iOS
$hash = hash_hmac("sha256", $originalPayload, "Secret Key", TRUE);
$signature = strtr(base64_encode($hash), array('=' => '', '+' => '-', '/' => '_'));
The functions work perfectly fine when using ASCII characters. But when they contain UTF-8 characteres iOS calculates the signature based on:
Ben’s iPhone
While PHP calculates it based on:
Ben\u2019s iPhone
Making the signatures invalid. I have tried multiple approaches and none of them have yielded any results. Does anyone have an idea for how to solve this issue?
Remark: Yes, I could simply use OAuth, but I'm just curious on how to implement it myself.
Update: I have tracked down the problem to a part in the code, that modifies the parsed JSON array in the PHP code. In order to hash, but not include it in the body, I added the temporary index 'time' that contained a UTC timestamp. That modification of the original array made PHP change the character representation (escaping UTF-8 characters). Just appending the timestamp work fine.
Nonetheless the functions should yield the same result, shouldn't they?
I am building a library to communicate with the back end that drives our iPad client.
The basic Structure for all requests is:
{
"metaData":{ JSON Object that is not important for this question },
"requestData":
{
"nonce":"random string",
"params":"JSON string containing request data"
}
"checksum":"hash of JSON string representing value of requestData",
"connectionString":"someIdentifier"
}
For the specific case where I am having trouble is when requestData has the following structure:
{
"requestData":
{
"nonce":"random string",
"params":
"{\"userId\":1,\"formData\":\"encrypted string, then Base 64 encoded on iPad\"}"
}
"checksum":"hashed value of string representation of requestData"
}
An example of the string (as printed by NSLog) used to generate the checksum is:
{"params":"{\"groupId\":3,\"formData\":\"SExvR0J1ZkJSQkhObU5xZkiEXdBede2moVN3LtMDZlxcXYVj7Uz!BFdiQC9SwxIhrrcGv2GtWJzjqMhHzdFDZW568tbnLTKQ9931efrpjtvqlK9mudInXj0FQdBLY0M6f9zBlLu6TcQ7sA6AD15DF0HyUPIi4fnc90ZV7omGqRpyI412aGSpDPJEbCUBSY5WMUFJqRstyK1+Qo0vmN8uMprztDIyEFufP24DHHtYZHVAic8Sg8CxbsUTTYDgDc!0ASQwahEgy1sWkMP!BVpK8VU7quXDdIJrxbSNL7OO4tsJrHIXyhhK7ZUNKMaZX+fBSdw6DbNtTM86K0X4NSRXPVLE0EAklAJ2OpMDBsoz9k!jhCba5gRXY7r48USpsMyyj1v8SsDKn58FsvDxsdCrPY77KmIX3Icy!n3iA!lBfc3ol6c90wkwPSqNvnO7uRDYrfbP1c0zRYSXbLTQvHLLdfAWKariCKtNg6YAXNfgQ6lWFRXce8flHgUz6E7rkt9tjc9i4K+EjcL10H+E3AGkidYPGtQOm1vey!M8oineM!Cgg3VcvNCv!yN90iq3T+tqI0ivvBnh+1aCw2H90tnNm8Gi+XCrIdhORN3QjSkkNbpfoSCLoIkuBmXlNuTskaJ4nnV3kHrmU!4hYMeZIIZ8OnZWPpU47xJi!kh3MDdI2c+WorT+y+M5XwcQO6jGv3tXyRVBy!ne+sSnU!InISm7x1VQjJLmjULMnqxRDoZatBsofxICJysEUaDJvgwZasMJpQk1zyrPraBWBJ0lVVaWhH5OTi6U0!hHNVs5Xf+H23JxmPpUNWqNvsAGfnTfY!kSoiLoSxEocICK8zsJFMc69101DNAanayf!MjFFDeFRlzpKhcRON7cxDfvBdSoc9hL1lcMzFbLemrL1w8jNNMfKlY7QDZ5ebOERJMjY0!o8znlxOa0ViuJ++O7+QrT!mGdSQYGh3NJ3MK1IdJkXuFpY!guyXOgohTsqcD0DZSk84OsI76L18snFvs4qMHw9SUf3l0jWPxbTYimmlM3DVUR7Sn7xOsGmQGcwpGK1tinlIDA+w8Ci+CLWESsjZ5QDQCr\",\"internalFormId\":\"MTN13511759141\",\"userId\":1,\"code\":\"\",\"_queuedSubmission\":false,\"groupName\":\"LDMAdmin\",\"saleType\":\"1\",\"formTableName\":\"myTableName\",\"emailName\":\"default\"}","nonce":"XqfK9Nxwuggw4m"}
When I examine the server logs, I find the checksum generated on the device does not equal the checksum generated on the server (in PHP, case sensitivity is not the issue), thus authentication fails.
An example of the structure that fails is:
PageManager.m - 905 -> Final Request:
{
"checksum":"9D51170D1510C4081936870D11E96C869DB26B895393B9C14B2A6BC3C1F10F23",
"connectionString":"testBed",
"metaData":
{
"accessToken":"myAccessToken",
"appId":"myAppID",
"deviceId":"1X:1X:1X:1X:1X:1X",
"groupId":3,
"groupName":"LDMAdmin",
"timestamp":1351018002.780379,
"userId":1,
"useragent":"iPhone OS",
"username":"admin"
},
"requestData":
{
"nonce":"1iezcBdjbE",
"params":"{\"groupId\":3,\"formData\":\"Tnh3dWdndzRtRmdxSFNmN/NXCIQSukpx3+mhmbNQh0PTGbLlEFoDinyrq3wRJGZ+8sQ/+xcjS4cU7evluipxqQDZIOvp4ZcoDnxTPeqBZJrG/bq5FHR6PVCYK2DaLHfj025z/H3RM8dUEoWcrTLqSUcW+E7Mfl8ZCApqJMxSa8eYYqLT7tm7r1SC+bjXNOQZLTC2laFhihQ5hLKqFFnO/z3AlUYAAUhKKD1lWIipnJUUNoyHdWuuOobMSS1ZZP5f5f+RTFsmGZUDe6qX6h2cjIQ2+VGPIsP//gqwO4iDx/FdHD+xrjCyEgL2Va/m/Z+ANxCr3DN2o2Jnwg8B8QycFN2tGrgusseAgoa9Ng9LRgooZW+KuECWDhorHzvuv2rOlhOskymj4XTu8890ZMJbcr1Ic6zwztm82R1qKaoy1o6gIbUNtVZFSqUlP8TO7mWHKr3Y8Awn7ih9HzSOg1486EDL4OjfOR9J2pw1jbK7ZJb7LxzrWFgoyrwDBAw3q7PrV4Ml9ngI6oXOh3veAq/wulyBOdF46n7evqIkAKg4FYdvzmFKd2bgOpxwBlAI7vL2IiC4v8GXI5977SkPPEKUZHXWmfrgr/VzF79gIxJDqV9N0ceAcgY8bWbBXf7DLd9H82obFa60yZBo5/MBjq9SNuD08vJEEauVGs4wfDr9+xzsr3z+plqxAejODdxKfF48Ra21L8Xozozv5papTP9cpGVU11mCWj+no5gtM0VQKRB7IQcpDWjQgQyThN2aoE06ecA2gY5SSXN0XHVRw5OKM0/rlNIuMiqow5wqHLl41IzDSF2HuJKj06Lv8t5CLLOd9rkOjYw6w8SrbsZeG5jwagJkyQ0UuKu+PIoIc2DJnUWDC5iqlb0TO9nPDNFKad+MYlfgDR0CxR+3ddkqWNBNSW5rsh5QZDlJHDjhQFLkuqiiRAnMvKOcbqAnXIZ9EuAo/DkcmtGPHkEyEaA2cb3mXysBP49jhY0m/qinloza+j3d7Kb/Fu35U929fOxH6+W+5oZv/r+a9KvkDhPoRwiFouVwTtTOwbjVDT+NEg2OUfDaEYbQ/RbM7i6X+XjSkZMLYsRs1Q9CwdBabY860uBNFQ==\",\"internalFormId\":\"MTN13510178111\",\"userId\":1,\"code\":\"\",\"_queuedSubmission\":false,\"groupName\":\"LDMAdmin\",\"saleType\":\"1\",\"formTableName\":\"myTableName\",\"emailName\":\"default\"}"
},
"url":"http://myurl.com/myAction"
}
The formData is generated by creating a JSON string of the desired data, encrypting this string, and encoding the resulting NSData object into a base 64 string. The checksum is generated by taking the hash of the JSON string representing the requestData value (with SHA256).
requestData is converted to a JSON string, and hashed with:
+(NSString *)hmacSHA256:(NSString *)string withKey:(NSString *)key {
NSString *hash = 0;
NSData *hmac = 0;
NSMutableString *temp = [[NSMutableString alloc] initWithString:#""];
#ifdef DEBUG
NSLog( #"%s - %d -> Values:\nString: %#\nKey: %#", __FILE__, __LINE__, string, key );
#endif
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [string cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
unsigned char *digest;
unsigned int dLength;
CCHmac( kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC );
hmac = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
digest = (unsigned char *)[hmac bytes];
dLength = hmac.length;
for ( int i = 0; i < dLength; ++i )
[temp appendFormat:#"%02X", digest[i]];
hash = [[NSString alloc] initWithString:temp];
return hash;
}
The PHP script uses a stock function to generate the hash:
protected static function _generateChecksum($data, $key, $output)
{
$jsonData = json_encode($data);
log_message('error', 'encoded string is: '.$jsonData);
$checksum = hash_hmac('sha256', $jsonData, $key);
//log_message('error', 'Checksum pre encode: '.$checksum);
// if($output == true)
// {
// $checksum = base64_encode($checksum);
// }
return $checksum;
}
The hash is compared to the hash sent in the request.
An example of the server log:
ERROR - 2012-10-23 21:31:16 --> Encoded Checksum recieved is: 14F03A1DCAEA9DBBC7EC8CA1D666D89C391760AA246C91B52164D526AC83E5E5
DEBUG - 2012-10-23 21:31:16 --> Model Class Initialized
DEBUG - 2012-10-23 21:31:16 --> Model Class Initialized
DEBUG - 2012-10-23 21:31:16 --> Database Driver Class Initialized
ERROR - 2012-10-23 21:31:16 --> encoded string is: {"params":"{\"groupId\":3,\"formData\":\"SE5tTnFmWHFmSzlOeHd1ZyYzHN9y\/aJSbsqEn6X1TypGwWcXtXGpW4ODrCDVwZyIjhq3oOeZ4C6hGCDFGqHsa5hhNXxeerWLG5SyvfksCTG1+GCvWFwMx0CzZwOAfJRwSoCBCaeZ\/pivs3dHQS22SEbWn6+2e2vayeap7mxvZZw9Jrl\/c4dGFAiNqQB5pQbNO661AqbJWDAHCS8EWBhXXsd0SbTHlZAip4H0MdlF2rnElCVfHlc01RcuJNXLF3NJvfjY9m4sXmI3BAED0c0C\/i0Uw2M6pe4iDJv\/OvOI0NVS8RKbRbjhTo3oktAmNttfKTG6xp0wMhbANppuoo4QY3XwQ5BKUjqhmr5kx8j0RTmebcTCmxsC9h1dqjHYnf1JnZDFATkVsKnn\/Ela1wSjhGL7uP6jl3r4xDGKGPWDj0E3iAPNN56pmJxzyQrHOOqUzGbmvU3qj7Ul039IGYZTzn74VUkWi3JsxJH+kU9iWSvuC+YoOHcf\/0OFn1PqBoDHjDTbN+3HV8wwSqrVFJ6z9RX4MwRfffVgKl8xL2hHqBnegjvyd65KbZbSd\/3OrEBeL0dAjuARiPioNTpjzwga4chFRA471gweLT+cKweZBXZMYll36sNqulIBzbCmqbndDk63Id9iSrs9\/fQVWUA7RJDudnAxvQPs8gTznp9Dz1SomyY4ONYrJ9EticAEnUEjF2sCdejYlgu61a3Zss19m+MzgEhxkwmRwttsRbFfNK44wP\/wB2FgdfjsY94nHpJ+6lPEZtRmWpYtNVxQMVC6mMde6CbSEem71byIiN424baPImtNIfF+bKl6BKxyEl7BhI3z25NXaKyfaflzxGY8Yvdg0f73SfT3omPP3KxdudFgJrQ6eiO2AXt5L6lPjezjRr17R6hTUNmYwvZ3C5S0zoY7ynmCHebeiNlavVepUBkn0Iu6w\/qKDJ5wr80n7XX3EXuo1ODCC5aCjOSr+gSS9eVm0\/IBQNF\/ec4kjI29LRyrOFOS\/2poHY9XzyagVURiwi101a0yPETRRsC8n4B\/XFmOFQ0VcCQNgTuXute52fsccxB3DkV7ixBbQ8mt6o2XDWGk2HnrDwmRuNX87rBHow==\",\"internalFormId\":\"MTNC13510277491\",\"userId\":1,\"code\":\"\",\"_queuedSubmission\":false,\"groupName\":\"LDMAdmin\",\"saleType\":\"1\",\"formTableName\":\"myTableName\",\"emailName\":\"default\"}","nonce":"gw4m"}
ERROR - 2012-10-23 21:31:16 --> Encoded Checksum expected is: 8d999d76e48907905e701da3ccdbccb4061d05ed5a7c18b58507b6e6352fb1f5
However, other components of the application use the same structure (minus the base 64 encoding) behave as designed and expected. The checksum is generated using the same method and key as the example above. Example:
{
"requestData":
{
"nonce":"random string",
"params":"{\"latitude\":37.7,\"longitude\":-122.4}"
}
"checksum":"hashed value of the string representation of requestData"
}
I have no idea why the PHP script on the server would be generating a different hash in the failed case mentioned above. Has anyone encountered this type of problem before?
UPDATE:
I am using SBJson for encoding and decoding my data structures into JSON strings (link).
UPDATE 2:
Base on the discussion that has occurred so far, there is no guarantee the order in which a JSON object (or in my case an NSDictionary) is serialized to a JSON string. But if that were the case, I do not understand why cases where data is sent in clear text, the same hash is generated on the server, and in the case where some data is in the form of base 64 string, a different hashes are generated between the server and the client.
The only solution, which is probably not right solution, is to remove / from the base 64 alphabet (I replaced it with !). I am pretty sure this will cause problems when trying to decode the string on the server side.
At this point, I would like to understand how a forward slash could be causing this problem.
There's no such thing as "the JSON string representing such-and-such". If you have a data structure in memory and use your platform's default JSON serializer on it, you have no guarantee that it will produce the same string as another platform's default JSON serializer -- or even that it will produce the same string as the JSON serializer you use will produce tomorrow, or on Fridays in leap years.
JSON gives serializers considerable latitude in where to place whitespace, how to format numbers, which order to emit object fields, and so forth. It cannot be used as a basis for hashing the underlying abstract dataset.
It was the forward slash that was in the encoding table, and somehow, this was causing a conflict when generating the hash in the php code.
The original table in my Objective C code:
//64 digit code
static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Which I had to replace with:
//64 digit code
static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+!";
This may be only a temporary solution, since this SO answer states that forward slashes are acceptable in a base 64 encoded string. Simply replacing the forward slash solved the issues for now.
I have been following this great tutorial and got my codes to working with most of the database transfers. Now I'd like to use the same method to update long text messages and images onto my server MySQL database, but it doesn't seem to work.
NSString *strURL = [NSString stringWithFormat:#"http://mydomain.com/sync.php?name=%#&message=%#&image=%#",nameField.text, messageField.text, image];
and then
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];
NSString *strResult = [[[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"StrResult is... %#", strResult);
Problem 1: When the messageField.text is a long text with blank spacing in between such as "This is a free text format with up to 1000 characters..." , it will cause strURL to have blank spaces as well, and thus strResult will become null.
Problem 2: image is of blob format which I already saved in the local sqlite. I'm not even sure if transferring this way is a good idea. But basically I want to upload the image from iPhone/iPad straight onto a server MySQL database.
Hope someone could help me with either one of these 2 problems. Or are there any better and easier alternatives to update MySQL on my server? Thank you!
Problem 1 : When you are sending parameters through a GET request, you need to escape your parameters to avoid illegal characters. By the way, you can not send unlimited data with this kind of request. For a large amount of data, you should rather use a POST request.
Problem 2 : ASIHTTPRequest is a wrapper used to communicate with remote servers. If you choose to use it (I encourage you to do), here is the documentation that you will need to POST a request . There is also an example to send an image to a remote server with NSData.
Problem 1: there's a lot of ways to do this... one of them are when you put it into the database do this:
messageField.text = [messageField.text stringByReplacingOccurrencesOfString:#" " withString:#"_"];
Maked the text: "This is a free text format with up to 1000 characters..." to: "This_is_a_free_text_format_with_up_to_1000_characters..."
And then when you take it out of the database again use the code opposite:
messageField.text = [messageField.text stringByReplacingOccurrencesOfString:#"_" withString:#" "];
That makes it from: "This_is_a_free_text_format_with_up_to_1000_characters..." to "This is a free text format with up to 1000 characters..."
But remember thats one way to do it... you can also replace the "_" with Something else like"I" or whatever you want.. something the user won't type...
Problem 2: I don't know a lot about databases and images.. but se this: Blob
:)