4

I have a function in PHP that encrypts and decrypts strings:

function encrypt_decrypt($action, $string) 
{
   $output = false;
   $key = 'mykeyhereblah';
   $iv = md5(md5($key));
   if( $action == 'encrypt' ) {
       $output = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, $iv);
       $output = base64_encode($output);
   }
   else if( $action == 'decrypt' ){
       $output = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($string), MCRYPT_MODE_CBC, $iv);
       $output = rtrim($output, "");
   }
   return $output;
}

and I call it like this:

echo encrypt_decrypt('decrypt', '2Fa9cICuUFa/UnmAAa5FjXZK4ht9q3cN2qgk1pCvDSs=');

How can I do this exact same thing on iOS with Objective-C for an NSString? It needs to be compatible with this PHP function.

Ethan Allen
  • 14,425
  • 24
  • 101
  • 194
  • try searching for AES encryption for objectiveC in google – Sam B Apr 15 '14 at 17:58
  • 2
    `encrypt_decrypt($action)` Oo – RandomSeed Apr 22 '14 at 14:03
  • 1
    put too much bounty for a simple question, not sure what's happening with StackOverflow – Hieu Vo Apr 22 '14 at 18:12
  • 1
    Check out this post http://stackoverflow.com/questions/19777342/rncryptor-aes256-to-match-php-mcrypt-rijndael-256 – Raj iOS Apr 23 '14 at 12:38
  • If you have any chance at all to ditch this code, do so. It is insecure (use of the same IV for every message with the same key, half of the IV is zero, because mcrypt zero pads the too short IV by default, the key is not derived with a reasonable PBKDF and zero padded to 256 bit length) and uses an arcane Rijndael variant that nobody implements today (block size of 256 bit). – Perseids Apr 29 '14 at 06:22
  • I've found a few old Rijndael implementations that are flexible enough for different block sizes (see specifically https://web.archive.org/web/20060404211619/http://homes.esat.kuleuven.be/~rijmen/rijndael/ ) but given that none of them implements CBC your best bet seems like including [mcrypt](http://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/) in your project. – Perseids Apr 29 '14 at 06:22

5 Answers5

8

So you want to encrypt using AES256 in CBC mode. The library you are looking for is CommonCrypto and you can find a good article about it here:http://robnapier.net/aes-commoncrypto.

You will also need an MD5 function that you can find here: http://www.makebetterthings.com/iphone/how-to-get-md5-and-sha1-in-objective-c-ios-sdk/

Your code should look something like this:

NSString *originalString,*keyString;
NSData *key = [[self md5:keyString] dataUsingEncoding:NSUTF8StringEncoding];
NSData *iv = [[self md5:[self md5:key]] dataUsingEncoding:NSUTF8StringEncoding];
NSData *data = [originalString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *cipherData = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128]; //The block size of MCRYPT_RIJNDAEL_256 is just like AES128
size_t outLength;

CCCryptorStatus result
       = CCCrypt(kCCEncrypt, // operation, replace with kCCDecrypt to decrypt
                 kCCAlgorithmAES, // Same as MCRYPT_RIJNDAEL_256
                 nil, // CBC mode
                 key.bytes, // key
                 32, // Since you are using AES256
                 iv.bytes,// iv
                 data.bytes, // dataIn
                 data.length, // dataInLength,
                 cipherData.mutableBytes, // dataOut
                 cipherData.length, // dataOutAvailable
                 &outLength); // dataOutMoved
NSString resultString = [cipherData base64Encoding];

And make sure you are using the same UTF8 encoding in both cases, and use this import:

#import <CommonCrypto/CommonCryptor.h>

I am pretty sure this should work.

EDIT: the key length should be 32 since you are using AES256 256bit=32bytes. The MD5 output wouldn't match this length by default I think.

sunkehappy
  • 8,970
  • 5
  • 44
  • 65
