How can I make the same PHP encryption / decryption function on iOS using 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 the same in iOS with Objective-C for NSString? It must be compatible with this PHP function.

+4
source share
5 answers

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

MD5, : http://www.makebetterthings.com/iphone/how-to-get-md5-and-sha1-in-objective-c-ios-sdk/

:

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];

, UTF8 , :

#import <CommonCrypto/CommonCryptor.h>

, .

EDIT: 32, AES256 256bit = 32bytes. , MD5 .

+8

mcrypt, PHP?

, Rijndael AES, , . Rijndael , AES - Rijndael os 128, 192 256, 128. mcrypt, PHP, .

++ - , , :

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à¢äè"˜‚)

, PHP- ( !:-)). .

  • libmcrypt ( 2.5.8 )
  • OpenSSL md5 Base64 ( ...)

... , OpenSSL md5() Base64, , , md5/base64, OpenSSL, . Apple CommonCrypto.

/////////////////////////
// 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;
}
+2

,

NSString +

#import <CommonCrypto/CommonCryptor.h>
0
0

, , ... MD5 , . IV , , , md5 . , IV .

, - PBKDF2

:

,

- (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 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 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;
}

SO-

, . , , , .

/**
 * Pads the data using PKCS7 padding scheme, as described in RFC 5652.
 * 
 * We do not want to rely on Mcrypt zero-padding, because it differs from
 * OpenSSL 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: , , .

0

Source: https://habr.com/ru/post/1536633/


All Articles