2010-03-16 3 views
6

Я стучал головой о стену с этим. Мне нужно закодировать приложение для iPhone, чтобы зашифровать 4-значный «контакт» с помощью 3DES в режиме ECB для передачи в веб-сервис, который, как мне кажется, написан на .NET.iPhone 3GES шифрование длина ключа вопрос

+ (NSData *)TripleDESEncryptWithKey:(NSString *)key dataToEncrypt:(NSData*)encryptData { 
NSLog(@"kCCKeySize3DES=%d", kCCKeySize3DES); 
char keyBuffer[kCCKeySize3DES+1]; // room for terminator (unused) 
bzero(keyBuffer, sizeof(keyBuffer)); // fill with zeroes (for padding) 

[key getCString: keyBuffer maxLength: sizeof(keyBuffer) encoding: NSUTF8StringEncoding]; 

// encrypts in-place, since this is a mutable data object 
size_t numBytesEncrypted = 0; 

size_t returnLength = ([encryptData length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1); 

// NSMutableData* returnBuffer = [NSMutableData dataWithLength:returnLength]; 
char* returnBuffer = malloc(returnLength * sizeof(uint8_t)); 

CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt, kCCAlgorithm3DES , kCCOptionECBMode, 
           keyBuffer, kCCKeySize3DES, nil, 
           [encryptData bytes], [encryptData length], 
           returnBuffer, returnLength, 
           &numBytesEncrypted); 

if (ccStatus == kCCParamError) NSLog(@"PARAM ERROR"); 
else if (ccStatus == kCCBufferTooSmall) NSLog(@"BUFFER TOO SMALL"); 
else if (ccStatus == kCCMemoryFailure) NSLog(@"MEMORY FAILURE"); 
else if (ccStatus == kCCAlignmentError) NSLog(@"ALIGNMENT"); 
else if (ccStatus == kCCDecodeError) NSLog(@"DECODE ERROR"); 
else if (ccStatus == kCCUnimplemented) NSLog(@"UNIMPLEMENTED"); 

if(ccStatus == kCCSuccess) { 
    NSLog(@"TripleDESEncryptWithKey encrypted: %@", [NSData dataWithBytes:returnBuffer length:numBytesEncrypted]); 
    return [NSData dataWithBytes:returnBuffer length:numBytesEncrypted]; 
} 
else 
    return nil; 
} } 

Я получаю значение, зашифрованное с использованием вышеуказанного кода, однако оно не соответствует значению веб-службы .NET.

Я считаю, что проблема заключается в том, что ключ шифрования, который я предоставил разработчикам веб-сервисов, составляет 48 символов.

Я вижу, что константа iPhone SDK «kCCKeySize3DES» равна 24. Поэтому я SUSPECT, но не знаю, что вызов API commoncrypto использует только первые 24 символа предоставленного ключа.

Это правильно?

Есть ли способ, которым я могу получить это, чтобы создать правильный зашифрованный вывод? Я выдаю байты данных из шифрования PRIOR в base64, кодируя его, и попытался сопоставить его с теми, которые генерируются из .NET-кода (с помощью .NET-разработчика, который отправил мне массив байтов). Не соответствуют ни закодированный байтовый массив, не содержащий base64, ни конечные строки с кодировкой base64.

ответ

3

3DES - это симметричный блочный шифр. Используя 24-байтовый ключ, 3DES шифрует 8-байтовый блок в другой 8-байтовый блок. С тем же 24-байтовым ключом шифрование является обратимым (т. Е. Вы можете расшифровать).

Ключ - произвольная последовательность байтов. Это не то же самое, что «персонажи». В частности, наличие одного из этих байтов с нулевым значением совершенно законно. Точно так же ввод и вывод могут быть произвольными байтами.

Если ключ, который вы получили, состоит из «символов», тогда он должен быть каким-то образом преобразован в соответствующую последовательность байтов. Поскольку у вас есть 48-символьная «ключевая строка», а 48 - ровно 24 * 2, вероятная догадка заключается в том, что ключ указан в шестнадцатеричной нотации: см., Содержит ли он только цифры и буквы от «a» до «f».

Что касается заполнения: 3DES шифрует только 8-байтовые блоки. Когда «сообщение» должно быть зашифровано и имеет некоторую длину, отличную от 8 байтов, тогда принято форматировать и разбивать и обрабатывать сообщение, чтобы оно могло быть зашифровано в ряде вызовов в 3DES. Два ключевых слова: padding и chaining. Заполнение - это добавление некоторых дополнительных байтов в конец (таким образом, чтобы этот байт можно было однозначно удалить), чтобы длина была подходящей (например, кратной 8). Цепочка - это определение того, что именно происходит в каждом вызове 3DES (простое разделение проложенного сообщения на незашифрованные блоки называется «ECB» и имеет слабые места).

Если ваш PIN-код содержит 4 цифры, то должно быть какое-то соглашение о том, как эти четыре цифры становятся не менее 8 байтами для подачи в 3DES. Если iPhone ведет себя аналогично тому, что описывает этот man page for MacOS X, то ваш код не должен работать успешно, если длина encryptData не равна восьми. Это означает, что код, который вы не показываете, который преобразует 4-значный PIN-код в 8-байтовый буфер, уже выполняет некоторые нетривиальные преобразования. Например, этот код может поместить четыре цифры в четыре байта (с использованием ASCII-кодирования) и установить четыре других байта в ноль. Или, может быть, это не удается.В любом случае, каждый из 64 входных битов в 3DES важен, и вы должны получить его точно так же, как сервер. Вы также должны проверить этот код.

