Projekt

Obecné

Profil

Stáhnout (22.8 KB) Statistiky
| Větev: | Tag: | Revize:
1 ef65f488 Stanislav Král
from typing import List
2
3 151e7604 Jan Pašek
from injector import inject
4
5 20b33bd4 Jan Pašek
from src.config.configuration import Configuration
6
from src.constants import ROOT_CA_ID, INTERMEDIATE_CA_ID, CA_ID, CERTIFICATE_ID, CERTIFICATE_STATES, \
7 c15357a9 Jan Pašek
    CERTIFICATE_REVOCATION_REASONS, SSL_ID, SIGNATURE_ID, AUTHENTICATION_ID, CERTIFICATE_VALID, CERTIFICATE_EXPIRED, \
8
    CERTIFICATE_REVOKED
9 4a40b0d2 Stanislav Král
from src.dao.certificate_repository import CertificateRepository
10 8ff50e4e Jan Pašek
from src.exceptions.certificate_not_found_exception import CertificateNotFoundException
11 485913d0 Captain_Trojan
from src.exceptions.database_exception import DatabaseException
12 9e6f791a Jan Pašek
from src.exceptions.unknown_exception import UnknownException
13 4a40b0d2 Stanislav Král
from src.model.certificate import Certificate
14 313b647b Stanislav Král
from src.model.private_key import PrivateKey
15 4a40b0d2 Stanislav Král
from src.model.subject import Subject
16
from src.services.cryptography import CryptographyService
17
18 313b647b Stanislav Král
import time
19
20 329216fe Stanislav Král
from src.utils.usages_to_extensions import usages_to_extension_lines, ExtensionFieldFlags, CRITICAL, KEY_CERT_SIGN, \
21
    CRL_SIGN, CA, DIGITAL_SIGNATURE, KEY_ENCIPHERMENT, KEY_AGREEMENT, SERVER_AUTH, NON_REPUDIATION, TIME_STAMPING, \
22
    CLIENT_AUTH
23
24 b3c80ccb David Friesecký
from src.utils.logger import Logger
25
26 7313994f Stanislav Král
VALID_FROM_TO_DATE_FORMAT = "%d.%m.%Y %H:%M:%S"
27 bbcb7c89 Stanislav Král
CA_EXTENSIONS = "basicConstraints=critical,CA:TRUE"
28 20b33bd4 Jan Pašek
CRL_EXTENSION = "crlDistributionPoints=URI:"
29
OCSP_EXTENSION = "authorityInfoAccess=OCSP;URI:"
30
STATUS_REVOKED = "revoked"
31
STATUS_VALID = "valid"
32 313b647b Stanislav Král
33 329216fe Stanislav Král
# define which flags are required for various usages
34
REQUIRED_USAGE_EXTENSION_FLAGS = {
35
    CA_ID: ExtensionFieldFlags({CRITICAL, KEY_CERT_SIGN, CRL_SIGN}, {}, {CRITICAL, CA}),
36
    SSL_ID: ExtensionFieldFlags({DIGITAL_SIGNATURE, KEY_ENCIPHERMENT, KEY_AGREEMENT}, {SERVER_AUTH}, {}),
37 52f2eca4 Jan Pašek
    SIGNATURE_ID: ExtensionFieldFlags({DIGITAL_SIGNATURE, NON_REPUDIATION}, {}, {}),
38 329216fe Stanislav Král
    AUTHENTICATION_ID: ExtensionFieldFlags({DIGITAL_SIGNATURE}, {CLIENT_AUTH}, {})}
39
40 4a40b0d2 Stanislav Král
41
class CertificateService:
42
43 151e7604 Jan Pašek
    @inject
44 20b33bd4 Jan Pašek
    def __init__(self, cryptography_service: CryptographyService,
45
                 certificate_repository: CertificateRepository,
46
                 configuration: Configuration):
47 4a40b0d2 Stanislav Král
        self.cryptography_service = cryptography_service
48
        self.certificate_repository = certificate_repository
49 20b33bd4 Jan Pašek
        self.configuration = configuration
50 4a40b0d2 Stanislav Král
51 bbcb7c89 Stanislav Král
    # TODO usages present in method parameters but not in class diagram
