Base64 issue in NSMutableURLRequest POST message?

2019-05-26 17:36发布

问题:

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!

回答1:

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



回答2:

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:

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.