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