Jump to content

Utilizar Webservices da AT


cjulio
Go to solution Solved by thoga31,

Recommended Posts

Já agora, criei o CSR e enviei para a AT e eles devolveram-me a chave pública no ficheiro .CER. Criei o ficheiro .PFX e uso este par para substituir os ficheiros disponibilizados para testes.

No entanto, dá erro se utilizar os meus ficheiros! Será por o servidor ser de testes? É que não me parece. Pois o objectivo de enviar o pfx é o servidor poder descodificar o campo Nonce.

<?xml version="1.0" encoding="UTF-8"?>

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>8 </faultcode>
<faultstring>Rejected: | Codigo: 8 | Erro: Não foi possível decifrar o campo | Tentativas Restantes: -1</faultstring>
<detail>fews.gdcontfsimpostos</detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

Se alguém já tiver passado por isto, agradeço desde já se me poder ajudar.

Edited by apocsantos
geshi
Link to comment
Share on other sites

Boas pessoal,

Tenho estado a desenvolver o mesmo que voces mas em PHP e estou exactamente no mesmo ponto que o RaulLima.

Segundo o código que retornam --> "8", no PDF disponibilizados por eles, corresponde a "8 ‐ Cifra da chave pública inválida;", não consigo concluir se a encriptação que estou a fazer está de acordo com o que eles pedem no PDF.

Porque segundo a mensagem de erro que retornam ao fazer o request --> "8 Rejected: | Codigo: 8 | Erro: Não foi possível decifrar o campo | Tentativas Restantes: -1 fews.gdcontfsimpostos", parece que não estamos a encriptar da maneira correcta e eles ao receberem não conseguem decifrar o conteúdo...

Não sei se poderá ser isto ou não, mas n encontro outra razão lógica para isto.

Alguma ideia? Alguem conseguiu-o cifrar correctamente em PHP, que possa dar alguma ajuda? que funções usaram para criar o nonce?

Edited by rmartyn
Link to comment
Share on other sites

Boas,

Só tive mesmo hipótese de colar o teu código e mudar para as minhas credenciais e neste momento estou com o Erro 7 - Codificação Base64 inválida, mas vendo o xml parece tudo bem ....

Por acaso não estarás a colocar algum tipo de caracter a mais quando estás a criar o xml? Isso aconteceu-me quando tentei imitar aquele softwarezinho que a AT disponibilizou, e que na mensagem enviada para lá ia com aqueles " " para dar o enter. Eu penso que desde que não haja outro carater dentro das tags (pass e created) que não aqueles gerados pelo algoritmo, não deve haver problemas.

Depois diz como correu.

Tenho estado a desenvolver o mesmo que voces mas em PHP e estou exactamente no mesmo ponto que o RaulLima.

Segundo o código que retornam --> "8", no PDF disponibilizados por eles, corresponde a "8 ‐ Cifra da chave pública inválida;", não consigo concluir se a encriptação que estou a fazer está de acordo com o que eles pedem no PDF.

Porque segundo a mensagem de erro que retornam ao fazer o request --> "8 Rejected: | Codigo: 8 | Erro: Não foi possível decifrar o campo | Tentativas Restantes: -1 fews.gdcontfsimpostos", parece que não estamos a encriptar da maneira correcta e eles ao receberem não conseguem decifrar o conteúdo...

Não sei se poderá ser isto ou não, mas n encontro outra razão lógica para isto.

Alguma ideia? Alguem conseguiu-o cifrar correctamente em PHP, que possa dar alguma ajuda? que funções usaram para criar o nonce?

Eu penso desta forma: se já consigo enviar a mensagem, de maneira a que elas fiquem guardadas no portaldasfinancas com o login da empresa, então está tudo bem. Se mudamos a chave publica (que foi dada pela AT) e apenas temos que criar o ficheiro pfx, então o problema deverá estar na criação do mesmo.

Eu estive a visualizar a informação contida no pfx de testes e está bastante diferente daquele que eu gerei. Eu apenas usei a chave privada e o certificado que me mandaram para criar um pfx. Contudo, o pfx de testes parece-me ser um certificado em cadeia. Por assim dizer, eles têm 3 certificados associados. Não percebendo ainda muito disto, penso que faltará algo mais para criar o pfx correctamente!

Espero que cheguemos a alguma conclusão.

Cumps

Link to comment
Share on other sites

