• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import absolute_import, division, print_function
6
7import os
8
9import pytest
10
11from cryptography import x509
12from cryptography.hazmat.backends.interfaces import DERSerializationBackend
13from cryptography.hazmat.backends.openssl.backend import _RC2
14from cryptography.hazmat.primitives import serialization
15from cryptography.hazmat.primitives.serialization import load_pem_private_key
16from cryptography.hazmat.primitives.serialization.pkcs12 import (
17    load_key_and_certificates,
18    serialize_key_and_certificates,
19)
20
21from .utils import load_vectors_from_file
22from ...doubles import DummyKeySerializationEncryption
23
24
25@pytest.mark.requires_backend_interface(interface=DERSerializationBackend)
26class TestPKCS12Loading(object):
27    def _test_load_pkcs12_ec_keys(self, filename, password, backend):
28        cert = load_vectors_from_file(
29            os.path.join("x509", "custom", "ca", "ca.pem"),
30            lambda pemfile: x509.load_pem_x509_certificate(
31                pemfile.read(), backend
32            ),
33            mode="rb",
34        )
35        key = load_vectors_from_file(
36            os.path.join("x509", "custom", "ca", "ca_key.pem"),
37            lambda pemfile: load_pem_private_key(
38                pemfile.read(), None, backend
39            ),
40            mode="rb",
41        )
42        parsed_key, parsed_cert, parsed_more_certs = load_vectors_from_file(
43            os.path.join("pkcs12", filename),
44            lambda derfile: load_key_and_certificates(
45                derfile.read(), password, backend
46            ),
47            mode="rb",
48        )
49        assert parsed_cert == cert
50        assert parsed_key.private_numbers() == key.private_numbers()
51        assert parsed_more_certs == []
52
53    @pytest.mark.parametrize(
54        ("filename", "password"),
55        [
56            ("cert-key-aes256cbc.p12", b"cryptography"),
57            ("cert-none-key-none.p12", b"cryptography"),
58        ],
59    )
60    def test_load_pkcs12_ec_keys(self, filename, password, backend):
61        self._test_load_pkcs12_ec_keys(filename, password, backend)
62
63    @pytest.mark.parametrize(
64        ("filename", "password"),
65        [
66            ("cert-rc2-key-3des.p12", b"cryptography"),
67            ("no-password.p12", None),
68        ],
69    )
70    @pytest.mark.supported(
71        only_if=lambda backend: backend.cipher_supported(_RC2(), None),
72        skip_message="Does not support RC2",
73    )
74    @pytest.mark.skip_fips(reason="Unsupported algorithm in FIPS mode")
75    def test_load_pkcs12_ec_keys_rc2(self, filename, password, backend):
76        self._test_load_pkcs12_ec_keys(filename, password, backend)
77
78    def test_load_pkcs12_cert_only(self, backend):
79        cert = load_vectors_from_file(
80            os.path.join("x509", "custom", "ca", "ca.pem"),
81            lambda pemfile: x509.load_pem_x509_certificate(
82                pemfile.read(), backend
83            ),
84            mode="rb",
85        )
86        parsed_key, parsed_cert, parsed_more_certs = load_vectors_from_file(
87            os.path.join("pkcs12", "cert-aes256cbc-no-key.p12"),
88            lambda data: load_key_and_certificates(
89                data.read(), b"cryptography", backend
90            ),
91            mode="rb",
92        )
93        assert parsed_cert is None
94        assert parsed_key is None
95        assert parsed_more_certs == [cert]
96
97    def test_load_pkcs12_key_only(self, backend):
98        key = load_vectors_from_file(
99            os.path.join("x509", "custom", "ca", "ca_key.pem"),
100            lambda pemfile: load_pem_private_key(
101                pemfile.read(), None, backend
102            ),
103            mode="rb",
104        )
105        parsed_key, parsed_cert, parsed_more_certs = load_vectors_from_file(
106            os.path.join("pkcs12", "no-cert-key-aes256cbc.p12"),
107            lambda data: load_key_and_certificates(
108                data.read(), b"cryptography", backend
109            ),
110            mode="rb",
111        )
112        assert parsed_key.private_numbers() == key.private_numbers()
113        assert parsed_cert is None
114        assert parsed_more_certs == []
115
116    def test_non_bytes(self, backend):
117        with pytest.raises(TypeError):
118            load_key_and_certificates(b"irrelevant", object(), backend)
119
120    def test_not_a_pkcs12(self, backend):
121        with pytest.raises(ValueError):
122            load_key_and_certificates(b"invalid", b"pass", backend)
123
124    def test_invalid_password(self, backend):
125        with pytest.raises(ValueError):
126            load_vectors_from_file(
127                os.path.join("pkcs12", "cert-key-aes256cbc.p12"),
128                lambda derfile: load_key_and_certificates(
129                    derfile.read(), b"invalid", backend
130                ),
131                mode="rb",
132            )
133
134    def test_buffer_protocol(self, backend):
135        p12 = load_vectors_from_file(
136            os.path.join("pkcs12", "cert-key-aes256cbc.p12"),
137            lambda derfile: derfile.read(),
138            mode="rb",
139        )
140        p12buffer = bytearray(p12)
141        parsed_key, parsed_cert, parsed_more_certs = load_key_and_certificates(
142            p12buffer, bytearray(b"cryptography"), backend
143        )
144        assert parsed_key is not None
145        assert parsed_cert is not None
146        assert parsed_more_certs == []
147
148
149def _load_cert(backend, path):
150    return load_vectors_from_file(
151        path,
152        lambda pemfile: x509.load_pem_x509_certificate(
153            pemfile.read(), backend
154        ),
155        mode="rb",
156    )
157
158
159def _load_ca(backend):
160    cert = _load_cert(backend, os.path.join("x509", "custom", "ca", "ca.pem"))
161    key = load_vectors_from_file(
162        os.path.join("x509", "custom", "ca", "ca_key.pem"),
163        lambda pemfile: load_pem_private_key(pemfile.read(), None, backend),
164        mode="rb",
165    )
166    return cert, key
167
168
169class TestPKCS12Creation(object):
170    @pytest.mark.parametrize("name", [None, b"name"])
171    @pytest.mark.parametrize(
172        ("encryption_algorithm", "password"),
173        [
174            (serialization.BestAvailableEncryption(b"password"), b"password"),
175            (serialization.NoEncryption(), None),
176        ],
177    )
178    def test_generate(self, backend, name, encryption_algorithm, password):
179        cert, key = _load_ca(backend)
180        p12 = serialize_key_and_certificates(
181            name, key, cert, None, encryption_algorithm
182        )
183
184        parsed_key, parsed_cert, parsed_more_certs = load_key_and_certificates(
185            p12, password, backend
186        )
187        assert parsed_cert == cert
188        assert parsed_key.private_numbers() == key.private_numbers()
189        assert parsed_more_certs == []
190
191    def test_generate_with_cert_key_ca(self, backend):
192        cert, key = _load_ca(backend)
193        cert2 = _load_cert(
194            backend, os.path.join("x509", "custom", "dsa_selfsigned_ca.pem")
195        )
196        cert3 = _load_cert(backend, os.path.join("x509", "letsencryptx3.pem"))
197        encryption = serialization.NoEncryption()
198        p12 = serialize_key_and_certificates(
199            None, key, cert, [cert2, cert3], encryption
200        )
201
202        parsed_key, parsed_cert, parsed_more_certs = load_key_and_certificates(
203            p12, None, backend
204        )
205        assert parsed_cert == cert
206        assert parsed_key.private_numbers() == key.private_numbers()
207        assert parsed_more_certs == [cert2, cert3]
208
209    def test_generate_wrong_types(self, backend):
210        cert, key = _load_ca(backend)
211        cert2 = _load_cert(backend, os.path.join("x509", "letsencryptx3.pem"))
212        encryption = serialization.NoEncryption()
213        with pytest.raises(TypeError) as exc:
214            serialize_key_and_certificates(
215                b"name", cert, cert, None, encryption
216            )
217        assert (
218            str(exc.value)
219            == "Key must be RSA, DSA, or EllipticCurve private key."
220        )
221
222        with pytest.raises(TypeError) as exc:
223            serialize_key_and_certificates(b"name", key, key, None, encryption)
224        assert str(exc.value) == "cert must be a certificate"
225
226        with pytest.raises(TypeError) as exc:
227            serialize_key_and_certificates(b"name", key, cert, None, key)
228        assert str(exc.value) == (
229            "Key encryption algorithm must be a "
230            "KeySerializationEncryption instance"
231        )
232
233        with pytest.raises(TypeError) as exc:
234            serialize_key_and_certificates(None, key, cert, cert2, encryption)
235
236        with pytest.raises(TypeError) as exc:
237            serialize_key_and_certificates(None, key, cert, [key], encryption)
238        assert str(exc.value) == "all values in cas must be certificates"
239
240    def test_generate_no_cert(self, backend):
241        _, key = _load_ca(backend)
242        p12 = serialize_key_and_certificates(
243            None, key, None, None, serialization.NoEncryption()
244        )
245        parsed_key, parsed_cert, parsed_more_certs = load_key_and_certificates(
246            p12, None, backend
247        )
248        assert parsed_cert is None
249        assert parsed_key.private_numbers() == key.private_numbers()
250        assert parsed_more_certs == []
251
252    def test_must_supply_something(self):
253        with pytest.raises(ValueError) as exc:
254            serialize_key_and_certificates(
255                None, None, None, None, serialization.NoEncryption()
256            )
257        assert str(exc.value) == (
258            "You must supply at least one of key, cert, or cas"
259        )
260
261    def test_generate_unsupported_encryption_type(self, backend):
262        cert, key = _load_ca(backend)
263        with pytest.raises(ValueError) as exc:
264            serialize_key_and_certificates(
265                None,
266                key,
267                cert,
268                None,
269                DummyKeySerializationEncryption(),
270            )
271        assert str(exc.value) == "Unsupported key encryption type"
272