Revize fe647b46
Přidáno uživatelem Stanislav Král před asi 4 roky(ů)
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
Re #8472 - Added sign_csr method that signs a CSR
Added 3 unit tests testing the added method.