WinInetでSSL通信

[戻る]
田中さん 2006/06/15(木) 08:59:50
開発環境はVisualC.NET+SDKです。

WinInetを使ってWebクライアントを作っています。

httpsページを取得しようとしていますが、InternetConnect関数で失敗しています。
どのあたりに間違いがありそうか探しています。何かヒントはありませんか?

コードは以下のように作りました。
void GetDataByInetSSL(void){
    HINTERNET hOpen;
    HINTERNET hConn;
    HINTERNET hInternet;
    DWORD dwReadSize;
    DWORD dwSize;
    HGLOBAL hGBuff;
    LPSTR lpszBuff;

    char szScheme[256], szServerName[256], szUserName[256], szPassword
[256],szObjectName[256], szExtraInfo[256];
    URL_COMPONENTSA UrlComponents;

    UrlComponents.dwStructSize = sizeof(URL_COMPONENTSA);    //コンポーネント
のサイズ確保
    UrlComponents.nScheme = INTERNET_SCHEME_DEFAULT;
    UrlComponents.lpszScheme    = szScheme;        // Schemeの領域
の確保
    UrlComponents.dwSchemeLength    = sizeof(szScheme);
    UrlComponents.lpszHostName  = szServerName;    // HostNameの領域の確保
    UrlComponents.dwHostNameLength  = sizeof(szServerName);
    UrlComponents.lpszUserName  = szUserName;    // UserNameの領域の確保
    UrlComponents.dwUserNameLength  = sizeof(szUserName);
    UrlComponents.lpszPassword  = szPassword;    // Passwordの領域の確保
    UrlComponents.dwPasswordLength  = sizeof(szPassword);
    UrlComponents.lpszUrlPath   = szObjectName;    // UrlPathの領域の確保
    UrlComponents.dwUrlPathLength   = sizeof(szObjectName);
    UrlComponents.lpszExtraInfo = szExtraInfo;    // ExtraInfoの領域の確保
    UrlComponents.dwExtraInfoLength = sizeof(szExtraInfo);

    InternetCrackUrl(inifiles.url,sizeof
(inifiles.url),ICU_DECODE,&UrlComponents);

    hOpen=InternetOpen("INET PROGRAM",
                       INTERNET_OPEN_TYPE_PRECONFIG,
                       NULL,
                       NULL,
                       0);
    if(hOpen==false){
        DISP("Internet関数に失敗しました");
        return;
    }

    hConn=InternetConnect(hOpen,        //Openハンドル
        UrlComponents.lpszHostName,        //サーバー名
        UrlComponents.nPort,        //ポート
        UrlComponents.lpszUserName,        //ユーザー名
        UrlComponents.lpszPassword,        //ユーザーパスワード
        UrlComponents.nScheme,        //サービス
        0,
        0);
    if(hConn==false){
        InternetCloseHandle(hOpen);
        DISP("InternetConnect関数に失敗しました");
        return;
    }

    hInternet=HttpOpenRequest(hConn,    //接続
        "GET",            //手法
        UrlComponents.lpszUrlPath,    //ページ名
        HTTP_VERSION,        //Version
        NULL,
        NULL,
        INTERNET_FLAG_SECURE,    //flags
        0);


    //読み込みバッファ確保
    InternetQueryDataAvailable(hInternet,&dwReadSize,0,0);
    hGBuff=GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwReadSize+1);
    lpszBuff=(LPSTR)GlobalLock(hGBuff);

    while(true){
        InternetReadFile(hInternet,lpszBuff,dwReadSize,&dwSize);
        if(dwSize==0){
            break;
        }
        lpszBuff[dwSize]='\0';
        DISP(lpszBuff);
    }
    GlobalUnlock(hGBuff);
    GlobalFree(hGBuff);
    InternetCloseHandle(hInternet);
    InternetCloseHandle(hConn);
    InternetCloseHandle(hOpen);
}

