ГИС ЖКХ — подпись сообщений
Ранее уже описывал как поднять защищенный туннель с ГИС ЖКХ.Теперь настало время научится делать запросы. Для этого их необходимо подписывать. Подписывается тело сообщения XML заключенное внутри тега body:

Сохраню его в файл in.xml. Далее для работы понадобится сертификат в формате x.509, он выгружается в формате BASE64 из Крипто-про

Понадобится он в бинарном виде:
cat 509.base64 | base64 -d >509.bin
Далее собираем следующую рыбу:
<ds:Signature Id="xmldsig-{signature_id}"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"/>
<ds:Reference Id="xmldsig-{signature_id}-ref0" URI="#{signed_id}">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>{digest1}</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-{signature_id}-signedprops">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>{digest3}</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-{signature_id}-sigvalue">
{signature_value}
</ds:SignatureValue>
<ds:KeyInfo
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
{x590_cert}
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
<ds:Object>
<xades:QualifyingProperties Target="#xmldsig-{signature_id}"
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#">
<xades:SignedProperties Id="xmldsig-{signature_id}-signedprops">
<xades:SignedSignatureProperties>
<xades:SigningTime>{signing_time}</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>{digest2}</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>{x509_issuer_name}</ds:X509IssuerName>
<ds:X509SerialNumber>{x509_sn}</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
</xades:SignedSignatureProperties>
</xades:SignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
digest1 считается как хеш-сумма по ГОСТу и выводится в виде BASE64 содержимым тега body (см. выше)
cat in.xml | openssl dgst -engine gost -md_gost94 -binary | base64 >digest1
digest2 считается как хэш-сумма бинарного файла сертификата x509 и преобразованное в base64:
cat 509.bin | openssl dgst -engine gost -md_gost94 -binary | base64 >digest2
Далее заполняем блок SignedProperties, а в частности поля x509_issuer_name и x509_sn. Их можно получить соответственно командами:
openssl x509 -in cert.pem -serial -nameopt sep_multiline,utf8 -noout
openssl x509 -in cert.pem -issuer -nameopt sep_multiline,utf8 -noout
Причем x509_issuer_name нужно привести к виду:
cn=Тестовый УЦ ООО \»КРИПТО-ПРО\»,o=ООО \»КРИПТО-ПРО\»,l=Москва,st=г. Москва,c=RU,street=ул. Сущёвский вал д.18,1.2.643.3.131.1.1=001234567890,1.2.643.100.1=1234567890123
Тег emailaddress, заменяется на 1.2.840.113549.1.9.1, inn на 1.2.643.100.4, а ogrn на 13 1.2.643.100.1
Теперь необходимо подписать то, что мы заполнили между тегами SignedInfo. У меня получилось что-то вроде:
<xd:SignedInfo
xmlns:base="http://dom.gosuslugi.ru/schema/integration/base/"
xmlns:drs="http://dom.gosuslugi.ru/schema/integration/drs/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xd="http://www.w3.org/2000/09/xmldsig#">
<xd:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></xd:CanonicalizationMethod>
<xd:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"></xd:SignatureMethod>
<xd:Reference Id="xmldsig-e99d1872-укепкеп54e8bb16d4-ref0" URI="#f9f93dкепуке6-11e5-b4ae-1c6f65dfe2b1">
<xd:Transforms>
<xd:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></xd:Transform>
<xd:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></xd:Transform>
</xd:Transforms>
<xd:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"></xd:DigestMethod>
<xd:DigestValue>b\'укепукепукепке/imNA=\'</xd:DigestValue>
</xd:Reference>
<xd:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-e99d1872-4534-11f0-b35a-e454e8bb16d4-signedprops">
<xd:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"></xd:DigestMethod>
<xd:DigestValue>DLYx2a/3ц4кц34к3/XQtrUiKviYQ=</xd:DigestValue>
</xd:Reference>
</xd:SignedInfo>
Подпись осуществляется так:
openssl dgst -sign key.key -out to_sign.sig -binary to_sign
Подпись сохранилась в файл to_sign.sig, помещаем содержимое внутри тега SignatureValue и получившийся таким образом файл пробуем отправить в ГИС ЖКХ.
Некоторые заметки:
1) Как вытащить закрытый ключ с флешки с подписью — нужно воспользоваться утилитой P12FromGostCSP. Если вы пользовались чем-то другим, то с большой долей вероятностью получите ошибку:
root@vlg1w-bb16d4:/home/укацук@укацук.ru/Desktop/ЖКХ/БоевыеКлючи# openssl pkcs12 -in укацук.p12 -engine gost
engine "gost" set.
Enter Import Password:
Bag Attributes
localKeyID: 01 00 00 00
friendlyName: e3йуцвйцуd8c1319fa1d5
Microsoft CSP Name: Crypto-Pro GOST R 34.10-2012 KC1 CSP
Error outputting keys and certificates
140097466021056:error:06074079:digital envelope routines:EVP_PBE_CipherInit:unknown pbe algorithm:../crypto/evp/evp_pbe.c:95:TYPE=1.2.840.113549.1.12.1.80
140097466021056:error:23077073:PKCS12 routines:PKCS12_pbe_crypt:pkcs12 algor cipherinit error:../crypto/pkcs12/p12_decr.c:41:
140097466021056:error:2306A075:PKCS12 routines:PKCS12_item_decrypt_d2i:pkcs12 pbe crypt error:../crypto/pkcs12/p12_decr.c:94:
2) Формировать подпись или через КриптоПро или через OpenSSL.
3) Есть готовая утилита для формирования шапки с подписью написанная на Python 2.7. Я её перевел на Python3, сделав минимальные косметические изменения. В принципе работает. Скачать тут
Update 16.06.2025: на текущий момент не удалось достичь успешной отправки запроса в ГИС ЖКХ. Ошибка:
"ErrorCode": "AUT011005",
"Description": "Ошибка формата подписи запроса"
Подпись не подходит ни крипто-про:
/opt/cprocsp/bin/amd64/csptest -keyset -cont 'HDIMAGE\\e32681d3.000\A3EB' -sign 'GOST12_256' -keytype exchange -in for_sign.xml -out for_sign.xml.sgn |cat for_sign.xml.sgn| base64 -w 0
Ни OpenSSL:
cat for_sign.xml | openssl dgst -md_gost12_256 -sign priv.key -binary | base64 -w 0