public inbox for gost@lists.cypherpunks.ru
Atom feed
From: Sergey Matveev <stargrave@stargrave•org>
To: gost@lists.cypherpunks.ru
Subject: Re: [GOST] Формат подписи CMS в pygost
Date: Thu, 30 Nov 2017 23:05:53 +0300 [thread overview]
Message-ID: <20171130200553.GA41502@stargrave.org> (raw)
In-Reply-To: <adbc3507-a2e9-63c2-bfcd-1e1c95895c84@s-c300.com>
[-- Attachment #1: Type: text/plain, Size: 12683 bytes --]
Приветствую!
*** Гневашев Дмитрий [2017-11-30 19:11]:
>У меня проблема в том, что принимающая сторона отклонила запрос на сертификат
>с такой формулировкой:
>
>"чтобы получить CMS формат, нужно использовать функцию CryptSignMessage или
>функцию CryptSignHash , но инвертировать байты самостоятельно"
Тут видимо они намекают на какой-то продукт/решение/программу -- тут уж
ничего не могу сказать.
>Есть ли в pygost возможность подписывать документы в формате CMS?
Вы делаете документ CMS (видимо, SignedData) и вопрос может ли pygost
сделать подпись которая окажется внутри SignerInfo этого CMS? Если так,
то он из коробки делает подпись которая без проблем засовывается в
SignerInfo как есть. Возможно только одно но: хэш, скорее всего, нужно
будет перевернуть перед подписью. Например:
hasher = GOST34112012(digest_size=32)
hasher.update(b'data to be signed')
curve = gost3410.GOST3410Curve(*gost3410.CURVE_PARAMS['GostR3410_2012_TC26_ParamSetA'])
prv_key = gost3410.prv_unmarshal(urandom(32))
signature = gost3410.sign(curve, prv_key, hasher.digest()[::-1], mode=2012)
Если же речь вообще про создание CMS, то pygost этого не умеет и вряд ли
будет, так как это уже не касается криптографических алгоритмов как
таковых, а форматов представления данных. CMS это RFC 5652 на 56 страниц
(плюс ещё ряд RFC касающихся использования ГОСТ алгоритмов в контексте
CMS) -- формат для описания того как подписывать, шифровать,
аутентифицировать и проверять целостность документов, используя ASN.1
DER кодек.
Примерно, в общих чертах, CMS с подписанными данными делается как-то так
ниже. Наверняка он не валиден (я версию выставил наобум) и что-то сделал
не так, но общий смысл создания примерно описан ниже. Использую
библиотеку PyDERASN (http://pyderasn.cypherpunks.ru/) для работы с ASN.1.
Идентификатор публичного ключа подписанта я делаю urandom(20) -- а
должен браться из расширения X.509 сертификата.
------------------------ >8 ------------------------
from base64 import b64encode
from os import urandom
from pyderasn import Any
from pyderasn import Choice
from pyderasn import Integer
from pyderasn import Null
from pyderasn import ObjectIdentifier
from pyderasn import OctetString
from pyderasn import Sequence
from pyderasn import SetOf
from pyderasn import tag_ctxc
from pyderasn import tag_ctxp
from pygost import gost3410
from pygost.gost34112012 import GOST34112012
class KeyIdentifier(OctetString):
pass
class SubjectKeyIdentifier(KeyIdentifier):
pass
class SignerIdentifier(Choice):
schema = (
('subjectKeyIdentifier', SubjectKeyIdentifier(impl=tag_ctxp(0))),
)
class SignatureValue(OctetString):
pass
class AttributeValue(Any):
pass
class AttributeValues(SetOf):
schema = AttributeValue()
class Attribute(Sequence):
schema = (
('attrType', ObjectIdentifier()),
('attrValues', AttributeValues()),
)
class SignedAttributes(SetOf):
schema = Attribute()
bounds = (1, 32)
class CMSVersion(Integer):
schema = (
('v0', 0),
)
class AlgorithmIdentifier(Sequence):
schema = (
('algorithm', ObjectIdentifier()),
('parameters', Any(optional=True)),
)
class SignatureAlgorithmIdentifier(AlgorithmIdentifier):
pass
class DigestAlgorithmIdentifier(AlgorithmIdentifier):
pass
class SignerInfo(Sequence):
schema = (
('version', CMSVersion()),
('sid', SignerIdentifier()),
('digestAlgorithm', DigestAlgorithmIdentifier()),
('signedAttrs', SignedAttributes(impl=tag_ctxc(0), optional=True)),
('signatureAlgorithm', SignatureAlgorithmIdentifier()),
('signature', SignatureValue()),
)
class SignerInfos(SetOf):
schema = SignerInfo()
class DigestAlgorithmIdentifiers(SetOf):
schema = AlgorithmIdentifier()
class ContentType(ObjectIdentifier):
pass
class EncapsulatedContentInfo(Sequence):
schema = (
('eContentType', ContentType()),
('eContent', OctetString(expl=tag_ctxc(0), optional=True)),
)
class SignedData(Sequence):
schema = (
('version', CMSVersion()),
('digestAlgorithms', DigestAlgorithmIdentifiers()),
('encapContentInfo', EncapsulatedContentInfo()),
('signerInfos', SignerInfos()),
)
class ContentInfo(Sequence):
schema = (
('contentType', ContentType()),
('content', Any(expl=tag_ctxc(0))),
)
id_contentType = ObjectIdentifier('1.2.840.113549.1.9.3')
id_messageDigest = ObjectIdentifier('1.2.840.113549.1.9.4')
id_data = ObjectIdentifier('1.2.840.113549.1.7.1')
id_signedData = ObjectIdentifier('1.2.840.113549.1.7.2')
id_tc26_signwithdigest_gost3410_2012_256 = ObjectIdentifier('1.2.643.7.1.1.3.2')
id_tc26_gost3411_2012_256 = ObjectIdentifier('1.2.643.7.1.1.2.2')
ai_tc26_signwithdigest_gost3410_2012_256 = AlgorithmIdentifier()
ai_tc26_signwithdigest_gost3410_2012_256['algorithm'] = id_tc26_signwithdigest_gost3410_2012_256
ai_tc26_gost3411_2012_256 = AlgorithmIdentifier()
ai_tc26_gost3411_2012_256['algorithm'] = id_tc26_gost3411_2012_256
ai_tc26_gost3411_2012_256['parameters'] = Any(Null())
eci = EncapsulatedContentInfo()
eci['eContentType'] = ContentType(id_data)
eci['eContent'] = OctetString(b'data to be signed')
hasher = GOST34112012(digest_size=32)
hasher.update(bytes(eci['eContent']))
signed_attrs = SignedAttributes()
for t, v in (
(id_contentType, eci['eContentType']),
(id_messageDigest, OctetString(hasher.digest())),
):
signed_attr = Attribute()
signed_attr['attrType'] = t
signed_attr['attrValues'] = AttributeValues((AttributeValue(v),))
prv_key = gost3410.prv_unmarshal(urandom(32))
curve = gost3410.GOST3410Curve(
*gost3410.CURVE_PARAMS['GostR3410_2001_CryptoPro_A_ParamSet']
)
hasher = GOST34112012(digest_size=32)
hasher.update(signed_attrs.encode())
signature = gost3410.sign(curve, prv_key, hasher.digest()[::-1], mode=2001)
si = SignerInfo()
si['version'] = CMSVersion('v0')
si['sid'] = SignerIdentifier(('subjectKeyIdentifier', SubjectKeyIdentifier(urandom(20))))
si['digestAlgorithm'] = DigestAlgorithmIdentifier(ai_tc26_gost3411_2012_256)
si['signatureAlgorithm'] = SignatureAlgorithmIdentifier(
ai_tc26_signwithdigest_gost3410_2012_256
)
si['signature'] = SignatureValue(signature)
sd = SignedData()
sd['version'] = CMSVersion('v0')
sd['digestAlgorithms'] = DigestAlgorithmIdentifiers((ai_tc26_gost3411_2012_256,))
sd['encapContentInfo'] = eci
sd['signerInfos'] = SignerInfos((si,))
ci = ContentInfo()
ci['contentType'] = ContentType(id_signedData)
ci['content'] = Any(sd)
print(b64encode(ci.encode()))
------------------------ >8 ------------------------
В итоге получится например вот такой вот CMS:
MIG/BgkqhkiG9w0BBwKggbEwga4CAQAxDjAMBggqhQMHAQECAgUAMCAGCSqGSIb3DQEHAaAT
BBFkYXRhIHRvIGJlIHNpZ25lZDF3MHUCAQCAFGXAA/03TMbk3IX+JpPXm7BchKefMAwGCCqF
AwcBAQICBQAwCgYIKoUDBwEBAwIEQO+QDSE4Ayt0YzGnTw178X0ruXlM6FDWGq2tJXpWwGnB
TAYTi4AGwbIMfCzeanbfMWwv6lvTDc7XTUGZ/UjcxYY=
внутренности которого выглядят как-то так:
------------------------ >8 ------------------------
0 [1,2, 191] ContentInfo SEQUENCE
3 [1,1, 9] . contentType: ContentType OBJECT IDENTIFIER id_signedData (1.2.840.113549.1.7.2)
17-3 [0,0, 177] . content: [0] EXPLICIT [UNIV 16] ANY
. . 30:81:AE:02:01:00:31:0E:30:0C:06:08:2A:85:03:07
. . 01:01:02:02:05:00:30:20:06:09:2A:86:48:86:F7:0D
. . 01:07:01:A0:13:04:11:64:61:74:61:20:74:6F:20:62
. . 65:20:73:69:67:6E:65:64:31:77:30:75:02:01:00:80
. . 14:65:C0:03:FD:37:4C:C6:E4:DC:85:FE:26:93:D7:9B
. . B0:5C:84:A7:9F:30:0C:06:08:2A:85:03:07:01:01:02
. . 02:05:00:30:0A:06:08:2A:85:03:07:01:01:03:02:04
. . 40:EF:90:0D:21:38:03:2B:74:63:31:A7:4F:0D:7B:F1
. . 7D:2B:B9:79:4C:E8:50:D6:1A:AD:AD:25:7A:56:C0:69
. . C1:4C:06:13:8B:80:06:C1:B2:0C:7C:2C:DE:6A:76:DF
. . 31:6C:2F:EA:5B:D3:0D:CE:D7:4D:41:99:FD:48:DC:C5
. . 86
14 [1,2, 174] . . DEFINED BY (1.2.840.113549.1.7.2): SignedData SEQUENCE
17 [1,1, 1] . . . version: CMSVersion INTEGER v0
20 [1,1, 14] . . . digestAlgorithms: DigestAlgorithmIdentifiers SET OF
22 [1,1, 12] . . . . 0: AlgorithmIdentifier SEQUENCE
24 [1,1, 8] . . . . . algorithm: OBJECT IDENTIFIER id_tc26_gost3411_2012_256 (1.2.643.7.1.1.2.2)
34 [0,0, 2] . . . . . parameters: [UNIV 5] ANY OPTIONAL
. . . . . . 05:00
36 [1,1, 32] . . . encapContentInfo: EncapsulatedContentInfo SEQUENCE
38 [1,1, 9] . . . . eContentType: ContentType OBJECT IDENTIFIER id_data (1.2.840.113549.1.7.1)
51-2 [1,1, 17] . . . . eContent: [0] EXPLICIT OCTET STRING 17 bytes OPTIONAL
. . . . . 64:61:74:61:20:74:6F:20:62:65:20:73:69:67:6E:65
. . . . . 64
70 [1,1, 119] . . . signerInfos: SignerInfos SET OF
72 [1,1, 117] . . . . 0: SignerInfo SEQUENCE
74 [1,1, 1] . . . . . version: CMSVersion INTEGER v0
77 [0,0, 22] . . . . . sid: SignerIdentifier CHOICE subjectKeyIdentifier
77 [1,1, 20] . . . . . . subjectKeyIdentifier: [0] SubjectKeyIdentifier OCTET STRING 20 bytes
. . . . . . . 65:C0:03:FD:37:4C:C6:E4:DC:85:FE:26:93:D7:9B:B0
. . . . . . . 5C:84:A7:9F
99 [1,1, 12] . . . . . digestAlgorithm: DigestAlgorithmIdentifier SEQUENCE
101 [1,1, 8] . . . . . . algorithm: OBJECT IDENTIFIER id_tc26_gost3411_2012_256 (1.2.643.7.1.1.2.2)
111 [0,0, 2] . . . . . . parameters: [UNIV 5] ANY OPTIONAL
. . . . . . . 05:00
113 [1,1, 10] . . . . . signatureAlgorithm: SignatureAlgorithmIdentifier SEQUENCE
115 [1,1, 8] . . . . . . algorithm: OBJECT IDENTIFIER id_tc26_signwithdigest_gost3410_2012_256 (1.2.643.7.1.1.3.2)
125 [1,1, 64] . . . . . signature: SignatureValue OCTET STRING 64 bytes
. . . . . . EF:90:0D:21:38:03:2B:74:63:31:A7:4F:0D:7B:F1:7D
. . . . . . 2B:B9:79:4C:E8:50:D6:1A:AD:AD:25:7A:56:C0:69:C1
. . . . . . 4C:06:13:8B:80:06:C1:B2:0C:7C:2C:DE:6A:76:DF:31
. . . . . . 6C:2F:EA:5B:D3:0D:CE:D7:4D:41:99:FD:48:DC:C5:86
------------------------ >8 ------------------------
Это всего-лишь подписанная 34.10-2012 256-бит с 34.11-2012 256-бит
строчка "data to be signed". CMS это очень не тривиально, плюс оно
разных версий бывает (CMSVersion).
>Какой смысл в функции prv_unmarshal? Она инвертирует байты ключа? Для чего
>она нужна?
Если честно, то уже смутно помню, но вроде бы сделана потому что видел
что приватные 34.10 ключи хранят в виде little-endian значений и эта
функция преобразует набор байт в little-endian число (bytes2long это
big-endian -- поэтому предварительно делается кручение).
--
Sergey Matveev (http://www.stargrave.org/)
OpenPGP: CF60 E89A 5923 1E76 E263 6422 AE1A 8109 E498 57EF
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2017-11-30 20:06 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-11-30 16:05 [GOST] Формат подписи CMS в pygost Гневашев Дмитрий
2017-11-30 20:05 ` Sergey Matveev [this message]
2017-12-01 7:42 ` Гневашев Дмитрий
2017-12-01 8:10 ` Sergey Matveev
2017-12-06 9:47 ` Гневашев Дмитрий
2017-12-06 9:57 ` Sergey Matveev
2017-12-06 11:37 ` Гневашев Дмитрий
2017-12-06 11:44 ` Sergey Matveev