dairygoods 2006/06/15(木) 09:05:15
とりあえず、GetLastError や InternetGetLastResponseInfo で
エラー理由を調べてみるのがよいと思います。

田中さん 2006/06/28(水) 13:27:48
すみません。ちょっと出張中でテストが出来ずレス遅くなりました。

GetLastError()ではエラーコードが87が帰ってきました。
InternetGetLastResponseInfoではうまくエラーメッセージが帰って来ませんでした。

サーバーは自作サーバーでApache+Opensslです。
リダイレクトなどの処理はしていませんので認証、認証の前準備がダメなのだと思いま
す。

どこかに糸口はありませんでしょうか?

通りすがり 2006/06/28(水) 14:46:47
Code Description Name 
87 The parameter is incorrect. ERROR_INVALID_PARAMETER 

InternetCrackUrlの成否判定してないみたいだけど

パラメータエラーなんだから
inifiles.urlに入ってる文字列が変とか、
inifiles.urlがポインタになってsizeofが…とか
そういう理由でInternetCrackUrlで分解失敗してて
InternetConnectも失敗してるとかじゃない?

通りすがり 2006/06/28(水) 15:04:53
ん?
InternetConnectの6番目のパラメータ間違ってるじゃん。
ここにいれるのはINTERNET_SCHEME〜じゃなくてINTERNET_SERVICEだよ

通りすがり 2006/06/28(水) 15:09:50
途中で送っちゃった

で、
INTERNET_SCHEME〜にはHTTPSの3ふがあるけど、
INTERNET_SERVICE〜には無いからパラメータエラーになってるのかと。

田中さん 2006/07/01(土) 14:46:03
ありがとうございます。上記のようにInternetConnectの関数を

hConn=InternetConnect(hOpen,        //Openハンドル
    UrlComponents.lpszHostName,        //サーバー名
    UrlComponents.nPort,            //ポート
    UrlComponents.lpszUserName,        //ユーザー名
    UrlComponents.lpszPassword,        //ユーザーパスワード
    INTERNET_SERVICE_HTTP,            //サービス
    0,
    0);
とすると先に進めるようになりました。

ただこの後に、また問題が出てきたのですが、この後の処理を以下のようにしました。
//---ここから
hInternet=HttpOpenRequest
(hConn,"GET",UrlComponents.lpszUrlPath,HTTP_VERSION,"",NULL,INTERNET_FLAG_SECURE
,0);

//作成したHTTP要求を発行
bRet=HttpSendRequest(hInternet,NULL,    0,NULL,0);
//----こきまで

この処理でHttpSendRequestが「不明な認証局の認証証」というエラーを返します。
自作のSSLサーバーなのでエラーが出るのは当然なのですがそれが回避できません。

試しにSSLで構築してある一般のサーバーにアクセスした場合は普通にページが取得でき
ました(IEで認証証をGETした後)。

不明な認証局を使う場合のフラッグの設定が悪いのか?と思っています。
もしくはHttpSendRequestに何かオプションを付ける必要があるのでしょうか?

田中さん 2006/07/03(月) 10:57:10
[[解決]]
すみません。
認証証の質問については文章を見直すとまとまっていないし、
最初のタイトルからは違った内容になっているので
改めて別の質問として発言を立てます。
一旦この質問は解決済みとします。

まりのちゃん 2006/07/03(月) 11:24:53
新スレがたたないようなので
一応私が実装したものを参考に書いておきます。

>この処理でHttpSendRequestが「不明な認証局の認証証」というエラーを返します。
>自作のSSLサーバーなのでエラーが出るのは当然なのですがそれが回避できません。

正式な認証サーバでないとHttpOpenRequestでは失敗するようです。
なので下記ロジックのようにHttpOpenRequest後にエラーを無視させる
フラグをsetoptionで設定します。
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
dwFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
dwFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
dwFlags |= SECURITY_FLAG_IGNORE_REDIRECT_TO_HTTP;
dwFlags |= SECURITY_FLAG_IGNORE_REDIRECT_TO_HTTPS;
これでHttpSendRequestがいけると思います。

