irishmann
@irishmann
Научись пользоваться дебаггером

Подпись по ГОСТ в php или как подписать SOAP ГОСТ алгоритмами?

Добавил в OpenSSL GOST Engine, проверил
adminka@ubuntu-nginx:/$ openssl ciphers|tr ':' '\n'|grep GOST
GOST2012-GOST8912-GOST8912
GOST2001-GOST89-GOST89

собрал из исходников PHP 7.2.24 с ключами --with-system-ciphers --with-openssl
Теперь вроде бы PHP видит гостовские алгоритмы
openssl_get_md_methods
Array
(
    [0] => GOST 28147-89 MAC
    [1] => GOST R 34.11-2012 with 256 bit hash
    [2] => GOST R 34.11-2012 with 512 bit hash
    [3] => GOST R 34.11-94
    [4] => blake2b512
    [5] => blake2s256
    [6] => gost-mac
    [7] => gost-mac-12
    [8] => md4
    [9] => md5
    [10] => md5-sha1
    [11] => md_gost12_256
    [12] => md_gost12_512
    [13] => md_gost94
    [14] => ripemd160
    [15] => sha1
    [16] => sha224
    [17] => sha256
    [18] => sha3-224
    [19] => sha3-256
    [20] => sha3-384
    [21] => sha3-512
    [22] => sha384
    [23] => sha512
    [24] => sha512-224
    [25] => sha512-256
    [26] => shake128
    [27] => shake256
    [28] => sm3
    [29] => whirlpool
)

Выпустил самоподписанный сертификат и закрытый ключ по ГОСТу 2012 256 бит, задал пароль
openssl req -newkey gost2012_256 -pkeyopt paramset:A -out cert.csr -keyout key.pem
openssl x509 -req -in cert.csr -out cert.pem -signkey key.pem -days 730

Пытаюсь пописать строку через openssl_sign
код
<?php
    ini_set('error_reporting', E_ALL);
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);

    $sert = 'file://' . __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'certs' . DIRECTORY_SEPARATOR . 'cert.pem';
    $private = 'file://' . __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'certs' . DIRECTORY_SEPARATOR . 'key.pem';

    $data = 'test string test string test string test string';

    openssl_sign($data, $sign, openssl_get_privatekey($private, '123456'), 'md_gost12_256');

    var_dump(base64_encode($sign));

    while ($msg = openssl_error_string())
    echo $msg . "<br />\n";

?>

На выходе получаю
Warning: openssl_sign(): key type not supported in this PHP build! in /home/adminka/callbacksoap/test.php on line 11
string(88) "xlRC/hbqWkqDb/ENW0QlvWC/Bx9pqZfr6hEZvA9ZazLKVE7rjIWwWciVFU8xPKH0MKmfHKltouC5xk32GZ3Erg=="

Если тип ключа не поддерживается, то каким образом openssl_sign не выдал false? Подозреваю что сертификат и ключ еще нужно во что-то преобразовывать. openssl_pkcs7_sign с моими сертификатами работает, но то что он выдает - это не то что нужно. Как мне решить эту проблему?

P.S.: Пытаюсь реализовать XMLDSig на ГОСТ 2012, конкретно тут я пытаюсь сформировать <ds:SignatureValue></ds:SignatureValue>. Тестовый самоподписанный сертификат на продакшене должен замениться подтвержденным в УЦ сертификатом.
  • Вопрос задан
  • 1872 просмотра
Решения вопроса 1
irishmann
@irishmann Автор вопроса
Научись пользоваться дебаггером
С OpenSSL ничего не вышло. Сделал с помощью КриптоПРО. Поставил CSP на сервер, установил сертификаты и собрал расширение phpcades. Ниже говнокод, ссылки на примеры и инструкции.

класс CryptoPro

<?php

namespace PHPCadesSOAP;

use CPStore;

class CryptoPro {

    public static function SetupStore($location, $name, $mode) {
        $store = new CPStore();
        $store->Open($location, $name, $mode);
        return $store;
    }

    public static function SetupCertificates($location, $name, $mode) {
        $store = self::SetupStore($location, $name, $mode);
        return $store->get_Certificates();
    }

    public static function SetupCertificate($location, $name, $mode, $find_type, $query, $valid_only, $number) {
        $certs = self::SetupCertificates($location, $name, $mode);
        if ($find_type != NULL) {
            $certs = $certs->Find($find_type, $query, $valid_only);
            if (is_string($certs))
                return $certs;
            else
                return $certs->Item($number);
        }
        else {
            $cert = $certs->Item($number);
            return $cert;
        }
    }

    public static function createMessageID() {
        $uuid = md5(uniqid(rand(), true));
        $guid = 'uudi:' .
                substr($uuid, 0, 8) . "-" .
                substr($uuid, 8, 4) . "-" .
                substr($uuid, 12, 4) . "-" .
                substr($uuid, 16, 4) . "-" .
                substr($uuid, 20, 12);

        return $guid;
    }

}


класс MySoapClient

<?php

namespace PHPCadesSOAP;

use SoapClient;
use PHPCadesSOAP\Logger;
use DOMDocument;
use PHPCadesSOAP\CryptoPro;
use CPSigner;
use CPSignedXml;

class MySoapClient extends SoapClient {

    private $certSubjectName = 'test certificate';
    private $IPS_id = '********-****-****-****-************';

