PHP API, который я вызываю из моего приложения iOS, требует, чтобы полезная нагрузка была зашифрована определенным образом. У меня возникают проблемы, повторяющие этот подход в Objective-C с помощью RNCryptor.
Вот код PHP, используемый для шифрования строки:
function encrypt($string) { $key = 'some-random-key'; return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, md5(md5($key)))); }
И это, как я пытаюсь добиться того же результата шифрования в Objective-C:
+ (NSData*)encryptData:(NSData*)sourceData { NSString *keyString = @"some-random-key"; NSData *key = [[keyString MD5String] dataUsingEncoding:NSUTF8StringEncoding]; NSData *iv = [[[keyString MD5String] MD5String] dataUsingEncoding:NSUTF8StringEncoding]; NSMutableData *encryptedData = [NSMutableData data]; RNCryptorEngine *cryptor = [[RNCryptorEngine alloc] initWithOperation:kCCEncrypt settings:kRNCryptorAES256Settings key:key IV:iv error:nil]; [encryptedData appendData:[cryptor addData:sourceData error:nil]]; [encryptedData appendData:[cryptor finishWithError:nil]]; return encryptedData; }
Но результаты двух функций никогда не совпадают. Например, для той же однословной строки код PHP возвращает J39gRcuBEaqMIPP1VlizdA8tRjmyAB6za4zG5wcOB/8=
, в то время как в Objective-C (после запуска base64EncodedStringWithOptions:
в результате NSData) я получаю 1FGpZpVm2p4z3BBY6KW2fw==
.
Есть ли что-то, что мне нужно для дальнейшей настройки в настройках RNCryptor, чтобы заставить его работать?
ОБНОВИТЬ
Я напрямую играл вокруг родной инфраструктуры iOS CommonCrypto, без использования стороннего RNCryptor lib. Я все равно получаю тот же результат, что и в RNCryptor. Я даже пытался реализовать AES128 в обоих моих объектных-С и PHP-фрагментах, но даже это не привело к тому, что результаты из двух сред совпали …
ОБНОВЛЕНИЕ 2
Метод MD5String
который я использую, является категорией в NSString и определяется следующим образом:
- (NSString *)MD5String { const char *cstr = [self UTF8String]; unsigned char result[16]; CC_MD5(cstr, strlen(cstr), result); return [[NSString stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15] ] lowercaseString]; }
Хотя в большинстве ответов основное внимание уделяется хеш-файлу MD5, наиболее вероятным вызовом является тот факт, что MCRYPT_RIJNDAEL_256 не является AES. Он не определяет размер ключа 256, а размер блока – 256, тогда как AES всегда имеет размер блока 128 бит. Что касается других параметров, распечатайте их значения в шестнадцатеричном порядке перед процедурой шифрования с обеих сторон, чтобы узнать их значения.
Я понимаю, что опаздываю на вечеринку, и вы, наверное, уже переехали. Но я хочу отметить, что теперь есть полный PHP-порт RNCryptor, который является поточным потоком, совместимым с реализацией Objective-C. Я сам внес свой вклад прошлым летом, и в настоящее время я поддерживаю его. 🙂
Поэтому теперь довольно легко шифровать с помощью RNCryptor PHP и расшифровывать с помощью RNCryptor Objective-C или наоборот. Ознакомьтесь с основным проектом RNCryptor для получения подробной информации и списка подпроектов.
Необычно возвращать md5
как hex-ascii в отличие от языков сценариев, даже php
предоставляет двоичный выходной параметр.
Есть несколько n стандартных вещей о php mcrypt_encrypt
:
pkcs7
. Из них iv будет правильной длиной из-за установки hex-ascii из метода md5
. Но это оставляет длину данных и заполнение, которое является нестандартным.
Ключ будет 32 байта, поэтому он будет дополнен 32 \ 0 символами в php. Это вызывает вопрос об использовании AES256 с 128-битным ключом.
Из основного CommonCrypto "CommonCryptor.h":
keyLength: Длина ключевого материала. Должна быть подходящей для выбранной операции и алгоритма.
Длина ключа неверна.
Дела, которые необходимо сделать:
1. Обрабатывать длину и длину данных
2. Обработать длину ключа
Для получения дополнительной информации, пожалуйста, укажите образцы данных, которые вы используете, и hex-ascii out, помещенный в mcrypt_encrypt
до кодировки base64.
Для справки см .:
mcrypt-encrypt.php и md5.php
Замечание Зафа, что
Данные будут дополнены символами \ 0 до кратного размера блока
поставил меня на путь и помог мне разобраться в части проблемы.
По сути, в mcrypt
PHP используется только \0
padding для данных, в то время как CommonCryptor от Apple позволяет вам выбирать между дополнением kCCOptionPKCS7Padding
( kCCOptionPKCS7Padding
) или без прокладки. Это была одна из причин, по которой я никогда не смог получить данные: она всегда была различной по-разному, прежде чем она собиралась зашифровать.
Решение для этого состоит в том, чтобы либо заставить PHP выполнять PKCS7 заполнение данных перед запуском mcrypt
( mcrypt
решение ), либо сделать Objective-C выполненным положением PHP-style \0
и обязательно удалить kCCOptionPKCS7Padding
(передать опции NULL в CCCrypt):
NSMutableData *dataToEncrypt = [sourceData mutableCopy]; NSUInteger dataLength = [dataToEncrypt length]; // See how much padding is required NSUInteger padding = kCCBlockSizeAES128 - (dataLength % kCCBlockSizeAES128); // Add that many \0's (there could be a more efficient way to do this) for (int i=0; i<padding; i++) [dataToEncrypt appendData:[@"\0" dataUsingEncoding:NSASCIIStringEncoding]]; // Recalculate the data length dataLength = dataToEncrypt.length;
Я закончил работу с RNCryptor и работал с родным CommonCryptor API напрямую, поэтому конечный результат выглядел примерно так (метод encryptedBase64String
здесь является категорией в NSString
в моем приложении, потому что мне только нужно зашифровать строки таким образом, а также отметить константа kHSEncryptionKey
представляющая строку ключа свободной формы):
- (NSString*)encryptedBase64String { // Prepare the data NSMutableData *sourceData = [[self dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; // Process the key NSString *key = [[kHSEncryptionKey MD5String] substringWithRange:NSMakeRange(0, 16)]; char keyPtr[kCCKeySizeAES128 + 1]; bzero(keyPtr, sizeof(keyPtr)); [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; // Process the iv NSString *iv = [[[kHSEncryptionKey MD5String] MD5String] substringWithRange:NSMakeRange(0, 16)]; char ivPtr[kCCKeySizeAES128 + 1]; bzero(ivPtr, sizeof(ivPtr)); [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding]; // Pad the data, PHP style NSUInteger dataLength = [sourceData length]; NSUInteger padding = kCCBlockSizeAES128 - (dataLength % kCCBlockSizeAES128); for (int i=0; i<padding; i++) [sourceData appendData:[@"\0" dataUsingEncoding:NSASCIIStringEncoding]]; dataLength = sourceData.length; // Buffer for the resulting data size_t bufferSize = dataLength + kCCBlockSizeAES128; void* buffer = malloc(bufferSize); // Run the encryption size_t numBytesEncrypted = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, NULL, keyPtr, kCCKeySizeAES128, ivPtr, sourceData.bytes, dataLength, /* input */ buffer, bufferSize, /* output */ &numBytesEncrypted); if (cryptStatus == kCCSuccess) { NSData *encyptedData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; return [encyptedData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; } free(buffer); return nil; }