MFCアプリからHTTPSへ接続できないとき、エラーコードだけでTLSバージョン不一致とは断定できません。この記事では、WinHTTPでTLS 1.2を明示して接続を試し、DNS、プロキシ、証明書、Schannelのどこで失敗したかを切り分けます。
最初に見るエラーと確認先
| 表示された結果 | 最初に確認する場所 |
|---|---|
ERROR_INTERNET_CANNOT_CONNECT(12029) | 接続先、ポート、DNS、プロキシを確認します。WinInetの一般的な接続失敗であり、TLS不一致を特定する値ではありません。 |
ERROR_WINHTTP_NAME_NOT_RESOLVED | ホスト名とDNSを確認します。 |
ERROR_WINHTTP_SECURE_FAILURE(12175) | 証明書の期限、名前、発行元、失効確認に加え、Schannel設定と共通して許可するTLSバージョンを調べます。 |
ERROR_WINHTTP_SECURE_CHANNEL_ERROR(12157) | Schannel設定と、クライアント・サーバーが共通して許可するTLSバージョンを確認します。 |
| HTTPステータスを取得できた | TLSハンドシェイクとHTTP応答受信までは完了しています。4xxや5xxはアプリケーション側の応答として別に扱います。 |
GetLastErrorは、WinHTTP関数が失敗を返した直後に取得します。成功後に取得した値は判定に使えません。
WinInetのセキュリティフラグではTLS 1.2を選べない
INTERNET_OPTION_SECURITY_FLAGSとSECURITY_FLAG_STRENGTH_STRONGは、TLS 1.2を有効化する設定ではありません。MicrosoftのWinINet仕様では、SECURITY_FLAG_STRENGTH_STRONGは128bit暗号が使われていることを示す照会結果で、InternetQueryOptionから返される値です。
そのため、次のような処理を追加してもTLS 1.2への切り替えにはなりません。
// TLS 1.2 の指定にはならない
DWORD flags = SECURITY_FLAG_STRENGTH_STRONG;
InternetSetOption(
hRequest,
INTERNET_OPTION_SECURITY_FLAGS,
&flags,
sizeof(flags));
WinInetは、デスクトップユーザーのInternet Options、プロキシ、資格情報などを利用する用途に向いています。TLSの許可状態はWindowsとSchannelの設定にも左右されるため、アプリ単位で許可プロトコルを比較したい場合はWinHTTPで切り分けます。
WinHTTPでTLS 1.2を指定して実通信する
WinHTTPでは、セッションハンドルへWINHTTP_OPTION_SECURE_PROTOCOLSを設定できます。次の部分でTLS 1.2だけを許可します。
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
HINTERNET session = WinHttpOpen(
L"MyApp/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (session == nullptr)
{
const DWORD error = GetLastError();
// error を記録して終了する
}
DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
if (!WinHttpSetOption(
session,
WINHTTP_OPTION_SECURE_PROTOCOLS,
&protocols,
sizeof(protocols)))
{
const DWORD error = GetLastError();
// error を記録し、session を閉じて終了する
}
設定後は、WinHttpConnect、WinHttpOpenRequest、WinHttpSendRequest、WinHttpReceiveResponseの順に呼びます。各戻り値を確認し、失敗直後のGetLastErrorと失敗したAPI名を一緒に記録してください。
応答を受信できたら、WinHttpQueryHeadersへWINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBERを渡してHTTPステータスを取得します。最後にrequest、connection、sessionの各ハンドルをWinHttpCloseHandleで確実に閉じます。
TLS 1.1のみの接続が失敗し、同じURLへのTLS 1.2のみの接続が成功するなら、接続先またはOS設定が旧TLSを許可していないことを確認できます。TLS 1.2も失敗する場合は、表示されたAPI名とエラーコードからDNS、プロキシ、証明書、Schannelへ確認範囲を広げます。

明示指定とOS既定値を使い分ける
WINHTTP_OPTION_SECURE_PROTOCOLSの既定値はWindowsのバージョンと更新状態で異なります。Microsoftの仕様では、Windows 11はTLS 1.2とTLS 1.3が既定です。Windows 10とWindows 8.1では既定値が異なるため、同じバイナリでも端末によって結果が変わる可能性があります。
TLS 1.2の明示指定は、古いアプリの互換性確認や障害の切り分けには有効です。一方、TLS 1.2だけへ固定すると、OSが将来利用できる新しいプロトコルへ自動で移行できません。対象OSの既定値で要件を満たせる新規実装では、OSの安全な既定値を使う設計も検討します。
アプリ側でTLS 1.2を指定しても、SchannelでTLS 1.2が無効なら上書きできません。Windows 8.1 / Windows Server 2012 R2以降ではTLS 1.2が既定で有効とされているため、レジストリ追加を最初の対処にはしません。端末のOS、更新状態、グループポリシー、現在のSchannel設定を確認してから、管理者の変更手順として扱います。
HTTPS接続失敗の確認順
- 失敗したAPI名と、その直後の
GetLastErrorを記録します。 - 同じURLへTLS 1.1のみ、TLS 1.2のみで接続し、HTTP応答の有無を比較します。
- TLS 1.2も失敗する場合は、DNS、プロキシ、証明書、Schannelの順に確認します。
WinInetの暗号強度フラグをTLS設定として使わず、WinHTTPの実通信結果とエラーコードを起点に切り分けることが重要です。
仕様の確認には、Microsoft LearnのWinINet Option Flags、WinHTTP Option Flags、WinHttpSendRequestを参照してください。