52 ca3ac7c0 Stanislav Král
    def create_root_ca(self, key: PrivateKey, subject: Subject, extensions: str = "", config: str = "",
53 2f5101f1 Stanislav Král
                       usages=None, days=30):
54 a6727aa9 Stanislav Král
        """
55
        Creates a root CA certificate based on the given parameters.
56
        :param key: Private key to be used when generating the certificate
57
        :param subject: Subject to be used put into the certificate
58
        :param config: String containing the configuration to be used
59
        :param extensions: Name of the section in the configuration representing extensions
60
        :param usages: A dictionary containing usages of the certificate to be generated (see constants.py)
61 2f5101f1 Stanislav Král
        :param days: Number of days for which the generated cert. will be considered valid
62 a6727aa9 Stanislav Král
        :return: An instance of Certificate class representing the generated root CA cert
63
        """
64 b3c80ccb David Friesecký
65
        Logger.debug("Function launched.")
66
67 ca3ac7c0 Stanislav Král
        if usages is None:
68
            usages = {}
69
70 20b33bd4 Jan Pašek
        cert_id = self.certificate_repository.get_next_id()
71
72 329216fe Stanislav Král
        # specify CA usage
73
        usages[CA_ID] = True
74
75
        # generate extension configuration lines based on the specified usages
76
        extensions = extensions + "\n" + "\n".join(usages_to_extension_lines(usages, REQUIRED_USAGE_EXTENSION_FLAGS))
77
78 313b647b Stanislav Král
        # create a new self signed  certificate
79
        cert_pem = self.cryptography_service.create_sscrt(subject, key.private_key, key_pass=key.password,
80 87c56935 Stanislav Král
                                                          extensions=extensions, config=config, days=days, sn=cert_id)
81 ca3ac7c0 Stanislav Král
82 4c19a9b1 Stanislav Král
        # wrap into Certificate class
83 a6727aa9 Stanislav Král
        certificate = self.__create_wrapper(cert_pem, key.private_key_id, usages, 0,
84 4c19a9b1 Stanislav Král
                                            ROOT_CA_ID)
85 313b647b Stanislav Král
86
        # store the wrapper into the repository
87
        created_id = self.certificate_repository.create(certificate)
88
89
        # assign the generated ID to the inserted certificate
90
        certificate.certificate_id = created_id
91 4a40b0d2 Stanislav Král
92 313b647b Stanislav Král
        return certificate
93 10fab051 Stanislav Král
94 a6727aa9 Stanislav Král
    def __create_wrapper(self, cert_pem, private_key_id, usages, parent_id, cert_type):
95
        """
96 a4e818dc Jan Pašek
        Wraps the given parameters using the Certificate class. Uses CryptographyService to find out the notBefore and
97 a6727aa9 Stanislav Král
        notAfter fields.
98
        :param cert_pem: PEM of the cert. to be wrapped
99
        :param private_key_id: ID of the private key used to create the given certificate
100
        :param usages: A dictionary containing usages of the generated certificate generated (see constants.py)
101
        :param parent_id: ID of the CA that issued this certificate
102
        :param cert_type: Type of this certificate (see constants.py)
103
        :return: An instance of the Certificate class wrapping the values passed  via method parameters
104
        """
105 b3c80ccb David Friesecký
106
        Logger.debug("Function launched.")
107
108 4c19a9b1 Stanislav Král
        # parse the generated pem for subject and notBefore/notAfter fields
109 a6727aa9 Stanislav Král
        # TODO this could be improved in the future in such way that calling openssl is not required to parse the dates
110 4c19a9b1 Stanislav Král
        subj, not_before, not_after = self.cryptography_service.parse_cert_pem(cert_pem)
111
        # format the parsed date
112 7313994f Stanislav Král
        not_before_formatted = time.strftime(VALID_FROM_TO_DATE_FORMAT, not_before)
113
        not_after_formatted = time.strftime(VALID_FROM_TO_DATE_FORMAT, not_after)
114 4c19a9b1 Stanislav Král
115
        # create a certificate wrapper
