Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > .NET Frameworks (точка нет фреймворки) > Общие вопросы .NET
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 05.11.2009, 12:29   #1
linkersites
 
Регистрация: 26.09.2009
Сообщений: 6
По умолчанию HttpSendRequest ERROR_INTERNET_SECURITY_CHANNEL_ERR OR 12157

Доброго времени суток

Согласно ТЗ необходимо обеспечить полнофункциональный диалог с сервером. Причем сервер использует "закрытый" сертификат. Сертификат был получен и установлен. При работе через браузер соединение проходит успешно.
При установке соединения SSL сервер выдает дополнительный запрос для авторизации. Ответ на который должен быть POST определенных данных.

Примечание: В ТЗ указано, что должна быть возможность посылать через указанный порт, поэтому не используем стандартный INTERNET_DEFAULT_HTTPS_PORT

Код
Код:
// используем модуль
# include <windows.h>
...
    // инициализируем WinInet
    m_hInternet = InternetOpen( m_lpszAgent,
                                        INTERNET_OPEN_TYPE_PRECONFIG,
                                        m_lpszHost,
                                        NULL,
                                        0);

    if (m_hInternet != NULL)
    {

        /// открываем HTTP сессию
        HINTERNET m_hConnect = InternetConnect(m_hInternet,
                                            m_lpszHost,
                                            m_lpszPort,
                                            NULL,NULL,
                                            INTERNET_SERVICE_HTTP,
                                            0,
                                            0);

        if (m_hConnect != NULL)
        {
            // открываем запрос
            HINTERNET m_hRequest = HttpOpenRequest(m_hConnect,
                                                  m_lpszMethod,
                                                  lpszAction,
                                                  NULL,
                                                  NULL,
                                                  0,
                                                  INTERNET_FLAG_KEEP_CONNECTION |
                                                  INTERNET_FLAG_DONT_CACHE |
                                                  INTERNET_FLAG_SECURE |
                                                  INTERNET_FLAG_IGNORE_CERT_CN_INVALID  |
                                                  INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
                                                  INTERNET_FLAG_NO_COOKIES,

	    // добавленная метка изначально ее не было
	    lAgain:
	    ;

            if (m_hRequest != NULL) {
            // установка флагов игнорирования сертификатов
            DWORD dwSeqFlags = 0;
            DWORD dwBuffLen = sizeof(dwSeqFlags);
            InternetQueryOption (m_hRequest, INTERNET_OPTION_SECURITY_FLAGS, (LPVOID)&dwSeqFlags, &dwBuffLen);

            dwSeqFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA |
                                 INTERNET_FLAG_IGNORE_CERT_CN_INVALID  | 
                                 INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;

            InternetSetOption (m_hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwSeqFlags, sizeof (dwSeqFlags) );

                     
             // выполним запрос
	     if (::HttpSendRequest(
					m_hRequest,
					m_lpszDefaultHeader,
					-1,
					(LPVOID)lpszData,
					lpszData? _tcslen(lpszData): 0) ) {
             // запрос в норме получи данные

              } else {
                  // здесь вылетает ошибка
                  DWORD dwError = GetLastError();

                  // закроем Request
                  InternetCloseHandle(m_hRequest);
                  m_hRequest = NULL;

                  // вернем неудачу
                  return false;
              }

// закрываем соединение
...
В большинстве случаев такой код подходит
Но при соединении с сервером с "закрытым" сертификатом вылетает ошибка ERROR_INTERNET_CLIENT_AUTH_CERT_NEE DED 12044

сертификат получен и установлен
linkersites вне форума Ответить с цитированием
Старый 05.11.2009, 12:33   #2
linkersites
 
Регистрация: 26.09.2009
Сообщений: 6
По умолчанию Re: ERROR_INTERNET_CLIENT_AUTH_CERT_NEE DED 12044

Два способа решения ошибки ERROR_INTERNET_CLIENT_AUTH_CERT_NEE DED 12044
Первый: Выведением окна с выбором сертификата

Код:
...
              // здесь вылетает ошибка
              DWORD dwError = GetLastError();

              // определим нужную ошибку
              if ( dwError == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED ){

                   if( InternetErrorDlg( GetDesktopWindow(), 
                             hReq,
                             ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
                             FLAGS_ERROR_UI_FILTER_FOR_ERRORS       |
                             FLAGS_ERROR_UI_FLAGS_GENERATE_DATA     |
                             FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, 
                             NULL) == ERROR_SUCCESS ){
                   // вывели окно повторим запрос немного криво не хотелось goto но пока так :(
                   goto lAgain ;
                   }
              }

              // закроем Request
              InternetCloseHandle(m_hRequest);
              m_hRequest = NULL;

              // вернем неудачу
              return false;
...
Второй: установим InternetSetOption INTERNET_OPTION_SECURITY_SELECT_CLI ENT_CERT
Код:
...
              // здесь вылетает ошибка
              DWORD dwError = GetLastError();

              // определим нужную ошибку
              if ( dwError == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED ){

                        DWORD dwCert = 0;
                        InternetSetOption(m_hRequest, INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT, &dwCert, sizeof(dwCert));

                   // повторим запрос
                   goto lAgain ;

              }

              // закроем Request
              InternetCloseHandle(m_hRequest);
              m_hRequest = NULL;

              // вернем неудачу
              return false;
...
Примечание: Пока установила метку lAgain извините исправлю
linkersites вне форума Ответить с цитированием
Старый 05.11.2009, 12:53   #3
linkersites
 
Регистрация: 26.09.2009
Сообщений: 6
По умолчанию Re: ERROR_INTERNET_CLIENT_AUTH_CERT_NEE DED (решено)

После всего этого мы получаем данные для повторной авторизации.
Отправляем данные сформированные из полученных с сервера методом POST. Опять вылетает ошибка ERROR_INTERNET_CLIENT_AUTH_CERT_NEE DED 12044. Снова вылетает окно с выбором сертификата, или не вылетает (если вторым способом) и опять данные авторизации.

Попробовала установить сертификат принудительно перед запросом HttpSendRequest
Код:
// используем wincrypt.h crypt32.dll
# include <wincrypt.h>
...
                        // Установка сертификата перед запросом
                        PCCERT_CONTEXT pCert = NULL;
                        pCert = CertEnumCertificatesInStore(m_hSystemStore, pCert);
                        InternetSetOption(m_hRequest,INTERNET_OPTION_CLIENT_CERT_CONTEXT,(LPVOID)pCert,sizeof(CERT_CONTEXT));
...
m_hSystemStore получаем
Код:
...
    HCERTSTORE  m_hSystemStore;
    m_hSystemStore = CertOpenSystemStore(NULL, "My");
...
после запросов удаляем
Код:
CertCloseStore(hSystemStore, 0);
Вылетает ошибка 12157 ERROR_INTERNET_SECURITY_CHANNEL_ERR OR

Судя по данным pCert->pCertInfo выбирается нужный сертификат
Кто может подсказать решение проблемы?

Дополнительно сообщу, что m_hConnect и m_hInternet создается только при первом запросе, потом используется поэтому потеря сессии и создание новой не является причиной ошибки.

Последний раз редактировалось linkersites; 05.11.2009 в 13:02. Причина: Забыла сказать по m_hConnect и m_hInternet !
linkersites вне форума Ответить с цитированием
Старый 08.11.2009, 10:45   #4
linkersites
 
Регистрация: 26.09.2009
Сообщений: 6
По умолчанию CertFindCertificateInStore решение

Для решения проблемы ERROR_INTERNET_SECURITY_CHANNEL_ERR OR 12157 нужно воспользоваться CertFindCertificateInStore Function (Windows).

Код:
bool THttp::OpenSystemStore(){
    bool okOpenSystemStore = false;
    if(m_hSystemStore){
        // уже открыт
        okOpenSystemStore = true;
    }else{
        // пытаемся открыть
        okOpenSystemStore = (m_hSystemStore = CertOpenStore(
                                                            CERT_STORE_PROV_SYSTEM,
                                                            0,
                                                            NULL,
                                                            CERT_SYSTEM_STORE_CURRENT_USER,
                                                            L"MY"
                                                            ));
    }
    return okOpenSystemStore;
}

void THttp::FreeCert(){
    if(m_pCert){
        ::CertFreeCertificateContext(m_pCert);
        m_pCert = NULL;
    }
}

bool THttp::SetCert(const String _certSubject){
    bool okSetCert = false;
    if (OpenSystemStore()){
        // перенести
        WideString wstr(_certSubject);
        wchar_t*pCertSubject=wstr.c_bstr();

        FreeCert();
        okSetCert =
            (m_pCert = ::CertFindCertificateInStore(
                                                    m_hSystemStore,
                                                    PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                                                    0,
                                                    CERT_FIND_SUBJECT_STR,
                                                    pCertSubject,
                                                    NULL));
        delete pCertSubject;

    }
    return okSetCert;
}
m_pCert - глобальная переменная класса, в случае если она установлена перед запросом устанавливается сертификат.

Код:
            // установить сертификат
            if (m_pCert)
                InternetSetOption(hRequest,INTERNET_OPTION_CLIENT_CERT_CONTEXT,(LPVOID)m_pCert,sizeof(CERT_CONTEXT));

                // дальше отправим запрос
                okSendRequest = ::HttpSendRequest(...
Перед запросом CertFindCertificateInStore необходимо, чтобы m_pCert был NULL. Дополнительная FreeCert() функция освобождает сертификат. Перед вызовом функции _certSubject приводится к уникоду. Это необходимо если CERT_FIND_SUBJECT_STR CertFindCertificateInStore не возвращает результат (и проект не уникод).

В конце работы необходимо очистить m_hSystemStore

Код:
bool THttp::CloseSystemStore(){
    bool okCloseSystemStore = false;
    if(m_hSystemStore){
        // удалим сертификат
        FreeCert();

        okCloseSystemStore = ::CertCloseStore(m_hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG);
        if (okCloseSystemStore) m_hSystemStore = NULL;
    }
    return okCloseSystemStore;
}
Функция возвращает true, если удаление прошло успешно и false, если ресурсы SystemStore заняты и их нельзя очистить.
linkersites вне форума Ответить с цитированием
Старый 08.11.2009, 11:11   #5
linkersites
 
Регистрация: 26.09.2009
Сообщений: 6
По умолчанию

К сожаление так и не найден ответ. Теперь страницы получаем минуя этап ERROR_INTERNET_CLIENT_AUTH_CERT_NEE DED 12044. Сервер сразу выдает страницу авторизации. После отправки на него POST, тот же самый результат. Данные авторизации хранятся в сессии SSL, как их сохранят и получить так и не разобралась.

Может подскажете где копать?
linkersites вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц