Projekt

Obecné

Profil

Stáhnout (12.5 KB) Statistiky
| Větev: | Tag: | Revize:
1 ef65f488 Stanislav Král
from typing import List
2
3 4c19a9b1 Stanislav Král
from src.constants import ROOT_CA_ID, INTERMEDIATE_CA_ID, CA_ID, CERTIFICATE_ID
4 4a40b0d2 Stanislav Král
from src.dao.certificate_repository import CertificateRepository
5
from src.model.certificate import Certificate
6 313b647b Stanislav Král
from src.model.private_key import PrivateKey
7 4a40b0d2 Stanislav Král
from src.model.subject import Subject
8
from src.services.cryptography import CryptographyService
9
10 313b647b Stanislav Král
import time
11
12
DATE_FORMAT = "%d.%m.%Y %H:%M:%S"
13 bbcb7c89 Stanislav Král
CA_EXTENSIONS = "basicConstraints=critical,CA:TRUE"
14 313b647b Stanislav Král
15 4a40b0d2 Stanislav Král
16
class CertificateService:
17
18
    def __init__(self, cryptography_service: CryptographyService, certificate_repository: CertificateRepository):
19
        self.cryptography_service = cryptography_service
20
        self.certificate_repository = certificate_repository
21
22 bbcb7c89 Stanislav Král
    # TODO usages present in method parameters but not in class diagram
23 ca3ac7c0 Stanislav Král
    def create_root_ca(self, key: PrivateKey, subject: Subject, extensions: str = "", config: str = "",
24 2f5101f1 Stanislav Král
                       usages=None, days=30):
25 a6727aa9 Stanislav Král
        """
26
        Creates a root CA certificate based on the given parameters.
27
        :param key: Private key to be used when generating the certificate
28
        :param subject: Subject to be used put into the certificate
29
        :param config: String containing the configuration to be used
30
        :param extensions: Name of the section in the configuration representing extensions
31
        :param usages: A dictionary containing usages of the certificate to be generated (see constants.py)
32 2f5101f1 Stanislav Král
        :param days: Number of days for which the generated cert. will be considered valid
33 a6727aa9 Stanislav Král
        :return: An instance of Certificate class representing the generated root CA cert
34
        """
35 ca3ac7c0 Stanislav Král
        if usages is None:
36
            usages = {}
37
38 313b647b Stanislav Král
        # create a new self signed  certificate
39
        cert_pem = self.cryptography_service.create_sscrt(subject, key.private_key, key_pass=key.password,
40 2f5101f1 Stanislav Král
                                                          extensions=extensions, config=config, days=days)
41 ca3ac7c0 Stanislav Král
        # specify CA usage
42
        usages[CA_ID] = True
43
44 4c19a9b1 Stanislav Král
        # wrap into Certificate class
45 a6727aa9 Stanislav Král
        certificate = self.__create_wrapper(cert_pem, key.private_key_id, usages, 0,
46 4c19a9b1 Stanislav Král
                                            ROOT_CA_ID)
47 313b647b Stanislav Král
48
        # store the wrapper into the repository
49
        created_id = self.certificate_repository.create(certificate)
50
51
        # assign the generated ID to the inserted certificate
52
        certificate.certificate_id = created_id
53 4a40b0d2 Stanislav Král
54 313b647b Stanislav Král
        return certificate
55 10fab051 Stanislav Král
56 a6727aa9 Stanislav Král
    def __create_wrapper(self, cert_pem, private_key_id, usages, parent_id, cert_type):
57
        """
58 a4e818dc Jan Pašek
        Wraps the given parameters using the Certificate class. Uses CryptographyService to find out the notBefore and
59 a6727aa9 Stanislav Král
        notAfter fields.
60
        :param cert_pem: PEM of the cert. to be wrapped
61
        :param private_key_id: ID of the private key used to create the given certificate
62
        :param usages: A dictionary containing usages of the generated certificate generated (see constants.py)
63
        :param parent_id: ID of the CA that issued this certificate
64
        :param cert_type: Type of this certificate (see constants.py)
65
        :return: An instance of the Certificate class wrapping the values passed  via method parameters
66
        """
67 4c19a9b1 Stanislav Král
        # parse the generated pem for subject and notBefore/notAfter fields
68 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
69 4c19a9b1 Stanislav Král
        subj, not_before, not_after = self.cryptography_service.parse_cert_pem(cert_pem)
70
        # format the parsed date
71
        not_before_formatted = time.strftime(DATE_FORMAT, not_before)
72
        not_after_formatted = time.strftime(DATE_FORMAT, not_after)
73
74
        # create a certificate wrapper
75 a6727aa9 Stanislav Král
        certificate = Certificate(-1, subj.common_name, not_before_formatted, not_after_formatted, cert_pem,
76 4c19a9b1 Stanislav Král
                                  private_key_id, cert_type, parent_id, usages)