116 894cc2cd David Friesecký
        certificate = Certificate(-1, not_before_formatted, not_after_formatted, cert_pem, cert_type, parent_id,
117
                                  private_key_id, usages, subj.common_name, subj.country, subj.locality, subj.state,
118
                                  subj.organization, subj.organization_unit, subj.email_address)
119 4c19a9b1 Stanislav Král
120
        return certificate
121
122 bbcb7c89 Stanislav Král
    # TODO config parameter present in class diagram but not here (unused)
123
    def create_ca(self, subject_key: PrivateKey, subject: Subject, issuer_cert: Certificate, issuer_key: PrivateKey,
124 ca3ac7c0 Stanislav Král
                  extensions: str = "", days: int = 30, usages=None):
125 a6727aa9 Stanislav Král
        """
126
        Creates an intermediate CA certificate issued by the given parent CA.
127
        :param subject_key: Private key to be used when generating the certificate
128
        :param subject: Subject to be used put into the certificate
129
        :param issuer_cert: Issuer certificate that will sign the CSR required to create an intermediate CA
130
        :param issuer_key: PK used to generate the issuer certificate
131
        :param extensions: Extensions to be used when generating the certificate
132
        :param usages: A dictionary containing usages of the certificate to be generated (see constants.py)
133
        :param days: Number of days for which the generated cert. will be considered valid
134
        :return: An instance of Certificate class representing the generated intermediate CA cert
135
        """
136 b3c80ccb David Friesecký
137
        Logger.debug("Function launched.")
138
139 ca3ac7c0 Stanislav Král
        if usages is None:
140
            usages = {}
141
142 329216fe Stanislav Král
        # specify CA usage
143
        usages[CA_ID] = True
144
145
        # generate extension configuration lines based on the specified usages
146
        extensions = extensions + "\n" + "\n".join(usages_to_extension_lines(usages, REQUIRED_USAGE_EXTENSION_FLAGS))
147
148 20b33bd4 Jan Pašek
        # Add CRL and OCSP distribution point to certificate extensions
149
        cert_id = self.certificate_repository.get_next_id()
150 ea1229ee Jan Pašek
        extensions = extensions + "\n" + CRL_EXTENSION + " " + self.__get_crl_endpoint(issuer_cert.certificate_id)
151
        extensions = extensions + "\n" + OCSP_EXTENSION + " " + self.__get_ocsp_endpoint(issuer_cert.certificate_id)
152 20b33bd4 Jan Pašek
153 bbcb7c89 Stanislav Král
        # TODO implement AIA URI via extensions
154
        cert_pem = self.cryptography_service.create_crt(subject, subject_key.private_key, issuer_cert.pem_data,
155
                                                        issuer_key.private_key,
156
                                                        subject_key_pass=subject_key.password,
157
                                                        issuer_key_pass=issuer_key.password, extensions=extensions,
158 87c56935 Stanislav Král
                                                        days=days,
159
                                                        sn=cert_id)
160 bbcb7c89 Stanislav Král
161 4c19a9b1 Stanislav Král
        # wrap into Certificate class
162 894cc2cd David Friesecký
        certificate = self.__create_wrapper(cert_pem, subject_key.private_key_id, usages,
163
                                            issuer_cert.certificate_id, INTERMEDIATE_CA_ID)
164 bbcb7c89 Stanislav Král
165
        # store the wrapper into the repository
166
        created_id = self.certificate_repository.create(certificate)
167
168
        # assign the generated ID to the inserted certificate
169
        certificate.certificate_id = created_id
170
171
        return certificate
172
173 4c19a9b1 Stanislav Král
    def create_end_cert(self, subject_key: PrivateKey, subject: Subject, issuer_cert: Certificate,
174
                        issuer_key: PrivateKey,
175
                        extensions: str = "", days: int = 30, usages=None):
176 a6727aa9 Stanislav Král
        """
177
        Creates an end certificate issued by the given parent CA.
178
        :param subject_key: Private key to be used when generating the certificate
179
        :param subject: Subject to be used put into the certificate
180
        :param issuer_cert: Issuer certificate that will sign the CSR required to create an intermediate CA
181
        :param issuer_key: PK used to generate the issuer certificate
182
        :param extensions: Extensions to be used when generating the certificate
183
        :param usages: A dictionary containing usages of the certificate to be generated (see constants.py)
184
        :param days: Number of days for which the generated cert. will be considered valid
185
        :return: An instance of Certificate class representing the generated cert
186
        """
