Projekt

Obecné

Profil

Stáhnout (20.5 KB) Statistiky
| Větev: | Tag: | Revize:
1 4a40b0d2 Stanislav Král
import re
2 36409852 Stanislav Král
import subprocess
3
import time
4 9a55ea8a Stanislav Král
import random
5 c0aed2f5 Stanislav Král
6 64cfca84 Jan Pašek
from src.constants import CRL_CONFIG
7
from src.model.certificate import Certificate
8
from src.model.private_key import PrivateKey
9 cc51ca2c Stanislav Král
from src.model.subject import Subject
10 b3c80ccb David Friesecký
from src.utils.logger import Logger
11 181e1196 Jan Pašek
from src.utils.temporary_file import TemporaryFile
12 c0aed2f5 Stanislav Král
13 36409852 Stanislav Král
# encryption method to be used when generating private keys
14 c0aed2f5 Stanislav Král
PRIVATE_KEY_ENCRYPTION_METHOD = "-aes256"
15
16
# openssl executable name
17
OPENSSL_EXECUTABLE = "openssl"
18
19 36409852 Stanislav Král
# format of NOT_BEFORE NOT_AFTER date fields
20
NOT_AFTER_BEFORE_DATE_FORMAT = "%b %d %H:%M:%S %Y %Z"
21
22 3e770afd Jan Pašek
# minimal configuration file to be used for openssl req command
23
# specifies distinguished_name that references empty section only
24
# openssl requires this option to be present
25
MINIMAL_CONFIG_FILE = "[req]\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\n\n"
26
27
# section to be used to specify extensions when creating a SSCRT
28
SSCRT_SECTION = "sscrt_ext"
29
30
CA_EXTENSIONS = "basicConstraints=critical,CA:TRUE"
31
32 9a55ea8a Stanislav Král
# upper bound of the range of random serial numbers to be generated
33
MAX_SN = 4294967296
34
35 c0aed2f5 Stanislav Král
36
class CryptographyService:
37
38
    @staticmethod
39 57898b2f Stanislav Král
    def __subject_to_param_format(subject):
40
        """
41
        Converts the given subject to a dictionary containing openssl field names mapped to subject's fields
42
        :param subject: subject to be converted
43
        :return: a dictionary containing openssl field names mapped to subject's fields
44
        """
45 b3c80ccb David Friesecký
46
        Logger.debug("Function launched.")
47
48 6c098d6e Stanislav Král
        subj_dict = {}
49
        if subject.common_name is not None:
50
            subj_dict["CN"] = subject.common_name
51
        if subject.country is not None:
52
            subj_dict["C"] = subject.country
53
        if subject.locality is not None:
54
            subj_dict["L"] = subject.locality
55
        if subject.state is not None:
56
            subj_dict["ST"] = subject.state
57
        if subject.organization is not None:
58
            subj_dict["O"] = subject.organization
59
        if subject.organization_unit is not None:
60
            subj_dict["OU"] = subject.organization_unit
61
        if subject.email_address is not None:
62
            subj_dict["emailAddress"] = subject.email_address
63
64
        # merge the subject into a "subj" parameter format
65
        return "".join([f"/{key}={value}" for key, value in subj_dict.items()])
66
67
    @staticmethod
68 7444d4cb Stanislav Král
    def __run_for_output(args=None, proc_input=None, executable=OPENSSL_EXECUTABLE):
69 c0aed2f5 Stanislav Král
        """
70
        Launches a new process in which the given executable is run. STDIN and process arguments can be set.
71
        If the process ends with a non-zero then <CryptographyException> is raised.
72 4691a56f Stanislav Král
73 c0aed2f5 Stanislav Král
        :param args: Arguments to be passed to the program.
74 6c098d6e Stanislav Král
        :param proc_input: String input to be passed to the stdin of the created process.
75 c0aed2f5 Stanislav Král
        :param executable: Executable to be run (defaults to openssl)
76
        :return: If the process ends with a zero return code then the STDOUT of the process is returned as a byte array.
77
        """