Apenas mais um pormenor que reparei: Se utilizar a chave publica da AT e o pfx criado por mim como certificado digital (criado partir da chave publica enviada pela AT em resposta ao envio do nosso CSR), funciona na mesma. O que será que isto significa? Posso usar um certificado digital qualquer? Não seria suposto que este certificado contivesse a chave privada para que o servidor pudesse descodificar o campo Nonce?

Percebo cada vez menos. Agora fica para amanha.

Link to comment
Share on other sites

Bom dia,

Neste momento, estou a obter o erro "base {System.SystemException} = {"The remote server returned an error: (500) Internal Server Error."}" && "Status=ProtocolError" tal como acontece a outros dos aqui presentes. Acredito que este erro seja resultante da estrutura inválida do XML do pedido:

string oRequest = string.Empty;
	oRequest += ("<?xml version='1.0' encoding='UTF-8'  standalone='no'?>");
	oRequest += ("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope\">");
	oRequest += "<SOAP-ENV:Header>";
	oRequest += "<wss:Security xmlns:wss=\"http://schemas.xmlsoap.org/ws/2002/12/secext\">";
	oRequest+="<wss:UsernameToken>";
	oRequest += "<wss:Username>XXXXX</wss:Username>";
	oRequest +="<wss:Password>" + encryptPass + "</wss:Password>";
		oRequest += "<wss:Nonce>"+rsaPublicKey+"</wss:Nonce>";
		oRequest += "<wss:Created>"+encryptDate+"</wss:Created>";
	oRequest+="</wss:UsernameToken>";
	oRequest+="</wss:Security>";
	oRequest += "</SOAP-ENV:Header>";

	oRequest += "<SOAP-ENV:Body>";
		oRequest += "<ns2:RegisterInvoiceElem xmlns:ns2=\"http://servicos.portaldasfinancas.gov.pt/faturas\">";
		oRequest+="<TaxRegistrationNumber>599999993</TaxRegistrationNumber>";
		oRequest += "<ns2:InvoiceNo>- /1</ns2:InvoiceNo>";
		oRequest+="<ns2:InvoiceDate>2012-12-12</ns2:InvoiceDate>";
		oRequest += "<ns2:InvoiceType>FT</ns2:InvoiceType>";
		oRequest+="<CustomerTaxID>299999998</CustomerTaxID>";
		oRequest+="<Line>";
			oRequest+="<ns2:DebitAmount>100</ns2:DebitAmount>";
			oRequest+="<ns2:Tax>";
				oRequest+="<ns2:TaxType>IVA</ns2:TaxType>";
				oRequest+="<ns2:TaxCountryRegion>PT</ns2:TaxCountryRegion>";
				oRequest+="<ns2:TaxPercentage>23</ns2:TaxPercentage>";
			oRequest+="</ns2:Tax>";
		oRequest+="</Line>";
		oRequest+="<DocumentTotals>";
			oRequest += "<ns2:TaxPayable>23</ns2:TaxPayable>";
			oRequest+="<ns2:NetTotal>100</ns2:NetTotal>";
			oRequest+="<ns2:GrossTotal>123</ns2:GrossTotal>";
		oRequest+="</DocumentTotals>";
[b]		[/b]	oRequest += "</ns2:RegisterInvoiceElem></SOAP-ENV:Body></SOAP-ENV:Envelope>";

no lugar de "SOAP-ENV" ja tentei com "S" e "soapenv". Alguem detecta algum erro?

Obrigado.

Link to comment
Share on other sites

Boas,

1º O que falas sobre o "SOAP-ENV" e o "S" não te vai mudar nada porque isso é apenas o namespace que a estrutura vai usar. E podes usar a variável que quiseres.

2º No campo Nonce reparo que tens a variável "rsaPublicKey" e queria deixar claro que neste campo, não é para colocar a chave pública mas sim a chave simetrica encriptada com a chave publica.

3º Falta-te a linha "<ns2:InvoiceStatus>N</ns2:InvoiceStatus>". Não dá para inserir sem isto (N=Normal; A=Anulada).

4º E por último, o erro que retorna, é o tipo de erro que retorna a todos sim, mas depois de obteres a excepção tens que ir pegar no response da exception e converte-la para mensagem. E aí vai ter a descrição dos erros propriamente dita.

Espero que ajude. Cumps.

  • Vote 1
Link to comment
Share on other sites

Obrigado pela dica Rui.

Peço desculpa pela ignorância, mas sou um júnior sem experiência em webservices a quem ainda por cima foi incumbida a tarefa, de resolver isto em 4 dias, sendo hoje o 3º!

A variável não é apenas muito sugestiva... :

