Cocoa Преобразовать NSArray/NSString восьмеричных чисел в NSData?

Я пытаюсь преобразовать этот фрагмент кода C в Cocoa, и я изо всех сил пытаюсь понять, как это сделать.

  char *deskey = "123 456 789 101 112 131 415 161";
  unsigned char key[16];
  memset(key, 0, sizeof(key));
  sscanf(deskey, "%o %o %o %o %o %o %o %o",
    (int*)&key[0], (int*)&key[1], (int*)&key[2],
    (int*)&key[3], (int*)&key[4], (int*)&key[5],
    (int*)&key[6], (int*)&key[7]);

Я пробовал использовать NSMutableArray и NSData, но безуспешно. Я смог отсканировать строку и вытащить числа, но я не уверен, как после этого сохранить в NSData.

  NSMutableArray *enckey = [[[NSMutableArray alloc] init] autorelease];
  NSScanner *scanner = [NSScanner scannerWithString:self.deskey];
  int pos = 0;

  while ([scanner isAtEnd] == NO) {
    if ([scanner scanInt:&pos]) {
      [enckey addObject:[NSString stringWithFormat:@"%o", pos]];
    }
    else {
      NSLog(@"Your DES key appears to be invalid.");
      return;
    }
  }

В основном пытаясь преобразовать ключ ascii DES в строку для использования для шифрования Triple DES. Любая помощь приветствуется, спасибо!


person Keenan    schedule 11.03.2011    source источник


Ответы (3)


@Keenan «Я надеялся избежать использования sscanf и char * вместо классов Cocoa». Ну, вы можете это сделать, но что вы надеетесь произвести? Если вы хотите получить массив байтов в качестве результата, вам нужно придерживаться unsigned char[], и возникает вопрос, почему вы вообще должны выполнять синтаксический анализ NSString.

Вот перевод вашего кода C на Objective-C. Обратите внимание, что восьмеричная система рассматривается Cocoa как древняя история, поэтому ее классы синтаксического анализа имеют дело только с десятичной и шестнадцатеричной, поэтому вам нужно написать свою собственную или использовать стандартную функцию C (strtol ниже).

В этом примере выводятся как unsigned char[], так и NSMutableArray — выберите один.

// There are no checks in the code, like in the original...
// BTW 789 is not an octal number...
NSString *descKey = @"123 456 789 101 112 131 415 161";         //    char *deskey = "123 456 789 101 112 131 415 161";
// pick one...
NSMutableArray *keyObjC = [NSMutableArray new];                 //    unsigned char key[16];
unsigned char keyC[16];
//    memset(key, 0, sizeof(key));

// As @JeremyP has pointed out the sscanf is wrong as %o produces a 4-byte value and you only want a 1-byte one.
// In C you would therefore need key to be an array of ints and then assign each element to a byte (unsigned char),
// or parse a different way.

unsigned ix = 0;    // for keyC choice only
NSArray *numbers = [descKey componentsSeparatedByString:@" "];  //    sscanf(deskey, "%o %o %o %o %o %o %o %o",
for (NSString *aNumber in numbers)                              //           (int*)&key[0], (int*)&key[1], (int*)&key[2],
{                                                               //           (int*)&key[3], (int*)&key[4], (int*)&key[5],
                                                                //           (int*)&key[6], (int*)&key[7]);
    unsigned char next = (unsigned char)strtol([aNumber UTF8String], NULL, 8);
    keyC[ix++] = next;                                          // for keyC choice
    [keyObjC addObject:[NSNumber numberWithUnsignedChar:next]]; // keyObjC choice
}

Если вы хотите приблизиться к одной строке вашего Python, просто сожмите итерацию до:

for (NSString *aNumber in [descKey componentsSeparatedByString:@" "]) { [keyObjC addObject:[NSNumber numberWithUnsignedChar:(unsigned char)strtol([aNumber UTF8String], NULL, 8)]]; }

но это все равно дольше конечно!

person CRD    schedule 11.03.2011
comment
большое спасибо за объяснение и ответ! Я действительно ценю твою помощь. :) - person Keenan; 14.03.2011

Быстрый и грязный метод - взять исходный код

char *deskey = "123 456 789 101 112 131 415 161";
unsigned char key[16];
memset(key, 0, sizeof(key));
sscanf(deskey, "%o %o %o %o %o %o %o %o",
       (int*)&key[0], (int*)&key[1], (int*)&key[2],
       (int*)&key[3], (int*)&key[4], (int*)&key[5],
       (int*)&key[6], (int*)&key[7]);

И добавьте следующую строку:

NSData* keyData = [NSData dataWithBytes: key length: sizeof key];

За исключением того, что ваш sscanf выглядит очень неправильно. Элементами ключа являются символы, а не целые числа.

person JeremyP    schedule 11.03.2011
comment
Благодарю за ваш ответ! Я надеялся избежать использования sscanf и char* вместо классов Cocoa. Что касается ключа, это то, что хочет функция OpenSSL EVP_EncryptInit: EVP_EncryptInit(&ctx, EVP_des_ecb(), key, NULL); Я должен был более четко указать это в своем исходном сообщении. - person Keenan; 11.03.2011
comment
@Keenan: Мой комментарий о неправильно выглядящем sscanf на самом деле связан с вашим приведением адресов элементов к int * и формату %o. На машине с 32-битным int каждый из этих указателей должен указывать на 4-байтовую ячейку, что означает, что сканирование значения в (int*)&key[0] перезапишет key[0], key[1], key[2] и key[3] фактическим восьмеричным числом в ключе [0] или key[3] в зависимости от того, является ли архитектура обратным или обратным порядком байтов. - person JeremyP; 11.03.2011
comment
Хм, я думал %o ожидаемый тип int* (или unsigned int*). Вот как я сделал это в python (который также работает с 3DES ecb): for i in deskey.split(' '): keyData += pack('B', int(i, 8)). Спасибо за помощь. - person Keenan; 11.03.2011
comment
@Keenan: Да, поэтому вы не можете использовать %o для сканирования в хранилище размером unsigned char. - person Peter Hosey; 12.03.2011

Поскольку CRD уже проделал хорошую работу, показав вам правильное решение, я воспользуюсь своим ответом, чтобы объяснить, почему ваши решения не сработали.

  unsigned char key[16];
  memset(key, 0, sizeof(key));
  sscanf(deskey, "%o %o %o %o %o %o %o %o",
    (int*)&key[0], (int*)&key[1], (int*)&key[2],
    (int*)&key[3], (int*)&key[4], (int*)&key[5],
    (int*)&key[6], (int*)&key[7]);

Ложь компилятору никогда не решит проблемы.

key представляет собой массив unsigned chars. Сообщение компилятору в вашем вызове функции sscanf о том, что ваши указатели в массиве являются указателями на ints, не делает их таковыми; хранилище, на которое указывает каждый, по-прежнему является хранилищем для unsigned char и не более того.

В результате каждая попытка сохранить один int по указанному вами адресу перезаписывает три четверти предыдущего int. Результатом этого является то, что sscanf представляет собой не более чем более сложный способ сделать то же самое, что и memset.

Я говорю «еще немного», а не «не более» по одной причине: вызов sscanf также записывает sizeof(int) - sizeof(unsigned char) байтов за конец массива, так что вы также разбиваете стек и настраиваетесь на потенциальный сбой.

    if ([scanner scanInt:&pos]) {
      [enckey addObject:[NSString stringWithFormat:@"%o", pos]];

scanInt: анализирует число из строки в десятичном формате. stringWithFormat: также делает то, о чем говорит его название: создает строку. В указанном вами формате он преобразует число в строку, а не наоборот.

Таким образом, вы используете scanInt: для разбора числа в десятичном виде и stringWithFormat: для преобразования его в восьмеричное (и кодируете его обратно в строку). Предполагая, что из кода на основе sscanf ввод был восьмеричным, это противоположно тому, что вы хотите.

Этот код, безусловно, даст вам числа, но он даст вам неправильные числа, что почти так же плохо, как код sscanf, который дал вам все нули плюс сбой.

Вы не можете использовать NSScanner для сканирования восьмеричных чисел.

Я также не уверен, почему вы назвали эту переменную pos, поскольку она не является позицией. Конечно, это не то, что дает вам scanInt:.

Я смог отсканировать строку и вытащить числа, но я не уверен, как после этого сохранить в NSData.

См. документацию по NSData.

Я пробовал использовать NSMutableArray и NSData, но безуспешно.

Это не имеет ничего общего с проблемой разбора входных данных.

Я не уверен, будет ли NSArray или массив C (последний необязательно завернутый в NSData) правильным местом для размещения результата. Это зависит от того, куда вы хотите передать числа после их анализа.

person Peter Hosey    schedule 12.03.2011
comment
Спасибо! Чтобы закрыть цикл, класс, который я использовал, требовал NSData, и, как вы указали, я делал все неправильно, чтобы превратить свои данные в NSData. Спасибо за ваше объяснение проблемы, я ценю вашу помощь! - person Keenan; 14.03.2011