+0

Спасибо Томасу за подробный ответ здесь. Понятия, о которых вы говорите, - это именно то, что я подробно рассмотрел в деталях. Мне повезло, что у меня был доступ к .NET-версии кода шифрования, и это дало мне ценные указатели. 4-значный PIN-код был преобразован в 8-значное значение для подачи в алгоритм 3DES путем добавления 4 0. Я добавил свой код к этому ответу, но обозначил его как решение, потому что ваши знания были абсолютно правильными. –

1

Возможно, вам нужно использовать прокладку? Попробуйте установить следующие параметры:

(kCCOptionPKCS7Padding | kCCOptionECBMode) 
+0

Спасибо за предложение, но мне сказали, что код .NET устанавливает отступы на «PaddingMode.None». Поэтому я полагаю, что мне не нужно набивать iPhone из приложения iPhone. Возможно, я ошибаюсь! –

+0

У вас есть доступ к коду .Net? Если вы это сделаете, я предлагаю прочитать его, чтобы переработать фактическое шифрование. –

2

ну, мне удалось решить это с большим чтением и комментариями здесь, в stackoverflow. Там, где несколько вопросов. Ключом, который я дал разработчикам .NET, было 48 символов. Это, конечно, нужно было читать в виде шестнадцатеричной строки и вместо этого преобразовать до 24 символов.

Я добавил код, чтобы сделать это, и полная процедура выглядит следующим образом. Я не уверен, что это будет полезно, поскольку это весьма специфично для нашей реализации.

+ (NSString *)doCipher3DES:(NSString *)sTextIn key:(NSString *)sKey { 
NSMutableData * dTextIn; 
CCCryptorStatus ccStatus = kCCSuccess; 

// need to add 4 zeros as sTextIn will be a 4 digit PIN 
sTextIn = [sTextIn stringByAppendingString:@"0000"]; 

// convert to data 
dTextIn = [[sTextIn dataUsingEncoding: NSASCIIStringEncoding] mutableCopy];   

// key will be a 48 char hex stream, so process it down to 24 chars 
const char * bytes = [sKey cStringUsingEncoding: NSUTF8StringEncoding]; 
NSUInteger length = strlen(bytes); 
unsigned char * r = (unsigned char *) malloc(length/2 + 1); 
unsigned char * index = r; 

while ((*bytes) && (*(bytes +1))) { 
    *index = strToChar(*bytes, *(bytes +1)); 
    index++; 
    bytes+=2; 
} 
*index = '\0'; 

NSData *dKey = [NSData dataWithBytes: r length: length/2]; 
free(r); 

NSLog(@"doCipher3DES - key: %@", dKey); 

uint8_t *bufferPtr1 = NULL;  
size_t bufferPtrSize1 = 0;  
size_t movedBytes1 = 0;  
uint8_t iv[kCCBlockSize3DES];  
memset((void *) iv, 0x0, (size_t) sizeof(iv));  
bufferPtrSize1 = ([sTextIn length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES -1);  
bufferPtr1 = malloc(bufferPtrSize1 * sizeof(uint8_t));  
memset((void *)bufferPtr1, 0x00, bufferPtrSize1);  

ccStatus = CCCrypt(kCCEncrypt, // CCOperation op  
        kCCAlgorithm3DES, // CCAlgorithm alg  
        kCCOptionECBMode, // CCOptions options  
        (const void *)[dKey bytes], // const void *key  
        kCCKeySize3DES, // size_t keyLength  
        nil, // const void *iv  
        (const void *)[dTextIn bytes], // const void *dataIn 
        [dTextIn length], // size_t dataInLength  
        (void *)bufferPtr1, // void *dataOut  
        bufferPtrSize1,  // size_t dataOutAvailable 
        &movedBytes1);  // size_t *dataOutMoved  

if (ccStatus == kCCParamError) NSLog(@"PARAM ERROR"); 
else if (ccStatus == kCCBufferTooSmall) NSLog(@"BUFFER TOO SMALL"); 
else if (ccStatus == kCCMemoryFailure) NSLog(@"MEMORY FAILURE"); 
else if (ccStatus == kCCAlignmentError) NSLog(@"ALIGNMENT"); 
else if (ccStatus == kCCDecodeError) NSLog(@"DECODE ERROR"); 
else if (ccStatus == kCCUnimplemented) NSLog(@"UNIMPLEMENTED"); 

NSString * sResult;  
NSData *dResult = [NSData dataWithBytes:bufferPtr1 length:movedBytes1];  

NSLog(@"doCipher3DES encrypted: %@", dResult); 

sResult = [Base64 encode:dResult];  

return sResult; } 

Код для strToChar выглядит следующим образом:

unsigned char strToChar (char a, char b) { 
char encoder[3] = {'\0','\0','\0'}; 
encoder[0] = a; 
encoder[1] = b; 
return (char) strtol(encoder,NULL,16); } 

Я надеюсь, что это поможет кому-то ...

Смежные вопросы