RSA_private_decrypt выходит из строя при повторном вызове

#c #objective-c #openssl #rsa #objective-c

#c #objective-c #openssl #rsa #objective-c

Вопрос:

Пожалуйста, взгляните на следующий метод:

 int BCVirtualCard::decrypt(std::string from, std::string *to, int keyId, bool padding)
{
    if (to == nullptr)
    {
        NSCAssert(NO, @"Invalid params");
        return 0;
    }

    NSString* privateKey            = [m_storage privateKeyForSlot:keyId];
    NSArray<NSString*>* components  = [privateKey componentsSeparatedByString:@"_"];
    const NSInteger componentsCount = 4;

    if (components.count != componentsCount)
    {
        *to = "";
        return 0;
    }

    const char* d = [components[0] UTF8String];
    const char* n = [components[1] UTF8String];
    const char* p = [components[2] UTF8String];
    const char* q = [components[3] UTF8String];

    RSA* rsa = RSA_new();

    BN_hex2bn(amp;rsa->d, d);
    BN_hex2bn(amp;rsa->n, n);
    BN_hex2bn(amp;rsa->p, p);
    BN_hex2bn(amp;rsa->q, q);

    unsigned char* _to = (unsigned char *)calloc(1, sizeof(unsigned char));

    int decryptedSize = RSA_private_decrypt((int)from.length(), (unsigned char *)from.c_str(), _to, rsa, RSA_NO_PADDING);

    free(_to);

    if (decryptedSize <= 0)
    {
        ERR_print_errors_cb(test, NULL);

        *to = "";
        return 0;
    }

    _to = (unsigned char *)calloc(decryptedSize, sizeof(unsigned char));

    RSA_private_decrypt((int)from.length(), (unsigned char *)from.c_str(), _to, rsa, RSA_NO_PADDING);

    *to = std::string((char *)_to, strlen((char *)_to));

    free(_to);

    RSA_free(rsa);

    return 1;
}
  

Здесь строка from должна быть расшифрована и записана в строку to . Для расшифровки я использую RSA_private_decrypt функцию. Я вызываю его два раза. Первый раз для определения размера расшифрованного текста и второй раз для записи расшифрованного текста в _to буфер. И когда я вызываю его во второй раз, он обычно вылетает следующим образом:

 malloc: Heap corruption detected, free list is damaged at 0x280ff3d70
*** Incorrect guard value: 0
No1BCmail(2171,0x170efb000) malloc: *** set a breakpoint in malloc_error_break to debug
  

Точка останова включена, и это позволяет мне найти место сбоя. Однако я не могу понять причину, по которой он вылетает. Я попытался воссоздать RSA структуру и поиграть с размером, выделенным для _to , во второй раз, но ничего не помогает. Вы видите, что здесь не так? Спасибо

Комментарии:

1. _to Действительно ли завершается null? Если нет, вам следует использовать *to = std::string((char *)_to, decryptedSize);

2. Кроме того, согласно этому , ваш первый вызов для расшифровки — UB: to должен указывать на раздел памяти, достаточно большой для хранения расшифрованных данных (который меньше, чем RSA_size (rsa)).

3. @NathanOliver Что касается вашего второго комментария — это единственная причина, по которой я вызываю RSA_private_decrypt два раза. Я просто не знаю размер, который я должен выделить для буфера _to. Мне нужно сначала получить его. вы видите?

4. Похоже, использование RSA_size(rsa) должно дать вам размер буфера для использования. Он получает это значение из rsa->n . Так что вы могли бы сделать unsigned char* _to = new unsigned char[RSA_size(rsa)]; ...; delete _to .

5. @NathanOliver Я не очень хорош в RSA. Вы имеете в виду, что размер расшифрованного текста всегда будет меньше или равен n? Верно?

Ответ №1:

RSA_private_decrypt требуется, чтобы to параметр указывал на буфер соответствующего размера. Ваш первый вызов использует только буфер размером 1, который слишком мал и поведение которого не определено. Что вам нужно сделать, это получить размер из rsa использования RSA_size(rsa) , а затем вы можете использовать это для выделения места для _to . Это означает, что вам не нужно вызывать функцию дважды, поскольку у вас уже будет размер в первый раз

Вы также должны использовать decryptedSize для длины, если строка для построения вместо использования strlen as _to может не заканчиваться нулем.

Собрав все это вместе, вы могли бы выглядеть примерно так

 int BCVirtualCard::decrypt(std::string from, std::string *to, int keyId, bool padding)
{
    if (to == nullptr)
    {
        NSCAssert(NO, @"Invalid params");
        return 0;
    }

    NSString* privateKey            = [m_storage privateKeyForSlot:keyId];
    NSArray<NSString*>* components  = [privateKey componentsSeparatedByString:@"_"];
    const NSInteger componentsCount = 4;

    if (components.count != componentsCount)
    {
        *to = "";
        return 0;
    }

    const char* d = [components[0] UTF8String];
    const char* n = [components[1] UTF8String];
    const char* p = [components[2] UTF8String];
    const char* q = [components[3] UTF8String];

    RSA* rsa = RSA_new();

    BN_hex2bn(amp;rsa->d, d);
    BN_hex2bn(amp;rsa->n, n);
    BN_hex2bn(amp;rsa->p, p);
    BN_hex2bn(amp;rsa->q, q);

    auto _to = std::make_unique<unsigned char[]>(RSA_size(rsa)); // use smart pointers so you don't have to worry about releasing the memory

    int decryptedSize = RSA_private_decrypt((int)from.length(), (unsigned char *)from.c_str(), _to.get(), rsa, RSA_NO_PADDING);

    if (decryptedSize <= 0)
    {
        ERR_print_errors_cb(test, NULL);    
        *to = "";
        return 0;
    }

    *to = std::string((char *)_to.get(), decryptedSize);
    return 1;
}