78 b3c80ccb David Friesecký
79
        Logger.debug("Function launched.")
80
81 c0aed2f5 Stanislav Král
        if args is None:
82
            args = []
83
        try:
84
            # prepend the name of the executable
85
            args.insert(0, executable)
86
87
            # create a new process
88 fe647b46 Stanislav Král
            proc = subprocess.Popen(args, stdin=subprocess.PIPE if proc_input is not None else None,
89
                                    stdout=subprocess.PIPE,
90 c0aed2f5 Stanislav Král
                                    stderr=subprocess.PIPE)
91
92 6c098d6e Stanislav Král
            out, err = proc.communicate(proc_input)
93 c0aed2f5 Stanislav Král
94
            if proc.returncode != 0:
95
                # if the process did not result in zero result code, then raise an exception
96 7d0aa304 Stanislav Král
                if err is not None and len(err) > 0:
97 b3c80ccb David Friesecký
                    Logger.error("CryptographyException")
98 c0aed2f5 Stanislav Král
                    raise CryptographyException(executable, args, err.decode())
99
                else:
100 b3c80ccb David Friesecký
                    Logger.error("CryptographyException")
101 c0aed2f5 Stanislav Král
                    raise CryptographyException(executable, args,
102
                                                f""""Execution resulted in non-zero argument""")
103
104
            return out
105
        except FileNotFoundError:
106 b3c80ccb David Friesecký
            Logger.error("CryptographyException")
107 c0aed2f5 Stanislav Král
            raise CryptographyException(executable, args, f""""{executable}" not found in the current PATH.""")
108
109
    def create_private_key(self, passphrase=None):
110
        """
111
        Creates a private key with the option to encrypt it using a passphrase.
112
        :param passphrase: A passphrase to be used when encrypting the key (if none is passed then the key is not
113
        encrypted at all). Empty passphrase ("") also results in a key that is not encrypted.
114 18588728 Stanislav Král
        :return: string containing the generated private key in PEM format
115 c0aed2f5 Stanislav Král
        """
116 b3c80ccb David Friesecký
117
        Logger.debug("Function launched.")
118
119 c0aed2f5 Stanislav Král
        if passphrase is None or len(passphrase) == 0:
120 7444d4cb Stanislav Král
            return self.__run_for_output(["genrsa", "2048"]).decode()
121 c0aed2f5 Stanislav Král
        else:
122 7444d4cb Stanislav Král
            return self.__run_for_output(
123 c0aed2f5 Stanislav Král
                ["genrsa", PRIVATE_KEY_ENCRYPTION_METHOD, "-passout", f"pass:{passphrase}", "2048"]).decode()
124
125 be2df9b7 Stanislav Král
    def create_sscrt(self, subject, key, config="", extensions="", key_pass=None, days=30, sn: int = None):
126 c0aed2f5 Stanislav Král
        """
127 329216fe Stanislav Král
        Creates a self signed certificate
128 c0aed2f5 Stanislav Král
129
        :param subject: an instance of <Subject> representing the subject to be added to the certificate
130 02f63b07 Stanislav Král
        :param key: private key of the CA to be used
131 c0aed2f5 Stanislav Král
        :param config: string containing the configuration to be used
132
        :param extensions: name of the section in the configuration representing extensions
133 18588728 Stanislav Král
        :param key_pass: passphrase of the private key
134 2f5101f1 Stanislav Král
        :param days: number of days for which the certificate will be valid
135 9a55ea8a Stanislav Král
        :param sn: serial number to be set, when "None" is set a random serial number is generated
136 c0aed2f5 Stanislav Král
137 18588728 Stanislav Král
        :return: string containing the generated certificate in PEM format
138 c0aed2f5 Stanislav Král
        """
139 b3c80ccb David Friesecký
140
        Logger.debug("Function launched.")
141
142 c0aed2f5 Stanislav Král
        assert key is not None
