Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/0039 rfc5126: support for CADES-BES/EPES with long term support #88

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6cb719d
1st commit for CADES RFC 5126. See #39.
erny Feb 4, 2018
53cd8bd
See #39. Replace ASN.1 with Annex A.1.
erny Feb 4, 2018
9a6c52e
Fix docs ASN.1 spec. See #39.
erny Feb 4, 2018
33d54ae
See #39. Fix some CMS attribute definitions.
erny Feb 4, 2018
6756651
Fix SetOfSignaturePolicy. See #39.
erny Feb 4, 2018
15deb18
See #39. Add commitment_type CMS Attribute.
erny Feb 4, 2018
5a12611
SignerLocation and ContentTimeStamp. See #39.
erny Feb 5, 2018
a49b423
signer-attributes attribute. See #39.
erny Feb 5, 2018
3a9b9dc
complete-certificate-references attr. See #39.
erny Feb 5, 2018
1161544
complete-revocation-references attr. See #39.
erny Feb 12, 2018
8e0c6f5
Make attribute names more uniform. See #39.
erny Feb 14, 2018
96965e4
Refactor for reuse. See #39.
erny Feb 14, 2018
083563a
Add rest of attributes. See #39.
erny Feb 14, 2018
0403794
Add missing explicit options. See #39.
erny Feb 14, 2018
297233e
Convert all key names to under_score_notation.
erny Mar 17, 2018
48a53b9
Some test data. See #39.
erny Mar 18, 2018
59c9d6f
make TODO of attributes to check / implement.
erny Apr 4, 2018
c88fe38
Import ContentInfo for direct use. See #39.
erny Apr 4, 2018
272af31
Remove objects not defined from comment. See #39.
erny Apr 4, 2018
e9e0989
Consider old timestamp attribute. See #39.
erny Apr 4, 2018
866e907
Basic parse test for CADES BES. See #39.
erny Apr 4, 2018
86c158c
Remove ipdb ;-)
erny Apr 4, 2018
61f5e65
Check signed content. See #39.
erny Apr 4, 2018
2ab1bf8
Implement additional ESS attributes. See #39.
erny Apr 4, 2018
cb95a1e
Some additional (cert, signing_time). See #39.
erny Apr 4, 2018
39f9937
Add tests for EPES and fix some attribute bugs. See #39.
erny Apr 4, 2018
e7d1e33
Add tests for CADES-EPES-A-Explicit. See #39.
erny Apr 5, 2018
8b51337
Test files for CADES EPES signatures. See #39.
erny Jan 27, 2018
5b74151
Replace test file. See #39.
erny Apr 5, 2018
b5c4d11
Fix strings for python3 in tests. See #39.
erny Apr 5, 2018
919b170
Python 2.6 compat in tests. See #39.
erny Apr 5, 2018
3bf7290
Python 3.3 and 2.6 in tests. See #39.
erny Apr 5, 2018
64d4662
Reduce line length for ci tests. See #39.
erny Apr 5, 2018
2734539
Python 3.2 compat of binary string. See #39.
erny Apr 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
971 changes: 971 additions & 0 deletions asn1crypto/cades.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def test_classes():

