Jump to content

Recommended Posts

Posted
Em 19/11/2024 às 12:44, tssousa disse:

Bom dia.

 

Alguem está com dificuldades em comunicar documentos de transporte no ambiente de testes?

 

Obrigado.

+1 

handshake_failure sem motivo aparente

Posted

O erro que estou a ter é de handshake_failure e começou a surgir no inicio desta semana. Todos os certificados estão válidos mas já se abriu ticket no e-balcao a solicitar informação sobre eventuais alterações do lado da AT.

Posted

Bom dia a todos, espero que se encontrem bem,

Depois de receber a CifraChavePublicaAT2025.cer (Depois converti para .pem) e o TesteWebservices.pfx para testar os envios, não consigo utilizar os mesmos porque aparece Internal Error, mas quando faço o teste de envio no site do e-Fatura funciona perfeitamente, por isso o erro está na minha parte. Já li os documentos que explicam os passos, mas dá-me sempre o mesmo erro.

Gostaria de saber se alguém me pode ajudar e explicar como é que posso resolver isto. Se ajudar, envio o código no final da mensagem.

Obrigado e o resto de um bom a todos.
 

Código Typescript:

export async function POST(request: Request) {

  // Função para gerar a chave simétrica KS com 128 bits (16 bytes) de acordo com a norma AES
  function generateSymmetricKey(): Buffer {
    // Gerando uma chave de 128 bits (16 bytes) aleatória com o crypto do Node.js
    return randomBytes(16); // 16 bytes = 128 bits
  }

  // Função para cifrar a senha com AES-ECB e padding PKCS5 (simulando o PKCS7)
  function encryptPassword(password: string, ks: Buffer): string {
    // Convertendo a senha para Buffer
    const passwordBuffer = Buffer.from(password, 'utf-8');

    // Adicionando padding PKCS5 (que é basicamente PKCS7 com tamanho de bloco de 16 bytes)
    const paddingLength = 16 - (passwordBuffer.length % 16);  // O AES no modo ECB exige blocos de 16 bytes
    const paddedPassword = Buffer.concat([passwordBuffer, Buffer.alloc(paddingLength, paddingLength)]);

    // Criando o cifrador AES-ECB com a chave simétrica gerada
    const cipher = createCipheriv('aes-128-ecb', ks, null);  // AES-128-ECB não utiliza IV, então passamos null

    // Cifrando a senha
    const encryptedPassword = Buffer.concat([cipher.update(paddedPassword), cipher.final()]);

    // Convertendo o resultado cifrado para Base64
    return encryptedPassword.toString('base64');
  }

  // Exemplo de uso:
  const ks = generateSymmetricKey();  // Gerando a chave simétrica de 128 bits

  const password = "@Vitorhugo10";
  const encryptedPassword = encryptPassword(password, ks);
  console.log("Senha cifrada em Base64:", encryptedPassword);


  function encryptSymmetricKeyWithRSA(KS: Buffer, publicKeyPath: string): string {
    // Carregar a chave pública RSA do arquivo .cer (ou .pem)
    const publicKey = readFileSync(publicKeyPath, 'utf8');

    // Cifrar a chave simétrica (KS) com a chave pública usando o algoritmo RSA e o padding PKCS1
    const encryptedKey = publicEncrypt({
      key: publicKey,
      padding: constants.RSA_PKCS1_PADDING,  // Usando o padding padrão RSA
    }, KS);

    // Converter o resultado criptografado para Base64
    return encryptedKey.toString('base64');
  }

  const publicKeyPath = 'certs/ChavePublicaAT.pem';  // Caminho para o arquivo da chave pública

  const encryptedSymmetricKey = encryptSymmetricKeyWithRSA(ks, publicKeyPath);
  console.log('Chave simétrica cifrada com RSA (Base64):', encryptedSymmetricKey);


  function getUTCNow(): string {
    const now = new Date();
    return now.toISOString(); // Formato ISO 8601
  }
 
  // Função para cifrar o timestamp com AES-ECB
  function encryptTimestamp(timestamp: string, symmetricKey: Buffer): string {
    const timestampBuffer = Buffer.from(timestamp, 'utf-8');
 
    // Adicionando padding PKCS5 (que é o mesmo que PKCS7 para blocos de 16 bytes)
    const paddingLength = 16 - (timestampBuffer.length % 16);
    const paddedTimestamp = Buffer.concat([timestampBuffer, Buffer.alloc(paddingLength, paddingLength)]);
 
    // Cifrando o timestamp com AES-128-ECB (sem IV)
    const cipher = createCipheriv('aes-128-ecb', symmetricKey, null); // AES-ECB não usa IV, então passamos null
    const encryptedTimestamp = Buffer.concat([cipher.update(paddedTimestamp), cipher.final()]);
 
    // Retorna o resultado cifrado em Base64
    return encryptedTimestamp.toString('base64');
  }
 
  // Função para gerar o timestamp cifrado em Base64
  function generateEncryptedTimestamp(symmetricKey: Buffer): string {
    const timestamp = getUTCNow(); // Obter o timestamp no formato ISO 8601
    const encryptedTimestamp = encryptTimestamp(timestamp, symmetricKey); // Cifrar o timestamp
    return encryptedTimestamp; // Retorna o timestamp cifrado em Base64
  }
 
  // Exemplo de uso
 
  // Gerando o timestamp cifrado em Base64
  const encryptedTimestampBase64 = generateEncryptedTimestamp(ks);
  console.log('Timestamp cifrado (Base64):', encryptedTimestampBase64);


  try {
    // Caminhos para os arquivos de certificado
    const pfxPath = path.join(process.cwd(), 'certs', 'TesteWebservices.pfx');
    const passphrase = 'TESTEwebservice'; // Senha fornecida para o .pfx
    const cerPath = path.join(process.cwd(), 'certs', 'ChavePublicaAT.pem');

    // Configurar o agente HTTPS
    const httpsAgent = new https.Agent({
      pfx: fs.readFileSync(pfxPath),
      passphrase: passphrase,
      rejectUnauthorized: false,
    })
    

    // Construção da estrutura SOAP com os dados encriptados
    const soapEnvelope = `
      <?xml version="1.0" encoding="utf-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
        <env:Header>
            <wss:Security xmlns:wss="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:at="http://at.pt/wsp/auth" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" S:Actor="http://at.pt/actor/SPA" at:Version="2">
            <wss:UsernameToken>
              <wss:Username>255476922/1</wss:Username> <!-- Exemplo de Username -->
              <wss:Password>${encryptedPassword}</wss:Password>
              <wss:Nonce>${encryptedSymmetricKey}</wss:Nonce> <!-- Nonce cifrado -->
              <wss:Created>${encryptedTimestampBase64}</wss:Created> <!-- Timestamp cifrado -->
            </wss:UsernameToken>
          </wss:Security>
        </S:Header>
        <S:Body>
          <RegisterInvoiceRequest xmlns="http://factemi.at.min_financas.pt/documents">
            <eFaturaMDVersion>0.0.1</eFaturaMDVersion>
            <AuditFileVersion>1.04_01</AuditFileVersion>
            <TaxRegistrationNumber>255476922</TaxRegistrationNumber>
            <TaxEntity>SEDE</TaxEntity>
            <SoftwareCertificateNumber>1111</SoftwareCertificateNumber>
            <InvoiceData>
              <InvoiceNo>FS 99/999999</InvoiceNo>
              <ATCUD>0</ATCUD>
              <InvoiceDate>2022-05-01</InvoiceDate>
              <InvoiceType>FS</InvoiceType>
              <SelfBillingIndicator>0</SelfBillingIndicator>
              <CustomerTaxID>100100112</CustomerTaxID>
              <CustomerTaxIDCountry>PT</CustomerTaxIDCountry>
              <DocumentStatus>
                <InvoiceStatus>N</InvoiceStatus>
                <InvoiceStatusDate>2022-05-01T00:00:00.000</InvoiceStatusDate>
              </DocumentStatus>
              <HashCharacters>DSFG</HashCharacters>
              <CashVATSchemeIndicator>0</CashVATSchemeIndicator>
              <PaperLessIndicator>0</PaperLessIndicator>
              <SystemEntryDate>2022-05-01T00:00:00.000</SystemEntryDate>
              <LineSummary>
                <TaxPointDate>2022-05-01</TaxPointDate>
                <DebitCreditIndicator>C</DebitCreditIndicator>
                <Amount>81.3</Amount>
                <Tax>
                  <TaxType>IVA</TaxType>
                  <TaxCountryRegion>PT</TaxCountryRegion>
                  <TaxCode>NOR</TaxCode>
                  <TaxPercentage>23.0</TaxPercentage>
                </Tax>
              </LineSummary>
              <DocumentTotals>
                <TaxPayable>18.7</TaxPayable>
                <NetTotal>81.3</NetTotal>
                <GrossTotal>100</GrossTotal>
              </DocumentTotals>
              <WithholdingTax>
                <WithholdingTaxType>IRS</WithholdingTaxType>
                <WithholdingTaxAmount>150.0</WithholdingTaxAmount>
              </WithholdingTax>
            </InvoiceData>
          </RegisterInvoiceRequest>
        </S:Body>
      </S:Envelope>`;

    // Enviar a requisição ao webservice
    const response = await axios.post('https://servicos.portaldasfinancas.gov.pt:723/factorews/ws/', soapEnvelope, {
      httpsAgent: httpsAgent,
      headers: {
        'Content-Type': 'text/xml',
        'SOAPAction': 'RegisterInvoiceRequest',
      },
    });

    // Responder com o resultado da AT
    return NextResponse.json({ message: 'Comunicação bem-sucedida', data: response.data }, { status: 200 });
  } catch (error) {
    console.error('Erro ao comunicar com o webservice da AT:', error);
    return NextResponse.json({ error: 'Erro ao comunicar com o webservice' }, { status: 500 });
  }
}
Posted

