I
I
irishmann2021-03-16 13:43:36
PHP
irishmann, 2021-03-16 13:43:36

GOST signature in php or how to sign SOAP GOST algorithms?

Added to OpenSSL GOST Engine, checked

[email protected]:/$ openssl ciphers|tr ':' '\n'|grep GOST
GOST2012-GOST8912-GOST8912
GOST2001-GOST89-GOST89

compiled from PHP 7.2.24 sources with keys --with-system-ciphers --with-openssl
Now it seems that PHP sees the guest algorithms
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
)

Issued a self-signed certificate and a private key in accordance with GOST 2012 256 bits, set a password
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

I'm trying to write a string using openssl_sign
the code
<?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";

?>

At the output I get
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=="

If the key type is not supported, how does openssl_sign not return false? I suspect that the certificate and key still need to be converted into something. openssl_pkcs7_sign works with my certificates, but what it gives out is not what you need. How can I solve this problem?

PS: I'm trying to implement XMLDSig on GOST 2012, specifically here I'm trying to form a <ds:SignatureValue></ds:SignatureValue>. The test self-signed certificate in production must be replaced by the certificate confirmed in the CA.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
I
irishmann, 2021-06-17
@irishmann

Nothing happened with OpenSSL. Made with CryptoPRO. Put CSP on the server, installed certificates and built the phpcades extension. Below is shitty code, links to examples and instructions.

CryptoPro class

<?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 class

<?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);
    }

}


Verification of such a signature

<?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";
    }


  • Compiling the CryptoPro extension for PHP
  • Error when compiling extension under Ubuntu 18.04 , topic on the CryptoPro forum
  • Creating and verifying an XML document signature from a template , an example for JavaScript
  • phpcades example
  • PHPCades CPSignedXML ISO-20020 - Payment signing... , forum topic

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question