string rsaPublicKey= RSAEncrypt(Convert.ToBase64String(symetricKey.Key), publicKey);



 AesCryptoServiceProvider symetricKey = new AesCryptoServiceProvider();
		symetricKey.GenerateKey();
		symetricKey.GenerateIV();
		symetricKey.Mode = CipherMode.ECB;
		symetricKey.Padding = PaddingMode.PKCS7;

eis o metodo de encriptação:

public string RSAEncrypt(string symetricKey, string publicKey)
	{			
		try
		{
			RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();		
			RSA.FromXmlString(publicKey);
			byte[] EncryptedStr = null;				
			EncryptedStr = RSA.Encrypt(Encoding.ASCII.GetBytes(symetricKey), false);				
			int i = 0;
			System.Text.StringBuilder s = new System.Text.StringBuilder();
			for (i = 0; i <= EncryptedStr.Length - 1; i++)
			{				  
				if (i != EncryptedStr.Length - 1)
				{
					s.Append(EncryptedStr[i] + " ");
				}
				else
				{
					s.Append(EncryptedStr[i]);
				}
			}
			return s.ToString();
		}
		catch (Exception err)
		{
			return string.Empty;
		}
	}

relativamente à mensagem gerada e a seguinte:

<?xml version="1.0" ?>

- <env:Envelope xmlns:env="[b]http://schemas.xmlsoap.org/soap/envelope/[/b]">


- <env:Body>


- <env:Fault>


<faultcode>env:Client</faultcode>

<faultstring>Internal Error</faultstring>
</env:Fault>
</env:Body>
</env:Envelope>

pelo que pesquisei deve-se ao facto de a mensagem estar incorrectamente formada ou conter informação incorrecta... Tendo em conta que supostamente, o webservice retornaria uma mensagem para cada uma das linhas que estivesse incorrecta, estará a mensagem mal formada, que depois de acrescentar a linha que indicaste, continuo sem saber porque... não encontras outro erro que salte a vista? Cumps.

Edited by apocsantos
geshi
Link to comment
Share on other sites

Boas joaoMoreira,

Estás a desenvolver em C#, penso eu. Repara no try catch que eu uso (mas em vb.net):

try {
// bla bla bla
// ocorre um erro e tal
} catch (WebException ex) {
if (ex.Status == WebExceptionStatus.ProtocolError) {
	WebResponse resp = ex.Response;
	StreamReader sr = new StreamReader(resp.GetResponseStream());
	return sr.ReadToEnd();
} else {
	return ex.Message;
}
}

Eu penso que assim já consegues visualizar o erro melhor.

Em relação à incriptação, aconcelho-te a pegar no código que pus anteiormente (página 1), convertes para C# e assim tens a certeza que funciona. Pelo menos, aquele é o meu código (tomar atenção à função que gera a chave simetrica, como tem que ser sempre diferente arranjei um método que usa a data do sistema, o nif da empresa e o utilizador das financas).

Depois comunica aí como correu.

Edited by RaulLima
Link to comment
Share on other sites

O teu catch imprime um resultado igual:

<?xml version="1.0" ?>

- <env:Envelope xmlns:env="[b]http://schemas.xmlsoap.org/soap/envelope/[/b]">


- <env:Body>


- <env:Fault>


<faultcode>env:Client</faultcode>

<faultstring>Internal Error</faultstring>
</env:Fault>
</env:Body>
</env:Envelope>

não me parece k seja problema da encriptação uma vez que supostamente o webservice contempla esse problema e enviaria um código de erro correspondente. gero também uma chave simétrica em cada chamada... De qualquer forma, vou experimentar a tua abordagem.

Link to comment
Share on other sites

Boas joaoMoreira,

Estás a desenvolver em C#, penso eu. Repara no try catch que eu uso (mas em vb.net):

try {
// bla bla bla
// ocorre um erro e tal
} catch (WebException ex) {
if (ex.Status == WebExceptionStatus.ProtocolError) {
	WebResponse resp = ex.Response;
	StreamReader sr = new StreamReader(resp.GetResponseStream());
	return sr.ReadToEnd();
} else {
	return ex.Message;
}
}

Eu penso que assim já consegues visualizar o erro melhor.

Em relação à incriptação, aconcelho-te a pegar no código que pus anteiormente (página 1), convertes para C# e assim tens a certeza que funciona. Pelo menos, aquele é o meu código (tomar atenção à função que gera a chave simetrica, como tem que ser sempre diferente arranjei um método que usa a data do sistema, o nif da empresa e o utilizador das financas).