    function __doRequest($request, $location, $saction, $version, $one_way = NULL) {
        Logger::debug('Неподписанный запрос от MySoapClient - ' . $request);

        $cert = CryptoPro::SetupCertificate(CURRENT_USER_STORE, "My", STORE_OPEN_READ_ONLY, CERTIFICATE_FIND_SUBJECT_NAME, $this->certSubjectName, 0, 1);
        $certData = preg_replace('/\n+/', '', $cert->export(0));

        $xml = new DOMDocument();
        $xml->loadXML($request);

        $Envelope = $xml->getElementsByTagName('Envelope')->item(0);
        $Body = $Envelope->getElementsByTagName('Body')->item(0);
        $prefix = $Envelope->prefix;

        $Envelope->setAttribute('xmlns:wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');
        $Envelope->setAttribute('xmlns:wsu', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd');
        $Envelope->setAttribute('xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#');
        $Envelope->setAttribute('xmlns:a', 'http://www.w3.org/2005/08/addressing');

        $Header = $xml->createElement($prefix . ':Header');

        $To = $xml->createElement('a:To', 'https://**********************');

        $Action = $xml->createElement('a:Action', 'sendResponse');

        $ReplyTo = $xml->createElement('a:ReplyTo');
        $ReplyToAddress = $xml->createElement('a:Address', 'http://www.w3.org/2005/08/addressing/anonymous');

        $FaultTo = $xml->createElement('a:FaultTo');
        $FaultToAddress = $xml->createElement('a:Address', 'http://www.w3.org/2005/08/addressing/anonymous');

        $MessageID = $xml->createElement('a:MessageID', CryptoPro::createMessageID());

        $Security = $xml->createElement('wsse:Security');

        $BinarySecurityToken = $xml->createElement('wsse:BinarySecurityToken', $certData);
        $BinarySecurityToken->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary');
        $BinarySecurityToken->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3');
        $bsd_id = uniqid('x509-');
        $BinarySecurityToken->setAttribute('wsu:Id', $bsd_id);

        $Signature = $xml->createElement('ds:Signature');

        $SignedInfo = $xml->createElement('ds:SignedInfo');

        $CanonicalizationMethod = $xml->createElement('ds:CanonicalizationMethod');
        $CanonicalizationMethod->setAttribute('Algorithm', 'http://www.w3.org/2001/10/xml-exc-c14n#');

        $SignatureMethod = $xml->createElement('ds:SignatureMethod');
        $SignatureMethod->setAttribute('Algorithm', 'urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256');

        $Reference_of_body = $xml->createElement('ds:Reference');
        $uri_on_body = uniqid('body-');
        $Reference_of_body->setAttribute('URI', '#' . $uri_on_body);
        $Body->setAttribute('wsu:Id', $uri_on_body);

        $Reference_of_body_Transforms = $xml->createElement('ds:Transforms');

        $Reference_of_body_Transforms_Transform = $xml->createElement('ds:Transform');
        $Reference_of_body_Transforms_Transform->setAttribute('Algorithm', 'http://www.w3.org/2001/10/xml-exc-c14n#');

        $Reference_of_body_DigestMethod = $xml->createElement('ds:DigestMethod');
        $Reference_of_body_DigestMethod->setAttribute('Algorithm', 'urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256');

        $Reference_of_body_DigestValue = $xml->createElement('ds:DigestValue');

        $SignatureValue = $xml->createElement('ds:SignatureValue');
        $KeyInfo = $xml->createElement('ds:KeyInfo');

        $SecurityTokenReference = $xml->createElement('wsse:SecurityTokenReference');

        $SecurityTokenReference_Reference = $xml->createElement('wsse:Reference');
        $SecurityTokenReference_Reference->setAttribute('ValueType', "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
        $SecurityTokenReference_Reference->setAttribute('URI', '#' . $bsd_id);

        $Envelope->insertBefore($Header, $Body);
        $Header->appendChild($To);
        $Header->appendChild($Action);
        $Header->appendChild($ReplyTo);
        $ReplyTo->appendChild($ReplyToAddress);
        $Header->appendChild($FaultTo);
        $FaultTo->appendChild($FaultToAddress);
        $Header->appendChild($MessageID);
        $Header->appendChild($Security);
        $Security->appendChild($BinarySecurityToken);
        $Security->appendChild($Signature);
        $Signature->appendChild($SignedInfo);
        $SignedInfo->appendChild($CanonicalizationMethod);
        $SignedInfo->appendChild($SignatureMethod);
        $SignedInfo->appendChild($Reference_of_body);
        $Reference_of_body->appendChild($Reference_of_body_Transforms);
        $Reference_of_body_Transforms->appendChild($Reference_of_body_Transforms_Transform);
        $Reference_of_body->appendChild($Reference_of_body_DigestMethod);
        $Reference_of_body->appendChild($Reference_of_body_DigestValue);
        $Signature->appendChild($SignatureValue);
        $Signature->appendChild($KeyInfo);
        $KeyInfo->appendChild($SecurityTokenReference);
        $SecurityTokenReference->appendChild($SecurityTokenReference_Reference);

        $request = $xml->saveXML();

        $signer = new CPSigner();
        $signer->set_Certificate($cert);
        $signer->set_Options(2);

        $sd = new CPSignedXml();
        $sd->set_SignatureType(2);
        $sd->set_Content($request);
        $sd->set_DigestMethod('urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-512');
        $sd->set_SignatureMethod('urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-512');
        $signedXml = $sd->Sign($signer, "//*[local-name()='Signature']");

        Logger::debug('подписанный запрос от MySoapClient - ' . $signedXml);
        return parent::__doRequest($signedXml, $location, $saction, $version);
    }

}


Проверка такой подписи

<?php
    $xml = file_get_contents('/var/www/dsig.xml');
    try {
        $xpath = "//*[local-name()='Signature' and namespace-uri()='http://www.w3.org/2000/09/xmldsig#']";
        $xmldsig = new CPSignedXML();
        $xmldsig->Verify($xml, $xpath);
        print("OK\n");
    } catch (Exception $e) {
        echo $e->getMessage() . "\n";
    }



Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы