Работа С Https Без Компонент

Тема в разделе "Delphi - FAQ", создана пользователем Vadik(R), 12 янв 2013.

  1. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Добрый вечер, форумчане!

    Есть у меня такой вопрос. Как на низком уровне работать с https?
    Мои попытки вот:
    Код (Delphi):
    program TestSSL;

    {$APPTYPE CONSOLE}

    uses
    Dialogs, SysUtils, Windows, WinSock;

    procedure SendText(S: TSocket; Buffer: AnsiString);
    begin
    send(S, Buffer[1], Length(Buffer), 0);
    end;

    procedure RecvText(S: TSocket; var Content: AnsiString);
    var
    Buffer: AnsiString;
    Bytes: Integer;
    begin
    Content := '';
    repeat
    SetLength(Buffer, 8192);
    Bytes := recv(S, Buffer[1], Length(Buffer), 0);
    SetLength(Buffer, Bytes);
    Content := Content + Buffer;
    until Bytes = 0;
    end;

    var
    Content: AnsiString;
    S: TSocket;
    SockAddr: TSockAddr;
    WSAData: TWSAData;

    begin
    WSAStartup($202, WSAData);
    SockAddr.sin_family := AF_INET;
    SockAddr.sin_port := htons(443);
    SockAddr.sin_addr.S_addr := inet_addr('69.58.188.34');
    S := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    connect(S, SockAddr, SizeOf(TSockAddr));
    Content := '';
    Content := Content + 'GET / HTTP/1.1' + #13#10;
    Content := Content + 'Host: bitly.com' + #13#10;
    Content := Content + 'Connection: close' + #13#10;
    Content := Content + 'User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17' + #13#10;
    Content := Content + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' + #13#10;
    Content := Content + 'Accept-Encoding: gzip,deflate,sdch' + #13#10;
    Content := Content + 'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4' + #13#10;
    Content := Content + 'Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.3' + #13#10;
    Content := Content + '' + #13#10;
    SendText(S, Content);
    RecvText(S, Content);
    closesocket(S);
    WSACleanup;
    ShowMessage(Content);
    end.
    Хочу получить код https://bit.ly.com/
    Но сервер выдаёт такую ошибку: 400 The plain HTTP request was sent to HTTPS port
    Предполагаю, что дело в том, что https-соединение защищено и прежде, чем делать запросы на получение кода, надо данные как-то зашифровать. И заранее об этом договориться с сервером, алгоритм шифрования, ключи...
    Но я не знаю, какие именно надо прежде запросы делать.
    Обычно смотрю, какие запросы надо делать через панель разработчика в Google Chrome. Но он ничего толкового в этот раз не подсказал, а с обычным http его всегда хватает.
    Помогите, пожалуйста.
     
  2. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Нашёл пример работы с https здесь http://synapse.ararat.cz/doku.php/download
    Вроде бы можно было и его использовать, но ведь интерес!
    Целый день дебажу прогу, но никак не могу понять, в каком моменте используется этот SSL :(
    Вероятно из-за того, что пока происходит выполнение по шагам сервер уже успевает разрывать соединение из-за тайм аута.
    Голова взрывается, а гугл не помогает. Неужели никто раньше этой проблемой не интересовался?

    P.S. WireShark чтоль подключить?
    Никак не пойму, то ли за защищённым соединением должна сама виндовс следить, а мне стоит просто передать в какую-нибудь WinAPI дополнительный параметр о защите...
    Толи я просто никак не могу отследить тот момент, где получают/передают какие-либо ключи и шифруется информация.
     
  3. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
  4. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Прошу помощи. Вот полная версия кода из последней ссылки: http://www.everfall.com/paste/id.php?qhdubuy2o9id

    Нужна помощь в этой части кода, что происходит до неё - понятно:
    Код (C++):
    bool Handshake(SOCKET s, wchar_t * pwzHostname, PCredHandle phCredential, PCtxtHandle phContext)
    {
    SecBufferDesc  InBufferDesc;
    SecBuffer     InBuffers[2];
    InBufferDesc.cBuffers = _countof(InBuffers);
    InBufferDesc.pBuffers = InBuffers;
    InBufferDesc.ulVersion = SECBUFFER_VERSION;
    SecBufferDesc  OutBufferDesc;
    SecBuffer     OutBuffers[1];
    OutBufferDesc.cBuffers = _countof(OutBuffers);
    OutBufferDesc.pBuffers = OutBuffers;
    OutBufferDesc.ulVersion = SECBUFFER_VERSION;
    char *       InSecBuffer = NULL;
    char *       OutSecBuffer = NULL;
    TimeStamp     tsExpiry;
    ULONG m_byteOrder = SECURITY_NETWORK_DREP;
    ULONG requiredSecurity =
    ISC_REQ_SEQUENCE_DETECT  |
    ISC_REQ_REPLAY_DETECT    |
    ISC_REQ_CONFIDENTIALITY  |
    ISC_RET_EXTENDED_ERROR  |
    ISC_REQ_STREAM;
    ULONG actualSecurity;

    SECURITY_STATUS scRet;
    bool            success = false;

    // Figure out how large of a buffer to allocate for a token.
    PSecPkgInfo pPackageInfo;
    scRet = QuerySecurityPackageInfo(SecurityPackage, &pPackageInfo);
    ULONG cbMaxTokenSize = pPackageInfo->cbMaxToken;
    FreeContextBuffer(pPackageInfo);

    // Allocate a buffer large enough to hold the largest token.
    InSecBuffer = (char *) LocalAlloc(LMEM_FIXED, cbMaxTokenSize);
    OutSecBuffer = (char *) LocalAlloc(LMEM_FIXED, cbMaxTokenSize);
    if (!InSecBuffer || !OutSecBuffer)
    {
    oom;
    goto cleanup;
    }
    ULONG& cbInSecBuffer = InBuffers[0].cbBuffer;
    ULONG& cbOutSecBuffer = OutBuffers[0].cbBuffer;

    //Do some one-time initialization of the buffers
    InBuffers[0].BufferType = SECBUFFER_TOKEN;
    InBuffers[0].pvBuffer = InSecBuffer;
    InBuffers[1].BufferType = SECBUFFER_EMPTY;
    InBuffers[1].pvBuffer = NULL;
    InBuffers[1].cbBuffer = 0;

    SecInvalidateHandle(phContext);
    do
    {
    cbInSecBuffer = 0;
    BOOL bFirstTime = !SecIsValidHandle(phContext);
    int cbData;

    do
    {
    if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
    {
    // The server sent more to the token than could be processed in a single
    // call to InitializeSecurityContext. We move this extra data into the token
    // buffer and prepare to call the function again.
    InBuffers[1].pvBuffer = (byte*)InBuffers[0].pvBuffer + cbData - InBuffers[1].cbBuffer;
    MoveMemory(InSecBuffer, InBuffers[1].pvBuffer, InBuffers[1].cbBuffer);
    InBuffers[0].pvBuffer = InSecBuffer;
    InBuffers[0].cbBuffer = InBuffers[1].cbBuffer;
    InBuffers[0].BufferType = SECBUFFER_TOKEN;

    InBuffers[1].BufferType = SECBUFFER_EMPTY;
    InBuffers[1].pvBuffer = NULL;
    InBuffers[1].cbBuffer = 0;
    }
    else // don't try to read more data from the server (yet) if there was data in the extra buffer
    {
    // Read in response from server if we already said something to it.
    if (!bFirstTime)
    {
    cbData = recv(s, InSecBuffer + cbInSecBuffer, cbMaxTokenSize - cbInSecBuffer, 0);
    if(cbData == SOCKET_ERROR || cbData == 0)
    {
    error("failure sending data to server 0x%x\n", WSAGetLastError());
    goto cleanup;
    }
    verbose("%5d bytes of handshake data received.\n", cbData);
    cbInSecBuffer += cbData;

    InBuffers[0].BufferType = SECBUFFER_TOKEN;
    InBuffers[0].pvBuffer = InSecBuffer;
    }
    }

    // Prepare client token.
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].pvBuffer = OutSecBuffer;
    OutBuffers[0].cbBuffer = cbMaxTokenSize;

    // Prepare client token.
    scRet = InitializeSecurityContextW(
    phCredential,
    bFirstTime ? NULL : phContext,
    bFirstTime ? pwzHostname : NULL,
    requiredSecurity,
    0,
    m_byteOrder,
    bFirstTime ? NULL : &InBufferDesc,
    0,
    bFirstTime ? phContext : NULL,
    &OutBufferDesc,
    &actualSecurity,
    &tsExpiry);
    } while (scRet == SEC_E_INCOMPLETE_MESSAGE);

    // Send token to server if there is a token.
    if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
    {
    cbData = send(s,
    (const char*)OutBuffers[0].pvBuffer,
    OutBuffers[0].cbBuffer,
    0);

    if(cbData == SOCKET_ERROR || cbData == 0)
    {
    error("failure sending data to server 0x%x\n", WSAGetLastError());
    goto cleanup;
    }

    verbose("%5d bytes of handshake data sent.\n", cbData);
    }
    } while (scRet == SEC_I_CONTINUE_NEEDED);

    if (scRet != SEC_E_OK)
    {
    error("server handshake failed with error code 0x%x\n", scRet);
    goto cleanup;
    }

    success = true;

    cleanup:

    if (InSecBuffer) LocalFree(InSecBuffer);
    if (OutSecBuffer) LocalFree(OutSecBuffer);

    if (!success)
    {
    if (SecIsValidHandle(phContext))
    {
    DeleteSecurityContext(phContext);
    SecInvalidateHandle(phContext);
    }
    }

    return success;
    }
    Из названия понятно, что здесь происходит рукопожатие. Причём двойное.
    Но в целом, тут мало что понятно. Куча каких-то непонятных структур безопасности, которые я смотрел на MSDN, но запутался. Не понятно, почему структуры заполняются именно такими параметрами, а не какими-либо другими.
    Хотелось бы найти объяснение этого участка кода в следующем виде:
    К сожалению, подобного разбора в интернете нигде не находил. Сам бы написал хорошую статью на эту тему, да вот пока не понимаю, как это всё работает... Кто-нибудь может помочь, кто силён в этой теме?
    Заранее спасибо!
     
  5. sinkopa

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Ну... на пальцах в "два плевка" не объяснишь... :rolleyes:
    Короче... Есть один парень, звать Владислав Баженов (Vlad) - болшой фанат библиотеки Synapse (библиотека бесплатная, с открытым кодом).
    На его сайте, вот тут http://www.webdelphi.ru/tag/synapse/
    больше 50 статей посвященных библиотеке, с подробнейщим описанием, разбором "что к чему", примерами и демо (ссылки для скачивания).
    На вскидку, по Вашей теме:
    Synapse. Авторизация на сайте. Работа с HTTPS.
    Библиотека Synapse. Работа с модулем HTTPSend.pas.
    Думаю, есть и еще...
    Почитайте. Даже если он Вас "не уговорит" использовать Synapse, там подробно описан порядок "рукопожатия"... :)
     
  6. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    sinkopa, за статьи спасибо, правда на тот момент, когда я хотел их почитать - сайт не открывался, лежал.
    Но от модуля Synapse решил отказаться, так как так и не смог разобраться, что именно происходит при установки https-соединения.
    Стал искать эту тему в интернете ещё, в итоге наткнулся на какое-то описание SSL (уже точно не помню, но вроде как раз я наткнулся именно на стандарт, что я и искал), на поддомене mozilla.org.
    Там было очень много текста, да ещё и на английском (было бы хотя бы что-то одно из этих двух), и я решил, что ответ на свой вопрос я нашёл, но он слишком большой, время всё-таки жалко было на его изучение. В итоге я просто разобрался в коде, который был выложен на сайте Microsoft, и перекодил его на Delphi (код, кстати не идеальный (например, вызывается функция ntohs на том месте, где должна быть htons, просто для двухбайтового числа работа этих функций одинакова), но рабочий, разобрался, пока транслировал программу на Delphi).
    http://www.everfall.com/paste/id.php?gtadnp23tdv2 - SSPI
    http://www.everfall.com/paste/id.php?yqfbd38lzjl0 - SSL
    http://www.everfall.com/paste/id.php?hbltmky7i1s3 - Unit1
    SSPI - это модуль Security Support Provider Interface, в Visual Studio он уже был готовый, на Delphi я такого не обнаружил, хотя видел в интернете частично переписанным его для Delphi. По сути, я тоже часть модуля и переписал, там только те функции, которые нужны для установки защищённого соединения из библиотеки security.dll.
    SSL - это тот код, который выложен на Microsoft. Просто захотелось его как-то отделить.
    Кого заинтересует, он нуждается в небольших доработках. В плане, что функция ReceiveResponse должна возвращать строку - ответ от сервера, а SendRequest логично бы было принимать строку, которую необходимо только зашифровать и передать. Собственно, это не сложно дописать, и я, возможно, выложу сюда дальнейшую версию этого модуля. Сразу переделывать эти функции я не стал, я просто был рад, что код и на Delphi стал рабочий, и прям "не дышать" хотелось :)
    Unit1 - пример использования. Вызываются функции из модуля SSL.

    Для себя интересную работу сделал, узнал что-то новое, вполне доволен. Хотя и времени она отняла достаточно.
    Выкладываю результаты сюда, вдруг пригодятся такому же извращенцу, как я, не принимающему готовых решений :)
     
Загрузка...
Похожие Темы - Работа Https Без
  1. Andrey Kha
    Ответов:
    0
    Просмотров:
    33
  2. Hoasker
    Ответов:
    0
    Просмотров:
    65
  3. garri671
    Ответов:
    0
    Просмотров:
    74
  4. lelik200969
    Ответов:
    0
    Просмотров:
    55
  5. Kozolick
    Ответов:
    0
    Просмотров:
    145

Поделиться этой страницей