Wininet SSL客户端验证奇怪性

时间:2012-03-25 15:21:25

标签: delphi authentication ssl certificate wininet

我有一个爬虫,用于HTTP切换到Wininet。

这通常效果很好,但对于网站我会收到错误 ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED

示例代码:

vErrorNone := HttpSendRequest(HttpOpen_Request, nil, 0, nil, 0);
if vErrorNone = False then
  begin
    vErrorID := GetLastError;
    if (vErrorID = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) then
      begin
        // this error
      end
    ;
  end
;

然后我尝试了一个实验:

      TmpFakePointer := nil;
      vErrorNone :=
        InternetErrorDlg(
          GetDesktopWindow()
          ,
          HttpOpen_Request
          ,
          ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED
          ,
          0
          or FLAGS_ERROR_UI_FILTER_FOR_ERRORS
          or FLAGS_ERROR_UI_FLAGS_GENERATE_DATA
          or FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS
          ,
          TmpFakePointer
        )
        = ERROR_SUCCESS
      ;
      if vErrorNone then
        begin
          vErrorNone := HttpSendRequest(HttpOpen_Request, nil, 0, nil, 0);
        end
      ;

然而,这里有两件事情很奇怪:

  • 1)没有对话框显示
  • 2)工作
  • 2.1)第二次HttpSendRequest
  • 没有错误
  • 2.2)返回有效数据

它发现上面的组合很奇怪而且与文档相反。由于用户没有选择任何证书,为什么这样做?如果Wininet在无法显示对话框时回退到匿名客户端身份验证证书(我假设我已经给它显示对话框的错误句柄了?虽然如果我明确地给它一个错误的句柄它会出错)是否有任何方法可以直接选择没有调用InternetErrorDlg?

1 个答案:

答案 0 :(得分:2)

您可以做两件事:

1:向用户显示证书错误,让他决定是否继续。
2:忽略您获得的任何证书错误。

我将告诉你如何做第二个选择:

当您收到ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED或任何其他证书错误时,您需要致电InternetSetOptions告诉wininet它应该忽略该错误并继续。之后,您必须重新发送请求。

function SetToIgnoreCerticateErrors(var aErrorMsg: string): Boolean;
var
  vDWFlags: DWord;
  vDWFlagsLen: DWord;
begin
  Result := False;
  try
    vDWFlagsLen := SizeOf(vDWFlags);
    if not InternetQueryOptionA(oRequestHandle, INTERNET_OPTION_SECURITY_FLAGS, @vDWFlags, vDWFlagsLen) then begin
      aErrorMsg := 'Internal error in SetToIgnoreCerticateErrors when trying to get wininet flags.' + GetWininetError;
      Exit;
    end;
    vDWFlags := vDWFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA or SECURITY_FLAG_IGNORE_CERT_DATE_INVALID or SECURITY_FLAG_IGNORE_CERT_CN_INVALID or SECURITY_FLAG_IGNORE_REVOCATION;
    if not InternetSetOptionA(oRequestHandle, INTERNET_OPTION_SECURITY_FLAGS, @vDWFlags, vDWFlagsLen) then begin
      aErrorMsg := 'Internal error in SetToIgnoreCerticateErrors when trying to set wininet INTERNET_OPTION_SECURITY_FLAGS flag .' + GetWininetError;
      Exit;
    end;
    Result := True;
  except
    on E: Exception do begin
      aErrorMsg := 'Unknown error in SetToIgnoreCerticateErrors.' + E.Message;
    end;
  end;
end;


vErrorNone := HttpSendRequest(HttpOpen_Request, nil, 0, nil, 0);
if vErrorNone = False then
  begin
    vErrorID := GetLastError;
    if (vErrorID = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) then
    begin
      //call SetToIgnoreCerticateErrors
      //re-send the request 
    end
  end
end;

我从wininet API中提取了SetToIgnoreCerticateErrors,但可能无法准确编译它。

这些是步骤:

1 - 得到错误
2 - 检查错误是否是证书错误
3 - 如果是证书错误,他们就像我一样调用InternetSetOption 4 - 重新发送请求。

我不知道如何实现第一个选项“向用户显示证书错误,让他决定是否继续。”因为我从来没有这样做过。

另外,请检查一下:How To Handle Invalid Certificate Authority Error with WinInet

我希望它可以帮到你。