Subject: Re: Проблема с верификацией ЭЦП pygost

From: Sergey Matveev <[email-masked]>
Date: Mon, 7 Jul 2025 17:03:30 +0300

Приветствую!

*** Богдан ветров [2025-07-07 16:41]:
>Буду крайне признателен,если вы посмотрите мой код и поможете понять в чем я не прав.
>Ссылка на github:  https://github.com/sebastianfym/pygost_test.git

* Если что, то я совершенно не знаю как устроен и работает asn1crypto.
  Не то чтобы это проблема, но с ним ничего не подскажу
* PyGOST ничего не знает про ASN.1 и он выполняет только низкоуровневые
  функции чисто криптографии

--8<--# 0 [                                  8a41d3c09 | main.py ]--8<--
  14 curve = CURVES["id-tc26-gost-3410-12-256-paramSetA"]
--8<----------------------------------------------------------------8<--
Судя по сертификату приложенному к вашей CMS, в нём используется

    publicKeyParamSet: OBJECT IDENTIFIER 1.2.643.2.2.36.0 (id_GostR3410_2001_CryptoPro_XchA_ParamSet)

а вы используете id-tc26-gost-3410-12-256-paramSetA. Это две разные
эллиптические кривые. id-tc26-gost-3410-12-256-paramSetB должна подойти.

--8<--# 1 [                                  8a41d3c09 | main.py ]--8<--
  35 public_key_info = certificate['tbs_certificate']['subject_public_key_info']
  37 public_key_bytes = public_key_info['public_key'].native
  39 pub = pub_unmarshal(public_key_bytes)
--8<----------------------------------------------------------------8<--

В ГОСТовых сертификатах хранится не сырое значение 512/1024 бит значение
ключа, а обёрнутое в ASN.1 OCTET STRING. pub_unmarshal просто преобразует
набор байт которые вы ему предоставили и не проверяет всё ли с ними корректно.

--8<--# 2 [                                  8a41d3c09 | main.py ]--8<--
  42 dgst = gost34112012256.new(pdf_data).digest()
  44 # Читаем подпись из файла .sig в бинарном режиме
  45 signer_info = signed_data['signer_infos'][0]
  46 signature = signer_info['signature'].native
  56 # Проверяем подпись
  57 result = verify(curve, pub, dgst, signature)
--8<----------------------------------------------------------------8<--
Так проверка подписи точно не пройдёт, так как у вас современный CMS,
где подпись делается не над detached данными, а над структурой
signedAttrs из CMS (содержащей id_pkcs9_at_contentType,
id_pkcs9_at_signingTime, id_pkcs9_at_messageDigest,
id_aa_signingCertificateV2 атрибуты). Необходимо проверять подпись над
этой signedAttrs структуры закодированной в DER, без IMPLICIT ASN.1 tag.
Как это сделать asn1crypto -- понятия не имею. Есть много примеров
работы с ГОСТ CMS и сертификатами в PyDERASN проекте:
http://www.pyderasn.cypherpunks.su/ -- там же и проверка совместно с
PyGOST напротив многочисленных примеров из ТК26 рекомендаций.

Хэш PDF-ки можно проверить напротив атрибута id_pkcs9_at_messageDigest.
Возможно хэш придётся перевернуть (у нас с ГОСТовыми алгоритмами разброд
и шатание касательно endianness).

-- 
Sergey Matveev (http://www.stargrave.org/)
LibrePGP: 12AD 3268 9C66 0D42 6967  FD75 CB82 0563 2107 AD8A