Я ударял по голове на стене с этим. Я должен кодировать свое приложение для iPhone для шифрования 4 использований "контакта" цифры 3DES в режиме ЕЦБ для передачи к веб-сервису, которому я верю, записан в.NET.
+ (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. Таким образом, я ПОДОЗРЕВАЮ, но не знаю, что commoncrypto вызов API только использует первые 24 символа предоставленного ключа.
Это корректно?
Есть ли КАКОЙ-ЛИБО способ, которым я могу заставить это генерировать корректный зашифрованный контакт? Я произвел байты данных от шифрования ДО base64, кодирующего его, и попытался соответствовать этому против сгенерированных из кода.NET (с помощью разработчика.NET, который отправил вывод массива байтов мне). Ни non-base64 не закодировал массив байтов, ни финал base64 закодированное строковое соответствие.
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 ведет себя аналогично тому, что описано на этой странице руководства для MacOS X , то ваш код не должен работать успешно, если длина encryptData
не кратна восьми. Это означает, что код, который вы не показываете, который преобразует 4-значный PIN-код в 8-байтовый буфер, уже выполняет некоторые нетривиальные преобразования. Например, этот код может поместить четыре цифры в четыре байта (с использованием кодировки ASCII) и установить четыре других байта в ноль. Или, может быть, этого не происходит. В любом случае важен каждый из 64 входных битов для 3DES, и вы должны получать его точно так же, как и сервер. Вам также следует изучить этот код.
Что ж, мне удалось решить эту проблему с помощью большого количества чтения и комментариев по 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); }
Надеюсь, это кому-то поможет ...
Может нужно использовать отступы? Попробуйте установить следующие параметры:
(kCCOptionPKCS7Padding | kCCOptionECBMode)