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

verify: Support XAdES encapsulated certificates #182

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions signxml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
ec="http://www.w3.org/2001/10/xml-exc-c14n#",
dsig_more="http://www.w3.org/2001/04/xmldsig-more#",
xenc="http://www.w3.org/2001/04/xmlenc#",
xenc11="http://www.w3.org/2009/xmlenc11#"
xenc11="http://www.w3.org/2009/xmlenc11#",
xades="http://uri.etsi.org/01903/v1.3.2#"
)

def ds_tag(tag):
Expand Down Expand Up @@ -586,6 +587,13 @@ def _get_signature(self, root):
else:
return self._find(root, "Signature", anywhere=True)

def _get_certificates(self, signature, x509_data):
certs = [cert.text for cert in self._findall(x509_data, "X509Certificate")]
encapsulated_path = "ds:Object/xades:QualifyingProperties//xades:EncapsulatedX509Certificate"
for encapsulated in signature.findall(encapsulated_path, namespaces=namespaces):
certs.append(encapsulated.text)
return certs

def _verify_signature_with_pubkey(self, signed_info_c14n, raw_signature, key_value, der_encoded_key_value,
signature_alg):
if der_encoded_key_value is not None:
Expand Down Expand Up @@ -823,7 +831,7 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
if self.x509_cert is None:
if x509_data is None:
raise InvalidInput("Expected a X.509 certificate based signature")
certs = [cert.text for cert in self._findall(x509_data, "X509Certificate")]
certs = self._get_certificates(signature, x509_data)
if len(certs) == 0:
x509_iss = x509_data.find("ds:X509IssuerSerial/ds:X509IssuerName", namespaces=namespaces)
x509_sn = x509_data.find("ds:X509IssuerSerial/ds:X509SerialNumber", namespaces=namespaces)
Expand Down
4 changes: 4 additions & 0 deletions test/cert-chain/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
all:
./generate.sh
clean:
rm -f intermediate.* leaf.* root.*
23 changes: 23 additions & 0 deletions test/cert-chain/config.ext
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[req]

req_extensions = root
distinguished_name = req_distinguished_name

[req_distinguished_name]

[root]
keyUsage = keyCertSign,cRLSign
subjectKeyIdentifier = hash
basicConstraints = critical, CA:true

[intermediate]
basicConstraints = critical, CA:true, pathlen:0
extendedKeyUsage = clientAuth,emailProtection
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid

[leaf]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = critical,CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
30 changes: 30 additions & 0 deletions test/cert-chain/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
set -eu

# This script generates three certificates:
# 1. A self-signed root certificate authority
# 2. An intermediate certificate authority
# 3. A leaf certificate, intended for signing/verifying XML signatures

# Generate private key and self-signed certificate for certificate authority
openssl genrsa -out root.key 2048
openssl req -new -key root.key -out root.csr -subj "/CN=root.example.com"
openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650 -sha256 -extfile config.ext -extensions root

# Generate private key for intermediate certificate
openssl genrsa -out intermediate.key 2048

# Generate intermediate certificate request
openssl req -new -key intermediate.key -out intermediate.csr -subj "/CN=intermediate.example.com"

# Generate intermediate certificate based on request
openssl x509 -req -in intermediate.csr -out intermediate.crt -CA root.crt -CAkey root.key -CAcreateserial -days 3650 -sha256 -extfile config.ext -extensions intermediate

# Generate private key for leaf certificate
openssl genrsa -out leaf.key 2048

# Generate leaf certificate request
openssl req -new -key leaf.key -out leaf.csr -subj "/CN=leaf.example.com"