187 b3c80ccb David Friesecký
188
        Logger.debug("Function launched.")
189
190 4c19a9b1 Stanislav Král
        if usages is None:
191
            usages = {}
192
193 87c56935 Stanislav Král
        # get the next certificate ID in order to be able to specify the serial number
194
        cert_id = self.certificate_repository.get_next_id()
195
196 329216fe Stanislav Král
        # generate extension configuration lines based on the specified usages
197
        extensions = extensions + "\n" + "\n".join(usages_to_extension_lines(usages, REQUIRED_USAGE_EXTENSION_FLAGS))
198
199 ea1229ee Jan Pašek
        # Add CRL and OCSP distribution point to certificate extensions
200
        extensions = extensions + "\n" + CRL_EXTENSION + " " + self.__get_crl_endpoint(issuer_cert.certificate_id)
201
        extensions = extensions + "\n" + OCSP_EXTENSION + " " + self.__get_ocsp_endpoint(issuer_cert.certificate_id)
202
203 4c19a9b1 Stanislav Král
        # generate a new certificate
204
        cert_pem = self.cryptography_service.create_crt(subject, subject_key.private_key, issuer_cert.pem_data,
205
                                                        issuer_key.private_key,
206
                                                        subject_key_pass=subject_key.password,
207
                                                        issuer_key_pass=issuer_key.password, extensions=extensions,
208 87c56935 Stanislav Král
                                                        days=days,
209
                                                        sn=cert_id
210
                                                        )
211 4c19a9b1 Stanislav Král
212
        # wrap the generated certificate using Certificate class
213 a6727aa9 Stanislav Král
        certificate = self.__create_wrapper(cert_pem, subject_key.private_key_id, usages,
214 4c19a9b1 Stanislav Král
                                            issuer_cert.certificate_id, CERTIFICATE_ID)
215
216
        created_id = self.certificate_repository.create(certificate)
217
218
        certificate.certificate_id = created_id
219
220
        return certificate
221
222 10fab051 Stanislav Král
    def get_certificate(self, unique_id: int) -> Certificate:
223 a6727aa9 Stanislav Král
        """
224
        Tries to fetch a certificate from the certificate repository using a given id.
225
        :param unique_id: ID of the certificate to be fetched
226
        :return: Instance of the Certificate class containing a certificate with the given id or `None` if such
227
        certificate is not found
228
        """
229 b3c80ccb David Friesecký
230
        Logger.debug("Function launched.")
231
232 10fab051 Stanislav Král
        return self.certificate_repository.read(unique_id)
233 2a90f4fd Stanislav Král
234 ef65f488 Stanislav Král
    def get_certificates(self, cert_type=None) -> List[Certificate]:
235 a6727aa9 Stanislav Král
        """
236
        Tries to fetch a list of all certificates from the certificate repository. Using the `cert_type` parameter only
237
        certificates of the given type can be returned.
238
        :param cert_type: Type of certificates to be returned
239
        :return: List of instances of the Certificate class representing all certificates present in the certificate
240
        repository. An empty list is returned when no certificates are found.
241
        """
242 b3c80ccb David Friesecký
243
        Logger.debug("Function launched.")
244
245 2a90f4fd Stanislav Král
        return self.certificate_repository.read_all(cert_type)
246 ef65f488 Stanislav Král
247
    def get_chain_of_trust(self, from_id: int, to_id: int = -1, exclude_root=True) -> List[Certificate]:
248 4e70d22a Stanislav Král
        """
249
        Traverses the certificate hierarchy tree upwards till a certificate with the `to_id` ID is found or till a
250
        root CA certificate is found. Root certificates are excluded from the chain by default.
251
        :param from_id: ID of the first certificate to be included in the chain of trust
252
        :param to_id: ID of the last certificate to be included in the chain of trust
253
        :param exclude_root: a flag indicating whether root CA certificate should be excluded
254
        :return: a list of certificates representing the chain of trust starting with the certificate given by `from_id`
255
        ID
256
        """
