所以我终于找到了方法。请注意,我必须更改 Soap.SOAPHTTPTrans.pas 并且您不应该更改标准 Delphi 文件。但我做到了,它解决了我的问题。首先,我写了一个函数来设置证书:
class procedure TMyCertificate.setCertificate(request:HINTERNET);
var
i: integer;
store: TStore;
c:ICertificate2;
cert: TCertificate;
certs: TCertificates;
ov: OleVariant;
CertContext : ICertContext;
PCertContext : PCCERT_CONTEXT;
begin
store := TStore.Create(pai);
store.Open(CAPICOM_CURRENT_USER_STORE, 'My', CAPICOM_STORE_OPEN_READ_ONLY);
certs := TCertificates.Create(pai);
certs.ConnectTo(store.Certificates as ICertificates2);
cert := TCertificate.Create(pai);
for i := 1 to certs.Count do
begin
ov := (certs.Item[i]);
c := IDispatch(ov) as ICertificate2;
cert.ConnectTo(IDispatch(ov) as ICertificate2);
if cert.HasPrivateKey And (cert.ValidFromDate <= Now) And
(cert.ValidToDate >= Now) then
begin
CertContext := c as ICertContext;
CertContext.Get_CertContext( Integer( PCertContext ) );
if InternetSetOption( request, INTERNET_OPTION_CLIENT_CERT_CONTEXT,
PCertContext, Sizeof( CERT_CONTEXT ) ) = False then
ShowMessage( 'Error setting certificate');
Break;
end;
end;
store.Close;
certs.Free;
store.Free;
end;
代码很丑,只是将证书设置为找到的第一个,但你明白了。这使用 CAPICOM 来获取证书。
然后,我在 SOAPHTTPTrans 中找到了以下函数:
function CallInternetErrorDlg: DWord;
var
P: Pointer;
begin
Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);
{ After selecting client certificate send request again,
Note: InternetErrorDlg always returns ERROR_SUCCESS when called with
ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED }
if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then
Result := ERROR_INTERNET_FORCE_RETRY;
end;
并改成:
function CallInternetErrorDlg: DWord;
var
P: Pointer;
begin
if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then begin
TMyCertificate.setCertificate(Request);
Result := ERROR_INTERNET_FORCE_RETRY;
end
else
Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);
end;
问题解决了。
我发现一个有趣的事实是,在 POST 之前,HTTPRIO 会发送一个 GET,它会在此 GET 操作中请求证书,因此在 onBeforePost 中设置证书是没有用的,因为它会在此 GET 之后执行。