Jump to content
  • Revista PROGRAMAR: Já está disponível a edição #60 da revista programar. Faz já o download aqui!

César

HTTPRIO certificado Sem estar instalado no Computador

Recommended Posts

César

Boas!
Já busquei na internet e aqui no blog, e só encontrei este comentário de 2013:
https://www.portugal-a-programar.pt/forums/topic/57734-utilizar-webservices-da-at/?page=108&tab=comments#comment-516188
Mas que usa o certificate store da clever internet suite.


Atualmente, com os componentes nativos do Delphi, existe uma forma de carregar o certificado Sem estar instalado no HTTPRIO?

Desde já, obrigado!!

Share this post


Link to post
Share on other sites
nunopicado

Com componentes nativos, pelo menos que eu saiba, não.
Mas porque também preciso de fazer isso, fiz há tempos um componente que serve para abrir directamente um certificado em formato PFX.

Se quiseres usar, fica à vontade.


Precisas do seguinte:

Precisas ainda de satisfazer a dependência do objecto IValue<T>, pertencente ao mesmo projecto Reusable Objects.

Basta meteres essas 4 units no path de compilação (se preferires podes clonar o projecto todo, há lá mais umas brincadeiras interessantes).

 

Para usar, basta que no evento OnBeforePost do HTTPWebNode associado ao HTTPRio coloques algo deste género:

procedure TForm1.HTTPReqResp1BeforePost(const HTTPReqResp: THTTPReqResp; Data: Pointer);
begin
  with TCertificate.New(Path_Para_o_PFX, Password_para_o_PFX) do
    if not (
      IsValid and
      InternetSetOption(
        Data,
        INTERNET_OPTION_CLIENT_CERT_CONTEXT,
        AsPCCert_Context,
        ContextSize
      )
    )
      then 
        raise Exception.Create(
          'Problema no certificado:' + sLineBreak +
          'Nº série: ' + SerialNumber + sLineBreak +
          'Valido até: ' + FormatDateTime('yyyy-mm-dd', NotAfter)
        );
end;

 

Edited by nunopicado
  • Vote 1

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
César
13 horas atrás, nunopicado disse:

Com componentes nativos, pelo menos que eu saiba, não.
Mas porque também preciso de fazer isso, fiz há tempos um componente que serve para abrir directamente um certificado em formato PFX.

Se quiseres usar, fica à vontade.


Precisas do seguinte:

Precisas ainda de satisfazer a dependência do objecto IValue<T>, pertencente ao mesmo projecto Reusable Objects.

Basta meteres essas 4 units no path de compilação (se preferires podes clonar o projecto todo, há lá mais umas brincadeiras interessantes).

 

Para usar, basta que no evento OnBeforePost do HTTPWebNode associado ao HTTPRio coloques algo deste género:


procedure TForm1.HTTPReqResp1BeforePost(const HTTPReqResp: THTTPReqResp; Data: Pointer);
begin
  with TCertificate.New(Path_Para_o_PFX, Password_para_o_PFX) do
    if not (
      IsValid and
      InternetSetOption(
        Data,
        INTERNET_OPTION_CLIENT_CERT_CONTEXT,
        AsPCCert_Context,
        ContextSize
      )
    )
      then 
        raise Exception.Create(
          'Problema no certificado:' + sLineBreak +
          'Nº série: ' + SerialNumber + sLineBreak +
          'Valido até: ' + FormatDateTime('yyyy-mm-dd', NotAfter)
        );
end;

 

Olá nunopicado, muito obrigado pela pronta resposta!

Estou implementando estas units no meu projeto, mas está ocorrendo um erro em "InternetSetOption" e "INTERNET_OPTION_CLIENT_CERT_CONTEXT",

Devo adicionar algumas uses ou alguma outra unit ao projeto?

Obrigado novamente!! =D

 

 

 

Share this post


Link to post
Share on other sites
nunopicado

Adiciona a unit WinINet ao teu projecto. ;)

  • Vote 1

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
César
Em 07/05/2019 às 11:35, nunopicado disse:

Adiciona a unit WinINet ao teu projecto. ;)

Muito obrigado novamente nunopicado.

O código funcionou e compilou certinho, mas não serviu para o meu problema.

Estou fazendo a chama do WebService dessa forma:

XmlRet := (DMNfeStatusServico.WSnfestatusservico As NFeStatusServico4Soap12).nfeStatusServicoNF(xData);

