Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do this same encrypt/decrypt PHP function on iOS with Objective-C?

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.

like image 213
Ethan Allen Avatar asked Dec 25 '22 10:12

Ethan Allen


2 Answers

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.

like image 135
Yotam Vaknin Avatar answered Dec 28 '22 10:12

Yotam Vaknin


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;
}
like image 45
Wagner Patriota Avatar answered Dec 28 '22 10:12

Wagner Patriota