77
78
        return certificate
79
80 bbcb7c89 Stanislav Král
    # TODO config parameter present in class diagram but not here (unused)
81
    def create_ca(self, subject_key: PrivateKey, subject: Subject, issuer_cert: Certificate, issuer_key: PrivateKey,
82 ca3ac7c0 Stanislav Král
                  extensions: str = "", days: int = 30, usages=None):
83 a6727aa9 Stanislav Král
        """
84
        Creates an intermediate CA certificate issued by the given parent CA.
85
        :param subject_key: Private key to be used when generating the certificate
86
        :param subject: Subject to be used put into the certificate
87
        :param issuer_cert: Issuer certificate that will sign the CSR required to create an intermediate CA
88
        :param issuer_key: PK used to generate the issuer certificate
89
        :param extensions: Extensions to be used when generating the certificate
90
        :param usages: A dictionary containing usages of the certificate to be generated (see constants.py)
91
        :param days: Number of days for which the generated cert. will be considered valid
92
        :return: An instance of Certificate class representing the generated intermediate CA cert
93
        """
94 ca3ac7c0 Stanislav Král
        if usages is None:
95
            usages = {}
96
97 bbcb7c89 Stanislav Král
        extensions = extensions + "\n" + CA_EXTENSIONS
98
        # TODO implement AIA URI via extensions
99
        cert_pem = self.cryptography_service.create_crt(subject, subject_key.private_key, issuer_cert.pem_data,
100
                                                        issuer_key.private_key,
101
                                                        subject_key_pass=subject_key.password,
102
                                                        issuer_key_pass=issuer_key.password, extensions=extensions,
103
                                                        days=days)
104
105 4c19a9b1 Stanislav Král
        # specify CA usage
106
        usages[CA_ID] = True
107
108
        # wrap into Certificate class
109 a6727aa9 Stanislav Král
        self.__create_wrapper(cert_pem, subject_key.private_key_id, usages,
110 4c19a9b1 Stanislav Král
                              issuer_cert.certificate_id, INTERMEDIATE_CA_ID)
111
112 bbcb7c89 Stanislav Král
        # parse the generated pem for subject and notBefore/notAfter fields
113
        subj, not_before, not_after = self.cryptography_service.parse_cert_pem(cert_pem)
114
115
        # format the parsed date
116
        not_before_formatted = time.strftime(DATE_FORMAT, not_before)
117
        not_after_formatted = time.strftime(DATE_FORMAT, not_after)
118
119 ca3ac7c0 Stanislav Král
        # specify CA usage
120
        usages[CA_ID] = True
121
122 bbcb7c89 Stanislav Král
        # create a certificate wrapper
123
        certificate = Certificate(-1, subject.common_name, not_before_formatted, not_after_formatted, cert_pem,
124 ca3ac7c0 Stanislav Král
                                  subject_key.private_key_id, INTERMEDIATE_CA_ID, issuer_cert.certificate_id, usages)
125 bbcb7c89 Stanislav Král
126
        # store the wrapper into the repository
127
        created_id = self.certificate_repository.create(certificate)
128
129
        # assign the generated ID to the inserted certificate
130
        certificate.certificate_id = created_id
131
132
        return certificate
133
134 4c19a9b1 Stanislav Král
    def create_end_cert(self, subject_key: PrivateKey, subject: Subject, issuer_cert: Certificate,
135
                        issuer_key: PrivateKey,
136
                        extensions: str = "", days: int = 30, usages=None):
137 a6727aa9 Stanislav Král
        """
138
        Creates an end certificate issued by the given parent CA.
139
        :param subject_key: Private key to be used when generating the certificate
140
        :param subject: Subject to be used put into the certificate
141
        :param issuer_cert: Issuer certificate that will sign the CSR required to create an intermediate CA
142
        :param issuer_key: PK used to generate the issuer certificate
143
        :param extensions: Extensions to be used when generating the certificate
144
        :param usages: A dictionary containing usages of the certificate to be generated (see constants.py)
145
        :param days: Number of days for which the generated cert. will be considered valid
146
        :return: An instance of Certificate class representing the generated cert
147
        """
148 4c19a9b1 Stanislav Král
        if usages is None:
149
            usages = {}
150
151
        # generate a new certificate
152
        cert_pem = self.cryptography_service.create_crt(subject, subject_key.private_key, issuer_cert.pem_data,
153
                                                        issuer_key.private_key,
154
                                                        subject_key_pass=subject_key.password,
155
                                                        issuer_key_pass=issuer_key.password, extensions=extensions,
156
                                                        days=days)
157
158
        # wrap the generated certificate using Certificate class
