Projekt

Obecné

Profil

« Předchozí | Další » 

Revize fe647b46

Přidáno uživatelem Stanislav Král před asi 4 roky(ů)

Re #8472 - Added sign_csr method that signs a CSR

Added 3 unit tests testing the added method.

Zobrazit rozdíly:

proj/services/cryptography.py
49 49
            args.insert(0, executable)
50 50

  
51 51
            # create a new process
52
            proc = subprocess.Popen(args, stdin=subprocess.PIPE if proc_input is not None else None, stdout=subprocess.PIPE,
52
            proc = subprocess.Popen(args, stdin=subprocess.PIPE if proc_input is not None else None,
53
                                    stdout=subprocess.PIPE,
53 54
                                    stderr=subprocess.PIPE)
54 55

  
55 56
            out, err = proc.communicate(proc_input)
......
108 109
            # it would be best to not send the pass phrase at all, but for some reason pytest then prompts for
109 110
            # the pass phrase (this does not happen when run from pycharm)
110 111

  
111
            # if key_passphrase is not None:
112
            #  add the passphrase even when None is passed. Otherwise when running tests with pytest some tests freeze
113
            # waiting for the passphrase to be typed in
112 114
            args.extend(["-passin", f"pass:{key_passphrase}"])
113 115

  
114 116
            return self._run_for_output(args, proc_input=bytes(key, encoding="utf-8")).decode()
115 117

  
116
    def make_csr(self, subject, subject_key, subject_key_pass=None):
118
    def make_csr(self, subject, subject_key, subject_key_pass=""):
117 119
        """
118 120
        Makes a CSR (Certificate Signing Request)
119 121

  
......
127 129

  
128 130
        args = ["req", "-new", "-subj", subj_param, "-key", "-"]
129 131

  
130
        if subject_key_pass is not None:
131
            args.extend(["-passin", f"pass:{subject_key_pass}"])
132
        # add the passphrase even when None is passed. Otherwise when running tests with pytest some tests freeze
133
        # waiting for the passphrase to be typed in
134
        args.extend(["-passin", f"pass:{subject_key_pass}"])
132 135

  
133 136
        return self._run_for_output(args, proc_input=bytes(subject_key, encoding="utf-8")).decode()
134 137

  
138
    def sign_csr(self, csr, issuer_pem, issuer_key, issuer_key_pass=None, config="", extensions=""):
139
        """
140
        Signs the given CSR by the given issuer CA
141
        :param csr: a string containing the CSR to be signed
142
        :param issuer_pem: string containing the certificate of the issuer that will sign this CSR in PEM format
143
        :param issuer_key: string containing the private key of the issuer's certificate in PEM format
144
        :param issuer_key_pass: string containing the private key of the issuer's certificate in PEM format
145
        :param config: TODO NOT USED
146
        :param extensions: extensions to be applied when signing the CSR
147
        :return: string containing the generated and signed certificate in PEM format
148
        """
149

  
150
        # concatenate CSR, issuer certificate and issuer's key (will be used in the openssl call)
151
        proc_input = csr + issuer_pem + issuer_key
152

  
153
        # prepare openssl parameters...
154
        # CSR, CA and CA's private key will be passed via stdin (that's the meaning of the '-' symbol)
155
        params = ["x509", "-req", "-in", "-", "-CA", "-", "-CAkey", "-", "-CAcreateserial"]
156

  
157
        # TODO delete created -.srl file
158

  
159
        with TemporaryFile("extensions.conf", extensions) as ext_path:
160
            # add the passphrase even when None is passed. Otherwise when running tests with pytest some tests freeze
161
            # waiting for the passphrase to be typed in
162
            params.extend(["-passin", f"pass:{issuer_key_pass}"])
163

  
164
            if len(extensions) > 0:
165
                params.extend(["-extfile", ext_path])
166

  
167
            return self._run_for_output(params, proc_input=(bytes(proc_input, encoding="utf-8"))).decode()
168

  
135 169

  
136 170
class CryptographyException(Exception):
137 171

  
proj/tests/services/cryptography/sign_csr_test.py
1
import subprocess
2

  
3
import pytest
4

  
5
from proj.model.subject import Subject
6
from proj.services.cryptography import CryptographyException
7

  
8

  
9
def export_csr(csr):
10
    return subprocess.check_output(["openssl", "x509", "-noout", "-text", "-in", "-"],
11
                                   input=bytes(csr, encoding="utf-8"), stderr=subprocess.STDOUT).decode()
12

  
13

  
14
def test_sign_csr(service):
15
    # create root CA
16
    root_key = service.create_private_key()
17
    root_ca = service.create_sscrt(root_key, Subject(common_name="foo"))
18

  
19
    # create a private key to be used to make a CSR for the intermediate CA
20
    inter_key = service.create_private_key()
21
    csr = service.make_csr(Subject(common_name="bar", country="CZ"), inter_key)
22

  
23
    # sign the created CSR with root CA
24
    inter_ca = service.sign_csr(csr, root_ca, root_key)
25

  
26
    inter_ca_printed = export_csr(inter_ca)
27

  
28
    # assert fields
29
    assert "Issuer: CN = foo" in inter_ca_printed
30
    assert "Subject: CN = bar, C = CZ" in inter_ca_printed
31

  
32

  
33
def test_sign_csr_passphrase(service):
34
    # create root CA and encrypt the private key of the root CA
35
    root_key_passphrase = "barbaz"
36
    root_key = service.create_private_key(passphrase=root_key_passphrase)
37
    root_ca = service.create_sscrt(root_key, Subject(common_name="foo"), key_passphrase=root_key_passphrase)
38

  
39
    # create a private key to be used to make a CSR for the intermediate CA
40
    inter_key = service.create_private_key()
41
    csr = service.make_csr(Subject(common_name="bar", country="CZ"), inter_key)
42

  
43
    # sign the created CSR with root CA and specify root key passphrase
44
    inter_ca = service.sign_csr(csr, root_ca, root_key, issuer_key_pass=root_key_passphrase)
45

  
46
    inter_ca_printed = export_csr(inter_ca)
47

  
48
    # assert fields
49
    assert "Issuer: CN = foo" in inter_ca_printed
50
    assert "Subject: CN = bar, C = CZ" in inter_ca_printed
51

  
52
    # try to sign it using a wrong passphrase
53
    with pytest.raises(CryptographyException) as e:
54
        service.sign_csr(csr, root_ca, root_key,
55
                         extensions="authorityInfoAccess = caIssuers;URI:bar.cz/baz.cert", issuer_key_pass="bazbaz")
56
    assert "bad decrypt" in e.value.message
57

  
58
    # try to sign it without specifying a passphrase
59
    with pytest.raises(CryptographyException) as e:
60
        service.sign_csr(csr, root_ca, root_key,
61
                         extensions="authorityInfoAccess = caIssuers;URI:bar.cz/baz.cert")
62
    assert "bad decrypt" in e.value.message
63

  
64

  
65
def test_sign_csr_extensions(service):
66
    # create root CA and encrypt the private key of the root CA
67
    root_key_passphrase = "barbaz"
68
    root_key = service.create_private_key(passphrase=root_key_passphrase)
69
    root_ca = service.create_sscrt(root_key, Subject(common_name="foo"), key_passphrase=root_key_passphrase)
70

  
71
    # create a private key to be used to make a CSR for the intermediate CA
72
    inter_key = service.create_private_key()
73
    csr = service.make_csr(Subject(common_name="bar", country="CZ"), inter_key)
74

  
75
    # sign the created CSR with root CA and specify root key passphrase and specify extensions (AIA and CA)
76
    inter_ca = service.sign_csr(csr, root_ca, root_key,
77
                                extensions="authorityInfoAccess = caIssuers;URI:bar.cz/baz/cert\nbasicConstraints=critical,CA:TRUE",
78
                                issuer_key_pass=root_key_passphrase)
79

  
80
    inter_ca_printed = export_csr(inter_ca)
81

  
82
    # assert fields
83
    assert "Issuer: CN = foo" in inter_ca_printed
84
    assert "Subject: CN = bar, C = CZ" in inter_ca_printed
85

  
86
    # assert extensions
87
    expected_extensions = """        X509v3 extensions:
88
            Authority Information Access: 
89
                CA Issuers - URI:bar.cz/baz/cert
90

  
91
            X509v3 Basic Constraints: critical
92
                CA:TRUE"""
93
    assert expected_extensions in inter_ca_printed

Také k dispozici: Unified diff