23 de março de 2011

Problemas comuns ao consumir Web Services para NFe em C#

Ok, já venceu ou está prestes a vencer o prazo para que uma grande parte das empresas utilizem obrigatoriamente a NFe - Nota Fiscal Eletrônica. Mas, como o brasileiro no geral deixa tudo para a última hora, é bastante provável que este post seja útil pra muita gente. Aqui eu abordo alguns problemas comuns que aparecem quando se utiliza os Web Services necessários para implementar no Visual Studio a comunicação com o site da Receita Federal no âmbito da NFe. Estou usando Visual Studio 2010 com a versão 3.5 do .NET mas creio que a maioria das soluções apresentadas sejam aplicáveis também a outras versões.

Neste outro post, eu falei sobre como utilizar Web Services em um programa .NET. Usando as dicas contidas naquele post para adicionar os Web Services da Receita Federal a um projeto do Visual Studio é possível que você se depare com uma mensagem similar a esta:
The request failed with HTTP status 403: Forbidden.
Metadata contains a reference that cannot be resolved: 'endereço-do-web-service'.
The HTTP request was forbidden with client authentication scheme 'Anonymous'.

O servidor da Receita Federal está configurado para exigir a identificação de todos os usuários através de um certificado digital válido. O acesso através do Visual Studio para obter a descrição WSDL dos serviços não é diferente. O que a mensagem está dizendo é que você não informou um certificado que o autorize a acessar o site. No caso do Visual Studio, é feita automaticamente a seleção de um certificado digital que tenha sido inserido na área Personal do Certificate Store do Windows. Por isso, ter instalado tal certificado válido emitido por uma entidade autorizada (Correios, Serasa ou Certisign, por exemplo) é imprescindível já no momento de adicionar os Web Services ao projeto e não apenas para assinar os XMLs que serão enviados à Receita Federal.

Depois que você conseguiu importar os Web Services, você pode usá-los em seu projeto para enviar solicitações de uso de notas fiscais, dentre outros serviços disponíveis. Quando seu programa chama a função de um dos Web Services para enviar ou solicitar informações ao servidor da Receita, este erro é reportado pelo programa:
The HTTP request was forbidden with client authentication scheme 'Anonymous'.

É bastante parecida com a mensagem anterior mas, nesse ponto, você já deve ter instalado um certificado válido no Certificate Store. Então, a causa provável é que você não configurou a forma com que a mensagem SOAP (o XML de comunicação com o Web Service) será transportada via internet, usando o protocolo HTTPS. Quando você importa um Web Service, o Visual Studio automaticamente insere num arquivo chamado app.config toda a configuração associada ao serviço, incluindo aquelas relativas ao transporte das mensagens via HTTPS.

Há particularmente dois parâmetros neste arquivo cujos valores padrões diferem do exigido pelo servidor da Receita: authenticationScheme e requireClientCertificate. O primeiro indica como o usuário será validado, sendo que o valor padrão é "Anonymous" para indicar que nenhuma validação é esperada. Como eu disse antes, o servidor da Receita só libera o acesso se um certificado foi informado. Portanto, o valor desse parâmetro deve ser alterado para "Digest". O segundo parâmetro deve ter valor "true", confirmando que o servidor exige que sua aplicação envie os dados do certificado para garantir a autenticação. O quadro abaixo traz o trecho de um app.config como exemplo, mostrando como fica a configuração para o serviço de consulta de uma nota fiscal:
<binding name="NfeConsulta2Soap12">
...
<httpsTransport authenticationScheme="Digest" requireClientCertificate="true" ... />
</binding>

Por uma questão de clareza, foram omitidas outras configurações que aparecem neste mesmo contexto. Lembre-se ainda que as configurações são feitas por Web Service, isto é, cada serviço da Receita que você for suportar tem que ser configurado neste arquivo da mesma forma que foi feito no exemplo.

Após realizar as modificações do app.config citadas, você ainda pode ser brindado com o erro abaixo:
The client certificate is not provided. Specify a client certificate in ClientCredentials.

Mais uma vez, a culpa é a falta do certificado digital. Nós declaramos que o serviço exige que especifiquemos um certificado. Mas, ao contrário do ambiente do Visual Studio, um Web Service no seu programa não é capaz de detectar automaticamente qual certificado deve ser usado. Por isso, devemos atribuí-lo manualmente antes de fazer a chamada à função do Web Service:
NfeConsulta2Soap12Client lServ;

lServ = new NfeConsulta2Soap12Client ("NfeConsulta2Soap12", _UrlWebservice);
lServ.ClientCredentials.ClientCertificate.Certificate = GetCertificado;

