2

I'm trying to figure out how to send encrypted data to my IOS app from a web page.

In php I have a page that encrypts a string and then echos it out as a base64 encoded string:

<?php
    function encode($json, $key, $iv){
        echo base64_encode($iv . "::" . openssl_encrypt ($json, 'AES-256-CTR', $key, OPENSSL_RAW_DATA, $iv));
    }

    $key = substr(sha1("super awesome key goes here", true), 0, 16);
    $iv = openssl_random_pseudo_bytes(16);
    $table = array("key1"=>"value1", "key2"=>"value2");
    $json = json_encode($table);

    encode($json, $key, $iv);
?>

I am able to take that echoed string and decode it in PHP just fine.

In IOS I have a function that opens that page and grabs the encoded string:

-(IBAction)fetchData:(id)sender{
    // Fetch data
    NSURL *url = [NSURL URLWithString: @"https://www.website.com/dataupdate.php"];

    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:nil  delegateQueue:[NSOperationQueue mainQueue]];
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
    NSString *params = @"";
    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];

    NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest: urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
        NSLog(@"response: %@ %@\n",response, error);
        if(error == nil){
            NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"Data = %@", text);

            NSData *decodedData = [[NSData alloc] initWithBase64EncodedString: text options:0];
            // Fails here
            NSString *decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
            NSLog(@"64 Decoded: %@",decodedString);
        }
    }];
    [dataTask resume];
}

I'm assuming it's failing because of the encoding:NSUTF8StringEncoding part, but I'm not sure what to do about that. I did try encoding:NSUTF32StringEncoding but didn't fare much better.

The PHP encrypting line has OPENSSL_RAW_DATA as an option, but I have not yet found anywhere that tells what encoding RAW data is...

EDIT:

This is what I get for trying to squeeze the question out while rushing to head home...

openssl_encrypt ($json, 'AES-256-CTR', $key, OPENSSL_RAW_DATA, $iv)
returns this: ��.�m� �#6��:�ĕO;֧ ª�0`")�-�

And after I base64 encode it with the IV I get this:
INI70ZBDUjYouoGlSIFGbzo6v6Eu320Hyg2pIzaOCIw6zMSVTzvWpyDCqr0wYCIpmy2P

On the IOS side, I can use NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; to send the base62 string into the text variable