143
        assert subject is not None
144
145 57898b2f Stanislav Král
        subj = self.__subject_to_param_format(subject)
146 c0aed2f5 Stanislav Král
147 3e770afd Jan Pašek
        # To specify extension for creating a SSCRT, one has to use a configuration
148
        # file instead of an extension file. Therefore the following code creates
149
        # the most basic configuration file with sscrt_ext section, that is later
150
        # reference in openssl req command using -extensions option.
151
        if len(config) == 0:
152 9e6f791a Jan Pašek
            config += MINIMAL_CONFIG_FILE
153 3e770afd Jan Pašek
        config += "\n[ " + SSCRT_SECTION + " ]" + "\n" + extensions
154
155 c0aed2f5 Stanislav Král
        with TemporaryFile("openssl.conf", config) as conf_path:
156 2f5101f1 Stanislav Král
            args = ["req", "-x509", "-new", "-subj", subj, "-days", f"{days}",
157 c0aed2f5 Stanislav Král
                    "-key", "-"]
158 3e770afd Jan Pašek
159 9a55ea8a Stanislav Král
            # serial number passed, use it when generating the certificate,
160
            # without passing it openssl generates a random one
161
            if sn is not None:
162
                args.extend(["-set_serial", str(sn)])
163
164 c0aed2f5 Stanislav Král
            if len(config) > 0:
165
                args.extend(["-config", conf_path])
166
            if len(extensions) > 0:
167 64cfca84 Jan Pašek
                args.extend(["-extensions", SSCRT_SECTION])  # when creating SSCRT, section references section in config
168 c0aed2f5 Stanislav Král
169
            # it would be best to not send the pass phrase at all, but for some reason pytest then prompts for
170
            # the pass phrase (this does not happen when run from pycharm)
171
172 fe647b46 Stanislav Král
            #  add the passphrase even when None is passed. Otherwise when running tests with pytest some tests freeze
173
            # waiting for the passphrase to be typed in
174 18588728 Stanislav Král
            args.extend(["-passin", f"pass:{key_pass}"])
175 c0aed2f5 Stanislav Král
176 7444d4cb Stanislav Král
            return self.__run_for_output(args, proc_input=bytes(key, encoding="utf-8")).decode()
177 6c098d6e Stanislav Král
178 87fd5afc Stanislav Král
    def __create_csr(self, subject, key, key_pass=""):
179 6c098d6e Stanislav Král
        """
180 bdf9a46c Stanislav Král
        Creates a CSR (Certificate Signing Request)
181 6c098d6e Stanislav Král
182
        :param subject: an instance of <Subject> representing the subject to be added to the CSR
183 87fd5afc Stanislav Král
        :param key: the private key of the subject to be used to generate the CSR
184
        :param key_pass: passphrase of the subject's private key
185 18588728 Stanislav Král
        :return: string containing the generated certificate signing request in PEM format
186 6c098d6e Stanislav Král
        """
187
188 b3c80ccb David Friesecký
        Logger.debug("Function launched.")
189
190 57898b2f Stanislav Král
        subj_param = self.__subject_to_param_format(subject)
191 6c098d6e Stanislav Král
192
        args = ["req", "-new", "-subj", subj_param, "-key", "-"]
193
194 fe647b46 Stanislav Král
        # add the passphrase even when None is passed. Otherwise when running tests with pytest some tests freeze
195
        # waiting for the passphrase to be typed in
196 87fd5afc Stanislav Král
        args.extend(["-passin", f"pass:{key_pass}"])
197 6c098d6e Stanislav Král
198 87fd5afc Stanislav Král
        return self.__run_for_output(args, proc_input=bytes(key, encoding="utf-8")).decode()
199 c0aed2f5 Stanislav Král
200 9a55ea8a Stanislav Král
    def __sign_csr(self, csr, issuer_pem, issuer_key, issuer_key_pass=None, extensions="", days=30, sn: int = None):
