我在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函数兼容。
答案 0 :(得分:8)
因此,您希望在CBC模式下使用AES256进行加密。 你正在寻找的图书馆是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>
我很确定这应该有用。
编辑:密钥长度应为32,因为您使用的是AES256 256bit = 32bytes。我认为默认情况下MD5输出与此长度不匹配。
答案 1 :(得分:2)
为什么不使用PHP使用的相同的mcrypt ?
问题在于Rijndael并不完全是AES,因此我不确定其他解决方案是否真的可以在这里运行。 Rijndael允许不同的块大小和密钥对, AES是Rijndael 的特殊情况,密钥大小为os 128,192和256,但块大小始终为128 。因此,使用PHP使用的相同mcrypt
将保证相同的结果。
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()和我的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 :(得分:0)
答案 3 :(得分:0)
我使用http://searchcode.com/codesearch/view/14846108进行MD5加密......并且证明我错了,但是假设MD5只是单向加密,它就不能像那样被解密。它可以使用强力或加密字符串的大型数据库进行解密。这就是它为什么安全的原因。
答案 4 :(得分: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'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;
}
以上代码来自this SO's answer
现在,以下是我在其中一个项目中使用的代码的一部分。请注意,这是函数是对象的一部分,我没有发布所有代码,只是相关的。
/**
* 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;
}
}
编辑:请记住,对于包含加密的软件,您需要遵守美国出口法律。