<?php

function carregarCertificado($arquivo, $senha)
{
    $certs = [];
    if (!file_exists($arquivo)) {
        throw new Exception("Erro: Certificado não encontrado.");
    }
    $conteudo = file_get_contents($arquivo);
    if (!openssl_pkcs12_read($conteudo, $certs, $senha)) {
        throw new Exception("Erro: Não foi possível carregar o certificado digital. Senha errada?");
    }
    return $certs;
}

function obterPrivateKey($certificados)
{
    return $certificados["pkey"];
}

function obterx509Certificate($certificados)
{
    $x509Certificate = $certificados["cert"];
    return trim(str_replace(["-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----", "\r", "\n"], '', $x509Certificate));
}

function montarTermoAutorizacao($destinatario, $assinante)
{
    $xml = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?><termoDeAutorizacao></termoDeAutorizacao>");
    $dados = $xml->addChild('dados');

    $sistema = $dados->addChild('sistema');
    $sistema->addAttribute('id', 'API Integra Contador');

    $termo = $dados->addChild('termo');
    $termo->addAttribute('texto', 'Autorizo a empresa CONTRATANTE, identificada neste termo de autorização como DESTINATÁRIO, a executar as requisições dos serviços web disponibilizados pela API INTEGRA CONTADOR, onde terei o papel de AUTOR PEDIDO DE DADOS no corpo da mensagem enviada na requisição do serviço web. Esse termo de autorização está assinado digitalmente com o certificado digital do PROCURADOR ou OUTORGADO DO CONTRIBUINTE responsável, identificado como AUTOR DO PEDIDO DE DADOS.');

    $avisoLegal = $dados->addChild('avisoLegal');
    $avisoLegal->addAttribute('texto', 'O acesso a estas informações foi autorizado pelo próprio PROCURADOR ou OUTORGADO DO CONTRIBUINTE, responsável pela informação, via assinatura digital. É dever do destinatário da autorização e consumidor deste acesso observar a adoção de base legal para o tratamento dos dados recebidos conforme artigos 7º ou 11º da LGPD (Lei n.º 13.709, de 14 de agosto de 2018), aos direitos do titular dos dados (art. 9º, 17 e 18, da LGPD) e aos princípios que norteiam todos os tratamentos de dados no Brasil (art. 6º, da LGPD).');

    $finalidade = $dados->addChild('finalidade');
    $finalidade->addAttribute('texto', 'A finalidade única e exclusiva desse TERMO DE AUTORIZAÇÃO, é garantir que o CONTRATANTE apresente a API INTEGRA CONTADOR esse consentimento do PROCURADOR ou OUTORGADO DO CONTRIBUINTE assinado digitalmente, para que possa realizar as requisições dos serviços web da API INTEGRA CONTADOR em nome do AUTOR PEDIDO DE DADOS (PROCURADOR ou OUTORGADO DO CONTRIBUINTE).');

    $dados->addChild('dataAssinatura')->addAttribute('data', date("Ymd"));
    $dados->addChild('vigencia')->addAttribute('data', date('Ymd', strtotime("+30 days")));

    $destNode = $dados->addChild('destinatario');
    foreach ($destinatario as $key => $value) {
        $destNode->addAttribute($key, $value);
    }

    $assinNode = $dados->addChild('assinadoPor');
    foreach ($assinante as $key => $value) {
        $assinNode->addAttribute($key, $value);
    }

    return $xml;
}

function assinar($xmlNaoAssinado, $certificados)
{
    $xml = new DOMDocument();
    $xml->preserveWhiteSpace = true;
    $xml->formatOutput = false;
    $xml->loadXML($xmlNaoAssinado->asXML());

    if (!$xml->documentElement) {
        throw new UnexpectedValueException('Indefindo o elemento documentElement');
    }

    $canonicalData = $xml->documentElement->C14N(true, false);
    $digestValue = openssl_digest($canonicalData, "sha256", true);
    
    if ($digestValue === false) {
        throw new UnexpectedValueException('Invalid digest value');
    }

    $digestValue = base64_encode($digestValue);

    $signatureElement = $xml->createElement('Signature');
    $signatureElement->setAttribute('xmlns', 'http://www.w3.org/2000/09/xmldsig#');
    $xml->documentElement->appendChild($signatureElement);

    $signedInfoElement = $xml->createElement('SignedInfo');
    $signatureElement->appendChild($signedInfoElement);

    $canonicalizationMethodElement = $xml->createElement('CanonicalizationMethod');
    $canonicalizationMethodElement->setAttribute('Algorithm', 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315');
    $signedInfoElement->appendChild($canonicalizationMethodElement);

    $signatureMethodElement = $xml->createElement('SignatureMethod');
    $signatureMethodElement->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256');
    $signedInfoElement->appendChild($signatureMethodElement);

    $referenceElement = $xml->createElement('Reference');
    $referenceElement->setAttribute('URI', '');
    $signedInfoElement->appendChild($referenceElement);

    $transformsElement = $xml->createElement('Transforms');
    $referenceElement->appendChild($transformsElement);

    $transformElement = $xml->createElement('Transform');
    $transformElement->setAttribute('Algorithm', 'http://www.w3.org/2000/09/xmldsig#enveloped-signature');
    $transformsElement->appendChild($transformElement);

    $transformElement = $xml->createElement('Transform');
    $transformElement->setAttribute('Algorithm', 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315');
    $transformsElement->appendChild($transformElement);

    $digestMethodElement = $xml->createElement('DigestMethod');
    $digestMethodElement->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmlenc#sha256');
    $referenceElement->appendChild($digestMethodElement);

    $digestValueElement = $xml->createElement('DigestValue', $digestValue);
    $referenceElement->appendChild($digestValueElement);

    $signatureValueElement = $xml->createElement('SignatureValue', '');
    $signatureElement->appendChild($signatureValueElement);

    $keyInfoElement = $xml->createElement('KeyInfo');
    $signatureElement->appendChild($keyInfoElement);

    $X509Data = $xml->createElement('X509Data');
    $keyInfoElement->appendChild($X509Data);

    $X509Certificate = $xml->createElement('X509Certificate', obterx509Certificate($certificados));
    $X509Data->appendChild($X509Certificate);

    $c14nSignedInfo = $signedInfoElement->C14N(true, false);
    
    $status = openssl_sign($c14nSignedInfo, $signatureValue, obterPrivateKey($certificados), OPENSSL_ALGO_SHA256);

    if (!$status) {
        throw new Exception('Falha no cálculo da assinatura.');
    }

    $xpath = new DOMXpath($xml);
    $signatureValueElement = $xpath->query('//SignatureValue', $signatureElement)->item(0);
    $signatureValueElement->nodeValue = base64_encode($signatureValue);
    
    return $xml->saveXML();
}
?> 