Ошибка
  • Копирование не удалось

Проверка подписи на PHP
(0 чел.) 
  • Страница:
  • 1
  • 2
  • 3
  • 4

ТЕМА: Проверка подписи на PHP

Re: Проверка подписи на PHP 8 мес., 2 нед. назад #4700

  • nbah1990
  • Новый участник
  • Постов: 2
  • Репутация: 0
final class XmlService
{
    /**
     * @param string $xmlContent
     * @param int $signatureAlg
     * @return bool
     * @throws \Exception
     */
    public function validateXml(string $xmlContent, $signatureAlg = OPENSSL_ALGO_SHA256): bool
    {
        $document = $this->getDOMDocument($xmlContent);
        $signXpath = $this->getDOMXpath($document);

        return $this->checkSign($document, $signXpath, $signatureAlg) && $this->matchDigest($document, $signXpath);
    }

    /**
     * @param \DOMDocument $document
     * @param \DOMXPath $signXpath
     * @param int $signatureAlg
     * @return bool
     * @throws \Exception
     */
    private function checkSign(\DOMDocument $document, \DOMXPath $signXpath, $signatureAlg = OPENSSL_ALGO_SHA256): bool
    {
        $signature = $signXpath->query('.//ds:Signature', $document)->item(0);
        $signedInfo = $signXpath->query('./ds:SignedInfo', $signature)->item(0);
        $signedInfo = $signedInfo->C14N(false, true);

        $signValue = $signXpath->query('./ds:SignatureValue', $signature)->item(0);
        $signedText = base64_decode(trim($signValue->textContent));

        $loadedCert = $this->getCertificateResource($document, $signXpath);

        if (!openssl_verify($signedInfo, $signedText, $loadedCert, $signatureAlg)) {
            $openSslErrors = [];
            while ($error = openssl_error_string()) {
                $openSslErrors[] = $error;
            }
            throw new \Exception('Openssl errors: ' . json_encode($openSslErrors));
        }

        return true;
    }

    /**
     * @param \DOMDocument $document
     * @param \DOMXPath $signXpath
     * @return bool
     * @throws \Exception
     */
    private function matchDigest(\DOMDocument $document, \DOMXPath $signXpath): bool
    {
        $signature = $signXpath->query('.//ds:Signature', $document)->item(0);
        $signedInfo = $signXpath->query('./ds:SignedInfo', $signature)->item(0);
        $reference = $signXpath->query('./ds:Reference', $signedInfo)->item(0);
        $refDigest = $signXpath->query('./ds:DigestValue', $reference)->item(0);

        $rootDoc = $document->childNodes->item(0);
        $rootDoc->removeChild($signature); // the node will be deleted everywhere
        $c14nDocument = $rootDoc->C14N(false, false);
        $matchingDigest = base64_encode(hash('sha256', $c14nDocument, true));

        if ($matchingDigest !== $refDigest->textContent) {
            throw new \Exception('Content was changed');
        }

        return true;
    }

    /**
     * @param \DOMDocument $document
     * @param \DOMXPath $signXpath
     * @return resource
     */
    private function getCertificateResource(\DOMDocument $document, \DOMXPath $signXpath)
    {
        $signature = $signXpath->query('.//ds:Signature', $document)->item(0);
        $signKeyInfo = $signXpath->query('./ds:KeyInfo', $signature)->item(0);
        $x509Cert = $signXpath->query('./ds:X509Data/ds:X509Certificate', $signKeyInfo)->item(0);

        $certificate = $x509Cert->textContent;
        $certificate = str_replace(["\n", "\r", ' '], '', $certificate);
        $certificate = "-----BEGIN CERTIFICATE-----\n" . chunk_split($certificate, 64, "\n") . "-----END CERTIFICATE-----\n";

        return openssl_x509_read($certificate);
    }

    /**
     * @param string $xmlContent
     * @return \DOMDocument
     */
    private function getDOMDocument(string $xmlContent): \DOMDocument
    {
        $domDocument = new \DOMDocument();
        $domDocument->loadXML($xmlContent);

        return $domDocument;
    }

    /**
     * @param \DOMDocument $document
     * @return \DOMXPath
     */
    private function getDOMXpath(\DOMDocument $document): \DOMXPath
    {
        $signXpath = new \DOMXPath($document);
        $signXpath->registerNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
        return $signXpath;
    }
}


Добрый день, коллеги. Вот пример с нативным php, тестировал только для sha256. Проверял на xml, которую можно сгенерить в "NCALayer\commonbundle_sample\index.html". Выкладываю как есть, пишите если есть предложения. Используйте на свой страх и риск, т.к. выше сказали что юридическую силу в спорных ситуациях имеют только оф. либы, которые, к сожалению, пока только для windows есть.
  • Страница:
  • 1
  • 2
  • 3
  • 4
FaLang translation system by Faboba