あとHttpSendRequestなんですが、
ヘッダ"Content-Type: application/x-www-form-urlencoded"
を設定しないとリクエストが飛びません。
なので以下を参考にしてみてください。



hHttpRequest = HttpOpenRequest(
            hHttpSession,
            "POST",
            requestFile,
            NULL,
            NULL,
            NULL,
            INTERNET_FLAG_SECURE,
            0);
if( hHttpRequest == NULL)
{
    InternetCloseHandle(hHttpSession);
    InternetCloseHandle(hInternet);
    return false;
}

DWORD dwError = 0;
DWORD dwFlags;
DWORD dwBuffLen = sizeof(dwFlags);

ret = InternetQueryOption (hHttpRequest, INTERNET_OPTION_SECURITY_FLAGS,
                                 (LPVOID)&dwFlags, &dwBuffLen);
if(ret == FALSE)
{
    InternetCloseHandle(hHttpSession);
    InternetCloseHandle(hInternet);
    return false;
}

dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
dwFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
dwFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
dwFlags |= SECURITY_FLAG_IGNORE_REDIRECT_TO_HTTP;
dwFlags |= SECURITY_FLAG_IGNORE_REDIRECT_TO_HTTPS;

ret = InternetSetOption (hHttpRequest, INTERNET_OPTION_SECURITY_FLAGS,
                            &dwFlags, 
sizeof (dwFlags) );
if(ret == FALSE)
{
    InternetCloseHandle(hHttpRequest);
    InternetCloseHandle(hHttpSession);
    InternetCloseHandle(hInternet);
    return false;
}

CString sHeader;
CString sOption;
char Opt[512];

// ヘッダー生成
sHeader = "Content-Type: application/x-www-form-urlencoded";

if(!HttpSendRequest(hHttpRequest, sHeader, sHeader.GetLength(), 
(LPVOID)"&TEST=777", sizeof("&TEST=777")))
{
    InternetCloseHandle(hHttpRequest);
    InternetCloseHandle(hHttpSession);
    InternetCloseHandle(hInternet);
    return false;
}

田中さん 2006/07/03(月) 13:21:08
まりのちゃんさんありがとうございます
コードを参考にして以下のようにしたら解決しました。
まりのちゃんさんのコードとの違いはリダイレクトの処理とPOSTの処理です。
エラー処理はGoto文で処理してるので省略させて頂いてます。

//---コードここから

    //リクエスト作成
    hInternet=HttpOpenRequest
(hConn,"GET",UrlComponents.lpszUrlPath,HTTP_VERSION,"",NULL,INTERNET_FLAG_SECURE
,0);    
    if(!hInternet){
        エラー処理・・・
    }

    //オプションを設定する
    DWORD dwFlags;
    DWORD dwFlagsLen=sizeof(dwFlags);
    bRet=InternetQueryOption(hInternet,    INTERNET_OPTION_SECURITY_FLAGS,
    (LPVOID)&dwFlags, &dwFlagsLen);
    if(!bRet){
        エラー処理・・・
    }
    dwFlags|=SECURITY_FLAG_IGNORE_UNKNOWN_CA;
    dwFlags|=SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
    dwFlags|=SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
    bRet=InternetSetOption(hInternet,INTERNET_OPTION_SECURITY_FLAGS, 
&dwFlags, sizeof(dwFlags));
    if(!bRet){
        エラー処理・・・
    }

    //作成したHTTP要求を発行
    bRet=HttpSendRequest(hInternet,    NULL,0,NULL,0);
    if(bRet==FALSE){
        エラー処理・・・
    }

//---ここまで

ありがとうございました。

古いスレッドには追加発言できません。