-
Notifications
You must be signed in to change notification settings - Fork 29
/
signingserver.py
151 lines (124 loc) · 4.32 KB
/
signingserver.py
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
144
145
146
147
148
149
150
151
'''
A client for Signing-Server
'''
import dataclasses
import hashlib
import io
import logging
import urllib.parse
import cryptography.x509
import cryptography.hazmat.primitives.serialization as crypto_serialiation
import requests
import urllib3
import ci.util
import model.signing_server as ms
logger = logging.getLogger(__name__)
@dataclasses.dataclass
class SigningserverClientCfg:
base_url: str
client_certificate: str
client_certificate_key: str
server_certificate_ca: str
validate_tls_certificate: bool = True
@dataclasses.dataclass
class SigningResponse:
'''
wrapper for response received for signing-request to signing-server
Instances are typically created from SigningserverClient.
'''
raw: str
signing_algorithm: ms.SigningAlgorithm
@property
def certificate(self) -> str:
'''
returns certificate (may be used to extract public key for signature validation)
certificate will be returned in PEM format
'''
start_idx = self.raw.find('-----BEGIN CERTIFICATE-----')
end_str = '-----END CERTIFICATE-----'
end_idx = self.raw.find(end_str)
return self.raw[start_idx:end_idx + len(end_str)]
@property
def signature(self) -> str:
'''
returns signature (without header/footer) as base64 str
'''
start_str = '-----BEGIN SIGNATURE-----'
start_idx = self.raw.find(start_str)
end_str = '-----END SIGNATURE-----'
end_idx = self.raw.find(end_str)
signature = self.raw[start_idx + len(start_str):end_idx].strip() # strip header and footer
# strip pre-ambel (Signature Algorithm: <alg>) + blank line
start_idx = signature.find('\n')
signature = signature[start_idx + 1:]
return signature.strip()
@property
def public_key(self) -> str:
'''
returns the PEM-encoded public-key corresponding to the private key used for creating
thus response's signature.
'''
certificate = cryptography.x509.load_pem_x509_certificate(
self.raw.encode('utf-8')
)
public_key = certificate.public_key()
public_key_str = public_key.public_bytes(
encoding=crypto_serialiation.Encoding.PEM,
format=crypto_serialiation.PublicFormat.SubjectPublicKeyInfo,
).decode('utf-8')
return public_key_str
class SigningserverClient:
def __init__(
self,
cfg: SigningserverClientCfg,
):
self.cfg = cfg
def sign(
self,
content: str | bytes | io.IOBase,
hash_algorithm='sha256',
signing_algorithm: ms.SigningAlgorithm | str = ms.SigningAlgorithm.RSASSA_PSS,
remaining_retries: int=3,
):
signing_algorithm = ms.SigningAlgorithm(signing_algorithm)
url = ci.util.urljoin(
self.cfg.base_url,
'sign',
signing_algorithm,
) + '?' + urllib.parse.urlencode({'hashAlgorithm': hash_algorithm})
hasher = getattr(hashlib, hash_algorithm, None)
if not hasher:
raise ValueError(hash_algorithm)
digest = hasher(content).digest()
kwargs = {}
if self.cfg.server_certificate_ca:
kwargs['verify'] = self.cfg.server_certificate_ca
if self.cfg.validate_tls_certificate is False:
kwargs['verify'] = False
urllib3.disable_warnings()
try:
resp = requests.post(
url=url,
headers={
'Accept': 'application/x-pem-file',
},
data=digest,
timeout=(4, 31),
cert=(self.cfg.client_certificate, self.cfg.client_certificate_key,),
**kwargs,
)
resp.raise_for_status()
except requests.exceptions.HTTPError as e:
if remaining_retries == 0:
raise
logger.warning(f'caught http error, going to retry... ({remaining_retries=}); {e}')
return self.sign(
content=content,
hash_algorithm=hash_algorithm,
signing_algorithm=signing_algorithm,
remaining_retries=remaining_retries - 1,
)
return SigningResponse(
raw=resp.text,
signing_algorithm=signing_algorithm,
)