1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
# signature.py - module for handling signed XML files
# coding: utf-8
#
# Copyright (C) 2017-2018 Arthur de Jong
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
"""Module for handling signed PSKC files.
This module defines a Signature class that handles the signature checking,
keys and certificates.
"""
def sign_x509(xml, key, certificate, algorithm=None, digest_algorithm=None,
canonicalization_method=None):
"""Sign PSKC data using X.509 certificate and private key.
xml: an XML document
key: the private key in binary format
certificate: the X.509 certificate
"""
import signxml
algorithm = algorithm or 'rsa-sha256'
digest_algorithm = digest_algorithm or 'sha256'
canonicalization_method = (
canonicalization_method or
getattr(signxml.XMLSignatureProcessor, 'default_c14n_algorithm', None) or
'http://www.w3.org/2006/12/xml-c14n11')
return signxml.XMLSigner(
method=signxml.methods.enveloped,
signature_algorithm=algorithm.rsplit('#', 1)[-1].lower(),
digest_algorithm=digest_algorithm.rsplit('#', 1)[-1].lower(),
c14n_algorithm=canonicalization_method,
).sign(xml, key=key, cert=certificate)
def verify_x509(tree, certificate=None, ca_pem_file=None):
"""Verify signature in PSKC data against a trusted X.509 certificate.
If a certificate is supplied it is used to validate the signature,
otherwise any embedded certificate is used and validated against a
certificate in ca_pem_file if it specified and otherwise the operating
system CA certificates.
"""
from signxml import XMLVerifier
return XMLVerifier().verify(
tree, x509_cert=certificate, ca_pem_file=ca_pem_file).signed_xml
class Signature(object):
"""Class for handling signature checking of the PSKC file.
Instances of this class provide the following properties:
is_signed: boolean to indicate whether a signature is present
algorithm: identifier of the signing algorithm used
canonicalization_method: identifier of the XML canonicalization used
digest_algorithm: algorithm used for creating the hash
issuer: issuer of the certificate
serial: serial number of the certificate
key: key that will be used when creating a signed PSKC file
certificate: the certificate that is embedded in the signature
signed_pskc: a PSKC instance with the signed information
"""
def __init__(self, pskc):
self.pskc = pskc
self._algorithm = None
self.canonicalization_method = None
self.digest_algorithm = None
self.issuer = None
self.serial = None
self.key = None
self.certificate = None
@property
def is_signed(self):
"""Test whether the PSKC file contains a signature.
This method does not check whether the signature is valid but only if
one was present in the PSKC file.
"""
return bool(
self.algorithm or self.canonicalization_method or
self.digest_algorithm or self.issuer or self.certificate)
@property
def algorithm(self):
"""Provide the signing algorithm used."""
if self._algorithm:
return self._algorithm
@algorithm.setter
def algorithm(self, value):
from pskc.algorithms import normalise_algorithm
self._algorithm = normalise_algorithm(value)
@property
def signed_pskc(self):
"""Provide the signed PSKC information."""
if not hasattr(self, '_signed_pskc'):
self.verify()
return self._signed_pskc
def verify(self, certificate=None, ca_pem_file=None):
"""Check that the signature was made with the specified certificate.
If no certificate is provided the signature is expected to contain a
signature that is signed by the CA certificate (or the CA standard CA
certificates when ca_pem_file is absent).
"""
from pskc import PSKC
from pskc.parser import PSKCParser
signed_xml = verify_x509(self.tree, certificate, ca_pem_file)
pskc = PSKC()
PSKCParser.parse_document(pskc, signed_xml)
self._signed_pskc = pskc
return True
def sign(self, key, certificate=None):
"""Add an XML signature to the file."""
self.key = key
self.certificate = certificate
def sign_xml(self, xml):
"""Sign an XML document with the configured key and certificate."""
return sign_x509(
xml, self.key, self.certificate, self.algorithm,
self.digest_algorithm, self.canonicalization_method)
|