Atualmente já estou a conseguir comunicar e enviar os documentos de transporte, mas agora recebo o seguinte alterta:

Alerta: Erro na validação do ATCUD. Campos número de documento e ATCUD com erros ou incompletos. 

No site das finanças bate tudo certo e o ATCUD encontra-se preenchido, bem como o numero tipodocumento local/numerodocumento. 

Alguém na mesma situação que possa dar uma ajuda? 

Posted

Olá a todos. 

Em primeiro lugar, peço desculpa se tiver a repetir a pergunta, tentei procurar no tópico mas não encontrei uma solução.

Eu gerei um CSR com os comandos openssl que a AT disponibiliza na documentação de comunicação com os web service da AT:

openssl req -new -subj "/C=PT/ST=Distrito da Sede/L=Local da Sede/O=Empresa/OU=Departamento de Informatica/CN=555555555/emailAddress=informatica@empresa.pt" -newkey rsa:2048 -nodes -out 555555555.csr -keyout 555555555.key

Até aqui tudo bem, gerou o CSR e uma chave privada que guardamos para usar posteriormente. A AT respondeu e conseguimos extrair o .pfx a partir do certificado que nos enviaram.

Passámos então ao passo de enviar o modelo 24 onde temos de fazer o upload de uma chave pública e a versão das chaves usadas. Eu extrai a chave pública a partir da chave privada usando o comando:

openssl rsa -in 555555555.key -pubout

Este gera uma chave pública mas quando tento fazer upload para o site do modelo 24, queixam-se que é grande de mais: o ficheiro tem 446 bytes enquanto que o site limita a 272 bytes. fiz alguma coisa mal neste processo? Alguém teve o mesmo problema? Obrigado.

Posted
On 11/29/2024 at 3:42 PM, Miguel Frias said:

Gerar a chave publica a partir da privada

# openssl rsa -in 555555555.key -outform PEM -pubout -out 555555555.pub

Obrigado pela resposta. No entanto, continua a ser maior do que os 272 bytes que o modelo 24 deixa fazer upload.

Posted

Estranho o meu tem 451 bytes e está sem alteração desde quando pedi o certificado. Só se mudaram algo entretanto.

 

openssl genrsa -des3 -out private.pem 2048

openssl rsa -in private.pem -outform PEM -pubout -out public.pem 

 

Vê se este novo ficheiro tem 272 bytes 

Posted
On 11/29/2024 at 3:58 PM, Miguel Frias said:

Estranho o meu tem 451 bytes e está sem alteração desde quando pedi o certificado. Só se mudaram algo entretanto.

 

openssl genrsa -des3 -out private.pem 2048

openssl rsa -in private.pem -outform PEM -pubout -out public.pem 

 

Vê se este novo ficheiro tem 272 bytes 

Executei esses dois comandos e a chave publica tem 451 bytes. Fizeste alguma coisa especial para conseguir fazer upload? Está num formato zip ou assim ? Obrigado.

Posted
Em 02/12/2024 às 11:33, Cr4zyKingLi0n disse:

Bom dia,

Alguem conhece alguma app ou site para validar o QrCode das facturas?

 

obrigado 

Em https://github.com/pchouse/atqrcodereader

App para android que valida e o QR code e permite também ligar ao ambiente de testes da validar os dados. Actualmente não está na play store tem que abrir com o Android studio compilar e instalar no telemóvel, 

  • Thanks 1
Posted
Em 18/11/2024 às 16:09, hbarbosa disse:

Resposta da AT:

Autoridade Tributária                                                                          18/11/2024 15:51:11

A Autoridade Tributária e Aduaneira (AT) agradece o seu contacto.

Bom dia, como pediram esclarecimentos sobre este assunto?

Enviei um pedido pelo E-Balcão há 2 semanas atrás e ainda não recebi qualquer resposta.

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.