257 b3c80ccb David Friesecký
258
        Logger.debug("Function launched.")
259
260 4e70d22a Stanislav Král
        # read the first certificate of the chain
261 ef65f488 Stanislav Král
        start_cert = self.certificate_repository.read(from_id)
262
263 4e70d22a Stanislav Král
        # if no cert is found or the current cert is root CA and root CAs should be excluded, then return an empty list
264 ef65f488 Stanislav Král
        if start_cert is None or (start_cert.type_id == ROOT_CA_ID and exclude_root):
265
            return []
266
267
        current_cert = start_cert
268
        chain_of_trust = [current_cert]
269
270
        # TODO could possibly be simplified
271
        if start_cert.type_id == ROOT_CA_ID:
272 4e70d22a Stanislav Král
            # the first cert found is a root ca
273 ef65f488 Stanislav Král
            return chain_of_trust
274
275
        while True:
276
            parent_cert = self.certificate_repository.read(current_cert.parent_id)
277
278 4e70d22a Stanislav Král
            # check whether parent certificate exists
279
            if parent_cert is None:
280
                break
281
282
            # check whether the found certificate is a root certificate
283
            if parent_cert.type_id == ROOT_CA_ID:
284 ef65f488 Stanislav Král
                if not exclude_root:
285 4e70d22a Stanislav Král
                    # append the found root cert only if root certificates should not be excluded from the CoT
286 ef65f488 Stanislav Král
                    chain_of_trust.append(parent_cert)
287
                break
288
289 4e70d22a Stanislav Král
            # append the certificate
290 ef65f488 Stanislav Král
            chain_of_trust.append(parent_cert)
291
292 4e70d22a Stanislav Král
            # stop iterating over certificates if the id of the found certificate matches `to_id` method parameter
293 ef65f488 Stanislav Král
            if parent_cert.certificate_id == to_id:
294
                break
295
296
            current_cert = parent_cert
297
298
        return chain_of_trust
299 3d639744 Stanislav Král
300 5f8a2c07 Captain_Trojan
    def delete_certificate(self, unique_id):
301 3d639744 Stanislav Král
        """
302 5f8a2c07 Captain_Trojan
        Deletes a certificate. Raises an Exception should any unexpected behavior occur.
303 3d639744 Stanislav Král
304
        :param unique_id: ID of specific certificate
305
        """
306 5f8a2c07 Captain_Trojan
307 b3c80ccb David Friesecký
        Logger.debug("Function launched.")
308
309 5f8a2c07 Captain_Trojan
        to_delete = self.certificate_repository.get_all_descendants_of(unique_id)
310
        if to_delete is None:
311 b3c80ccb David Friesecký
            Logger.error(f"No such certificate found 'ID = {unique_id}'.")
312 5f8a2c07 Captain_Trojan
            raise CertificateNotFoundException(unique_id)
313
314
        for cert in to_delete:
315
            try:
316
                self.set_certificate_revocation_status(cert.certificate_id, STATUS_REVOKED)
317
            except CertificateAlreadyRevokedException:
318 b3c80ccb David Friesecký
                Logger.info(f"Certificate already revoked 'ID = {unique_id}'.")
319 5f8a2c07 Captain_Trojan
                # TODO log as an info/debug, not warning or above <-- perfectly legal
320 02954c9d Jan Pašek
                pass
321 5f8a2c07 Captain_Trojan
322 b3c80ccb David Friesecký
            if not self.certificate_repository.delete(cert.certificate_id):
323
                Logger.error(f"The certificate has not been deleted 'ID = {cert.certificate_id}'.")
324
325 c4ba6bb7 Jan Pašek
326 485913d0 Captain_Trojan
    def get_certificates_issued_by(self, unique_id):
327
        """
328
        Returns a list of all children of a certificate identified by an unique_id.
329
        Raises a DatabaseException should any unexpected behavior occur.
330
        :param unique_id: target certificate ID
331
        :return: children of unique_id
332
        """
333 b3c80ccb David Friesecký
334
        Logger.debug("Function launched.")
335
336 485913d0 Captain_Trojan
        try:
337
            if self.certificate_repository.read(unique_id) is None:
338 b3c80ccb David Friesecký
                Logger.error(f"No such certificate found 'ID = {unique_id}'.")
339 485913d0 Captain_Trojan
                raise CertificateNotFoundException(unique_id)
340
        except DatabaseException:
341 b3c80ccb David Friesecký
            Logger.error(f"No such certificate found 'ID = {unique_id}'.")
342 485913d0 Captain_Trojan
            raise CertificateNotFoundException(unique_id)
343
344
        return self.certificate_repository.get_all_issued_by(unique_id)
345
346 20b33bd4 Jan Pašek
    def set_certificate_revocation_status(self, id, status, reason="unspecified"):
347
        """
348
        Set certificate status to 'valid' or 'revoked'.
349
        If the new status is revoked a reason can be provided -> default is unspecified
350
        :param reason: reason for revocation
351
        :param id: identifier of the certificate whose status is to be changed
352
        :param status: new status of the certificate
353
        """
354 b3c80ccb David Friesecký
355
        Logger.debug("Function launched.")
356
357 20b33bd4 Jan Pašek
        if status not in CERTIFICATE_STATES:
358 b3c80ccb David Friesecký
            Logger.error(f"Wrong parameter, invalid status '{status}'.")
359 20b33bd4 Jan Pašek
            raise CertificateStatusInvalidException(status)
360
        if reason not in CERTIFICATE_REVOCATION_REASONS:
361 b3c80ccb David Friesecký
            Logger.error(f"Wrong parameter, invalid reason '{reason}'.")
362 20b33bd4 Jan Pašek
            raise RevocationReasonInvalidException(reason)
363
364 9e6f791a Jan Pašek
        # check whether the certificate exists
365
        certificate = self.certificate_repository.read(id)
366
        if certificate is None:
367 b3c80ccb David Friesecký
            Logger.error(f"No such certificate found 'ID = {id}'.")
368 9e6f791a Jan Pašek
            raise CertificateNotFoundException(id)
369
370 9c704fb1 Jan Pašek
        updated = False
371 20b33bd4 Jan Pašek
        if status == STATUS_VALID:
372 9c704fb1 Jan Pašek
            updated = self.certificate_repository.clear_certificate_revocation(id)
373 20b33bd4 Jan Pašek
        elif status == STATUS_REVOKED:
374 9e6f791a Jan Pašek
            # check if the certificate is not revoked already
375
            revoked = self.certificate_repository.get_all_revoked_by(certificate.parent_id)
376
            if certificate.certificate_id in [x.certificate_id for x in revoked]:
377 b3c80ccb David Friesecký
                Logger.error(f"Certificate already revoked 'ID = {id}'.")
378 9e6f791a Jan Pašek
                raise CertificateAlreadyRevokedException(id)
379
380 20b33bd4 Jan Pašek
            revocation_timestamp = int(time.time())
381 9c704fb1 Jan Pašek
            updated = self.certificate_repository.set_certificate_revoked(id, str(revocation_timestamp), reason)
382
383
        if not updated:
384 b3c80ccb David Friesecký
            Logger.error(f"Repository returned 'false' from clear_certificate_revocation() "
385
                         f"or set_certificate_revoked().")
386 9e6f791a Jan Pašek
            raise UnknownException("Repository returned 'false' from clear_certificate_revocation() "
387
                                   "or set_certificate_revoked().")
388 20b33bd4 Jan Pašek
389 c4ba6bb7 Jan Pašek
    def get_subject_from_certificate(self, certificate: Certificate) -> Subject:
390
        """
391
        Get Subject distinguished name from a Certificate
392
        :param certificate: certificate instance whose Subject shall be parsed
393
        :return: instance of Subject class containing resulting distinguished name
394
        """
395 b3c80ccb David Friesecký
396
        Logger.debug("Function launched.")
397
398 894cc2cd David Friesecký
        subject = Subject(certificate.common_name,
399
                          certificate.country_code,
400
                          certificate.locality,
401
                          certificate.province,
402
                          certificate.organization,
403
                          certificate.organizational_unit,
404
                          certificate.email_address)
405 c4ba6bb7 Jan Pašek
        return subject
406 d3bfacfc Stanislav Král
407
    def get_public_key_from_certificate(self, certificate: Certificate):