# Generate leaf certificate based on request
openssl x509 -req -in leaf.csr -out leaf.crt -CA intermediate.crt -CAkey intermediate.key -CAcreateserial -days 3650 -sha256 -extfile config.ext -extensions leaf
20 changes: 20 additions & 0 deletions test/cert-chain/intermediate.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDNjCCAh6gAwIBAgIJAPWimSdeOwH3MA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
BAMMEHJvb3QuZXhhbXBsZS5jb20wHhcNMjIwNDA0MTI1ODM0WhcNMzIwNDAxMTI1
ODM0WjAjMSEwHwYDVQQDDBhpbnRlcm1lZGlhdGUuZXhhbXBsZS5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/kwWE285/2ExkQfmdY75L7Dl08pp
uXG1WswmEdziM2HcAhLtOWUWzrIl/nSyVE05u3tx7Ylv1aG5bkSxcRJR8CqRIbuJ
OM+zQCWeJd4JHIsXQOB4MZKgOHLHbyzeIrbMhi1Hliv+khsR4CBkrKQFrgqn8IjC
w32PAgqgBeMM9rnNkWLe4T1TWbgSbtuVP2+EEYG6NpQ0j5dl6QjEd/lWbh4itlwh
EO5otrZbrRmEYwIutZhfUJwkHIojOLvWvOLRvx20h6pS3xsA+V0DQqyQeT2E0gdS
c6Sqkiol2xh1v6cQzXVdIKCIQnK5yIU3Y2KNy0ni9Jqvp2DJYGXfzq9NAgMBAAGj
dTBzMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
AQUFBwMEMB0GA1UdDgQWBBTWzCF4+GsA/dxOPPKtpo/jYDwbtTAfBgNVHSMEGDAW
gBTA2CuH0iKWFP743/WvxRSV4KuO7DANBgkqhkiG9w0BAQsFAAOCAQEAIXsgm9/f
iaeUHR32L6KrMwmP552YS4Ds546AUdvdem/xgtfEOZMLjdKFNUGom7c85Qx8p1N0
RGjISDL25RBhRFq3NEURAcF+bFwMtheCvFZwl2B2akOw/4EakApXDxHrTfDulcCx
KNvZPT+5E7jkkScTcbDecPzFpJgJtkpIvR0CUyygHgueYvt759qIoOt1mkqP6HQr
OgZiFe+5iyA5YepJo7FKqf6xGnq1U5o/DpXEm5wvnkt/QPN9u4yUOeBPJpaQ+Fpc
Tw2InPCzYyaiKIPKeYHf28ZskbPcUDh8r9+N5GqanrmKUYxLqBFjZOhQxFZFhNw0
81bA+LF0vJLJ6g==
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions test/cert-chain/intermediate.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICaDCCAVACAQAwIzEhMB8GA1UEAwwYaW50ZXJtZWRpYXRlLmV4YW1wbGUuY29t
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxv5MFhNvOf9hMZEH5nWO
+S+w5dPKablxtVrMJhHc4jNh3AIS7TllFs6yJf50slRNObt7ce2Jb9WhuW5EsXES
UfAqkSG7iTjPs0AlniXeCRyLF0DgeDGSoDhyx28s3iK2zIYtR5Yr/pIbEeAgZKyk
Ba4Kp/CIwsN9jwIKoAXjDPa5zZFi3uE9U1m4Em7blT9vhBGBujaUNI+XZekIxHf5
Vm4eIrZcIRDuaLa2W60ZhGMCLrWYX1CcJByKIzi71rzi0b8dtIeqUt8bAPldA0Ks
kHk9hNIHUnOkqpIqJdsYdb+nEM11XSCgiEJyuciFN2NijctJ4vSar6dgyWBl386v
TQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHePfNLKAIBfBNSEVBcR1bqSfY3p
h6/PXsfHeyWH3pMj2ADwRifHs4RrK4SvbEkgFr6gHKccBpHeweNQLeru0wg1/DLI
ZKPswMOfQ+U3wGYLMahMYXvdpECSBiIPMHVlwPzy4kpy+WkC9y661J8epNaglmjI
9YUn6ygOpkYmhcuZGP5oANH/9FvF0w0bc+qwlciawzYkBKNPo0umFifKh1epKj+E
sQu9u6SHtjiaerg1o4mFLwUU8jRjx+Xrns9NNXafdBp1wLh9OZAPwXJl+IWKbfYk
ZrYTwIm255zBuqpoot4m0bSsfzDlhPtgvERMmhevkrLS4sbERGMYMzqvVtg=
-----END CERTIFICATE REQUEST-----
27 changes: 27 additions & 0 deletions test/cert-chain/intermediate.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxv5MFhNvOf9hMZEH5nWO+S+w5dPKablxtVrMJhHc4jNh3AIS
7TllFs6yJf50slRNObt7ce2Jb9WhuW5EsXESUfAqkSG7iTjPs0AlniXeCRyLF0Dg
eDGSoDhyx28s3iK2zIYtR5Yr/pIbEeAgZKykBa4Kp/CIwsN9jwIKoAXjDPa5zZFi
3uE9U1m4Em7blT9vhBGBujaUNI+XZekIxHf5Vm4eIrZcIRDuaLa2W60ZhGMCLrWY
X1CcJByKIzi71rzi0b8dtIeqUt8bAPldA0KskHk9hNIHUnOkqpIqJdsYdb+nEM11
XSCgiEJyuciFN2NijctJ4vSar6dgyWBl386vTQIDAQABAoIBABkjUMtNIzTpbQbM
8nmLikcyXh96dMyMT7AAS1/Xy9aLdSZM4MU1Be8up1SjY2yPt72+UGbhAgatryN0
qZrc1NqjA02YPE+mbJ6RUO88i2MNRwjl+jVU1dgFqYJGKh3Ztsv5e0ja0K31GRRW
AuGU6ZeKF/CFM1GfToI9WMhEVh1x6JF9CRpjvrBtayrVM11TyE5Du6nbmvOJqd84
8IjOqWtckQNwJTk4r1cVn2/jTrwZEvIW3q1xRcOYnMQ+dA0M2/9JoEfEvbjOYp8X
RlBgdVRRLae4xuAykD/NdvlXGRBRUvnnxm1LtokxzpKDJ2MDdmW9ZBaOP1hHvlvB
dqAyTgUCgYEA6cKS9TxyBqFkT5rPTGLHOjVmTvsDNzbFXkV9QPkspahuZCGvr3Z+
ExfIzm0LlZ0f+qohjDp9sOJx64VyPQAM0f249RKoPwehXrRTCXAWj4AIF1Km+Gwa
U8dq4+I10Y8D3NAyfC79T4ofUYn90v8kxOsLmLbe7Kkt8m5TApSJPr8CgYEA2ezz
Rzd7NXP1HIQaruAe6wBP63w3U73LSvDmfCMDRcOk/bD3Ecyu2rRvqvhJTryQpCll
bMOjimoB7lYvX1hIcio217qr06MtzDWV3RgUl1269b6xjHkmrvhvsyUvg9W+34Tz
JsmTmRLn7AXHHMU4itwtQK5Y2DeLUJ/jqa6f4PMCgYAymHvIesnPZ5VXqZFe3i5z
CeXYCHqcs80qZ24B5yzjuj4SjDwDhWkqZsZ+75lHS9gFIFfcizhzVcAEk0CztVQR
C5LB8MTbx6IE5pDmhQ1NCBA4RBqBwJw+L/aR1n+BmSvj2mhi+qS3V5UJyA1ZYwIp
YRqEdmhv3vpj878h2taN7wKBgQCbu9s2v41K66z0TeuLoRo3Mifqzv/y1iUbTwBH
IfZKur+DlB1cGPuzy1IpyfkJTXPH/NVAXqbWWV875VdBOaO8AjLjA8GbIneuAXUx
ZO4CJbdfuoDDNCjSzTN1wFMuUWdv8GCbDV5u+7XFU3OxObdodGPaXz2adkcWvMxD
DEmwxQKBgGrNk3a+ZMwrNFUKd26ay0Zs6OY8Uhbr2Hj10Lk+DKWNyxHd4XnCywr1
CuGgBbIFDtS5yrLiK9lqKE9tWkMT+NHn+dvPmv3HSLxqbK/obCwPKVS09d3or2Xv
hdBd18clnYN44dwpLtHwzbzgRL26FSu/Gt4KAYcR1TN5I/90bFEW
-----END RSA PRIVATE KEY-----
1 change: 1 addition & 0 deletions test/cert-chain/intermediate.srl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
815A6E9E93988530
19 changes: 19 additions & 0 deletions test/cert-chain/leaf.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIJAIFabp6TmIUwMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNV
BAMMGGludGVybWVkaWF0ZS5leGFtcGxlLmNvbTAeFw0yMjA0MDQxMjU4MzRaFw0z
MjA0MDExMjU4MzRaMBsxGTAXBgNVBAMMEGxlYWYuZXhhbXBsZS5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7/fHkBOM7nhKA2IowLhAA1gvddUIE
1t00x3AT2tsl2fZo2YiODiaHauNVTtv5oigCQxR9yITpxfibgTHYnyojjeUrJPvR
zEaFAchoYRFtlyRyjxRRlBBl94edCDB5JPnkxCp3WzRVH2b5gg0Bxb6lgR1qZsfU
/sYlxYFf32V34Rfqltwp7u/vSIiQX9k/+GftQ5yXQc9dhvrS8ebj7AktA2v5e/tI
pdaxgfKAFNAvw05nVXP7a8t+JM+45gSicemXV3EbJr3CnahIsUK+cdOrgdp9A+6k
2uH0G2UqVHaAVBsvyEnIti1qSG+dlOrYR0toJ5Hj4fxwBYjkIHZTV4LdAgMBAAGj
YDBeMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRLRYy6
MzS9gPt1zHOc8s9bw/t7njAfBgNVHSMEGDAWgBTWzCF4+GsA/dxOPPKtpo/jYDwb
tTANBgkqhkiG9w0BAQsFAAOCAQEAwGQ0mTu5SZ7rvvKiaSCoZrvUov5nncovYATd
tvHDpC+0HCtvg2vSw6kmJXjc8XHsoNiIWFYO0qAZTVlJMUVzlTWVjQjeaiHokKTn
10I+dakBQdnxAcafipCfTjL52y2uQxBDykJf8gjuIgXAxnIPV83ye1E7fiDlW8xp
tbLx0uv7m1U8BurH26rSbrI+/msT3O+flOY66JKCl0wNl/ZVWPNSKxGmoS0DPD0k
OKURGlhkcUugIkYYrlnfrw7bUAVUUJ7iv4KDdGc6ecjyH4ydf9zOnoMKbekeXZ6c
zLXYEZ5D/itoPEs4YazZjipyfz9bxX144JPt9Y9iw2609Rx05g==
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions test/cert-chain/leaf.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICYDCCAUgCAQAwGzEZMBcGA1UEAwwQbGVhZi5leGFtcGxlLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALv98eQE4zueEoDYijAuEADWC911QgTW
3TTHcBPa2yXZ9mjZiI4OJodq41VO2/miKAJDFH3IhOnF+JuBMdifKiON5Ssk+9HM
RoUByGhhEW2XJHKPFFGUEGX3h50IMHkk+eTEKndbNFUfZvmCDQHFvqWBHWpmx9T+
xiXFgV/fZXfhF+qW3Cnu7+9IiJBf2T/4Z+1DnJdBz12G+tLx5uPsCS0Da/l7+0il
1rGB8oAU0C/DTmdVc/try34kz7jmBKJx6ZdXcRsmvcKdqEixQr5x06uB2n0D7qTa
4fQbZSpUdoBUGy/ISci2LWpIb52U6thHS2gnkePh/HAFiOQgdlNXgt0CAwEAAaAA
MA0GCSqGSIb3DQEBCwUAA4IBAQCILxnkHMMCuY4uNEOXo2KQ56KKoWjGf0w1hpHX
z/8xR/Jqb8tvTYv8IC7u0fjcFndZjJKyqjKN4g/8iJVo5TK+PKVFuajGkJjlpH5n
NcGc8ZTYFGophGsRS1YbpQJ4Waz6CtyZutAYowMzuP4X1l6+Jkggjo7V0ePUMODi
zuLerM8OO5zQav4ypEhGP6S++SnIUU2OI1mnrJl9Th0Ni0ij5bAy2XUWAOiWvXmr
NqHdPAUqZhhwr+kQ+hekjTuY+Zm7QDn+cUNAP3zysu0CMR8zHwGx6FhN3wGyupE2
TK86NZJZhC4cUbztvPmk/r2BlUzqD52/CknqmUjEfmEm9L1h
-----END CERTIFICATE REQUEST-----
27 changes: 27 additions & 0 deletions test/cert-chain/leaf.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu/3x5ATjO54SgNiKMC4QANYL3XVCBNbdNMdwE9rbJdn2aNmI
jg4mh2rjVU7b+aIoAkMUfciE6cX4m4Ex2J8qI43lKyT70cxGhQHIaGERbZckco8U
UZQQZfeHnQgweST55MQqd1s0VR9m+YINAcW+pYEdambH1P7GJcWBX99ld+EX6pbc
Ke7v70iIkF/ZP/hn7UOcl0HPXYb60vHm4+wJLQNr+Xv7SKXWsYHygBTQL8NOZ1Vz
+2vLfiTPuOYEonHpl1dxGya9wp2oSLFCvnHTq4HafQPupNrh9BtlKlR2gFQbL8hJ
yLYtakhvnZTq2EdLaCeR4+H8cAWI5CB2U1eC3QIDAQABAoIBACbVZHx3zgDovGXF
VXZybXfev4C10jdxsyxN7ocLdK2zAsXR+fVuUuMyX1el/Kbqql0dQkRhgaNQ8/Qb
khJvfqLtkuOAtkw2aHLdVWrKcnEzAaJwj//yGBkYMvlrUagUzVJGe4dAXeUfoYP1
K36sPJ+vtkXeiXUkVAZFcF4/pZRHnnJTGgIUob+Ensf9KaIpnwyqHA3eCMAWmGyL
uN4+ZBZ1HvyVD6MQ0iq9nvCWTAfFm3nrYHushAH7OuwcH6ys/WvOnDhq+hODMJkt
jDwFG0ki1oNzTWcewxkiMdV1JdKVyehtkE+KKsXly+Y/51vwggxx4LJO+mesWaFB
SBjoLrkCgYEA6uoz2vf30XVRwc5EAsDsrEH042nm7t/pwZqXSoIwZg/MXWec7Q8n
InWnjeb9lR9DEMjUm2PZYMCXp0WRDP0B51FiewD+yd32Hkx9w82PaYZCQMft9VLE
EyILQWWB3ILYaa+WiuV1s6z6dvpuZglByeF1exI3Ty2t1WZ6MNxULIsCgYEAzN2Q
RK46XYTF+860JN74WLpgJQA8FIcoM0x4nN/C7hgWKhulkNnD4+60lFwtkgKXGQ6C
KYGBGgY3jjKHAbDsDg7QxIBcBVGWobDmWvHrA0WenAv17LgCa0gcaapxpWD+Wa4K
oFi1yR8OoKFqHA08r065Z49gFVN15O2nTgIu8zcCgYEAq0E7tnkph+BwTspxWFSO
9XFL9vIAccp43jQ18RKB+BWyVbGwfD2cuQlusgtgeHMG8FiIbhPXqCofJMmZWg+k
cJ8rHSL3m/CdeS8oTDMyRqqi6BkYh8zjtlMOQ4mdp0UYQcvJAs8PBIKpQU2GaAp9
lJAxlhwMiENzw+vmTnKBRssCgYA2+q8g/3ECcAOCFHMcAgq8JShwRaPaDnunaLpl
v1dc9nHcUxcXzJlZrlIgDqzcJn3OZ6pe4TZ4eXqnWsAIoCZ5j1hPo/MOKls2gXcQ
qSPc+O4cCsmxAaEEkZGueeON8n3QK3kGl0gR9ZfDXA5SAOtsUMBJHp02m/NjwLPJ
p6RZ+QKBgQC/aM9VOFd33vkjcOAL/0PsgF9qqPf5tbOU+StwM/oK9W7s1G0h6M4g
TvGOJb2ZAH35R+cgrcZZ7m5Koddnoocu8Y/I7A0aXAUiH5dHvK3ou8OMknPgqa3K
R9AdkJJt9UYUsq8hqhR4kKetoKr4hhm8rffqxQ8fiUedqAbq9KP59A==
-----END RSA PRIVATE KEY-----
18 changes: 18 additions & 0 deletions test/cert-chain/root.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC+DCCAeCgAwIBAgIJAK8CuHZ8q/VTMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
BAMMEHJvb3QuZXhhbXBsZS5jb20wHhcNMjIwNDA0MTI1ODM0WhcNMzIwNDAxMTI1
ODM0WjAbMRkwFwYDVQQDDBByb290LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAyH2fpTMLu2USKAZzFLLM3virbctKkbS8A38ZNKKT
GyodwUuIYGHG1kf1RtIXnrXg5xG/XndeNciZWFJFgYKjxwn4YutRlGjnywan7pMM
LS+IuBJx4XIsRoP05+vyt0t3V/ntphGwuyZBZWLpj+DZm2HfbeYob4NrcaaUbmXv
x7xsLMwuNYcQZiSf3uUPq7/fWmRhIFczoQRUzVicZdmH2WRaiXdPdX9XHys9C4LQ
RdF+Ge6GAMdAF7lMhG518OXZu2g8aViysaBs7wUAnOAGJScDmfe7gL4AbZAPh1VR
i4Q8c/hPtY6/UUUmy1cYLsH80G/c/j2AGHdCC0q31Cst9wIDAQABoz8wPTALBgNV
HQ8EBAMCAQYwHQYDVR0OBBYEFMDYK4fSIpYU/vjf9a/FFJXgq47sMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHFci3DC44wA7/qQ6BJUSk8ThuDY
p6QBO/xIc/6gUq+9119Vs29E8yLj79qp66r5i4dWAYI//jgLY2o5piq53F1KJG47
dduIoycnpRMXo+/Ko4ZKJSraz3wyAWi6ifhUg9xID8Id8c+iKp2M1CcaYsjJUkGX
XrthBSLTezE/t/Wv2rUVO85LvqrTDFQCdPUnYuEYhstIIc0H9Gv/vjxFB0nWYjoR
KSXZgXeH6weoOvJ5+ExuU7MJLPHAavg4PXsP5SsuVcIlWXgV5qVeqrbx1tueJmFc
+YcKyBysH+2nP16rg2/l0YXN8shEm0rN/dnndO2M8zNsYi+s/If3XfPHyfI=
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions test/cert-chain/root.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICYDCCAUgCAQAwGzEZMBcGA1UEAwwQcm9vdC5leGFtcGxlLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMh9n6UzC7tlEigGcxSyzN74q23LSpG0
vAN/GTSikxsqHcFLiGBhxtZH9UbSF5614OcRv153XjXImVhSRYGCo8cJ+GLrUZRo
58sGp+6TDC0viLgSceFyLEaD9Ofr8rdLd1f57aYRsLsmQWVi6Y/g2Zth323mKG+D
a3GmlG5l78e8bCzMLjWHEGYkn97lD6u/31pkYSBXM6EEVM1YnGXZh9lkWol3T3V/
Vx8rPQuC0EXRfhnuhgDHQBe5TIRudfDl2btoPGlYsrGgbO8FAJzgBiUnA5n3u4C+
AG2QD4dVUYuEPHP4T7WOv1FFJstXGC7B/NBv3P49gBh3QgtKt9QrLfcCAwEAAaAA
MA0GCSqGSIb3DQEBCwUAA4IBAQDHoXzoTB/nnM16ssKMCsgo9kB33YX788bfSyqb
ZX1+YEbSrU0W21/bvX34Rlv2bELyR5UWQaVP+o9PCPVzsBCB9UivhqgVAUaPqWzq
riFs/mqT3Tu4CbOLFTCy4Y4jvsNMg/oTB/BUoL8kSdZRBeFSgoTf9fias/4ypiZS
1XML7KEKP5ffwZYtZCQpqkhY4gofInWmZwDFsnnPiSJ72pCaZ6pLBZ4Ha9UzhrJW
saYiFAI9v7qPCAtOPaMS7GzPr3D1n4r8ufRGW3aPAY6H2tijsMR7tVQdnFpOvfRZ
uqDGzEF9AoCq+g8+sctPz673x/EgHHT8HXU8FfWsaX8AnsBL
-----END CERTIFICATE REQUEST-----
27 changes: 27 additions & 0 deletions test/cert-chain/root.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyH2fpTMLu2USKAZzFLLM3virbctKkbS8A38ZNKKTGyodwUuI
YGHG1kf1RtIXnrXg5xG/XndeNciZWFJFgYKjxwn4YutRlGjnywan7pMMLS+IuBJx
4XIsRoP05+vyt0t3V/ntphGwuyZBZWLpj+DZm2HfbeYob4NrcaaUbmXvx7xsLMwu
NYcQZiSf3uUPq7/fWmRhIFczoQRUzVicZdmH2WRaiXdPdX9XHys9C4LQRdF+Ge6G
AMdAF7lMhG518OXZu2g8aViysaBs7wUAnOAGJScDmfe7gL4AbZAPh1VRi4Q8c/hP
tY6/UUUmy1cYLsH80G/c/j2AGHdCC0q31Cst9wIDAQABAoIBAAKKzT5sAkcTBgLp
6gWmKsDdCFdxD3X/g67LqscnFJRwmUX9LjKCquHGsc5/FXuWtM5ZRELvYotuCru6
91SoEmmQr8yZTUSDfATwBasZxDrCcopa+YSxTOxr+ewC6uX2GAJOkfvcF5CX8JZX
hbykTj4RJKMhGvwm/3LGePVZZ4vxyvJMP5cCAnttd1a5Z8p3mDWHwv/NjcF9GKa+
eoEdMTsO6hq1uXS+Trpq8/Vw1MJviMnMNYl088ChN/dvI/FuHkOR8U4O5XM8hhjm
7XEMV4sCxW6g6kRpNm87B6XMaCMWAo7a0BA3uw4W03icE9p1TyovEekbvY8+h8Ym
BEbQoxkCgYEA5q7DyP2Edh/lAhM7xorELyZJ9AHBYq/F7YpSzzlXFKcEEHxCbkpx
Md3X5pFYOf927gKXiM3GfODASMT8735/xNYRs18VHeyqBV7/a7YP4XfNW4yZBQGk
5WRg8Ggy8qcdO7keuiySn1KLIyKcs4T2wtaBy1+lFmDNQmWn2riGSSUCgYEA3n6W
wUAkpYm66dQOyaFTlfrQBSDhHo+iLCm4tZQcruI8MiNwl7SGJQgfxn2eqjd56o8t
TCqI3JrLPNir8NxaeDAiNAO7kaXbMTZeMqMbIN8o2JpbDFhxQlTTDGH3keD35uTw
tsvl3EbZkndE4PSaXFPP2X9HzYjPk52w51NuFesCgYEAiHkUMzBo0UmFPnkKgCdQ
cWSBk+4F7tB2lFWlMBuIPRuh9+7LjlxUI2BPfoS2YXmVbrHx3GmA6Bg/Kc4Apd98
z4Kl7ixuXcnJvGu7SInpU0aBI4xGawPR/jQodZHvskbTsKWQXCxKgh9fWWX8tugO
5K3rE9p93INga8ugRnGsYFECgYBKQDaxUWWpdNhS1nkE4vjREX3AaGOYXljBoj7J
Ih+cYVWzac5WYmFuFFL+W8fKiG8ATz3PsFYyQNpYePqQjAkJGQ8hCqnbBl2rA7jV
70bLZo3sEz8VVKKff01bUYpcPZadkIOgjVUteNt9HJ548EhfTX0M2YQt3F1hpvt1
h4sydwKBgQDXXScbgU7dCGcXyQD0Bk1qoDLHv5aKtjiyC3LC5wU87g3ULq4K/eKK
5CFJ27icBz4reBu7CqZhidsJWeoWKnCXibBv+oVkiXoLjkRfSKDVmKBDhSEZHv78
cbveKuFULjoMtg76o1JtAGgQXW5dEZtMoyuu+FNnY/RHs+cZfGR5Yw==
-----END RSA PRIVATE KEY-----
1 change: 1 addition & 0 deletions test/cert-chain/root.srl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
F5A299275E3B01F7
47 changes: 47 additions & 0 deletions test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ def reset_tree(t, method):
continue
s.getparent().remove(s)