Enviando o xData e Recebendo o XML de retorno.


Nesse momento a tela de Segurança Do Windows pedindo para que seja informado o certificado do WindowsStore já aparece,
antes da chamada do evento "procedure TDMNfeStatusServico.WSnfestatusservicoHTTPWebNode1BeforePost( const HTTPReqResp: THTTPReqResp; Data: Pointer);"

A ideia é que essa tela não apareça, e que o sistema busque direto do disco, isso é possível?

Mas de qualquer maneira, muito obrigado pela atenção!

Share this post


Link to post
Share on other sites
nunopicado

Possível é com certeza, eu estou a usar esse componente para o mesmo efeito...
Estás a fazer a chamada ao webservice de raíz, ou a usar algum componente (tipo ACBr, por exemplo)?
 

É suposto que o OnBeforePost seja chamado antes da conexão (daí o nome), e ao passar no InternetSetOption o context do certificado, ele já não o vai pedir.
Dizes que no teu caso está a aparecer a janela antes de ser executado o OnBeforePost? Mas ele chega a ser executado depois, ou nem executa?

Assim sem conhecer o projecto, o meu palpite é que pode haver mais do que um HTTPWebNode envolvido e que não seja esse o correcto (mas saberás melhor que eu confirmar isso).

  • Vote 1

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
César
3 horas atrás, nunopicado disse:

Possível é com certeza, eu estou a usar esse componente para o mesmo efeito...
Estás a fazer a chamada ao webservice de raíz, ou a usar algum componente (tipo ACBr, por exemplo)?
 

É suposto que o OnBeforePost seja chamado antes da conexão (daí o nome), e ao passar no InternetSetOption o context do certificado, ele já não o vai pedir.
Dizes que no teu caso está a aparecer a janela antes de ser executado o OnBeforePost? Mas ele chega a ser executado depois, ou nem executa?

Assim sem conhecer o projecto, o meu palpite é que pode haver mais do que um HTTPWebNode envolvido e que não seja esse o correcto (mas saberás melhor que eu confirmar isso).

Olá novamente!
Estou fazendo a chamada de raíz mesmo, mas talvez eu esteja fazendo errado.

Vou descrever o processo.

Primeiro eu realizei a importação do arquivo WSDL do WebService:
http://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx?wsdl
E adicionei a unit ao meu projecto:

// ************************************************************************ //
// The types declared in this file were generated from data read from the
// WSDL File described below:
// WSDL     : https://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx?wsdl
//  >Import : https://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx?wsdl>0
// Encoding : utf-8
// Version  : 1.0
// (28/02/2019 10:33:03 - - $Rev: 69934 $)
// ************************************************************************ //

unit nfestatusservico4;

interface

uses Soap.InvokeRegistry, Soap.SOAPHTTPClient, System.Types, Soap.XSBuiltIns;

const
  IS_REF  = $0080;


type

  // ************************************************************************ //
  // The following types, referred to in the WSDL document are not being represented
  // in this file. They are either aliases[@] of other types represented or were referred
  // to but never[!] declared in the document. The types from the latter category
  // typically map to predefined/known XML or Embarcadero types; however, they could also 
  // indicate incorrect WSDL documents that failed to declare or import a schema type.
  // ************************************************************************ //
  // !:schema          - "http://www.w3.org/2001/XMLSchema"[Lit][Gbl]

  nfeResultMsg    = TXMLData;       { "http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4"[Lit][GblElmMxd] }
  nfeDadosMsg     = TXMLData;       { "http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4"[Lit][GblElmMxd] }

  // ************************************************************************ //
  // Namespace : http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4
  // soapAction: http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4/nfeStatusServicoNF
  // transport : http://schemas.xmlsoap.org/soap/http
  // style     : document
  // use       : literal
  // binding   : NFeStatusServico4Soap12
  // service   : NFeStatusServico4
  // port      : NFeStatusServico4Soap12
  // URL       : https://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx
  // ************************************************************************ //
  NFeStatusServico4Soap12 = interface(IInvokable)
  ['{ED6C990A-BD24-7EF9-BCBF-FEC9A7DE41F4}']

    // Cannot unwrap: 
    //     - Input element wrapper name does not match operation's name
    //     - The output part is not a complex type
    function  nfeStatusServicoNF(const nfeDadosMsg: nfeDadosMsg): nfeResultMsg; stdcall;
  end;

function GetNFeStatusServico4Soap12(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): NFeStatusServico4Soap12;


