Base64 issue in NSMutableURLRequest POST message?

2019-05-26 17:19发布

I'm having communcation issues between my app and the server. I'm using RNCryptor to encrypt a message, which I then base64 encode and transfer to the server in the request. This is done in both the DATA header, and within the http body as post data. I think I'm making a mistake in how I'm converting & transferring the base64 encoded message via POST.

If I receive the encrypted message via the header, it decrypts perfectly fine, every single time. However, if I take the message via the POST data, I'm getting varying results. Most of the time, it fails, else it partially decrypts (first few letters), with 1 in 20 or so successful decryptions.

The objective-c code is:

- (NSString *)sendEncryptedTestMessage:(NSString *)address{
    NSString* messageContent    = @"Hello my name is Bob.";
    NSError * error             = nil;
    NSString* responseString2   = nil;

    NSData*   postData = [RNEncryptor encryptData:[messageContent dataUsingEncoding:NSUTF8StringEncoding]
                                    withSettings:kRNCryptorAES256Settings
                                        password:@"123456"
                                           error:&error];

    NSString* messageServer     = [NSString base64forData:postData];
    NSString* postMessage       = [@"message=" stringByAppendingString:messageServer];
              postData          = [postMessage dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; // problem here I think

    NSString* postLength        = [NSString stringWithFormat:@"%ld",(unsigned long)[postData length]];

    NSURL*    URLToRequest      = [NSURL URLWithString:address];

    NSMutableURLRequest* semisystem = [[[NSMutableURLRequest alloc] initWithURL:URLToRequest] autorelease];

    [semisystem setHTTPMethod:@"POST"];

    [semisystem setHTTPBody:postData];
    [semisystem setValue:postLength                           forHTTPHeaderField:@"Content-Length"];
    [semisystem setValue:self.activationURL                   forHTTPHeaderField:@"EncryptionKey"];
    [semisystem setValue:messageServer                        forHTTPHeaderField:@"data"];

    NSURLResponse* response;
    NSData* data = [NSURLConnection sendSynchronousRequest:semisystem
                                         returningResponse:&response
                                                     error:&error];

    responseString2 = [NSString stringWithFormat:@"%.*s", (int)[data length], [data bytes]];
    return responseString2;
}

PHP code:

function decrypt2($b64_data,$password)
    {
           // back to binary
            //$bin_data = mb_convert_encoding($b64_data, "UTF-8", "BASE64");
            $bin_data = base64_decode($b64_data);
            // extract salt
            $salt = substr($bin_data, 2, 8);
            // extract HMAC salt
            $hmac_salt = substr($bin_data, 10, 8);
            // extract IV
            $iv = substr($bin_data, 18, 16);
            // extract data
            $data = substr($bin_data, 34, strlen($bin_data) - 34 - 32);
            $dataWithoutHMAC = chr(2).chr(1).$salt.$hmac_salt.$iv.$data;
            // extract HMAC
            $hmac = substr($bin_data, strlen($bin_data) - 32);
            // make HMAC key
            $hmac_key = pbkdf2('SHA1', $password, $hmac_salt, 10000, 32, true);
            // make HMAC hash
            $hmac_hash = hash_hmac('sha256', $dataWithoutHMAC , $hmac_key, true);
            // check if HMAC hash matches HMAC
            if($hmac_hash != $hmac) {
                echo "HMAC mismatch".$nl.$nl.$nl;
               // return false;
            }
            // make data key
            $key = pbkdf2('SHA1', $password, $salt, 10000, 32, true);
            // decrypt
            $ret = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);      
        return $ret;
    }
$passkey = "123456";

$messageBase64                  = $_POST['message'];// THIS barely works
$messageBase64              = $_SERVER['HTTP_DATA'];// THIS WORKS
$message                = decrypt2($messageBase64,$passkey);

Many thanks in advance!

3条回答
男人必须洒脱
2楼-- · 2019-05-26 17:31

I've just found the solution. During the request, the '+' symbols are being interpreted as whitespaces by the server, breaking the base64 code. The following line fixed this problem:

postMessage       = [postMessage stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
查看更多
淡お忘
3楼-- · 2019-05-26 17:32

I know this is a old question, but for a long time I used the same solution and the problem was that we are not encoding properly the url before making the request to the server. The documentation says:

  According to RFC 3986, the reserved characters in a URL are:
  reserved    = gen-delims / sub-delims
  gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"
  sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
              / "*" / "+" / "," / ";" / "="

And here is how to encode the string:/

CFStringRef encodedString =
    CFURLCreateStringByAddingPercentEscapes(
    kCFAllocatorDefault,
    (__bridge CFStringRef)(originalString),
    NULL,
    CFSTR(":/?#[]@!$&'()*+,;="),kCFStringEncodingUTF8);

And to get the string again:

    NSString* stringEncoded = CFBridgingRelease
   (CFURLCreateWithString(kCFAllocatorDefault, encodedString, NULL));

I think this is the best we can do, because we make sure that string will be properly encoded and during the request the symbols will not be replaced for other thing. here is the references:

http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFURLRef/Reference/reference.html#//apple_ref/c/func/CFURLCreateStringByAddingPercentEscapes

http://developer.apple.com/library/ios/#documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/WorkingWithHTTPAndHTTPSRequests/WorkingWithHTTPAndHTTPSRequests.html

查看更多
在下西门庆
4楼-- · 2019-05-26 17:49

you can check your encoded/decoded string, in this url

http://meyerweb.com/eric/tools/dencoder/

As you can see "+" change by "%2B" when you press encode button.

查看更多
登录 后发表回答