def extract_pem(pem):
r = re.sub(r"-+BEGIN CERTIFICATE-+(.*?)-+END CERTIFICATE-+", r"\1", pem, flags=re.DOTALL)
r = re.sub(r"[ \t\r\n]", "", r, count=0, flags=re.DOTALL)
return r

# See https://www.w3.org/TR/XAdES/#Syntax_forXAdES-X-L_form
def add_certificate_path(signature_elem, certs):
object_elem = etree.SubElement(signature_elem, "{http://www.w3.org/2000/09/xmldsig#}Object")
qprop_elem = etree.SubElement(object_elem, "{http://uri.etsi.org/01903/v1.3.2#}QualifyingProperties")
usprop_elem = etree.SubElement(qprop_elem, "{http://uri.etsi.org/01903/v1.3.2#}UnsignedProperties")
ussigprop_elem = etree.SubElement(usprop_elem, "{http://uri.etsi.org/01903/v1.3.2#}UnsignedSignatureProperties")
certvalues_elem = etree.SubElement(ussigprop_elem, "{http://uri.etsi.org/01903/v1.3.2#}CertificateValues")

for cert in certs:
cert_elem = etree.SubElement(certvalues_elem, "{http://uri.etsi.org/01903/v1.3.2#}EncapsulatedX509Certificate")
cert_elem.text = cert