from .test_algos import AlgoTests
from .test_cms import CMSTests
from .test_cades import CADESTests
from .test_crl import CRLTests
from .test_csr import CSRTests
from .test_keys import KeysTests
Expand All @@ -54,6 +55,7 @@ def test_classes():
return [
AlgoTests,
CMSTests,
CADESTests,
CRLTests,
CSRTests,
KeysTests,
Expand Down
Binary file added tests/fixtures/cades/cades-bes-implicit.csig
Binary file not shown.
Binary file added tests/fixtures/cades/cades-bes-implicit.der
Binary file not shown.
Binary file added tests/fixtures/cades/cades-epes-a-explicit.csig
Binary file not shown.
Binary file added tests/fixtures/cades/cades-epes-a-explicit.der
Binary file not shown.
Binary file added tests/fixtures/cades/cades-epes-explicit.csig
Binary file not shown.
Binary file added tests/fixtures/cades/cades-epes-explicit.der
Binary file not shown.
59 changes: 59 additions & 0 deletions tests/fixtures/cades/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# CAdES test signatures

## About

This directory includes real-world signatures, i.e. the signatures:

* Have been created using a valid trans-european qualified certificate, valid at the time of writing
* Have been created using then open source AutoFirma 1.5/1.6 signature client (cliente @firma project) supported by the spanish government
* The A / XL formats have been created by a official Trust Service Provide (TSP) (Ministry for Public Administrations)

## CAdES Intro

CMS Advanced Electronic Signatures (CAdES) is a signature format (as efined by European Telecommunications Standards Institute, ETSI TS 101 733 and RFC 5126) which is basically aimed at long term signature validations, i.e. it should be possible to validate signatures long time after the signing certificate has expired. Whenever signature / digest algorithms get weak, signatures can be sealed again using more modern ones.

For this purpose, full timestamped verification data (OCSP responses/CRL data) with complete certificates are added to data structure and sealed by an external trusted service.

## Policies

A *Signature Policy* makes explicit under what conditions signatures are accomplished and how they should be verified.

CAdES signatures may have or not explicit signature policy:

* BES: Basic Enhanced Signature: No policy info
* EPES: Excplict Policy Enhanced Signature: An explicit policy is given through a URI, normally a URL which points to a human readable document, and a registered Object Identifier.

## Implicit / Explicit

CAdES, like XADES, can have the full original document encapsulated or just a hash of it:

* Implicit: The original document is encapsulated
* Explicit: The original document is not included, just a hash

## Extended info: T, C, X, XL, A

Extended info can be added to enable long term verification:

* T (timestamp): Third-party (TSP) timestamp info is added to the signature
* C (complete): Complete validation references are enabled to allow for off-line validations
* X (extended): Additional date/time info is included expressing when the validation info was added
* XL (extended long-term): Complete certificates are included, i.e., the complete certificate hierarchy of both, signer and revocation verifier. *Long-Term-Verification enabled*.
* A (archive): Additional info is added about the resealing policy. *Long-Term-Verification enabled*

The test data file names have been structured like this:
<pre>
cades-(bes|epes)[-(T|C|X|XL|A)]-(explicit|implicit).der
</pre>

## Other additional attributes

Beside this, some additional attributes are added to basic signature data, e.g.:

* CommitmentType: make explicit what the signer wants to express with his signature RFC 5126 except:
* Proof of origin indicates that the signer recognizes to have created, approved, and sent the message.
* Proof of receipt indicates that signer recognizes to have received the content of the message.
* Proof of delivery indicates that the TSP providing that indication has delivered a message in a local store accessible to the recipient of the message.
* Proof of sender indicates that the entity providing that indication has sent the message (but not necessarily created it).
* Proof of approval indicates that the signer has approved the content of the message.
* Signer Location: Signer address / city where the document has been signed.

164 changes: 164 additions & 0 deletions tests/test_cades.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from __future__ import unicode_literals, division, absolute_import, print_function

import hashlib
import os
import sys

from asn1crypto import cades
from tests import test_cms

if sys.version_info < (3,):
byte_cls = str
else:
byte_cls = bytes

tests_root = os.path.dirname(__file__)
fixtures_dir = os.path.join(tests_root, 'fixtures', 'cades')


class CADESTests(test_cms.CMSTests):
"""Cades RFC 5126 Tests"""

def test_parse_cades_bes_implicit(self):
with open(os.path.join(fixtures_dir, 'cades-bes-implicit.der'), 'rb') as f:
info = cades.ContentInfo.load(f.read())

self.assertEqual(
'signed_data', # type signed_data
info['content_type'].native
)
content = info['content']
self.assertEqual(
'v1', # CMS v1
content['version'].native
)
self.assertEqual( # This is the signed content
b'Hello world!\n',
content['encap_content_info'].native['content']
)
self.assertEqual(
'sha512', # message digest algorithm: SHA512
content['digest_algorithms'][0]['algorithm'].native
)
signer_info = content['signer_infos'][0]
self.assertEqual(
'rsassa_pkcs1v15',
signer_info['signature_algorithm']['algorithm'].native
)
# check how signing certicate is specified
self.assertEqual(
'issuer_and_serial_number',
signer_info['sid'].name
)
self.assertEqual(
'AC FNMT Usuarios',
signer_info['sid'].chosen['issuer'].native['common_name']
)
signer_certificate_serial = signer_info['sid'].chosen['serial_number'].native
self.assertEqual(
40136907034564109132020389771952983570,
signer_certificate_serial
)
signature = signer_info['signature'].native
self.assertEqual(
'a47bb19e77f531c1c34a4d6e2e59a9341d8066ab',
hashlib.sha1(signature).hexdigest()
)
signed_attrs = signer_info['signed_attrs']
signed_attrs = dict((s['type'].native, s['values']) for s in signed_attrs)
self.assertTrue('signing_certificate_v2' in signed_attrs)
self.assertEqual(
signer_certificate_serial,
signed_attrs['signing_certificate_v2'][0]['certs'][0]['issuer_serial']['serial_number'].native
)
self.assertTrue('message_digest' in signed_attrs)
# no signature policy
self.assertFalse('signature_policy' in signed_attrs)
self.assertTrue('content_hints' in signed_attrs)
self.assertEqual(
'net.sf.jmimemagic.detectors.TextFileDetector',
signed_attrs['content_hints'][0]['content_description'].native,
)
self.assertEqual(
'data',
signed_attrs['content_hints'][0]['content_type'].native,
)
self.assertTrue('content_type' in signed_attrs)
self.assertTrue('signing_time' in signed_attrs)
self.assertEqual(
'2017-03-14 22:04:22+00:00',
str(signed_attrs['signing_time'][0].native)
)

def test_parse_cades_epes_explicit(self):
with open(os.path.join(fixtures_dir, 'cades-epes-explicit.der'), 'rb') as f:
info = cades.ContentInfo.load(f.read())
content = info['content']
self.assertEqual(
'sha1',
content['digest_algorithms'][0]['algorithm'].native
)
# now we have no content, signature is 'explicit'
self.assertEqual(
None,
content['encap_content_info'].native['content']
)
signer_info = content['signer_infos'][0]
signed_attrs = signer_info['signed_attrs']
signed_attrs = dict((s['type'].native, s['values']) for s in signed_attrs)
# now we have signature policy
self.assertTrue('signature_policy' in signed_attrs)
self.assertEqual(
'signature_policy_id',
signed_attrs['signature_policy'][0].name
)
self.assertEqual(
'https://sede.060.gob.es/politica_de_firma_anexo_1.pdf',
signed_attrs['signature_policy'][0].chosen['sig_policy_qualifiers'][0]['sig_qualifier'].native
)

def test_parse_cades_epes_a_explicit(self):
with open(os.path.join(fixtures_dir, 'cades-epes-a-explicit.der'), 'rb') as f:
info = cades.ContentInfo.load(f.read())
self.assertEqual(
'signed_data', # type signed_data
info['content_type'].native
)
content = info['content']
self.assertEqual(
'sha256',
content['digest_algorithms'][0]['algorithm'].native
)
# now we have no content, signature is 'explicit'
self.assertEqual(
None,
content['encap_content_info'].native['content']
)
signer_info = content['signer_infos'][0]
signed_attrs = signer_info['signed_attrs']
signed_attrs = dict((attr['type'].native, attr['values']) for attr in signed_attrs)
self.assertTrue('signature_policy' in signed_attrs)
self.assertEqual(
'signature_policy_id',
signed_attrs['signature_policy'][0].name
)
self.assertEqual(
'https://sede.060.gob.es/politica_de_firma_anexo_1.pdf',
signed_attrs['signature_policy'][0].chosen['sig_policy_qualifiers'][0]['sig_qualifier'].native
)
# now we have unsigned attributes
unsigned_attrs = signer_info['unsigned_attrs']
unsigned_attrs = dict((attr['type'].native, attr['values']) for attr in unsigned_attrs)
for key in (
'signature_time_stamp_token',
'complete_certificate_references',
'certificate_revocation_values',
'archive_time_tamp_token',
'certificate_values',
'complete_revocation_references',
'cades_c_time_stamp_token',
):
self.assertTrue(key in unsigned_attrs)
# We still have errors parsing some attribues which should be DER encoded OctetStrings, but are not.
# unsigned_attrs['certificate_revocation_values'][0].native
# unsigned_attrs['complete_revocation_references'][0].native