408
        """
409
        Extracts a public key from the given certificate
410
        :param certificate: an instance of the Certificate class containing the certificate from which a public key
411
        should be extracted.
412
        :return: a string containing the extracted public key in PEM format
413
        """
414 b3c80ccb David Friesecký
415
        Logger.debug("Function launched.")
416
417 d3bfacfc Stanislav Král
        return self.cryptography_service.extract_public_key_from_certificate(certificate.pem_data)
418 20b33bd4 Jan Pašek
419 a53e5aef Jan Pašek
    def get_certificate_state(self, id: int) -> str:
420
        """
421
        Check whether the certificate is expired, valid or revoked.
422
            - in case it's revoked and expired, revoked is returned
423
        :param id: identifier of the certificate
424
        :return: certificates state from {valid, revoked, expired}
425
        :raises CertificateNotFoundException: in case id of non-existing certificate is entered
426
        """
427 4ff15a44 Jan Pašek
        Logger.debug("Function launched.")
428 c15357a9 Jan Pašek
        status = CERTIFICATE_VALID
429
430
        # Read the selected certificate from the repository
431
        certificate = self.certificate_repository.read(id)
432
        if certificate is None:
433 4ff15a44 Jan Pašek
            Logger.error("Certificate whose details were requested does not exists.")
434 c15357a9 Jan Pašek
            raise CertificateNotFoundException(id)
435
436
        # check the expiration date using OpenSSL
437
        if not self.cryptography_service.verify_cert(certificate.pem_data):
438
            status = CERTIFICATE_EXPIRED
439
440
        # check certificate revocation
441
        all_revoked_by_parent = self.certificate_repository.get_all_revoked_by(certificate.parent_id)
442
        all_revoked_by_parent_ids = [cert.certificate_id for cert in all_revoked_by_parent]
443
444
        if id in all_revoked_by_parent_ids:
445
            status = CERTIFICATE_REVOKED
446
447
        return status
448
449 a53e5aef Jan Pašek
450 20b33bd4 Jan Pašek
    def __get_crl_endpoint(self, ca_identifier: int) -> str:
451
        """
452
        Get URL address of CRL distribution endpoint based on
453
        issuer's ID
454
455
        :param ca_identifier: ID of issuing authority
456
        :return: CRL endpoint for the given CA
457
        """
458 b3c80ccb David Friesecký
459
        Logger.debug("Function launched.")
460
461 20b33bd4 Jan Pašek
        return self.configuration.base_server_url + "/api/crl/" + str(ca_identifier)
462
463
    def __get_ocsp_endpoint(self, ca_identifier: int) -> str:
464
        """
465
        Get URL address of OCSP distribution endpoint based on
466
        issuer's ID
467
468
        :param ca_identifier: ID of issuing authority
469
        :return: OCSP endpoint for the given CA
470
        """
471 b3c80ccb David Friesecký
472
        Logger.debug("Function launched.")
473
474 20b33bd4 Jan Pašek
        return self.configuration.base_server_url + "/api/ocsp/" + str(ca_identifier)
475
476
477
class RevocationReasonInvalidException(Exception):
478
    """
479
    Exception that denotes that the caller was trying to revoke
480
    a certificate using an invalid revocation reason
481
    """
482
483
    def __init__(self, reason):
484
        self.reason = reason
485
486
    def __str__(self):
487
        return f"Revocation reason '{self.reason}' is not valid."
488
489
490
class CertificateStatusInvalidException(Exception):
491
    """
492
    Exception that denotes that the caller was trying to set
493
    a certificate to an invalid state
494
    """
495
496
    def __init__(self, status):
497
        self.status = status
498
499
    def __str__(self):
500
        return f"Certificate status '{self.status}' is not valid."
501 9c704fb1 Jan Pašek
502
503 9e6f791a Jan Pašek
class CertificateAlreadyRevokedException(Exception):
504
    """
505
    Exception that denotes that the caller was trying to revoke
506
    a certificate that is already revoked
507
    """
508
509
    def __init__(self, id):
510
        self.id = id
511
512
    def __str__(self):
513
        return f"Certificate id '{self.id}' is already revoked."