def test_add_remove_cert(): ''' Test add_cert Метод `CertStore.add_cert(cert) добавляет сертификат `cert` в хранилище. Объект сертификата может быть загружен из сообщения, взят из другого хранилища или получен дублированием с помощью функции `Cert.duplicate()`. Если экземпляр сертификата привязан к хранилищу, его можно удалить оттуда вызовом метода `Cert.remove_from_store()`. При этом им можно продолжать пользоваться, пока он не будет удален из памяти. ''' my = csp.CertStore(None, b"MY") n1 = len(list(my)) with open(case_path('logical.cms'), 'rb') as f: testdata = f.read() msg = csp.CryptMsg(testdata) ids = [] for crt in csp.CertStore(msg): my.add_cert(crt) my.add_cert(crt.duplicate()) ids.append(crt.thumbprint()) assert len(ids) for cert_id in ids: cs = list(my.find_by_thumb(cert_id)) for cert in cs: cert.remove_from_store() print(len(list(my)), n1) assert len(list(my)) == n1
def test_memory_store(): ''' Test: memory store Хранилище сертификатов может быть создано в памяти вызовом конструктора `CertStore()` без параметров. ''' my = csp.CertStore(None, b"MY") cert = list(my.find_by_name(test_cn))[0] cs = csp.CertStore() # метод `CertStore.add_cert(c)` добавляет сертификат `c` в хранилище. cs.add_cert(cert) assert len(list(cs))
def setup_package(): ''' Создание тестового ключевого контейнера и сертификата. ''' assert cryptoapi.gen_key(test_container, local=test_local) cs = csp.CertStore(None, b"MY") certs = list(cs.find_by_name(test_cn)) if not certs: if not os.path.isfile(test_cer_fn): req_params = dict( Attributes=[(CN, test_cn)], KeyUsage=[ 'dataEncipherment', 'nonRepudiation', 'keyEncipherment', 'digitalSignature' ], EKU=[ csp.szOID_PKIX_KP_EMAIL_PROTECTION, csp.szOID_PKIX_KP_CLIENT_AUTH ], # CertificatePolicies=[('1.2.643.100.113.1', []), # ('1.2.643.100.113.2', [])], ) request = cryptoapi.create_request(test_container, req_params, local=test_local) open(test_req_fn, 'wb').write(b64encode(request)) print(''' Creating certificate request in file '{req}'. Submit request to CA and save certificate in file '{cer}'. Then re-run tests. '''.format(req=test_req_fn, cer=test_cer_fn)) assert False else: cert = open(test_cer_fn, 'rb').read() cryptoapi.bind_cert_to_key(test_container, cert, local=test_local) os.remove(test_cer_fn)
def test_store_iter(): ''' Итерация по контейнеру перебирает все хранящиеся в нем сертификаты. ''' cs = csp.CertStore(None, b"MY") for c in cs: assert c
def test_sign_data(): ''' При создании нового пустого сообщения, конструктору передается либо контекст, либо конструктор вызывается без параметров. Метод `CryptMsg.add_signer_cert(c)` сохраняет в сообщении сертификат `c`, которым будет подписано сообщение. Таким образом можно добавлять несколько подписантов и их сертификатов. Метод `CryptMsg.sign_data(s)` создает сообщение, закодированное в PKCS7, подписанное всеми сертификатами из списка. Подписи и сертификаты входят в байтовую строку, которая возвращается функцией. ''' ctx = csp.Crypt( test_container, PROV_GOST, 0, test_provider ) cs = csp.CertStore(ctx, b"MY") cert = list(cs.find_by_name(test_cn))[0] mess = csp.CryptMsg() print(csp.CertInfo(cert).name()) data = mess.sign_data(b'hurblewurble', cert, False) assert len(data) return data
def test_store_in_context(): ''' Первый параметр позволяет связать хранилище с контекстом провайдера. ''' context = test_context_simple() cs = csp.CertStore(context, b"MY") assert cs
def test_verify_hash(): sign, data = test_sign_hash() ctx = test_cert_acquire_key() cs = csp.CertStore(ctx, b"MY") cert = list(c for c in cs.find_by_name(bytes(test_cn)) if csp.CertInfo(c).name() == b'CN=' + test_cn)[0] length = 0 if test_cn.endswith(b'2012') else 2001 hash1 = csp.Hash(ctx, data, length) assert hash1.verify(cert, sign)
def main(): global ctxname try: ctx = csp.Crypt(b'{0}'.format(ctxname), csp.PROV_GOST_2001_DH, 0 | silent, provider) except: ctx = None if ctx is None: ctx = csp.Crypt( b'{0}'.format(ctxname), csp.PROV_GOST_2001_DH, csp.CRYPT_NEWKEYSET | silent, provider) msg = csp.CryptMsg(ctx) cs = csp.CertStore(ctx, b"My") rec_c = list(cs.find_by_name(b'123456789abcdef'))[0] ci = csp.CertInfo(rec_c) print(ci.name(), ci.issuer(), hexlify(ci.serial())) print(ci.not_before(), ci.not_after()) msg.add_recipient(rec_c) data = msg.encrypt_data(b'Test byte string') print(len(data)) #rec_c = list(cs.find_by_name(b'test'))[0] cs2 = csp.CertStore() cs2.add_cert(rec_c) encrypted = csp.CryptMsg(data) print('type:', encrypted.get_type()) print('data:', encrypted.get_data()) return_data = encrypted.decrypt(cs2) print(return_data) signed = msg.sign_data(b'Test signed data', rec_c) print(len(signed)) msg2 = csp.CryptMsg(signed, ctx) info = csp.CertInfo(msg2, 0) print('version:', info.version()) print('type:', msg2.get_type()) print(msg2.verify(0)) detached = msg.sign_data(b'Test signed data', rec_c, True) print(len(detached)) sig = csp.Signature(detached, ctx) print(sig.verify_data(b'Test signed data', 0)) print(sig.verify_data(b'Test zigned Data', 0)) sig2 = csp.CryptMsg(open('tests/data1.p7s', 'rb').read()) print('sig type:', sig2.get_type()) print('sig data:', sig2.get_data())
def test_certinfo_sign_algorithm(): ''' Test: sign algorithm Метод `CertInfo.sign_algorithm()` возвращает идентификатор алгоритма ЭЦП. ''' cs = csp.CertStore(None, b"MY") cert = list(cs.find_by_name(test_cn))[0] assert csp.CertInfo(cert).sign_algorithm() == '1.2.643.2.2.3'
def test_extract_cert(): ''' Метод `Cert.extract()` возвращает закодированный сертификат в виде байтовой строки. ''' cs = csp.CertStore(None, b"MY") cert = list(c for c in cs.find_by_name(bytes(test_cn)) if csp.CertInfo(c).name() == b'CN=' + test_cn)[0] cdata = cert.extract() assert len(cdata) return cdata
def test_import_public_key_info(): context = csp.Crypt(test_container, PROV_GOST, 0, test_provider) cs = csp.CertStore(None, b"MY") cert = list(c for c in cs.find_by_name(bytes(test_cn)) if csp.CertInfo(c).name() == b'CN=' + test_cn)[0] pkey = context.import_public_key_info(cert) assert pkey keyBlob = pkey.encode(None) print(len(keyBlob)) assert len(keyBlob) >= 100
def test_cert_subject_id(): ''' Метод `Cert.subject_id()` возвращает идентификатор SubjectKeyId сертификата в виде бинарной строки. Для перевода в обычную строку, может потребоваться кодирование в base64 или вызов функции `hexlify()`. ''' cs = csp.CertStore(None, b"MY") ids = [cert.subject_id() for cert in cs] assert len(ids) and all(len(x) == 20 for x in ids)
def test_cert_from_detached(): ''' Конструктору хранилища сертификатов `CertStore(msg)` можно передать экземпляр класса `CryptMsg` или `Signature`. Через это хранилище можно извлекать хранящиеся в сообщении или подписи сертификаты. ''' data = test_detached_sign() sgn = csp.Signature(data) cs = csp.CertStore(sgn) assert len(list(cs))
def test_certinfo_issuer(): ''' Test: issuer Метод `Cert.issuer()` возвращает информацию о том, кто выдал сертификат, работает аналогично `Cert.name()`. ''' cs = csp.CertStore(None, b"MY") issuers = list(csp.CertInfo(cert).issuer() for cert in cs) print((issuers)) assert all(s for s in issuers)
def test_duplicate_cert(): ''' Метод `Cert.duplicate()` создает веременную копию серта, не сохраняемую в хранилище. ''' cs = csp.CertStore(None, b"MY") for c in cs: cdup = c.duplicate() print((b64encode(c.thumbprint()))) print((b64encode(cdup.thumbprint())))
def _cert_thumb(): ''' Метод `Cert.thumb()` возвращает отпечаток сертификата в виде бинарной строки. Для перевода в обычную строку, может потребоваться кодирование в base64. ''' cs = csp.CertStore(None, b"MY") thumbs = [cert.thumbprint() for cert in cs] assert thumbs return thumbs[0]
def test_cert_find_by_name(): ''' Test: find by name Метод `CertStore.find_by_name(s)` перечисляет все сертификаты, в RDN которых так или иначе встречается байтовая строка `s`. ''' name = test_cn cs = csp.CertStore(None, b"MY") res = list(cs.find_by_name(name)) assert len(res)
def test_cert_find_by_thumb(): ''' Test: find by thumb Метод `CertStore.find_by_thumb(s)` перечисляет все сертификаты с отпечатком, равным байтовой строке `s`. ''' thumb = _cert_thumb() cs = csp.CertStore(None, b"MY") res = list(cs.find_by_thumb(thumb)) assert len(res)
def test_certinfo_name(): ''' Метод `Cert.name()` возвращает строку с RDN сертификата в виде бинарной строки. Для дальнейшей работы с ней может потребоваться модуль `RDN` и ее перекодирование в unicode. ''' cs = csp.CertStore(None, b"MY") names = list(csp.CertInfo(cert).name() for cert in cs) print(names) assert all(name for name in names)
def test_cert_acquire_key(): ''' Контекст криптопровайдера может быть создан путем вызова конструктора на объект `Cert`. При этом вернется контекст, связанный с контейнером ключа данного сертификата. Если получить ключ не удастся, будет брошено исключение `ValueError`. ''' cs = csp.CertStore(None, b"MY") cert = list(c for c in cs.find_by_name(bytes(test_cn)) if csp.CertInfo(c).name() == b'CN=' + test_cn)[0] ctx = csp.Crypt(cert) return ctx
def test_cert_from_data(): ''' Конструктор `Cert(s)`, при передаче ему байтовой строки `s`, декодирует и загружает из нее новый экземпляр сертификата, не сохраненный в хранилище. При необходимости его можно туда добавить функцией `CertStore.add_cert()`. ''' cdata = test_extract_cert() print(len(cdata)) newc = csp.Cert(cdata) assert newc memstore = csp.CertStore() memstore.add_cert(newc) assert len(list(memstore)) == 1
def test_encrypt_data(): ''' Шифрование сообщения представлено методом `CryptMsg.encrypt_data(s)`, где `s` -- бинарная строка. Результатом является шифрованный блок бинарных данных. Предварительно необходимо задать один или более сертификатов получателя функцией `CryptMsg.add_recipient_cert(cert)`. В Python 3 необходимо перевести шифруемую строку в какую-нибудь 8-битную кодировку. ''' cs = csp.CertStore(None, b"MY") re_cert = list(cs.find_by_name(test_cn))[0] msg = csp.CryptMsg() msg.add_recipient(re_cert) data = msg.encrypt_data(bindata) assert data return data
def test_decrypt_data(): ''' Зашифрованный блок данных можно передавать по незащищенному каналу. Расшифровка производится созданием пустого экземпляра `CryptMsg` и вызовом от него функции `CryptMsg.decrypt_data(data)`, где `data` -- бинарная строка с шифрованными данными. Пустое сообщение можно создать вызовом коструктора без параметрое, либо передать конструктору контекст криптопровайдера. Тогда ключи расшифровки будут извлекаться из хранилища ключей для этого контекста. ''' data = test_encrypt_data() decrcs = csp.CertStore(None, b'MY') msg = csp.CryptMsg(data) assert msg res = msg.decrypt(decrcs) assert res == bindata
def test_verify_file(): ''' Проверка отсоединенной подписи, созданной через командную строку. ''' names = ('data1', 'data2') for name in names: with open(case_path('{0}.bin'.format(name)), 'rb') as f: data = f.read() with open(case_path('{0}.p7s'.format(name)), 'rb') as f: sigdata = f.read() sign = csp.Signature(sigdata) print(name, sign.num_signers()) if sign.num_signers() <= 0: continue for c in csp.CertStore(sign): print(b64encode(c.thumbprint())) assert all(sign.verify_data(data, n) for n in range(sign.num_signers()))
def _test_verifying(): thumb = get_test_thumb() cert = cryptoapi.get_certificate(thumb) cs = csp.CertStore(None, b'My') wrong_certs = list(x.extract() for x in cs if hexlify(x.thumbprint()) != thumb) sig = cryptoapi.sign(thumb, msg, False) assert sig assert cryptoapi.check_signature(cert, sig, msg) assert not cryptoapi.check_signature(cert, sig, msg[:-1]) assert cryptoapi.check_signature(None, sig, msg) assert not cryptoapi.check_signature(None, sig, msg[:-1]) if len(wrong_certs): assert not any( cryptoapi.check_signature(c, sig, msg) for c in wrong_certs) return sig
def tease(s): for f in range(10): cs = csp.CertStore(None, "MY") lst = list(cs) print(len(lst)) del lst del cs for n in range(10): cc = csp.Cert(s) print(list(cc.eku())) del cc for n in range(100): cont = b'123456789abcdefj' ctx = csp.Crypt(cont, 75, 0) del ctx req = cryptoapi.create_request(cont, req_params) del req del cont
def test_detached_sign(): ''' Второй необязательный параметр `detach` функции CryptMsg.sign_data()` управляет режимом подписывания. При вызове со значение `True` функция подписи вернет не полное подписанное сообщение, а только подпись, которую можно хранить отдельно от подписанных ей данных. ''' ctx = csp.Crypt( test_container, PROV_GOST, 0, test_provider ) assert ctx cs = csp.CertStore(None, b"MY") cert = list(cs.find_by_name(test_cn))[0] mess = csp.CryptMsg() data = mess.sign_data(b'hurblewurble', cert, True) assert len(data) return data
def test_detached_sign2(): ''' Класс `Signature` наследует всю функциональность `CryptMsg`, но ориентирован на работу с отсоединенными подписями. Поэтому метод `Signature.sign_data()` по умолчанию возвращает отсоединенную подпись. ''' ctx = csp.Crypt( test_container, PROV_GOST, 0, test_provider ) assert ctx cs = csp.CertStore(ctx, b"MY") cert = list(cs.find_by_name(test_cn))[0] # создание новой (пустой) отсоединенной подписи sgn = csp.Signature(ctx) # подписывание данных экземпляром `Signature` по умолчанию дает # отсоединенную подпись. data = sgn.sign_data(b'hurblewurble', cert) assert len(data) return data
def test_encrypt_decrypt(): thumb = get_test_thumb() cert = cryptoapi.get_certificate(thumb) cs = csp.CertStore(None, b'My') certs = [] for x in ('res1.cer', ): certs.append(open(case_path(x), 'rb').read()) certs.append(cert) wrong_thumbs = list(t for t in (hexlify(c.thumbprint()) for c in cs) if t != thumb) encrypted_data = cryptoapi.encrypt(certs, msg) open('encrypted_data.bin', 'wb').write(encrypted_data) assert encrypted_data decrypted_data = cryptoapi.decrypt(encrypted_data, thumb) assert msg == decrypted_data bad_thumbs = [] for th in wrong_thumbs[:1]: try: cryptoapi.decrypt(encrypted_data, th) except Exception: bad_thumbs.append(th) assert bad_thumbs == wrong_thumbs[:1]
def test_msg_signatures(): ''' Поле `CryptMsg.num_signers` содержит количество подписантов сообщения. Функция `CryptMsg.get_nth_signer_info(n)` возвращает структуру, уникально идентифицирующую сертификат `n`-го подписанта. По ней можно получить сертификат подписанта, либо из хранилища в сообщении, либо из системного хранилища. Для этого служит функция `CertStore.get_cert_by_info()`. Если сертификата нет в хранилище, функция возвращает `None`. Полученный таким образом сертификат `c` можно использовать для проверки подписи под сообщением с помощью функции `CryptMsg.verify_cert(c)`. Функция возвращает `True`, если для данного сертификата соответствующая ему подпись верна. Для упрощения обработки сертификатов, функция `CryptMsg.signer_certs()` перечисляет сертификаты всех подписантов сообщения. ''' ctx = csp.Crypt( b'', PROV_GOST, csp.CRYPT_VERIFYCONTEXT, test_provider ) testdata = test_sign_data() # testdata = open('tests/logical.cms', 'rb').read() # загрузка сообщения из блоба данных msg = csp.CryptMsg(testdata, ctx) del testdata # сведения о раскодированном сообщении print(msg.num_signers()) print(len(msg.get_data())) # TODO переделать тесты ниже return # Идентификационная информация для 1-го подписанта. psi = msg.get_nth_signer_info(0) assert psi my = csp.CertStore(msg) sys_my = csp.CertStore(None, "MY") # Из сообщения можно извлечь сертификат по этой информации verify_cert = my.get_cert_by_info(psi) t1 = verify_cert.thumbprint() verify_cert = my.get_cert_by_info(psi) # Сертификат можно извлечь так же из системного хранилища, если он там # есть. same_cert = sys_my.get_cert_by_info(psi) t2 = same_cert.thumbprint() assert t1 == t2 # разнообразные проверки подписей через сертификаты print(verify_cert.name()) assert msg.verify_cert(verify_cert) ns = list(c.name() for c in msg.signer_certs()) assert len(ns) cs = list(csp.CertStore(msg)) print([(msg.verify_cert(x), x.name()) for x in cs]) assert all(msg.verify_cert(c) for c in cs)