XmlNode status = lServ.nfeConsultaNF2 (ref lCabec, XmlDoc);

O post neste link mostra como navegar no Certificate Store para encontrar um certificado específico. É o que faz a minha função GetCertificado, usada no código de exemplo acima. A variável _UrlWebservice também é minha e contem o endereço (URL) real do Web Service que será acessado.

A arquitetura montada pela Receita Federal permite que você opere num ambiente de homologação (para testes) ou direto no de produção (onde as notas fiscais entram efetivamente na base de dados da Receita). Além disso, cada unidade da Federação disponibiliza seu próprio conjunto de endereços dos Web Services destinados aos contribuintes dessa unidade. É normal, portanto, que os programas para atender a NFe sejam construídos de forma flexível, permitindo receber os diferentes conjuntos possíveis. Obviamente, essa flexibilidade traz consigo o risco de que um endereço errado seja especificado. Se isto ocorrer, a seguinte mensagem será reportada:
Unable to handle request. The action 'http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsulta2/nfeConsultaNF2' was not recognized.

Isso significa apenas que você está tentando chamar uma função que não está implementada no endereço URL que você informou. A solução aqui é simples: verifique o endereço que está sendo passado à instância do Web Service - no exemplo anterior, seria descobrir o conteúdo da variável _UrlWebservice. Você pode até mesmo gerar um log imediatamente antes de fazer a chamada à função do Web Service, se for o caso.

Uma última recomendação. Se for criar um serviço Windows para monitorar as notas fiscais que entram para submetê-las à Receita, fique atento ao fato de que esse serviço pode ser executado com as credenciais de um usuário diferente daquele que está logado no Windows - normalmente, a conta Local System.

Neste cenário, o serviço não enxergará certificados digitais instalados na área Personal do Store de seu usuário Windows e você poderá receber uma porção das mensagens citadas neste post. Por isso, é recomendável que o certificado esteja instalado na área Personal do Store do Local Computer pois nessa área ele estará disponível a qualquer usuário do computador. Não se esqueça de restringir o acesso ao login desse computador por questões de segurança.


33 comentários :

Anônimo disse...

Valeu pela dica, nem percebi que estava chamando a função errada. Estava recebendo o erro:
Unable to handle request. The action 'http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsulta2/nfeConsultaNF2' was not recognized.

Anônimo disse...

Muito interessante seu post. No entanto, tenho uma duvida e fico grato se puder me responder:
em algumas pesquisas sobre WebServices, alguem levantou um topico que a receita federal disponibilizava um metodo que retornava o XML quando se consultava uma NFe no site. Sei que esta duvida não esta bem no contexto deste tópico, mas gostaria de saber se o Sr. sabe da existencia deste método, pois quero importar o xml direto do site da receita para minha aplicacão e com um metodo que retornasse o xml pelo WS ficaria mais facil...
Grato pela atenção!

Luís Gustavo Fabbro disse...

Desconheço a existência desse tipo de serviço ... Oficialmente, a lista dos Web Services disponíveis é a que se está no endereço http://hom.nfe.fazenda.gov.br/portal/webServices.aspx?tipoConteudo=Wak0FwB7dKs= (ambiente de homologação).

Explicações sobre o funcionamento de cada um deles - incluindo as informações que eles retornam - podem ser encontradas no Manual de Integração distribuido pela Receita Federal e acessível neste link.

Anônimo disse...

Olha nem sei como agradecer, meu ajudou muito essa informação, obrigado mesmo

Sérgio Maria disse...

O seu post é muito bom. No entanto, continuo com problemas a configurar a o serviço..
Na versão 4.0 é um pouco diferente...






e em código estou a associar o certificado à referência para o web Service... mesmo assim aparece a mensagem
"The HTTP request was forbidden with client authentication scheme 'Anonymous'."

Agradeço desde já se tiver alguma dica que possa ajudar. Obrigado

Luís Gustavo Fabbro disse...

Sérgio

Recompilei minha aplicação com target na versão 4 do .NET e ainda assim funcionou o acesso ao Web Service.

O seu problema acontece já no desenvolvimento da aplicação ou só depois que a pôs em produção ? Em qualquer das situações, confirme que o arquivo .config modificado de acordo com o post está na mesma pasta do executável.

Certifique-se também que alterou o .Config da aplicação e não diretamente o que está na pasta release e/ou debug. Esses últimos são atualizados automaticamente pelo VS, o que sobreporia quaisquer alterações ...

[]

Sérgio Maria disse...

Obrigado pela resposta Luís,

Entretanto consegui resolver o problema. A configuração exigia outras definições adicionais porque estou a aceder ao serviço de NFS-e.
Obrigado uma vez mais

Henrique sergio disse...

The impersonation level 'Identification' was specified, yet HTTP Digest authentication can only support 'Impersonation' level when used with an explicit credential.

heringer disse...

Se você estiver usando um certificado válido e mesmo assim receber a mensagem de "403: Forbidden" na hora de usar o webservice, verifique se tem algum outro certificado instalado que esteja vencido. Se tiver, remova-o e tente novamente.

Daniela disse...

Bom Dia! Alguem pode me conceder esse codigo para consumir o nfeconsulta2 ?

Luís Gustavo Fabbro disse...

Daniel

Consumir o web service no VS é bastante simples. Depois que você importou as definições do serviço (como mostrado em Usando um Web Service num projeto do Visual Studio), basta instanciar a classe NfeConsulta2Soap12Client gerada automaticamente pela importação e chamar a função nfeConsultaNF2.

[]s

Unknown disse...

Muito bom o post, porém não estou conseguindo configurar o serviço no Visual Studio. Estou usando a versão 2010 e .Net 4.5. Está dando forbidden porém já instalei os certificados na pasta pessoal. Alguém tem alguma ideia do que possa ser?

Luís Gustavo Fabbro disse...

Nathalia

Usando o mesmo certificado você consegue acessar o endereço do Web Service no Internet Explorer (ou outro navegador que você use) ? Pode haver algum problema com o certificado (falta da chave privada, por exemplo) ou com a sua infraestrutura de rede (um firewall, por exemlo).

[]s

GC disse...

Sergio, tambem estou com o mesmo problema. Como conseguiu resolver? Quais as definicoes adicionais necessarias? Gostaria muito se me ajudasse nisso.

Obrigado.

Unknown disse...


Eu não estava conseguindo add os web references. Desinstalei todos os certificados e deixei somente o certificado necessário e funcionou. vlw heringer..

Unknown disse...

Cara você é um gênio, deu certo

Anônimo disse...

Tem alguns web services que não tem como adicionar a referência, o visual studio não permite, por exemplo a de João Pessoa PB. Sabes se tem alguma forma de consumir esse webservice através do wsdl fornecido?

Luís Gustavo Fabbro disse...

Se você tiver o arquivo WSDL, pode informar o caminho dele no seu disco local para fazer a carga. Mas, você deverá assegurar a validade do endereço acessado, informa manualmente o endereço real do serviço. De qualquer forma, será exigido um certificado digital válido para realização de testes.

[]s

Anônimo disse...

Tu tens um exemplo de um web service utilizando um arquivo local WSDL em C#?

[]s

Luís Gustavo Fabbro disse...

A diferença entre usar um WSDL remoto ou um local é apenas a forma de gerar os fontes para consumir o serviço. No WSDL local, informa-se o caminho do arquivo enquanto no remoto a informação necessária é a url onde o WSDL pode ser encontrado.

No mais, ao criar (com new) uma instância do serviço para consumi-lo, você deve passar ao construtor o endereço correto onde o serviço está disponível:

NfeConsulta2Soap12Client lServ;
lServ = new NfeConsulta2Soap12Client("NfeConsulta2Soap12", "https://homologacao.nfe.fazenda.sp.gov.br/nfeweb/services/NfeRecepcao2.asmx");

[]s

Anônimo disse...

Esse artigo salvou meu dia. Obrigado por dividir seu conhecimento!

Unknown disse...

Boa tarde, muito Obrigado pela ajuda, seu blog é muito bom.

estou com esse erro, algum pode me ajudar

The HTTP request was forbidden with client authentication scheme 'Digest'.

Luís Gustavo Fabbro disse...

Cleyton

Há algumas situações em que isso pode ocorrer. Verifique se o parâmetro requireClientCertificate foi corretamente ajustado para true. Verifique também se não existe um certificado vencido no Certificate Store do usuário ou no do computador.

Certas configurações de rede também afetam a comunicação. Neste caso, veja com o administrador de sua rede se há alguma regra barrando a comunicação. Um sniffer ou outra ferramenta similar pode ajudar a detectar o ponto em que a falha está ocorrendo.

[]s

Unknown disse...

Muito Obrigado Luís, graças a seu blog eu pude entender melhor como funciona a comunicação com o WS da SEFAZ. Eu mudei , já olhei tudo mas a mensagem: The HTTP request was forbidden with client authentication scheme 'Digest'. Ainda permanece, tenho quase certeza que o problema é no app.config.

o meu está assim:






Seguindo seu topico eu fiz isso, mas não sei se está correto, poderia me ajudar por favor. só falta essa parte para comunicar com o WS.