201 fe647b46 Stanislav Král
        """
202
        Signs the given CSR by the given issuer CA
203 ad068f9d Stanislav Král
204 fe647b46 Stanislav Král
        :param csr: a string containing the CSR to be signed
205
        :param issuer_pem: string containing the certificate of the issuer that will sign this CSR in PEM format
206
        :param issuer_key: string containing the private key of the issuer's certificate in PEM format
207 9dbbcdae Stanislav Král
        :param issuer_key_pass: string containing the passphrase of the private key of the issuer's certificate in PEM
208
        format
209 fe647b46 Stanislav Král
        :param extensions: extensions to be applied when signing the CSR
210 c4b2f4d2 Stanislav Král
        :param days: number of days for which the certificate will be valid
211 9a55ea8a Stanislav Král
        :param sn: serial number to be set, when "None" is set a random serial number is generated
212 fe647b46 Stanislav Král
        :return: string containing the generated and signed certificate in PEM format
213
        """
214
215 b3c80ccb David Friesecký
        Logger.debug("Function launched.")
216
217 fe647b46 Stanislav Král
        # concatenate CSR, issuer certificate and issuer's key (will be used in the openssl call)
218
        proc_input = csr + issuer_pem + issuer_key
219
220 9a55ea8a Stanislav Král
        # TODO find a better way to generate a random serial number or let openssl generate a .srl file
221
        # when serial number is not passed generate a random one
222
        if sn is None:
223
            sn = random.randint(0, MAX_SN)
224
225 fe647b46 Stanislav Král
        # prepare openssl parameters...
226
        # CSR, CA and CA's private key will be passed via stdin (that's the meaning of the '-' symbol)
227 2510f01a Stanislav Král
        params = ["x509", "-req", "-in", "-", "-CA", "-", "-CAkey", "-", "-CAcreateserial", "-days", str(days),
228
                  "-set_serial", str(sn)]
229 fe647b46 Stanislav Král
230
        with TemporaryFile("extensions.conf", extensions) as ext_path:
231
            # add the passphrase even when None is passed. Otherwise when running tests with pytest some tests freeze
232
            # waiting for the passphrase to be typed in
233
            params.extend(["-passin", f"pass:{issuer_key_pass}"])
234
235
            if len(extensions) > 0:
236
                params.extend(["-extfile", ext_path])
237
238 7444d4cb Stanislav Král
            return self.__run_for_output(params, proc_input=(bytes(proc_input, encoding="utf-8"))).decode()
239 fe647b46 Stanislav Král
240 18588728 Stanislav Král
    def create_crt(self, subject, subject_key, issuer_pem, issuer_key, subject_key_pass=None, issuer_key_pass=None,
241 5fdd01a6 Stanislav Král
                   extensions="",
242 9a55ea8a Stanislav Král
                   days=30,
243
                   sn: int = None):
244 9dbbcdae Stanislav Král
        """
245 61a42455 Stanislav Král
        Creates a certificate by using the given subject, subject's key, issuer and its key.
246 ad068f9d Stanislav Král
247 9dbbcdae Stanislav Král
        :param subject: subject to be added to the created certificate
248 18588728 Stanislav Král
        :param subject_key: string containing the private key to be used when creating the certificate in PEM format
249 9dbbcdae Stanislav Král
        :param issuer_key: string containing the private key of the issuer's certificate in PEM format
250
        :param issuer_pem: string containing the certificate of the issuer that will sign this CSR in PEM format
251 36409852 Stanislav Král
        :param subject_key_pass: string containing the passphrase of the private key used when creating the certificate
252
        in PEM format
253 9dbbcdae Stanislav Král
        :param issuer_key_pass: string containing the passphrase of the private key of the issuer's certificate in PEM
254
        format
255
        :param extensions: extensions to be applied when creating the certificate
256 5fdd01a6 Stanislav Král
        :param days: number of days for which the certificate will be valid
257 9a55ea8a Stanislav Král
        :param sn: serial number to be set, when "None" is set a random serial number is generated
258 18588728 Stanislav Král
        :return: string containing the generated certificate in PEM format
259 9dbbcdae Stanislav Král
        """