Depois comunica aí como correu.

Mas RuiLima, quanto a esta parte aqui "como tem que ser sempre diferente arranjei um método que usa a data do sistema, o nif da empresa e o utilizador das financas", isto pode ser o que tu quiseres? basta que seja sempre diferente em cada pedido ao webservice? isto não ira ter nenhuma implicação quando eles tentam receber?

Link to comment
Share on other sites

Mas RuiLima, quanto a esta parte aqui "como tem que ser sempre diferente arranjei um método que usa a data do sistema, o nif da empresa e o utilizador das financas", isto pode ser o que tu quiseres? basta que seja sempre diferente em cada pedido ao webservice? isto não ira ter nenhuma implicação quando eles tentam receber?

A minha interpretação é a seguinte: a cada envio de documentos a chave simétrica tem que ser única, mas tem que ser tão única ao ponto de mais ninguém ter usado a mesma. Alias, existe um código de erro para o caso da chave simétrica já ter sido utilizada: "12 ? Chave simétrica repetida;".

A única coisa que não tenho a certeza é se eu posso utilizar uma chave simetrica igual a outro produtor de software numa determinada transação. Se assim for, basta utilizar a datahora mais um identificativo interno sobre aquele cliente. Mas se a chave simétrica for para uma base de dados global, em que basta haver uma já existente para não deixar utilizar, então se usarmos a datahora, e se no mesmo micro-segundo houver outro software a enviar um documento, um deles vai ser rejeitado.

Se tivermos que garantir que a chave nunca foi utilizada, é mais complicado. Eu utilizo 3 variáveis para gerar a chave: datahora, nif e subutilizador. Faço uns somatórios e tal e obtenho um número de 16 digitos. Eu espero que isto baste, mas sinceramente ainda não pensei a profundo nisto.

O teu catch imprime um resultado igual:

<?xml version="1.0" ?>

- <env:Envelope xmlns:env="[b]http://schemas.xmlsoap.org/soap/envelope/[/b]">


- <env:Body>


- <env:Fault>


<faultcode>env:Client</faultcode>

<faultstring>Internal Error</faultstring>
</env:Fault>
</env:Body>
</env:Envelope>

não me parece k seja problema da encriptação uma vez que supostamente o webservice contempla esse problema e enviaria um código de erro correspondente. gero também uma chave simétrica em cada chamada... De qualquer forma, vou experimentar a tua abordagem.

Se calhar o problema estará na criação do request? Sinceramente não estou a ver o que poderá ser. Eu cheguei a passar por isso, mas já foi há tanto tempo que não me lembro o porquê. Mas era algo simples.

Link to comment
Share on other sites

Estou com uma dificuldade a nível de certificados.

Utilizei o openssl para gerar o CSR – Certificate Signing Request.

Ao criar também foi gerado outro ficheiro (chave privada). Enviei para a AT pelo portaldasfinancas o CSR e eles responderam-me com um ficheiro tipo "-----BEGIN CERTIFICATE-----", semelhante àquele que forneceram para os testes (ChavePublicaAT.cer).

Mas não enviaram mais nada. Falta o ficheiro correspondente .PFX. O meu problema é aqui. Eu utilizo o openssl para gerar este ficheiro e usando os novos ficheiros (.pfx e .cer) o erro que me é devolvido é "Codigo: 8 | Erro: Não foi possível decifrar o campo". Contudo se utilizar os ficheiros de testes funciona. E também já detetei que se utilizar o meu .pfx e o de testes .cer também funcionar.

O que significa isto? Estou preso neste ponto. Fico a pensar que os novos certificados não são aceites no servidor de testes!!!

Link to comment
Share on other sites

Peço desculpa pelo aborrecimento..

tambem me parece que tenha a ver com o pedido uma vez que a resposta é igual à que tenho se aceder ao endereço via browser (request sem XML)

eis a estrutura do pedido/resposta:

           HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://servicos.portaldasfinancas.gov.pt:700/fews/faturas/");
           req.ClientCertificates.Add(certs[0]);
           req.Headers.Add("SOAPAction", "\"https://servicos.portaldasfinancas.gov.pt:700/fews/faturas/RegisterInvoice\"");
           req.ContentType = "text/xml; charset=\"utf-8\"";
           req.Accept = "text/xml";
           req.Method = "POST";

           using (Stream stm = req.GetRequestStream())
           {
               using (StreamWriter stmw = new StreamWriter(stm))
               {
                   stmw.Write(oRequest);
               }
           }        
           try
           {
               WebResponse response = req.GetResponse();
               StreamReader srd = new StreamReader(response.GetResponseStream());
               string resulXmlFromWebService = srd.ReadToEnd();
               IAsyncResult asyncResult = req.BeginGetResponse(null, null);
               asyncResult.AsyncWaitHandle.WaitOne();
               string soapResult;
               using (WebResponse webResponse = req.EndGetResponse(asyncResult))
               {
                   using (StreamReader rd = new StreamReader(webResponse.GetResponseStream()))
                   {
                       soapResult = rd.ReadToEnd();
                   }
               }
               string result = soapResult;
           }

