0

我正在构建一个库来与驱动我们的 iPad 客户端的后端进行通信。

所有请求的基本结构是:

{
    "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"
}

对于我遇到问题的特定情况是 requestData 具有以下结构:

{
    "requestData":
        {
            "nonce":"random string",
            "params":
                "{\"userId\":1,\"formData\":\"encrypted string, then Base 64 encoded on iPad\"}"
        }
    "checksum":"hashed value of string representation of requestData"
}

NSLog用于生成校验和的字符串(由 打印)的示例是:

{"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"}

当我检查服务器日志时,我发现设备上生成的校验和不等于服务器上生成的校验和(在 PHP 中,区分大小写不是问题),因此身份验证失败。

失败的结构的一个例子是:

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"
}

formData是通过创建JSON所需数据的字符串、加密该字符串并将生成的NSData对象编码为 base 64 字符串来生成的。是通过获取表示值的字符串的checksum哈希值(使用 SHA256)生成的。JSONrequestData

requestData转换为 JSON 字符串,并使用以下内容进行哈希处理:

+(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;
}

PHP 脚本使用 stock 函数来生成哈希:

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;
    }

哈希值与请求中发送的哈希值进行比较。

服务器日志示例:

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

但是,应用程序的其他组件使用相同的结构(减去 base 64 编码)的行为与设计和预期的一样。checksum使用与上述示例相同的方法和密钥生成。例子:

{
    "requestData":
        {
            "nonce":"random string",
            "params":"{\"latitude\":37.7,\"longitude\":-122.4}"
        }
    "checksum":"hashed value of the string representation of requestData"
}

我不知道为什么服务器上的 PHP 脚本会在上述失败的情况下生成不同的哈希值。有没有人遇到过这种类型的问题?

更新:

SBJson用于将我的数据结构编码和解码为 JSON 字符串(链接)。

更新 2:

根据迄今为止发生的讨论,不能保证 JSON 对象(或者在我的情况下是NSDictionary)被序列化为JSON字符串的顺序。但如果是这样的话,我不明白为什么在以明文形式发送数据的情况下,在服务器上生成相同的哈希值,而在某些数据是 base 64 字符串形式的情况下,不同的哈希值是在服务器和客户端之间生成。

唯一的解决方案(可能不是正确的解决方案)是/从 base 64 字母表中删除(我将其替换为!)。我很确定这会在尝试解码服务器端的字符串时引起问题。

在这一点上,我想了解正斜杠如何导致此问题。

4

2 回答 2

0

它是编码表中的正斜杠,不知何故,这在 php 代码中生成哈希时导致了冲突。

我的 Objective C 代码中的原始表:

//64 digit code
static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

我不得不替换为:

//64 digit code
static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+!";

这可能只是一个临时解决方案,因为这个 SO 答案指出正斜杠在 base 64 编码字符串中是可以接受的。只需替换正斜杠即可解决现在的问题。

于 2012-10-24T16:31:56.620 回答
0

没有“代表某某的 JSON 字符串”之类的东西。如果您在内存中有一个数据结构并在其上使用您平台的默认 JSON 序列化程序,您无法保证它会生成与另一个平台的默认 JSON 序列化程序相同的字符串——甚至无法保证它会生成与 JSON 序列化程序相同的字符串您使用将在明天或闰年的星期五生产。

JSON 为序列化程序在放置空格的位置、如何格式化数字、发出对象字段的顺序等方面提供了相当大的自由度。它不能用作散列底层抽象数据集的基础。

于 2012-10-23T21:39:55.323 回答