260 b3c80ccb David Friesecký
261
        Logger.debug("Function launched.")
262
263 87fd5afc Stanislav Král
        csr = self.__create_csr(subject, subject_key, key_pass=subject_key_pass)
264 87a7a4a5 Stanislav Král
        return self.__sign_csr(csr, issuer_pem, issuer_key, issuer_key_pass=issuer_key_pass, extensions=extensions,
265 9a55ea8a Stanislav Král
                               days=days, sn=sn)
266 5fdd01a6 Stanislav Král
267
    @staticmethod
268 61a42455 Stanislav Král
    def verify_cert(certificate):
269
        """
270
        Verifies whether the given certificate is not expired.
271
272
        :param certificate: certificate to be verified in PEM format
273
        :return: Returns `true` if the certificate is not expired, `false` when expired.
274
        """
275 b3c80ccb David Friesecký
276
        Logger.debug("Function launched.")
277
278 5fdd01a6 Stanislav Král
        # call openssl to check whether the certificate is valid to this date
279
        args = [OPENSSL_EXECUTABLE, "x509", "-checkend", "0", "-noout", "-text", "-in", "-"]
280
281
        # create a new process
282
        proc = subprocess.Popen(args, stdin=subprocess.PIPE,
283
                                stdout=subprocess.PIPE,
284
                                stderr=subprocess.PIPE)
285
286
        out, err = proc.communicate(bytes(certificate, encoding="utf-8"))
287
288
        # zero return code means that the certificate is valid
289
        if proc.returncode == 0:
290
            return True
291
        elif proc.returncode == 1 and "Certificate will expire" in out.decode():
292
            # 1 return code means that the certificate is invalid but such message has to be present in the proc output
293
            return False
294
        else:
295
            # the process failed because of some other reason (incorrect cert format)
296 b3c80ccb David Friesecký
            Logger.error("CryptographyException")
297 5fdd01a6 Stanislav Král
            raise CryptographyException(OPENSSL_EXECUTABLE, args, err.decode())
298 9dbbcdae Stanislav Král
299 19e5260d Stanislav Král
    def extract_public_key_from_private_key(self, private_key_pem: str, passphrase=None) -> str:
300 5c748d51 Stanislav Král
        """
301 e8face67 Stanislav Král
        Extracts a public key from the given private key passed in PEM format
302
        :param private_key_pem: PEM data representing the private key from which a public key should be extracted
303
        :param passphrase: passphrase to be provided when the supplied private key is encrypted
304 5c748d51 Stanislav Král
        :return: a string containing the extracted public key in PEM format
305
        """
306 b3c80ccb David Friesecký
307
        Logger.debug("Function launched.")
308
309 e8face67 Stanislav Král
        args = ["rsa", "-in", "-", "-pubout"]
310
        if passphrase is not None:
311
            args.extend(["-passin", f"pass:{passphrase}"])
312
        return self.__run_for_output(args, proc_input=bytes(private_key_pem, encoding="utf-8")).decode()
313 5c748d51 Stanislav Král
314 19e5260d Stanislav Král
    def extract_public_key_from_certificate(self, cert_pem: str) -> str:
315
        """
316
        Extracts a public key from the given certificate passed in PEM format
317
        :param cert_pem: PEM data representing a certificate from which a public key should be extracted
318
        :return: a string containing the extracted public key in PEM format
319
        """
320 b3c80ccb David Friesecký
321
        Logger.debug("Function launched.")
322
323 19e5260d Stanislav Král
        # extracting public key from a certificate does not seem to require a passphrase even when
324
        # signed using an encrypted PK
325
        args = ["x509", "-in", "-", "-noout", "-pubkey"]
326
        return self.__run_for_output(args, proc_input=bytes(cert_pem, encoding="utf-8")).decode()