implementation
  uses System.SysUtils;

function GetNFeStatusServico4Soap12(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): NFeStatusServico4Soap12;
const
  defWSDL = 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx?wsdl';
  defURL  = 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx';
  defSvc  = 'NFeStatusServico4';
  defPrt  = 'NFeStatusServico4Soap12';
var
  RIO: THTTPRIO;
begin
  Result := nil;
  if (Addr = '') then
  begin
    if UseWSDL then
      Addr := defWSDL
    else
      Addr := defURL;
  end;
  if HTTPRIO = nil then
  begin
    RIO := THTTPRIO.Create(nil);
  end
  else
    RIO := HTTPRIO;
  try
    Result := (RIO as NFeStatusServico4Soap12);
    if UseWSDL then
    begin
      RIO.WSDLLocation := Addr;
      RIO.Service := defSvc;
      RIO.Port := defPrt;
    end else
      RIO.URL := Addr;
  finally
    if (Result = nil) and (HTTPRIO = nil) then
      RIO.Free;
  end;
end;


initialization
  { NFeStatusServico4Soap12 }
  InvRegistry.RegisterInterface(TypeInfo(NFeStatusServico4Soap12), 'http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4', 'utf-8');
  InvRegistry.RegisterDefaultSOAPAction(TypeInfo(NFeStatusServico4Soap12), 'http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4/nfeStatusServicoNF');
  InvRegistry.RegisterInvokeOptions(TypeInfo(NFeStatusServico4Soap12), ioDocument);
  InvRegistry.RegisterInvokeOptions(TypeInfo(NFeStatusServico4Soap12), ioLiteral);
  InvRegistry.RegisterInvokeOptions(TypeInfo(NFeStatusServico4Soap12), ioSOAP12);
  RemClassRegistry.RegisterXSInfo(TypeInfo(nfeResultMsg), 'http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4', 'nfeResultMsg');
  RemClassRegistry.RegisterXSInfo(TypeInfo(nfeDadosMsg), 'http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4', 'nfeDadosMsg');

end.

Feito isso, no componente THTTPRIO adicionei:

DMNfeStatusServico.WSnfestatusservico.WSDLLocation := http://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx?wsdl;
DMNfeStatusServico.WSnfestatusservico.Service      := 'NFeStatusServico4'
DMNfeStatusServico.WSnfestatusservico.Port         := 'NFeStatusServico4Soap1';

A chamada do WebService faço assim:

unit cNfeStatusServico;

interface

uses
 System.SysUtils, Soap.XSBuiltIns, Xml.XMLIntf;

function IniciaServico:Boolean;
var
XmlStatusServ:string;

implementation

uses
  Unfestatusservico, nfestatusservico4, cFuncoes;

function IniciaServico:Boolean;
var
xData,XmlRet:TXMLData;
StartNode:IXMLnode;
cStat,xMotivo:string;
begin
  cFuncoes.EscreveMemo('NFeStatusServico4: Iniciando');
  XmlStatusServ:='<consStatServ versao="'+'4.00'+'" xmlns="http://www.portalfiscal.inf.br/nfe">'+
                   '<tpAmb>'+'2'+'</tpAmb>'+
                   '<cUF>'+'35'+'</cUF>'+
                   '<xServ>'+'STATUS'+'</xServ>'+
                 '</consStatServ>';

  try
    xData := TXMLData.Create;
    xData.LoadFromXML('<base />');
	//AQUI O CERTIFICADO É CHAMADO.
    XmlRet := (DMNfeStatusServico.WSnfestatusservico As NFeStatusServico4Soap12).nfeStatusServicoNF(xData);
  except
    on E:Exception do
    begin
      cfuncoes.EscreveMemo('NFeStatusServico4: ERRO: '+E.Message);
      cfuncoes.EscreveMemo('Não foi possível estabelecer conexão com a Sefaz');
      cfuncoes.EscreveMemo(' - Verifique sua conexão com a internet');
      Result:=false;
      cfuncoes.desligarservicos;
      Exit;
    end;
  end;

  StartNode:=XmlRet.XMLNode;
  cStat    :=StartNode.ChildNodes['cStat'].Text; //Deve ser 107
  xMotivo  :=StartNode.ChildNodes['xMotivo'].Text; //Deve ser Serviço Ativo
  if cStat<>'107' then
  begin
    cfuncoes.EscreveMemo('Erro ao se conectar ao nfestatusservico4');
    cfuncoes.EscreveMemo(cStat);
    cfuncoes.EscreveMemo(xMotivo);
    Result:=false;
    cfuncoes.desligarservicos;
    Exit;
  end
  else
    cfuncoes.EscreveMemo(xMotivo);
    Result:=true;