identificam alguma coisa?

Edited by thoga31
GeSHi
Link to comment
Share on other sites

joaoMoreira,

Infelizmente o código é confuso. Por isso o melhor mesmo é dar-te o que uso e experimenta 🙂

Dim EnderecoWebService As String = "https://servicos.portaldasfinancas.gov.pt:700/fews/faturas"
Dim CaminhoCertificado As String = "C:\Caminho_Do_Certificado\TestesWebservices.pfx"
Dim SenhaCertificado as String = "TESTEwebservice"
Dim oRequest As String = "<xml ..." ' Esta é a variável da mensagem a enviar
Try
Dim request As HttpWebRequest = HttpWebRequest.Create(EnderecoWebService)
request.Headers.Add("SOAPAction", "http://servicos.portaldasfinancas.gov.pt/faturas/RegisterInvoice")
Dim cert As New X509Certificate2
cert.Import(CaminhoCertificado, SenhaCertificado, X509KeyStorageFlags.DefaultKeySet)
request.ClientCertificates.Add(cert)
request.Method = "POST"
request.ContentType = "text/xml; charset=utf-8"
request.Accept = "text/xml"
Dim postData As String = oRequest
Dim byteArray As Byte() = Encoding.UTF8.GetBytes(postData)
request.ContentLength = byteArray.Length
Dim dataStream As Stream = request.GetRequestStream()
dataStream.Write(byteArray, 0, byteArray.Length)
dataStream.Close()
Dim response As HttpWebResponse = request.GetResponse()
dataStream = response.GetResponseStream()
Dim reader As New StreamReader(dataStream)
Dim responseFromServer As String = reader.ReadToEnd()
reader.Close()
dataStream.Close()
response.Close()
Return responseFromServer
Catch ex As WebException
If ex.Status = WebExceptionStatus.ProtocolError Then
Dim resp As WebResponse = ex.Response
Dim sr As StreamReader = New StreamReader(resp.GetResponseStream())
Return sr.ReadToEnd()
Else
Return ex.Message
End If
End Try

string EnderecoWebService = "https://servicos.portaldasfinancas.gov.pt:700/fews/faturas";
string CaminhoCertificado = "C:\\Caminho_Do_Certificado\\TestesWebservices.pfx";
string SenhaCertificado = "TESTEwebservice";
// Esta é a variável da mensagem a enviar
string oRequest = "<xml ...";
try {
HttpWebRequest request = HttpWebRequest.Create(EnderecoWebService);
request.Headers.Add("SOAPAction", "http://servicos.portaldasfinancas.gov.pt/faturas/RegisterInvoice");
X509Certificate2 cert = new X509Certificate2();
cert.Import(CaminhoCertificado, SenhaCertificado, X509KeyStorageFlags.DefaultKeySet);
request.ClientCertificates.Add(cert);
request.Method = "POST";
request.ContentType = "text/xml; charset=utf-8";
request.Accept = "text/xml";
string postData = oRequest;
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
HttpWebResponse response = request.GetResponse();
dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
reader.Close();
dataStream.Close();
response.Close();
return responseFromServer;
} catch (WebException ex) {
if (ex.Status == WebExceptionStatus.ProtocolError) {
WebResponse resp = ex.Response;
StreamReader sr = new StreamReader(resp.GetResponseStream());
return sr.ReadToEnd();
} else {
return ex.Message;
}
}
Edited by RaulLima
  • Vote 1
Link to comment
Share on other sites

És o maior 🙂

Obrigado!

Agora retorna o erro 8.. se entretanto resolver publico aqui a solução.

Cumps.

Estás a usar os ficheiros cedidos pela AT (os de testes)? Ou estás a usar os teus próprios certificados?

É que se estiveres a usar os teus, então chegaste ao mesmo ponto que eu. Se estiveres a usar os da AT, então aí... à mais qualquer coisa errada!

Link to comment
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
×
×
  • Create New...

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.