159 a6727aa9 Stanislav Král
        certificate = self.__create_wrapper(cert_pem, subject_key.private_key_id, usages,
160 4c19a9b1 Stanislav Král
                                            issuer_cert.certificate_id, CERTIFICATE_ID)
161
162
        created_id = self.certificate_repository.create(certificate)
163
164
        certificate.certificate_id = created_id
165
166
        return certificate
167
168 10fab051 Stanislav Král
    def get_certificate(self, unique_id: int) -> Certificate:
169 a6727aa9 Stanislav Král
        """
170
        Tries to fetch a certificate from the certificate repository using a given id.
171
        :param unique_id: ID of the certificate to be fetched
172
        :return: Instance of the Certificate class containing a certificate with the given id or `None` if such
173
        certificate is not found
174
        """
175 10fab051 Stanislav Král
        return self.certificate_repository.read(unique_id)
176 2a90f4fd Stanislav Král
177 ef65f488 Stanislav Král
    def get_certificates(self, cert_type=None) -> List[Certificate]:
178 a6727aa9 Stanislav Král
        """
179
        Tries to fetch a list of all certificates from the certificate repository. Using the `cert_type` parameter only
180
        certificates of the given type can be returned.
181
        :param cert_type: Type of certificates to be returned
182
        :return: List of instances of the Certificate class representing all certificates present in the certificate
183
        repository. An empty list is returned when no certificates are found.
184
        """
185 2a90f4fd Stanislav Král
        return self.certificate_repository.read_all(cert_type)
186 ef65f488 Stanislav Král
187
    def get_chain_of_trust(self, from_id: int, to_id: int = -1, exclude_root=True) -> List[Certificate]:
188 4e70d22a Stanislav Král
        """
189
        Traverses the certificate hierarchy tree upwards till a certificate with the `to_id` ID is found or till a
190
        root CA certificate is found. Root certificates are excluded from the chain by default.
191
        :param from_id: ID of the first certificate to be included in the chain of trust
192
        :param to_id: ID of the last certificate to be included in the chain of trust
193
        :param exclude_root: a flag indicating whether root CA certificate should be excluded
194
        :return: a list of certificates representing the chain of trust starting with the certificate given by `from_id`
195
        ID
196
        """
197
        # read the first certificate of the chain
198 ef65f488 Stanislav Král
        start_cert = self.certificate_repository.read(from_id)
199
200 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
201 ef65f488 Stanislav Král
        if start_cert is None or (start_cert.type_id == ROOT_CA_ID and exclude_root):
202
            return []
203
204
        current_cert = start_cert
205
        chain_of_trust = [current_cert]
206
207
        # TODO could possibly be simplified
208
        if start_cert.type_id == ROOT_CA_ID:
209 4e70d22a Stanislav Král
            # the first cert found is a root ca
210 ef65f488 Stanislav Král
            return chain_of_trust
211
212
        while True:
213
            parent_cert = self.certificate_repository.read(current_cert.parent_id)
214
215 4e70d22a Stanislav Král
            # check whether parent certificate exists
216
            if parent_cert is None:
217
                break
218
219
            # check whether the found certificate is a root certificate
220
            if parent_cert.type_id == ROOT_CA_ID:
221 ef65f488 Stanislav Král
                if not exclude_root:
222 4e70d22a Stanislav Král
                    # append the found root cert only if root certificates should not be excluded from the CoT
223 ef65f488 Stanislav Král
                    chain_of_trust.append(parent_cert)
224
                break
225
226 4e70d22a Stanislav Král
            # append the certificate
227 ef65f488 Stanislav Král
            chain_of_trust.append(parent_cert)
228
229 4e70d22a Stanislav Král
            # stop iterating over certificates if the id of the found certificate matches `to_id` method parameter
230 ef65f488 Stanislav Král
            if parent_cert.certificate_id == to_id:
231
                break
232
233
            current_cert = parent_cert
234
235
        return chain_of_trust
236 3d639744 Stanislav Král
237 a6727aa9 Stanislav Král
    def delete_certificate(self, unique_id) -> bool:
238 3d639744 Stanislav Král
        """
239
        Deletes a certificate
240
241
        :param unique_id: ID of specific certificate
242
243 a6727aa9 Stanislav Král
        :return: `True` when the deletion was successful. `False` in other case
244 3d639744 Stanislav Král
        """
245
        # TODO delete children?
246
        return self.certificate_repository.delete(unique_id)
247 c4ba6bb7 Jan Pašek
248
    def get_subject_from_certificate(self, certificate: Certificate) -> Subject:
249
        """
250
        Get Subject distinguished name from a Certificate
251
        :param certificate: certificate instance whose Subject shall be parsed
252
        :return: instance of Subject class containing resulting distinguished name
253
        """
254
        (subject, _, _) = self.cryptography_service.parse_cert_pem(certificate.pem_data)
255
        return subject