327
328 4a40b0d2 Stanislav Král
    def parse_cert_pem(self, cert_pem):
329 cc51ca2c Stanislav Král
        """
330 36409852 Stanislav Král
        Parses the given certificate in PEM format and returns the subject of the certificate and it's NOT_BEFORE
331
        and NOT_AFTER field
332 cc51ca2c Stanislav Král
        :param cert_pem: a certificated in a PEM format to be parsed
333 36409852 Stanislav Král
        :return: a tuple containing a subject, NOT_BEFORE and NOT_AFTER dates
334 cc51ca2c Stanislav Král
        """
335 b3c80ccb David Friesecký
336
        Logger.debug("Function launched.")
337
338 cc51ca2c Stanislav Král
        # run openssl x509 to view certificate content
339 36409852 Stanislav Král
        args = ["x509", "-noout", "-subject", "-startdate", "-enddate", "-in", "-"]
340 cc51ca2c Stanislav Král
341 36409852 Stanislav Král
        cert_info_raw = self.__run_for_output(args, proc_input=bytes(cert_pem, encoding="utf-8")).decode()
342
343
        # split lines
344
        results = re.split("\n", cert_info_raw)
345 3e770afd Jan Pašek
        subj_line = results[0].strip()
346
        not_before_line = results[1].strip()
347
        not_after_line = results[2].strip()
348 36409852 Stanislav Král
349
        # attempt to extract subject via regex
350
        match = re.search(r"subject=(.*)", subj_line)
351 4a40b0d2 Stanislav Král
        if match is None:
352 cc51ca2c Stanislav Král
            # TODO use logger
353 36409852 Stanislav Král
            print(f"Could not find subject to parse: {subj_line}")
354 cc51ca2c Stanislav Král
            return None
355 4a40b0d2 Stanislav Král
        else:
356 36409852 Stanislav Král
            # find all attributes (key = value)
357
            found = re.findall(r"\s?([^c=\s]+)\s?=\s?([^,\n]+)", match.group(1))
358 cc51ca2c Stanislav Král
            subj = Subject()
359
            for key, value in found:
360
                if key == "C":
361 3e770afd Jan Pašek
                    subj.country = value.strip()
362 cc51ca2c Stanislav Král
                elif key == "ST":
363 3e770afd Jan Pašek
                    subj.state = value.strip()
364 cc51ca2c Stanislav Král
                elif key == "L":
365 3e770afd Jan Pašek
                    subj.locality = value.strip()
366 cc51ca2c Stanislav Král
                elif key == "O":
367 3e770afd Jan Pašek
                    subj.organization = value.strip()
368 cc51ca2c Stanislav Král
                elif key == "OU":
369 3e770afd Jan Pašek
                    subj.organization_unit = value.strip()
370 cc51ca2c Stanislav Král
                elif key == "CN":
371 3e770afd Jan Pašek
                    subj.common_name = value.strip()
372 cc51ca2c Stanislav Král
                elif key == "emailAddress":
373 3e770afd Jan Pašek
                    subj.email_address = value.strip()
374 36409852 Stanislav Král
375
        # extract notBefore and notAfter date fields
376
        not_before = re.search(r"notBefore=(.*)", not_before_line)
377
        not_after = re.search(r"notAfter=(.*)", not_after_line)
378
379
        # if date fields are found parse them into date objects
380
        if not_before is not None:
381 4faab824 Jan Pašek
            not_before = time.strptime(not_before.group(1).strip(), NOT_AFTER_BEFORE_DATE_FORMAT)
382 36409852 Stanislav Král
        if not_after is not None:
383 4faab824 Jan Pašek
            not_after = time.strptime(not_after.group(1).strip(), NOT_AFTER_BEFORE_DATE_FORMAT)
384 36409852 Stanislav Král
385
        # TODO wrapper class?
386
        # return it as a tuple
387
        return subj, not_before, not_after
388 4a40b0d2 Stanislav Král
389 81dbb479 Jan Pašek
    def get_openssl_version(self) -> str:
390
        """
391
        Get version of the OpenSSL installed on the system
392
        :return: version of the OpenSSL as returned from the process
393
        """
394 b3c80ccb David Friesecký
395
        Logger.debug("Function launched.")
396
397 81dbb479 Jan Pašek
        return self.__run_for_output(["version"]).decode("utf-8")
398
399 64cfca84 Jan Pašek
    def generate_crl(self, cert: Certificate, key: PrivateKey, index_file_path: str) -> str:
400 0fd6d825 Jan Pašek
        """
401
        Generate a CertificateRevocationList for a specified
402
        certificate authority.
403
404 64cfca84 Jan Pašek
        :param key: key that is used to sign the CRL (must belong to the given certificate)
405
        :param cert: Certificate of the certificate authority that issue the CRL
406 0fd6d825 Jan Pašek
        :param index_file_path: path to a file that contains the openssl index with all revoked certificates
407
        :return: CRL encoded in PEM format string
408
        """
409 b3c80ccb David Friesecký
410
        Logger.debug("Function launched.")
411
412 dd91fb7a Jan Pašek
        # openssl ca requires the .srl file to exists, therefore a dummy, unused file is created
413 64cfca84 Jan Pašek
        with TemporaryFile("serial.srl", "0") as serial_file, \
414
             TemporaryFile("crl.conf", CRL_CONFIG % (index_file_path, serial_file)) as config_file, \
415
             TemporaryFile("certificate.pem", cert.pem_data) as cert_file, \
416
             TemporaryFile("private_key.pem", key.private_key) as key_file:
417
418
            args = ["ca", "-config", config_file, "-gencrl", "-keyfile", key_file, "-cert", cert_file, "-outdir", "."]
419 94e89bb1 Jan Pašek
420
            if key.password is not None and key.password != "":
421
                args.extend(["-passin", f"pass:{key.password}"])
422
423 64cfca84 Jan Pašek
            return self.__run_for_output(args).decode("utf-8")
424
425 7f9b2c58 Captain_Trojan
    def generate_ocsp(self, cert, key, index_path, der_ocsp_request):
426
        """
427
        Generate an OCSP Response from an OCSP Request given the issuer cert, issuer cert key and the index file.
428
        The OSCP Response is signed by the CA itself (recommended way according to multiple sources).
429
430
        :param cert: issuer certificate
431
        :param key: corresponding key
432
        :param index_path: path/to/the/generated/index/file
433
        :param der_ocsp_request: DER encoded OCSP Request
434
        :return: DER encoded OCSP Response
435
        """
436 b3c80ccb David Friesecký
437
        Logger.debug("Function launched.")
438
439 7f9b2c58 Captain_Trojan
        with TemporaryFile("certificate.pem", cert.pem_data) as ca_certificate, \
440
             TemporaryFile("private_key.pem", key.private_key) as key_file, \
441
             TemporaryFile("request.der", der_ocsp_request) as request_file:
442
443
            args = ["ocsp", "-index", index_path, "-CA", ca_certificate, "-rsigner", ca_certificate, "-rkey", key_file,
444
                    "-reqin", request_file, "-respout", "-"]
445
446
            if key.password is not None and key.password != "":
447
                args.extend(["-passin", f"pass:{key.password}"])
448
449
            return self.__run_for_output(args)
450
451 c0aed2f5 Stanislav Král
452
class CryptographyException(Exception):
453
454
    def __init__(self, executable, args, message):
455
        self.executable = executable
456
        self.args = args
457
        self.message = message
458
459
    def __str__(self):
460 b3c80ccb David Friesecký
        # TODO check log is valid here
461
        msg = f"""
462 c0aed2f5 Stanislav Král
        EXECUTABLE: {self.executable}
463
        ARGS: {self.args}
464
        MESSAGE: {self.message}
465
        """
466 b3c80ccb David Friesecký
467
        Logger.error(msg)
468
        return msg