But when I use [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; to turn the base64 string into the openssl_encrypt string, it just returns nil

After I get get it to return a value then I can work on the actual decryption. I'm just trying to figure out one step at a time.

EDIT: Trying again...

So, playing around with it, I think I'm missing something vital...

    if(error == nil){
        NSString *key = @"05nszDCobKjjavWBfG/ZcC/A4DQ=";
        NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSArray *items = [text componentsSeparatedByString:@"::"];
        NSString *iv = [items objectAtIndex:0];
        NSString *msg = [items objectAtIndex:1];
        NSLog(@"\n\n\n IV = %@ \n\n\n Data = %@", iv, msg);

        NSData *decodedKey = [[NSData alloc] initWithBase64EncodedString:key options:0];
        NSData *decodedIV = [[NSData alloc] initWithBase64EncodedString: iv options:0];
        NSData *decodedMsg = [[NSData alloc] initWithBase64EncodedString: msg options:0];
        NSLog(@"\n\n\n DecodedKey = %@ \n\n\n DecodedIV = %@ \n\n\n DecodedData = %@", decodedKey, decodedIV, decodedMsg);

        NSString *hexKey = NSDataToHex(decodedKey);
        NSString *hexIV = NSDataToHex(decodedIV);
        NSString *hexMsg = NSDataToHex(decodedMsg);
        NSLog(@"\n\n\n HexKey = %@ \n\n\n HexIV = %@ \n\n\n HexData = %@", hexKey, hexIV, hexMsg);

        CkoCrypt2 *decrypt = [[CkoCrypt2 alloc] init];

        decrypt.CryptAlgorithm = @"aes";
        decrypt.CipherMode = @"ctr";
        decrypt.KeyLength = [NSNumber numberWithInt: 256];
        decrypt.EncodingMode = @"hex";
        [decrypt SetEncodedIV:hexIV encoding:@"hex"];
        [decrypt SetEncodedKey:hexKey encoding:@"hex"];

        NSString *decryptedStringHex = [decrypt DecryptEncoded:hexMsg];

        NSData *decryptedData = [decrypt DecryptBytes:decodedMsg];
        NSString *decryptedStringData = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
        NSLog(@"decoded and decrypted Hex: %@ /n/nData: %@",decryptedStringHex, decryptedStringData);

    }

And then for my Data to hex:

static inline char itoh(int i){
    if (i > 9) return 'A' + (i - 10);
    return '0' + i;
}

NSString * NSDataToHex(NSData *data) {
    NSUInteger i, len;
    unsigned char *buf, *bytes;

    len = data.length;
    bytes = (unsigned char*)data.bytes;
    buf = malloc(len*2);

    for (i=0; i<len; i++){
        buf[i*2] = itoh((bytes[i] >> 4) & 0xF);
        buf[i*2+1] = itoh(bytes[i] & 0xF);
    }

    return [[NSString alloc] initWithBytesNoCopy:buf length:len*2 encoding:NSASCIIStringEncoding freeWhenDone:YES];
}

NSString *decryptedString = [decrypt DecryptEncoded:hexMsg]; returns nil.

`NSData *decryptedData = [decrypt DecryptBytes:decodedMsg]; NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];' Also returns nil.

Here is my log:

2017-03-24 13:20:26.222 GWM[47000:3317901]

IV = xiiRvStJen2M2VrUuavHZg==

Data = c4Okkz+MCpdSX935O6nNudoJ0ud+oS4sz9GbPFMDsCL5+yJaQ0mzJckP7S6Q/oE=

2017-03-24 13:49:40.548 GWM[47283:3332493]

DecodedKey =

DecodedIV =

DecodedData = <7383a493 3f8c0a97 525fddf9 3ba9cdb9 da09d2e7 7ea12e2c cfd19b3c 5303b022 f9fb225a 4349b325 c90fed2e 90fe81

2017-03-24 13:49:40.548 GWM[47283:3332493]

HexKey = D399ECCC30A86CA8E36AF5817C6FD9702FC0E034

HexIV = C62891BD2B497A7D8CD95AD4B9ABC766

HexData = 7383A4933F8C0A97525FDDF93BA9CDB9DA09D2E77EA12E2CCFD19B3C5303B022F9FB225A4349B325C90FED2E90FE81

2017-03-24 13:49:49.779 GWM[47283:3332493] decoded and decrypted

Hex: (null)

Data:

AndyD273
  • 7,177
  • 12
  • 54
  • 92
  • Failing how? Do you get an error message? Or what is the expected result vs what is the actual result? Also as you aren't providing either input, output or a encryption key, it would be rather hard for anyone to reproduce. For the encryption key you of course you will need to give a one you aren't intending to actually use ;-) – Dijkgraaf Mar 20 '17 at 21:25
  • 1
    Your PHP code is building a JSON string, encrypting it with SSL then base-64 encoding the results. So you'll need to do the steps in reverse order on the receiving side: base-64 decode (now you have 8 bit data.) decrypt the data using SSL and the same key. Now convert the resulting data to a JSON string (probably with UTF8, or ASCII, but it shouldn't matter because the JSON string should not contain any non-ASCII characters.) – Duncan C Mar 20 '17 at 22:05
  • 2
    The code you posted does not have any SSL decryption code at all. How could it possibly succeed? – Duncan C Mar 20 '17 at 22:05
  • 1
    Provide some debug output, sample input data, intermediate values and exactly the error. – zaph Mar 20 '17 at 22:22
  • @Dijkgraaf Added some details. Sorry – AndyD273 Mar 21 '17 at 13:09
  • @zaph Added some details. Sorry. It isn't throwing an error, just returning nil instead of the AES encrypted text. – AndyD273 Mar 21 '17 at 13:10
  • @DuncanC Your comment about ASCII got me researching, and I tried NSASCIIStringEncoding instead of NSUTF8StringEncoding with slightly better results, but the string was very short. The base64 string was 13000 characters. The NSASCIIStringEncoding string was 6 characters. This makes me think that it's a problem with stopping when it hits a character it doesn't understand, which is why NSUTF8 returned nil, and ASCII only made it through 6. – AndyD273 Mar 21 '17 at 13:39
  • Right. Your current code can't work. You're encrypting the data before sending it, end, and not decrypting it on the receiving end. – Duncan C Mar 21 '17 at 13:58

3 Answers3

4

As people have mentioned, you're just missing the actual decryption step. Usually easiest to use an existing 3rd party resource to help - something like this should work. So basically:

  1. Base64 decode.
  2. Decrypt the data using the above library, and your encryption key.
  3. Then convert the decrypted data to a string (likely using UTF8.)

Edit:

The following is adapted from the linked resources, and should work for decrypting your Base64 decoded data:

if(error == nil){
    NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"Data = %@", text);
    NSData *decodedData = [[NSData alloc] initWithBase64EncodedString: text options:0];

    CkoCrypt2 *decrypt = [[CkoCrypt2 alloc] init];

    decrypt.CryptAlgorithm = @"aes";
    decrypt.CipherMode = @"ctr";
    decrypt.KeyLength = [NSNumber numberWithInt:256];
    decrypt.EncodingMode = @"hex";
    [decrypt SetEncodedIV:IV encoding:@"hex"];
    [decrypt SetEncodedKey:KEY encoding:@"hex"];

    NSData *decryptedData = [decrypt decryptEncoded:decodedData];

    NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
    NSLog(@"64 Decoded and decrypted: %@",decodedString);
}

Note that some of the settings may need a little tweaking, but the logic should work.

Allan Poole
  • 378
  • 1
  • 10
  • Thanks for the reply. I added some more details. Basically I'm having troube base54 decoding it, because I don't think that NSString (or at least UTF8) can handle the AES encrypted text. It just returns `nil`. – AndyD273 Mar 21 '17 at 13:08
  • 1
    The data you receive won't contain AES encrypted text. It will contain a Base64 conversion of AES encrypted text. See my answer. – Duncan C Mar 21 '17 at 14:04
  • @AndyD273 I've updated my answer based on code from the linked resource. Should hopefully get you in the right direction at least :) – Allan Poole Mar 22 '17 at 00:26
1

I ended up getting this to work using RNCryptor because it has versions for both IOS and PHP, and even Android for down the road. This way I don't have to try to work around different methods on each platform.

PHP:

$table = array("first"=>"First Value", "second"=>"Second value");

$json = json_encode($table);

$password = "my super awesome password";
$cryptor = new \RNCryptor\RNCryptor\Encryptor;
$base64Encrypted = $cryptor->encrypt($json, $password);
echo $base64Encrypted;

XCode:

NSURL *url = [NSURL URLWithString:@"https://www.website.com/dataupdate.php"];
NSData *encryptedData = [[NSData alloc] initWithBase64EncodedString: [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil] options:0];

NSString *password = @"my super awesome password";
NSError *error = nil;
NSData *decryptedData = [RNDecryptor decryptData: encryptedData withPassword:password error:&error];

self.jsonList = [NSJSONSerialization JSONObjectWithData:decryptedData options:NSJSONReadingMutableContainers error:&error];
AndyD273
  • 7,177
  • 12
  • 54
  • 92
0

Your PHP code starts with JSON.

It runs SSL on the JSON, then base64 encodes the results. So you have

JSON>SSL>base64.

On the receiving side, you need to:

  1. Base64 decode the received string (output will be binary data)
  2. SSL decrypt the resulting binary data (output will be JSON, represented as binary data)
  3. Parse the JSON (JSONSerialization want's a Data object)

You are missing step 2 in your iOS code, so it fails.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • So if I'm understanding you correctly, I'd want to try to decode the NSData directly? If so I probably can't put the IV in with the data like I'm doing now (`$iv . "::" . openssl_encrypt(...)`) unless there is a way to get it out of NSData... I've seen examples where you cut out the first 16 bytes, but I don't know how to do that in IOS *yet*. Is there a better way to do it before I sink a lot of time into figuring it out? – AndyD273 Mar 21 '17 at 15:03