Yotam Vaknin
  • 676
  • 4
  • 19
  • @WagnerPatriota Actually AES 256 *is* Rijndael. More or less at least. AES256 is in fact based on Rijndael algorithm but limited to a block size of 128 bits whereas Rijndael supports any block size multiple of 32 bits from 128 to 256 bits. – Antonio E. Apr 28 '14 at 15:30
  • @AntonioE. AES256 is "a" Rijndael, correct, but not every Rijndael is AES. As Wagner has pointed out in his answer the RIJNDAEL_256 algorithm in mcrypt is a Rijndael with 256 bit block size (you can verify this in `./modules/algorithms/rijndael-256.c` of the [mcrypt source](http://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/)) but AES256 has 128 bit block size. (Both have 256 bit keys.) – Perseids Apr 29 '14 at 05:35
  • 2
    Just to be clear: This code won't work for several reasons: 1. It does not use Rijndael with 256bit block size; 2. The key is not large enough with only 128bit (whereas 256bit are expected), mcrypt zero pads it to 256bit by default; 3. The IV is not large enough for 256bit block size (though this is not relevant for CommonCrypto as it uses the - in this case - incorrect 128bit block size anyway), again mcrypt zero pads the IV. – Perseids Apr 29 '14 at 06:13
  • @Perseids Never said that every Rijndael is AES but rather that AES is a special case of Rijndael. The problem with Wagner's answer is that, even if it shows how to encrypt/decrypt using AES256 in C++, it doesn't answer the question since it uses frameworks not directly available on the iOS platform. And even if you can get them to work on iOS apple discourage their uses because you are supposed to use the apple's wrappers already bundled in the platform. – Antonio E. Apr 29 '14 at 06:38
  • @AntonioE.: What I wanted to say is that it is beside the point that AES is a special case of Rijndael when the Rijndael actually used in PHP is not that special case. – Perseids Apr 29 '14 at 08:52
  • @AntonioE. C/C++ is a subset of Obj-C/ObjC++... So my solution is also a Obj-C/ObjC++ solution... – Wagner Patriota Apr 29 '14 at 14:48
  • The fact that `kCCAlgorithmAES, // Same as MCRYPT_RIJNDAEL_256` is absolutely, completely wrong makes this answer completely wrong. Listen to Perseids, or at least try to understand. `kCCAlgorithmAES` -> block size = 128 bits `MCRYPT_RIJNDAEL_256` -> block size 256 bits. For both the key size - and therefore if AES-128, 192 or 256 is used *depends on the actual key size*. – Maarten Bodewes Oct 06 '14 at 19:03
2

Why not use the same mcrypt that PHP uses?

The problem is that Rijndael is NOT exactly AES, so I am not sure if the other solutions will really work here. The Rijndael allows different pairs of block sizes and keys, AES is a particular case of Rijndael with key sizes os 128, 192 and 256 but block size always 128. So use the same mcrypt that PHP uses will guarantee the same result.

This sample in C++ is exactly what you need, here is the output:

plain text: the book is on the table!
cipher text: dGhlIGJvb2sgaXMgb24gdGhlIHRhYmxlIQ==
back to: the book is on the table!

sample cipher text: 2Fa9cICuUFa/UnmAAa5FjXZK4ht9q3cN2qgk1pCvDSs=
sample plain text: “:F‚m&X”Öwÿ ï@í`D’ühà¢äè"˜‚)

The output of your sample is exactly like your PHP code (just test it! :-)). The code below compiles by itself.

  • It requires you to compile libmcrypt (I used the version 2.5.8 in this sample)
  • It also requires OpenSSL for md5 and Base64 functions (but you can skip...)

... note that I used OpenSSL just for md5() and my Base64 class, that is the same one that I use for many things, but you can replace for other md5/base64 solution, then you get rid of OpenSSL. Pretty easy. Apple is moving to CommonCrypto now...

/////////////////////////
// Base64.h

#ifndef BASE64_H
#define BASE64_H

#include <string>
#include <vector>

class Base64
{
public:
    static std::string encode( const unsigned char * p_buffer, size_t p_size );

    static std::string encode( const std::string & p_string );

    static std::string encode( const std::vector< unsigned char > & p_buffer );

    static std::string decode( const std::string & p_input );

    static void decode( const std::string & p_input, std::vector< unsigned char > & p_output );
};

#endif // BASE64_H

/////////////////////////
// Base64.cpp

//#include "Base64.h"

#include <openssl/evp.h>

using namespace std;

string Base64::encode( const unsigned char * p_buffer, size_t p_size )
{
    unsigned char * output( new unsigned char[ p_size * 4 / 3 + 4 ] );

    size_t outputLength( EVP_EncodeBlock( output, p_buffer, static_cast< int >( p_size ) ) );

    string ret( reinterpret_cast< char * >( output ), outputLength );

    delete [] output;

    return ret;
}

string Base64::encode( const string & p_string )
{
    return Base64::encode( reinterpret_cast< const unsigned char * >( p_string.c_str() ), p_string.size() );
}

string Base64::encode( const vector< unsigned char > & p_buffer )
{
    return Base64::encode( &p_buffer[ 0 ], p_buffer.size() );
}

void Base64::decode( const string & p_input, vector< unsigned char > & p_output )
{
    p_output.resize( p_input.length() * 3 / 4 );

    size_t outputLength( EVP_DecodeBlock( &p_output[ 0 ], reinterpret_cast< const unsigned char * >( p_input.c_str() ), static_cast< int >( p_input.size() ) ) );

    size_t length( p_input.length() );

    if ( p_input[ length - 2 ] == '=' )
    {
        outputLength -= 2;
    }
    else if ( p_input[ length - 1 ] == '=' )
    {
        outputLength--;
    }

    p_output.resize( outputLength );
}

string Base64::decode( const string & p_input )
{
    vector< unsigned char > output;
    Base64::decode( p_input, output );
    return reinterpret_cast< const char * >( &output[ 0 ] );
}

/////////////////////////
// main.cpp

#include <iostream>
#include <string>
#include <regex>

#include <openssl/evp.h>
#include <mcrypt.h>

#define MCRYPT_MODE_CBC "cbc"

using namespace std;

string md5( const string & p_string )
{
    EVP_MD_CTX mdContext;
    const EVP_MD * md;
    unsigned int outputLength;
    unsigned char output[ 16 ];

    OpenSSL_add_all_digests();

    if ( !( md = EVP_get_digestbyname( "MD5" ) ) )
    {
        throw std::runtime_error( "Unable to init MD5 digest." );
    }

    EVP_MD_CTX_init( &mdContext );
    EVP_DigestInit_ex( &mdContext, md, 0 );
    EVP_DigestUpdate( &mdContext, p_string.c_str(), p_string.length() );
    EVP_DigestFinal_ex( &mdContext, output, &outputLength );
    EVP_MD_CTX_cleanup( &mdContext );

    char outputString[ sizeof( output ) * 2 + 1 ];

    for ( int i( 0 ); i < sizeof( output ); ++i )
    {
        snprintf( outputString + i * 2, 2 + 1, "%02x", output[ i ] );
    }

    return outputString;
}

string trimString( const string & p_string )
{
    string ret( p_string );

    regex functionRegex( "\\s*(.*)\\s*", regex_constants::icase );
    smatch matches;

    if ( regex_search( p_string, matches, functionRegex ) )
    {
        ret = matches[ 1 ].str();
    }

    return ret;
}

void mcrypt_encrypt( vector< unsigned char > & p_output, const char * p_cryptEngine, const string & p_key, const vector< unsigned char > & p_input, const char * p_mode, const string & p_iv )
{
    MCRYPT td = mcrypt_module_open( ( char * )p_cryptEngine, 0, ( char * )p_mode, 0 );

    if ( td == MCRYPT_FAILED )
    {
        throw std::runtime_error( "can't init mcrypt" );
    }

    if ( mcrypt_generic_init( td, ( char * )p_key.c_str(), mcrypt_enc_get_key_size( td ), ( char * )p_iv.c_str() ) < 0 )
    {
        throw std::runtime_error( "can't setup key/iv" );
    }

    p_output.reserve( p_input.size() );
    copy( p_input.begin(), p_input.end(), back_inserter( p_output ) );

    mcrypt_generic( td, ( void * )&p_output[ 0 ], (int)p_output.size() );

    mcrypt_generic_end( td );
}

void mcrypt_decrypt( vector< unsigned char > & p_output, const char * p_cryptEngine, const string & p_key, const vector< unsigned char > & p_input, const char * p_mode, const string & p_iv )
{
    MCRYPT td = mcrypt_module_open( ( char * )p_cryptEngine, 0, ( char * )p_mode, 0 );

    if ( td == MCRYPT_FAILED )
    {
        throw std::runtime_error( "can't init mcrypt" );
    }

    if ( mcrypt_generic_init( td, ( char * )p_key.c_str(), mcrypt_enc_get_key_size( td ), ( char * )p_iv.c_str() ) < 0 )
    {
        throw std::runtime_error( "can't setup key/iv" );
    }

    p_output.reserve( p_input.size() );
    copy( p_input.begin(), p_input.end(), back_inserter( p_output ) );

    mdecrypt_generic( td, ( void * )&p_output[ 0 ], (int)p_output.size() );

    mcrypt_generic_end( td );
}

string encrypt_decrypt( const string & action, const string & p_string )
{
    string output = "";

    string key = "mykeyhereblah";
    string iv = md5( md5( key ) );

    vector< unsigned char > cipherText, plainText;

    if ( action == "encrypt" )
    {
        copy( p_string.begin(), p_string.end(), back_inserter( plainText ) );

        mcrypt_encrypt( cipherText, MCRYPT_RIJNDAEL_256, md5( key ), plainText, MCRYPT_MODE_CBC, iv );

        output = Base64::encode( cipherText );
    }
    else if ( action == "decrypt" )
    {
        Base64::decode( p_string, cipherText );
        mcrypt_decrypt( plainText, MCRYPT_RIJNDAEL_256, md5( key ), cipherText, MCRYPT_MODE_CBC, iv );

        output = string( ( char* )&plainText[ 0 ], plainText.size() );
        output = trimString( output );
    }

    return output;
}

int main( int argc, char * argv[] )
{
    string plainText = "the book is on the table!";
    string cipherText = encrypt_decrypt( "encrypt", plainText );

    cout << "plain text: " << plainText << endl;
    cout << "cipher text: " << cipherText << endl;
    cout << "back to: " << encrypt_decrypt( "decrypt", cipherText ) << endl;
    cout << endl;
    cout << "your sample: " << encrypt_decrypt( "decrypt", "2Fa9cICuUFa/UnmAAa5FjXZK4ht9q3cN2qgk1pCvDSs=" ) << endl;

    return 0;
}
Wagner Patriota
  • 5,494
  • 26
  • 49
  • Wagner is right. The issue is the block size. CCCrypt implements AES256 by using a 256 bit key but the iv and the block size is still 128 bit. mcrypt_encrypt:MCRYPT_RIJNDAEL_256 on the other hand uses a 256 bit key, iv and block size. – itsben Apr 27 '14 at 21:55
  • The OP asked for an objective C solution, what you are giving here is a C++ solution using openssl and mcrypt libraries that are not available out of the box in iOS. Even though openssl can be compiled to work on iOS too (but this is not a beginner thing) apple's discourage this and suggest to use the wrapper around the bundled openssl included in the Security framework. – Antonio E. Apr 28 '14 at 15:13
  • I did not tell him to use this code on iOS. He must port it. I don't even touch on NString. This code is more than enough to learn how to use mcrypt in C/C++, that is a subset of Objective-C, so it's correct to show C/C++ code. I "told" in my answer that one should change to CommonCrypto for md5 and anything else for Base64 obviously... this is more than obvious. You don't need to downvote my question just because I downvoted yours. Your answer just doesn't have anything to do with his question. ;-) – Wagner Patriota Apr 28 '14 at 15:25
  • The downvote is not because of the downvote you gave me, I'm not that childish. The downvote is because your answer even if correct is NOT the right solution, not even to port. This is because since iOS developers do not have direct access to mcrypt and openssl it is hardly portable to Obj-C. And even if they could have access by compiling both mcrypt and openssl, they really shouldn't, because that will mean update the application each time the two libraries get an update/patch, thing you don't have to worry about using apple's ones. – Antonio E. Apr 29 '14 at 07:05
  • 1
    @AntonioE. mcrypt is actually a C library so afaik you should at least theoretically be able to include it in your project. I'm neither fond of the solution Wagner has proposed nor that the answer is mostly a proof of concept, but it seems like there is no "good" solution available given that there are basically no "Rijndael-256/256" implementations available anywhere. Regarding updates: The last change in the changelog of mcrypt is more than 10 years old, so that should be no problem ;) – Perseids Apr 29 '14 at 09:18
  • @Perseids and regarding openssl when was the last change? ;) – Antonio E. Apr 29 '14 at 09:25
  • @AntonioE. I know I know :), but importing openssl just for md5 and base64 is an overkill anyway. – Perseids Apr 29 '14 at 09:29
  • @AntonioE, I removed my downvote considering your intention. I see you felt disturbed for this. About my solution, it's not wrong. As Perseids said. It's the proof os concept. Let's analyse it: 1. C/C++ is a subset of Obj-C/Obj-C++ (.m/.mm), therefore a solution in C/C++ is ALSO a solution in Obj-C. 2. Use of thirdy-party libraries is NOT any problem in iOS (mcrypt/OpenSSL). It's not forbidden and it's done all the time with iOS programmers. – Wagner Patriota Apr 29 '14 at 14:37
  • and 3. About the OpenSSL itself. It's not forbidden to use, as I said, but EVEN I IN MY ANSWER told that this I only used to show this example BECAUSE I already had those functions done! But I DID ENCOURAGE him to use CommonCrypto for md5! [and something else on iOS for Base64]. – Wagner Patriota Apr 29 '14 at 14:39
  • 1
    @AntonioE, so I don't wanna make an enemy here. As you told, StackOverflow is made for us to help each other! But I really would suggest you to review your comments about saying tha "my answer is wrong". This is only bad for you. You may downvote/or do whatever you want for any reason, but not because my answer is wrong. I used to work with iOS for some years (before CommonCrypto) and now I work intimately with OpenSSL/Cryptography and I kow this is the best solution for him to solve his exact problem. Hope you understand... ;-) – Wagner Patriota Apr 29 '14 at 14:44
  • @WagnerPatriota Rijndael is an implementation of AES. AES is an algorithm (concept if you like) and Rijndael is an approved practice by NSA/NASA. cited at Wikipedia cited from US Government approvals for data security. – Tala Mar 02 '15 at 13:45
0

I think this category might help you

NSString+hashes

also don't forget to import

#import <CommonCrypto/CommonCryptor.h>
Roma
  • 1,107
  • 9
  • 19
0

I use http://searchcode.com/codesearch/view/14846108 for MD5 encryption... and prove me wrong but it suppose MD5 is just one way encryption, it can't be decrypted just like that. It can be decrypted with brute force or with a large database of encrypted strings. That's why it's suppose to be safe.

user2387149
  • 1,219
  • 16
  • 28
  • 1
    MD5 is not an encryption algorithm, it's a hash algorithm function : http://en.wikipedia.org/wiki/Hash_function. He is using it to hash his password (And also to create an IV, which is probably a bad idea..) – Yotam Vaknin Apr 25 '14 at 17:05
0

Ok there are a few things to point out here... First of all MD5 is not going to give you enough entropy to consider that key secure. While the IV can be even public it should anyway be random and thus, md5 is not working there either. Mind that having a fixed IV is more or less not having it at all.

If you want to really use a passphrase to generate your encryption key the best way to do it is to use PBKDF2

Now to the code:

This is the code I'm currently using in one of my project

- (NSData *)AES256EncryptWithKey:(NSString *)key andIV:(const void*)iv
{
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
    bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc( bufferSize );

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          iv /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted );
    if( cryptStatus == kCCSuccess )
    {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free( buffer ); //free the buffer
    return nil;
}


- (NSData *)AES256DecryptWithKey:(NSString *)key andIV:(const void*)iv
{
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc( bufferSize );

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          iv /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesDecrypted );

    if( cryptStatus == kCCSuccess )
    {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

    free( buffer ); //free the buffer
    return nil;
}

The above code was borrowed from this SO's answer

Now following is part of the code I used in one of my projects. Mind that this is functions are part of an object and I didn't post all the code, just the relevant.

/**
 * Pads the data using PKCS7 padding scheme, as described in RFC 5652.
 * 
 * We do not want to rely on Mcrypt's zero-padding, because it differs from
 * OpenSSL's PKCS7 padding.
 * 
 * Note: $data is passed by reference.
 * 
 * @param string &$data 
 */
static public function pkcs7Pad(&$data)
{

    $blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $padding = $blockSize - (strlen($data) % $blockSize);

    $data .= str_repeat(chr($padding), $padding);
}

/**
 * Removes the (PKCS7) padding bytes from $data.
 * 
 * Note: $data is passed by reference.
 * 
 * @param string &$data 
 */
static public function pkcs7Strip(&$data)
{
    $paddingByte = substr($data, -1);
    $paddingLen = ord($paddingByte);
    $dataLen = strlen($data) - $paddingLen;

    // Simple sanity check to make sure we have correct padding bytes. If padding
    // is not correct, we simply set $data to false. At this point, there
    // should be no need to worry about leaking side-channels.
    if (!isset($data[15]) || $paddingLen < 1 || $paddingLen > mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC) )
    {
        //$data = false;
    }
    else if (substr($data, $dataLen) === str_repeat($paddingByte, $paddingLen))
    {
        // Padding is correct, strip it off.
        $data = substr($data, 0, $dataLen);
    }
    else
    {
        //$data = false;
    }
}

public static function encrypt($dataString, $aesCipherKey, $iv = null, $returnBase64Encoded = false){

    // ensure source file exist
    if (!$dataString || empty($dataString))
        return null;

    try{

            // ===========
            // Ciphering
            $ciphered_data = null;

            //Make sure padding is pkcs7 based
            self::pkcs7Pad($dataString);                    

            //Encrypt data with AES
            $ciphered_data = @mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $aesCipherKey, $dataString, MCRYPT_MODE_CBC, $iv);

            return ( $returnBase64Encoded ? base64_encode( $ciphered_data ) : $ciphered_data );


        }
    catch(Exception $ex){

        return null;
    }


}

public static function decrypt($dataString, $aesCipherKey, $iv = null, $returnBase64Encoded = false){

    // ensure source file exist
    if (!$dataString || empty($dataString))
        return null;

    try{

            // ===========
            // Ciphering
            $ciphered_data = null;

            //Decrypt data with AES
            $ciphered_data = @mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $aesCipherKey, $dataString, MCRYPT_MODE_CBC, $iv);

            //Ensure no pkcs7 padding is left overs
            self::pkcs7Strip($ciphered_data);   

            return ( $returnBase64Encoded ? base64_encode( $ciphered_data ) : $ciphered_data );


        }
    catch(Exception $ex){

        return null;
    }

}

EDIT: Remember that you will need to comply to U.S. export laws for software that contains encryption.

Community
  • 1
  • 1
Antonio E.
  • 4,381
  • 2
  • 25
  • 35
  • 1
    The key pout is: Do not want to rely on Mcrypt's zero-padding, because it differs from PKCS7 padding. – zaph Apr 28 '14 at 12:33
  • What he wants is: "the exact same thing on iOS with Objective-C for an NSString" "It needs to be compatible with his PHP function."... – Wagner Patriota Apr 28 '14 at 14:58
  • 1
    @WagnerPatriota Of course the question is that, but StackOverflow's point is not just give an answer to a question (and thus blindly accept everything) but to also give advices on how to do something better IMHO. That is why I included my PHP code and started my answer by explaining what is not good in his server side code. Now if you think this is a good reason to downvote my answer, fine. – Antonio E. Apr 28 '14 at 15:09
  • I strongly disagree. I would have to rewrite the code of each and every question if I just wanted to show "something better". People will get here to find a solution to the 256 bit block size problem. It won't be very helpful if they find some generic piece of encryption - it's not as if there are no samples available for generic AES encryption. I mean, here you are promoting a zero IV and a mode of encryption that will be broken in seconds if a padding oracle applies - and it more than likely does. Answer the question, methods of improvement in the comments (or as final paragraph). – Maarten Bodewes Oct 06 '14 at 19:12