class URIResolver(etree.Resolver):
def resolve(self, url, id, context):
print("Resolving URL '%s'" % url)
Expand Down Expand Up @@ -174,6 +192,35 @@ def test_x509_certs(self):
XMLVerifier().verify(signed_data)
# TODO: negative: verify with wrong cert, wrong CA

def test_x509_cert_chain(self):
from OpenSSL.crypto import load_certificate, FILETYPE_PEM, Error as OpenSSLCryptoError

tree = etree.parse(self.example_xml_files[0])
root_filename = os.path.join(os.path.dirname(__file__), "cert-chain", "root.crt")
with open(root_filename, "r") as fh:
root_cert = fh.read()
with open(os.path.join(os.path.dirname(__file__), "cert-chain", "intermediate.crt"), "r") as fh:
intermediate_cert = fh.read()
with open(os.path.join(os.path.dirname(__file__), "cert-chain", "leaf.crt"), "r") as fh:
leaf_cert = fh.read()
with open(os.path.join(os.path.dirname(__file__), "cert-chain", "leaf.key"), "r") as fh:
leaf_key = fh.read()

certs = [root_cert, intermediate_cert, leaf_cert]
method = methods.enveloped
hash_alg = "sha256"
data = tree.getroot()
signer = XMLSigner(method=method, signature_algorithm="rsa-" + hash_alg)
signed = signer.sign(data, key=leaf_key, cert=leaf_cert)

signature_elem = signed.find("{http://www.w3.org/2000/09/xmldsig#}Signature")
add_certificate_path(signature_elem, certs)
signed_data = etree.tostring(signed)

with self.assertRaisesRegex(InvalidCertificate, "unable to get local issuer certificate"):
XMLVerifier().verify(signed_data)
XMLVerifier().verify(signed_data, ca_pem_file=root_filename)

def test_xmldsig_interop_examples(self):
ca_pem_file = os.path.join(os.path.dirname(__file__), "interop", "cacert.pem").encode("utf-8")
for signature_file in glob(os.path.join(os.path.dirname(__file__), "interop", "*.xml")):
Expand Down