I have the following PHP code:
require('/var/www/third_party_plugins/phpseclib/vendor/autoload.php');
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
use phpseclib3\Math\BigInteger;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\Random;
$message = hex2bin('f5f905e8b2d8f0a72e179a169a59bc373021a75865e55c6797627bc43ddc6af0d9bd673bf94f5e8defc5af81019fd87c7d504a6aa758ba1e2f1f9858d0293b0b');
$key = hex2bin('d2ce45fd5f80c15db0a4ab26a7e27f42b507ed9469f0d63c1dbe4f89ed84c0c2');
$iv = hex2bin('db9d7e844b00282327221bb563639f96');
$cipher = new AES('cbc');
$cipher->setIV($iv);
$cipher->setKey($key);
//$cipher->disablePadding();
$Decrypted = bin2hex($cipher->decrypt($message));
print("\n" . $Decrypted . "\n");
When I run this code, I get the following result:
240dcbefc0f82fadc00ef8494488aaa81400000c2def01e79fec6c4d9a822358dd8a910cac606e8afcb607793cb442093a56b7b40b
Inside of this result, I can see the message I WANT, which is:
1400000c2def01e79fec6c4d9a822358
However, there are 16 bytes of data in the front of the message which make no sense to me, it seems like some kind of padding, but I dont want it in my result nor do I understand why it is there, because from what I understand the padding should be removed by phpseclib or openssl functions
240dcbef c0f82fad c00ef849 4488aaa8
I understand that there is a 20 byte MAC at the end, however, I notice ONE extra byte in front of the mac:
dd8a910cac606e8afcb607793cb442093a56b7b40b // THIS IS 21 BYTES, NOT 20...why?
I'm also having trouble re-encrypting the data and getting a valid response from the server, as I'm re-encrypting it with PHP, then sending the SAME data to my C# server, and it is unable to decrypt the message.
I have an iOS app and PHP web service that take a user submitted "password" and hash it.
The problem arises when you enter an emoji:
PHP hashes the salt + 😍
iOS hashes the salt + [heart-for-eyes smiley emoji]
Is there a way to convert the emoji on iOS to the corresponding HTML Entity (like PHP does automatically) so that the resulting hash values are the same?
stringWithUTF8String does not work
NSNonLossyASCIIStringEncoding converts it to "\ud83d\ude0d"
Here is a solution, perhaps there is a built-in:
NSString *emojiString = #"😍";
NSLog(#"emojiString: %#", emojiString);
NSData *codePointData = [emojiString dataUsingEncoding:NSUTF32LittleEndianStringEncoding];
u_int32_t codePoint = *((u_int32_t *)(codePointData.bytes));
NSString *escapedEmoji = [NSString stringWithFormat:#"&#%d", codePoint];
NSLog(#"escapedEmoji: %#", escapedEmoji);
NSLog output:
emojiString: 😍
escapedEmoji: 😍
This based on the fact that the escaped emoji is the decimal number of the code point. The binary code point is the UTF-32 encoding of the unicode character.
Unfortunately I was not able to find a pre-existing solution.
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 trying to implement some encryption between an iPhone app and a PHP web service. It's not working however. It seems like the first half of the text is NOT decrypted while the second half is decrypted just fine. What should I do?
The PHP encryption method is as follows:
function decrypt($str, $iv) {
$iv .= "00000000";
$str = base64_decode($str);
return self::decrypt_data($str, $iv, self::secret_key);
}
The iPhone stuff that encrypts the text uses a CryptoHelper class like this:
NSString *encrypted = [[CryptoHelper sharedInstance] encryptString:dataString];
The CryptoHelper class can be seen at http://pastie.org/1267796.
Try a simple example where you send a known Base64 encoded string from the iPhone app to PHP.
Compare the known valid string to what PHP is getting. I know recently, when trying to do an Ajax post from a script to PHP, we were having trouble with some characters (specifically +) being converted to spaces by PHP because it was doing a URL decode automatically. We had to switch all + to their % (URL-encoded %2B) equivalent. This fixed the problem for us.