Ir para conteúdo
  • Cadastre-se

galegoga

Membros
  • Total de ítens

    28
  • Registro em

  • Última visita

Posts postados por galegoga

  1. Seria bom que colocasse esse ajuste, recentemente tive que atualizar o ACBr e tive que modificar novamente a unit.

    Não consegui solucionar utilizando a versão que utiliza componentes do Windows. Ele gera diversos arquivos numa pasta do AppData ficando enorme.

    Usando o OpenSSL consegui resolver o problema que consumia muita memória, em uma hora de uso a aplicação consumia 500MB só de leitura de certificado e continuava a crescer até dar crash no aplicativo.

    • Obrigado 1
  2. Boa tarde!

    Acho que consegui algo.

    Consegui reproduzir o teste do inicio do post sem que ocorra vazamento de memória na leitura do certificado.

    Mas antes de prosseguir volto ao post anterior, onde é mencionado uma solução com o método sk_X509_pop_free, ele até existe no arquivo ssl_openssl.pas, mas é inócuo, já que o método não existe na DLL. "_SkX509PopFree := GetProcAddr(SSLUtilHandle, 'SK_X509_POP_FREE');" sempre retorna nulo.

    A solução que encontrei foi uma comparação dos arquivos ssl_openssl.pas e ACBrDFeOpenSSL.pas.

    Comparei os métodos TSSLOpenSSL.LoadPFX e TDFeOpenSSL.LerPFXInfo e peguei trechos de código de um e adicionei ao outro. Código comparados:

    Código da classe: ssl_openssl.TSSLOpenSSL

    function TSSLOpenSSL.LoadPFX(pfxdata: Ansistring): Boolean;
    var
      cert, pkey, ca: SslPtr;
      b: PBIO;
      p12: SslPtr;
    begin
      Result := False;
      b := BioNew(BioSMem);
      try
        BioWrite(b, pfxdata, Length(PfxData));
        p12 := d2iPKCS12bio(b, nil);
        if not Assigned(p12) then
          Exit;
        try
          cert := nil;
          pkey := nil;
          ca := nil;
          try {pf}
            if PKCS12parse(p12, FKeyPassword, pkey, cert, ca) > 0 then
              if SSLCTXusecertificate(Fctx, cert) > 0 then
                if SSLCTXusePrivateKey(Fctx, pkey) > 0 then
                  Result := True;
    
            //  Set Certificate Verification chain
            if Result and (ca <> nil) then
              SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca);
          {pf}
          finally
            EvpPkeyFree(pkey);
            X509free(cert);
            SkX509PopFree(ca,_X509Free); // for ca=nil a new STACK was allocated...
          end;
          {/pf}
        finally
          PKCS12free(p12);
        end;
      finally
        BioFreeAll(b);
      end;
    end;

     

    Código da classe ACBrDFeOpenSSL.TDFeOpenSSL:

    function TDFeOpenSSL.LerPFXInfo(const PFXData: Ansistring): Boolean;
    var
      ca, p12: Pointer;
      b: PBIO;
    begin
      Result := False;
      DestroyKey;  
    
      b := BioNew(BioSMem);
      try
        BioWrite(b, PFXData, Length(PFXData));
        p12 := d2iPKCS12bio(b, nil);
        if not Assigned(p12) then
          Exit;
    
        try
          DestroyCert;
          DestroyKey;
          ca := nil;
          if PKCS12parse(p12, FpDFeSSL.Senha, FPrivKey, FCert, ca) > 0 then
          begin
            if (FCert <> nil) then
            begin
              GetCertInfo( FCert );
              Result := True;
            end;
          end;
        finally
          PKCS12free(p12);
        end;
      finally
        BioFreeAll(b);
      end;
    end;

    Basicamente o que encontrei diferente foi o código, que passei para ACBrDFeOpenSSL :

            if SSLCTXusecertificate(Fctx, Fcert) > 0 then
              if SSLCTXusePrivateKey(Fctx, FPrivKey) > 0 then
                Result := True;
    
            //  Set Certificate Verification chain
            if Result and (ca <> nil) then
              SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca);

    A modificação Final ficou assim:

      TDFeOpenSSL = class(TDFeSSLCryptClass)
      private
        ....
        Fctx: PSSL_CTX; // Adicionado
      proteced
        ...    
        procedure FreeContext; // Adicionado
      end;
    
    constructor TDFeOpenSSL.Create(ADFeSSL: TDFeSSL);
    begin
      inherited Create(ADFeSSL);
      ...  
      Fctx := nil;  // Adicionado
    end;
    
    destructor TDFeOpenSSL.Destroy;
    begin
      ...
      FreeContext; // Adicionado
      inherited Destroy;
    end;
    
    procedure TDFeOpenSSL.FreeContext; // Adicionado
    begin
      if assigned (Fctx) then
      begin
        SslCtxFree(Fctx);
        Fctx := nil;
        ErrRemoveState(0);
      end;
    end;
    
    function TDFeOpenSSL.LerPFXInfo(const PFXData: Ansistring): Boolean;
    
      procedure SetContex; // Adicionado
      begin
        FreeContext;
        Fctx := nil;
        case FpDFeSSL.SSLType of
          LT_SSLv2:
            Fctx := SslCtxNew(SslMethodV2);
          LT_SSLv3:
            Fctx := SslCtxNew(SslMethodV3);
          LT_TLSv1:
            Fctx := SslCtxNew(SslMethodTLSV1);
          LT_TLSv1_1:
            Fctx := SslCtxNew(SslMethodTLSV1_1);
          LT_TLSv1_2:
            Fctx := SslCtxNew(SslMethodTLSV1_2);
          LT_TLSv1_3:
            Fctx := SslCtxNew(SslMethodTLSV1_3);
          LT_all:
            begin
              //try new call for OpenSSL 1.1.0 first
              Fctx := SslCtxNew(SslTLSMethod);
              if Fctx=nil then
                //callback to previous versions
                Fctx := SslCtxNew(SslMethodV23);
            end;
        else
          Exit;
        end;
      end;
    
    var
      ca, p12: Pointer;
      b: PBIO;
    begin
      Result := False;
      DestroyKey;
      SetContex; // Adicionado
    
      b := BioNew(BioSMem);
      try
        BioWrite(b, PFXData, Length(PFXData));
        p12 := d2iPKCS12bio(b, nil);
        if not Assigned(p12) then
          Exit;
    
        try
          DestroyCert;
          DestroyKey;
          ca := nil;
          if PKCS12parse(p12, FpDFeSSL.Senha, FPrivKey, FCert, ca) > 0 then
          begin
            // Adicionado Inicio =================================================
            if SSLCTXusecertificate(Fctx, Fcert) > 0 then
              if SSLCTXusePrivateKey(Fctx, FPrivKey) > 0 then
                Result := True;
    
            //  Set Certificate Verification chain
            if Result and (ca <> nil) then
              SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca);
            // Adicionado Fim ====================================================
    
            if (FCert <> nil) then
            begin
              GetCertInfo( FCert );
              Result := True;
            end;
          end;
        finally
          PKCS12free(p12);
        end;
      finally
        BioFreeAll(b);
      end;
    end;

    Depois dessa modificação o memoryleak parou de acontecer. Não sei o que esses métodos fazem, mas me parece que ele cria um vinculação da Variável "ca" com Fctx  e quando o método FreeContex finaliza Fctx ele também deve estar eliminando os objetos apontados por "ca". Li na documentação de PKCS12parse que quando é passado "ca" como null ele cria um objeto internamente. Talvez seja isso.

     

    • Curtir 2
  3. Acho que encontrei alguma coisa. O problema está na chamada "PKCS12parse" dentro do método TDFeOpenSSL.LerPFXInfo.

    Efetuei uma pesquisa e encontrei um link que menciona memoryleak nesse método, não exatamente no método, mas a falta de execução de um método para Limpar o conteúdo.  A solução é data por Stephen Henson, veja o problema relatado com a solução:

    in order to try and prove that the memory leak i am seeing
    in PKCS12_parse() is not specific to my embedded system,
    i compiled the following using OpenSSL 0.9.6 on solaris
    void parsetest(BIO *databio)
    {
    EVP_PKEY *Pkey=NULL;
    X509 *Cert=NULL;
    STACK_OF(X509) *Ca=NULL;
    PKCS12 *PK12=NULL;
    PK12 = d2i_PKCS12_bio(databio, NULL);
    PKCS12_parse(PK12, NULL, &Pkey, &Cert, &Ca);
    PKCS12_free(PK12);
    if (Pkey)
    EVP_PKEY_free(Pkey);
    if (Cert)
    X509_free(Cert);
    if (Ca)
    sk_X509_free(Ca);

    The above line is the problem, it just frees up the
    STACK, not its contents. It should be:

    sk_X509_pop_free(Ca, X509_free);

    Steve.

    Link oficial do problema relatado.

    Ele menciona que o método sk_X509_pop_free deve ser executado para limpar o conteúdo e não somente o STACK(não sei o que é).

    Tentei localizar esse método na Unit OpenSSLExt, mas não encontrei. Mas no arquivo ssl_openssl_lib.pas dentro da pasta "ACBr\Fontes\Terceiros\synalist" eu encontrei.

    Tentei transpo-lo para o arquivo OpenSSLExt para utiliza-lo mas não consegui. Existem algumas diferenças entre os arquivos e me falta conhecimento para código de mais baixo nível, além de não conhecer nada sobre a documentação do OpenSSL.

    Se alguém puder ajudar eu agradeço.

     

     

     

  4. 46 minutos atrás, Juliomar Marchetti disse:

    volto a insistir não crie o arquivo físico PFX e sim carrege do banco direto ao componente

    Juliomar, o arquivo, na aplicação real, recebe do banco de dados. O arquivo encontra-se  em base64 armazenado na variavel FDadosPFXBase64, converto ele para para um StreamByte depois converto para Ansi para passar ao ACBr. Não sei se estou fazendo algo errado mas se estiver aceito sugestões, veja código abaixo da aplicação real:

    function TDistribuicaoEngineDFeModel.GetDadosPFXToAnsi: AnsiString;
    var
      vMemory: TMemoryStream;
      vOut:    TBytesStream;
      vBytes:  TArray<Byte>;
    begin
      Result  := '';
      if (FDadosPFXBase64.Trim.IsEmpty) then begin
        Exit;
      end;
      vMemory := TStringStream.Create(FDadosPFXBase64);
      vOut    := TBytesStream.Create;
      try
        TNetEncoding.Base64.Decode( vMemory, vOut );
        vBytes := vOut.Bytes;
        SetString(Result, PAnsiChar(@vBytes[0]), Length(vBytes));
      finally
        vOut.Free;
        vMemory.Free;
      end;
    end;

    Agora eu não entendi seu posicionamento, ler um arquivo físico do disco é problema ? ao meu ver deveria ser a mesma coisa.

    O código do início do post, lendo um arquivo físico do disco, é o código de teste que criei para constatar onde estava o problema. É um código de teste.

    Agora, vou fazer um outro teste, vi que há uma atualização do openSSL "3.1.3" com dll nomeada libcrypto-3..., meus teste foram com a dll  versão "1.1.1.10" libcrypto-1_1-... Vou atualizar meu acbr e fazer novos teste. Reporto aqui o resultado.

  5. Agora, Renato Rubinho disse:

    Bom dia,

    Caso tenha alguma sugestão de correção, por favor anexe o fonte corrigido, identifique como podemos simular o problema para validação do ajuste.

    Bom dia, Renato!

    O vazamento pode ser reproduzido com o exemplo de código no início do post. No exemplo acima está cryWinCrypt, mas o teste que fiz agora foi com cryOpenSSL.

    Fiz um Loop de 1000. Mas o problema ocorre somente com alguns certificados.

    Só para ressaltar, meu teste foi com a ultima versão da dll.

     

  6. Realmente, acabei de concluir que a classe TDFeOpenSSL, localizada na unit ACBrDFeOpenSSL, está vazando memória.

    Custei a descobrir devido ao fato que o despejo ocorre com alguns certificados e outros não.

    Porém o defeito é silencioso, não retornando nenhuma exceção, além do mais o certificado é lido corretamente.

    Vou trabalhar aqui para tentar descobrir o que se trata.

    se alguém souber de alguma coisa....

     

  7. 14 horas atrás, Juliomar Marchetti disse:

    porque tu cria o arquivo para usar? porque não joga direto no memorystream e dai no componente?

    Não entendi. Porque eu crio TDFeSSL ou porque eu dou load no pfx no memoryStream para depois passar pro TDFeSSL ?

    Na rotina original eu pego o PFX através do banco de dados. E crio o TDFeSSL para obter as informações do pfx, no caso, quero saber se o certificado é da empresa e se está vencido antes de entrar na rotina.

     

  8. Boa tarde!

    Estou com um problema de vazamento de memória no componente ACBr.

    Mas não consegui localizar exatamente onde ocorre.

    O vazamento é relacionado ao certificado digital.

    Utilizo carregamento de PFX com a configuração cryWinCrypt, além do lixo na memória ele também cria diversos arquivos na pasta "..\AppData\Roaming\Microsoft\Crypto\RSA".  OBS: Se alguém souber uma forma de evitar a criação dos arquivos na pasta RSA ou a exclusão automática ao termino da execução seria de grande ajuda. Minha rotina é com milhares de empresas e com diversas ações, em um dia está estourando o disco.

    Mas vamos ao caso da MemoryLeak, no exemplo abaixo eu rodei um loop de 1000, nele é possível ver que a memória sobe. Fazendo esse teste com cryOpenSSL a memória permanece estável (OBS: Usando ACBr com cryOpenSSL em consultas(Distribuicao e consulta de status) o consumo da memória também sobe, mas não sei se é o acbr ou falha do meu código, ainda estou tentando localizar):

    unit ACBrDFeSSL;
    
      function LoadCertificado( const AFileName: String ): AnsiString;
      var
        vMemory: TBytesStream;
        vBytes:  TArray<Byte>;
      begin
        vMemory := TBytesStream.Create();
        try
          vMemory.LoadFromFile( AFileName );
          vBytes := vMemory.Bytes;
          SetString(Result, PAnsiChar(@vBytes[0]), Length(vBytes));
        finally
          vMemory.Free;
        end;
      end;
    
    procedure TForm2.MetodoTeste( ALoop: Integer );
    var
     vDFeSSL: TDFeSSL;
     vPFX:    Boolean;
     vCT:     Integer;
     vMetodo: TSSLCryptLib;
    begin
      vPFX := True;
      vMetodo := cryWinCrypt;
      for vCT := 1 to ALoop do begin
        vDFeSSL := TDFeSSL.Create;
        //  TSSLCryptLib = (cryNone, cryOpenSSL, cryCapicom, cryWinCrypt);
        try
          if vPFX then begin
            vDFeSSL.DadosPFX    := LoadCertificado(ExtractFilePath( Application.ExeName )+'certificado.pfx');
            vDFeSSL.Senha       := '123456';
          end else begin
            vDFeSSL.NumeroSerie := '';
          end;
          vDFeSSL.SSLCryptLib := vMetodo;
          vDFeSSL.CarregarCertificado;
        finally
          vDFeSSL.DescarregarCertificado;
          FreeAndNil( vDFeSSL );
        end;
      end;
    end;

     

  9. Boa tarde!

    Desde 14/11/2023 o serviço de distribuição MDFe "https://mdfe.svrs.rs.gov.br/ws/MDFeDistribuicaoDFe/MDFeDistribuicaoDFe.asmx" começou a apresentar a mensagem "Versao do arquivo XML não suportada".

    Anteriormente estava funciona normalmente.

    Não consegui identificar o problema, procurei alguma documentação que pudesse esclarecer ou mencionar alguma mudança, mas nada encontrei.

    Também verifiquei alguma mudança no ACBr(visto que minha versão é antiga), mas também não localizei nenhuma mudança também.

    Se alguém tiver alguma informação útil para me ajudar, eu agradeço.

    Segue os arquivos gerados em anexos.

    20231117174647-con-dist-dfe.xml 20231117174647-con-dist-dfe-soap.xml 20231117174647-dist-dfe.xml 20231117174647-dist-dfe-soap.xml

  10. Segue em anexo os arquivos modificados.

    Abaixo, resumo das modificações.

    Arquivo pcnReinfR5011.pas:

      // Adicionado classes:
      TRAquisCollection = class;
      TRAquisCollectionItem = class;  
    

    Arquivo pcnReinfRetConsulta.pas:

      // Adicionado classes:  
      TRAquisCollection = class;
      TRAquisCollectionItem = class;  
      
      // Adicionado campos
      TInfoTotalContrib = class(TObject)
      public
        property RAquis: TRAquisCollection read FRAquis;
      end;
      
      TRTomCollectionItem = class(TObject)
      public
        property cno: String read Fcno; 
      end;

     

    pcnReinfR5011.pas pcnReinfRetConsulta.pas

    • Curtir 1
  11. Nossa, me pegou de surpresa!

    Vou esperar até semana que vem para ver se alguém mais se manifesta.

    Caso eu venha a desenvolver, por qual caminho faço o envio das contribuições ?

    Digo isso porque uma certa vez fiz um ajuste em um arquivo do reinf e envie aqui pelo fórum, mas nunca foi versionado. Era coisa boba, mas deu problema quando atualizei o acbr, que sobrepôs os ajustes.

  12. 16 horas atrás, magistech disse:

    Boa tarde galegona.

    Faz o seguinte, pega o arquivo que desenvolvi e coloca no seu projeto.

    Nesse caminho da pasta ACBR     Fontes\ACBrDFe\ACBrReinf\PCNReinf

    Para chamar a função é esse código que implementei, praticamente é o mesma ideia que tem nos exemplos do projeto.

    PerApur := ano + '-' + mes;
    TipoEvento := 'R-2055';
    nrInscEstab := FcnpjEmpresa;
    nrInscEstab := retiraCaracter(seleciona2055cpfCnpj.Value);
    cnpjPrestador := FcnpjEmpresa;
    DataApur := '';

     if ACBrReinf1.ConsultaReciboEvento(PerApur, StrToTipoEvento(Ok, TipoEvento),
                                           nrInscEstab, cnpjPrestador,
                                           nrInscTomador, dtApur) then
      begin

      end;

    Bom dia!

    Obrigado, mas seu fonte ainda não foi versionado.

    Não consegui ter acesso ao fonte postado. Quando tento acessá-lo retorna a mensagem:

    This attachment is not available. It may have been removed or the person who shared it may not have permission to share it to this location.

    Estou esperando o versionamento, mas caso ele não conste terei de modificar por conta própria. Não queria fazer isso para não ter diferença com o ACBr.

    Vou aguardar até o dia 13. Se não for versionado, terei de modificar.

     

     

×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.