У меня есть функция в PHP, которая шифрует и расшифровывает строки:
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; }
и я называю это так:
echo encrypt_decrypt('decrypt', '2Fa9cICuUFa/UnmAAa5FjXZK4ht9q3cN2qgk1pCvDSs=');
Как я могу сделать это точно так же в iOS с Objective-C для NSString
? Он должен быть совместим с этой функцией PHP.
Поэтому вы хотите шифровать с помощью AES256 в режиме CBC. Библиотека, которую вы ищете, является CommonCrypto, и вы можете найти хорошую статью об этом здесь: 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 не будет соответствовать этой длине.
Почему бы не использовать тот же самый mcrypt, который использует PHP?
Проблема в том, что Rijndael НЕ ТОЛЬКО AES, поэтому я не уверен, действительно ли будут работать другие решения. Rijndael допускает различные пары размеров блоков и ключей, AES – частный случай Rijndael с размерами клавиш os 128, 192 и 256, но размер блока всегда равен 128 . Поэтому использовать тот же самый mcrypt
который использует PHP, гарантирует тот же результат.
Этот пример в C ++ – это именно то, что вам нужно, вот вывод:
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-код (просто проверьте его! :-)). Код ниже компилируется сам по себе.
… обратите внимание, что я использовал OpenSSL только для md5 () и моего базового класса, который является тем же, который я использую для многих вещей, но вы можете заменить его для другого решения 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; }
Я думаю, эта категория может помочь вам
NSString + хэши
также не забудьте импортировать
#import <CommonCrypto/CommonCryptor.h>
Я использую http://searchcode.com/codesearch/view/14846108 для шифрования MD5 … и докажу, что я ошибаюсь, но полагаю, что MD5 – это всего лишь одностороннее шифрование, оно не может быть расшифровано именно так. Его можно расшифровать с помощью грубой силы или с большой базой данных зашифрованных строк. Вот почему это должно быть безопасно.
Хорошо, есть несколько вещей, чтобы указать здесь … Прежде всего 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'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; }
Вышеприведенный код был заимствован из ответа SO
Теперь следующее является частью кода, который я использовал в одном из моих проектов. Имейте в виду, что это функции являются частью объекта, и я не размещал весь код, просто релевантный.
/** * 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: Помните, что вам необходимо будет соблюдать законы США об экспорте для программного обеспечения, которое содержит шифрование.