end;

end.

Nessa linha "XmlRet := (DMNfeStatusServico.WSnfestatusservico As NFeStatusServico4Soap12).nfeStatusServicoNF(xData); //AQUI O CERTIFICADO É CHAMADO" A tela de Segurança do Windows pedindo  o certificado já aparece, e não chegou ainda em "procedure TDMNfeStatusServico.WSnfestatusservicoHTTPWebNode1BeforePost(
  const HTTPReqResp: THTTPReqResp; Data: Pointer);".

Estou buscando onde pode estar outro HTTPWebNode, mas ainda não encontrei.

O DataModule onde o componete THTTPRIO fica é:
 

unit Unfestatusservico;

interface

uses
  System.SysUtils, System.Classes, Soap.InvokeRegistry, Xml.xmldom, Xml.XMLIntf,
  Xml.XMLDoc, Soap.Rio, Soap.SOAPHTTPClient, Data.DB, Datasnap.DBClient,
  Soap.SOAPConn, Soap.SOAPDomConv, Soap.OPToSOAPDomConv, Soap.SOAPHTTPTrans,
  Datasnap.DSHTTP;

type
  TDMNfeStatusServico = class(TDataModule)
    WSnfestatusservico: THTTPRIO;
    procedure WSnfestatusservicoBeforeExecute(const MethodName: string;
      SOAPRequest: TStream);
    procedure WSnfestatusservicoHTTPWebNode1BeforePost(
      const HTTPReqResp: THTTPReqResp; Data: Pointer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DMNfeStatusServico: TDMNfeStatusServico;

implementation

uses
  cFuncoes, cNfeStatusServico, RO.TCertificate, Wininet;

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

procedure TDMNfeStatusServico.WSnfestatusservicoBeforeExecute(const MethodName: string; SOAPRequest: TStream);
var
sTmp: TStringList;
XmlResp:string;
begin
  XmlResp:='<?xml version="1.0" encoding="utf-8"?>'+
       '<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'+
       ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"'+
       ' xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">'+
         '<soap12:Body>'+
           '<nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4">'+
             cNfeStatusServico.XmlStatusServ+
           '</nfeDadosMsg>'+
         '</soap12:Body>'+
       '</soap12:Envelope>';

  sTmp:=TStringList.Create;
  sTmp.Text:=XmlResp;
  SOAPRequest.Position := 0;
  sTmp.Text:=cfuncoes.RetiraEnter(sTmp.Text);
  sTmp.SaveToStream(SOAPRequest);
  SOAPRequest.Position := 0;
  cfuncoes.EscreveMemo(sTmp.Text);
end;

procedure TDMNfeStatusServico.WSnfestatusservicoHTTPWebNode1BeforePost(
  const HTTPReqResp: THTTPReqResp; Data: Pointer);
begin
  with TCertificate.New('C:\Users\user\Desktop\PROJETOS\bin\Certificado123456.p12','123456') do
  if not (
    IsValid and
    InternetSetOption(
      Data,
      INTERNET_OPTION_CLIENT_CERT_CONTEXT,
      AsPCCert_Context,
      ContextSize
    )
  )
  then
  raise Exception.Create(
      'Problema no certificado:' + sLineBreak +
      'Nº série: ' + SerialNumber + sLineBreak +
      'Valido até: ' + FormatDateTime('yyyy-mm-dd', NotAfter)
  );
end;



end.

 

Esse é o processo para esse WebService.

Vou baixar o seu projeto completo para dar uma olhada, posso o fazer?

Muito obrigado novamente!

 

 



 

 

 

 

 

Share this post


Link to post
Share on other sites
nunopicado

Parece-me tudo em ordem...
Só não estou certo é a extensão p12 no certificado irá funcionar com o meu componente, só testei com pfx.
Mas o OnBeforePost teria de ser chamado na mesma.

Chegaste a confirmar se ele é chamado? Experimenta meter um ShowMessage no inicio do onbeforepost para ver se aparece antes ou depois da janela do certificado (ou então usa os breakpoints do sistema de debug)

 

Quanto ao projecto, fica à vontade... :)


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.