Muito Obrigado !

Luís Gustavo Fabbro disse...

Cleyton

O código que você colocou foi truncado pelo blogger; trechos XML devem ser formatados manualmente para o blogger aceitar....

De qqer forma, se vc já incluiu tb o requireClientCertificate , o problema deve mesmo estar em algum ponto da sua rede. Não conheço muito dessa configuração mas a mensagem pode estar barrando num servidor interno; verifique se a cadeia de certificação está atualizada em todos aqueles por onde a mensagem passa antes de seguir para internet (externamente à sua empresa).

Confirme também que seu programa não está obtendo um certificado cuja validade expirou.

[]s

Unknown disse...

Bom dia Luis Gustavo, não tive disponibilidade de agradecer a ajuda, estou bem avançado no desenvolvimento da minha aplicação MDFe, Muito Obrigado!

Unknown disse...

Senhores,

Boa noite,

Estou tentando consumir um webservice da Fazenda para fazer o download dos xmls de clientes que emitiram o manifesto em nome da minha empresa, para isso estou utilizando os webservices da SEFAZ, porem estou com dificuldades, é minha "primeira vez" com a fazenda e não sei exatamente como passar os parametros via c# então vou postar até onde cheguei e se alguem tiver uma ideia, por favor ajude.

protected void Page_Load(object sender, EventArgs e)
{

X509Certificate2Collection lcerts;
X509Store lStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);

// Abre o Store
lStore.Open(OpenFlags.ReadOnly);

// Lista os certificados
lcerts = lStore.Certificates;

foreach (X509Certificate2 cert in lcerts)
{
if (cert.HasPrivateKey && cert.NotAfter > DateTime.Now && cert.NotBefore < DateTime.Now)
{
MDFeConsultaSoap12Client iServ;
iServ = new MDFeConsultaSoap12Client();
iServ.ClientCredentials.ClientCertificate.Certificate = cert;


var mdfeCabecMsg = new mdfeCabecMsg()
{
cUF = "35",
versaoDados = "1.00"
};

var mdfeDadosMsg = new XmlDataDocument();

//Quando executo esta chamada, após autenticar ele executa e retorna um xml mal formado,
//O que esta faltando??
XmlNode status = iServ.mdfeConsultaMDF(ref mdfeCabecMsg, mdfeDadosMsg);


}
}
lStore.Close();


}

Luís Gustavo Fabbro disse...

Daniel

Não é suficiente instanciar um XmlDocument; você precisa montá-lo com a estrutura definida no manual da SEFAZ. A versão mais recente do manual pode ser encontrada no link https://www.fazenda.sp.gov.br/mdfe/MDFe_Nota_Tecnica_2013_0041.pdf no próprio site da SEFAZ. Procure no manual o método mdfeConsultaMDF e logo abaixo está descrito quais tags devem estar presentes no XML.

[]s

Neibala disse...

Daniel

Veja o que poderia ser, estou utilizando o Visual Studio 2010, com Windows Forms com vb.net, e até o momento não estou conseguindo fazer o add service reference, veja o que poderia ser ?
Veja o erro que esta dando :
There was an error downloading 'https://nfe.fazenda.sp.gov.br/nfeweb/services/nfestatusservico2.asmx'.
The request was aborted: Could not create SSL/TLS secure channel.
Metadata contains a reference that cannot be resolved: 'https://nfe.fazenda.sp.gov.br/nfeweb/services/nfestatusservico2.asmx'.
The HTTP request was forbidden with client authentication scheme 'Anonymous'.

E veja o meu app.config até o momento :


















Anônimo disse...

Bom dia
Sr.Luís Gustavo,

Estou com erro de forbidden, já mudei o app.config e tudo mais porém não sei onde mudar mais, tenho certificado está ok e valido.

Luís Gustavo Fabbro disse...

Bruno

Como você está usando a classe gerada pela importação do WSDL? Há uma versão do construtor dela que admite como parâmetro uma classe de configuração equivalente ao app.config. Nesse caso, o arquivo externo é ignorado e a configuração do httpsTransport com valor Digest tem que ser feita através dessa classe extra.

[]s

Anônimo disse...

Pela classe gerada estou só colocando os parâmetros
authenticationScheme="Digest" requireClientCertificate="true" como vi no tutorial, além de uma adaptação que fiz pois tenho 3 serviços web adicionados, posso te enviar por email o app.config, pois não consigo postar aqui

Luís Gustavo Fabbro disse...

Bruno

Tem como postar o código onde vc instancia sua classe e faz essa configuração? Talvez esteja faltando alguma outra propriedade para ser ajustada.

[]s

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.