Coverage for jbank/csr_helpers.py : 73%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import logging
2from typing import Optional
4import cryptography
5from cryptography import x509
6from cryptography.hazmat.primitives import hashes, serialization
7from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
8from cryptography.hazmat.primitives.serialization import load_pem_private_key
9from django.core.exceptions import ValidationError
12logger = logging.getLogger(__name__)
15def create_private_key(public_exponent: int = 65537, key_size: int = 2048) -> RSAPrivateKey:
16 """
17 Creates RSA private key.
18 :param public_exponent: int, exponent
19 :param key_size: int, bits
20 :return: RSAPrivateKey
21 """
22 backend = cryptography.hazmat.backends.default_backend()
23 return cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key(
24 public_exponent=public_exponent, key_size=key_size, backend=backend
25 )
28def get_private_key_pem(private_key: RSAPrivateKey) -> bytes:
29 """
30 Returns private key PEM file bytes.
31 :param private_key: RSPrivateKey
32 :return: bytes
33 """
34 return private_key.private_bytes( # type: ignore
35 encoding=serialization.Encoding.PEM,
36 format=serialization.PrivateFormat.PKCS8,
37 encryption_algorithm=serialization.NoEncryption(),
38 )
41def load_private_key_from_pem_data(pem_data: bytes, password: Optional[bytes] = None) -> RSAPrivateKey:
42 backend = cryptography.hazmat.backends.default_backend()
43 res = load_pem_private_key(pem_data, password=password, backend=backend)
44 assert isinstance(res, RSAPrivateKey)
45 return res
48def load_private_key_from_pem_file(filename: str, password: Optional[bytes] = None) -> RSAPrivateKey:
49 with open(filename, "rb") as fp:
50 return load_private_key_from_pem_data(fp.read(), password)
53def write_private_key_pem_file(filename: str, key_base64: bytes):
54 """
55 Writes PEM data to file.
56 :param filename: PEM filename
57 :param cert_base64: Base64 encoded certificate data without BEGIN CERTIFICATE / END CERTIFICATE
58 """
59 if b"BEGIN" not in key_base64 or b"END" not in key_base64:
60 raise ValidationError(
61 "write_private_key_pem_file() assumes PEM data does contains BEGIN / END header and footer"
62 )
63 with open(filename, "wb") as fp:
64 fp.write(key_base64)
65 logger.info("%s written", filename)
68def strip_pem_header_and_footer(pem: bytes) -> bytes:
69 """
70 Strips -----BEGIN and -----END parts of the CSR PEM.
71 :param pem: bytes
72 :return: bytes
73 """
74 if not pem.startswith(b"-----BEGIN "):
75 raise Exception("PEM does not appear to have header: {}...".format(pem[:32].decode() + "..."))
76 return b"\n".join(pem.split(b"\n")[1:-2])
79def create_csr_pem( # pylint: disable=too-many-arguments,too-many-locals
80 private_key: RSAPrivateKey,
81 common_name: str,
82 country_name: str,
83 dn_qualifier: str = "",
84 business_category: str = "",
85 domain_component: str = "",
86 email_address: str = "",
87 generation_qualifier: str = "",
88 given_name: str = "",
89 jurisdiction_country_name: str = "",
90 jurisdiction_locality_name: str = "",
91 jurisdiction_state_or_province_name: str = "",
92 locality_name: str = "",
93 organizational_unit_name: str = "",
94 organization_name: str = "",
95 postal_address: str = "",
96 postal_code: str = "",
97 pseudonym: str = "",
98 serial_number: str = "",
99 state_or_province_name: str = "",
100 street_address: str = "",
101 surname: str = "",
102 title: str = "",
103 user_id: str = "",
104 x500_unique_identifier: str = "",
105) -> bytes:
106 """
107 See http://fileformats.archiveteam.org/wiki/PKCS10
108 :return: CSR PEM as bytes
109 """
110 pairs = [
111 (common_name, "COMMON_NAME"),
112 (country_name, "COUNTRY_NAME"),
113 (dn_qualifier, "DN_QUALIFIER"),
114 (business_category, "BUSINESS_CATEGORY"),
115 (domain_component, "DOMAIN_COMPONENT"),
116 (email_address, "EMAIL_ADDRESS"),
117 (generation_qualifier, "GENERATION_QUALIFIER"),
118 (given_name, "GIVEN_NAME"),
119 (jurisdiction_country_name, "JURISDICTION_COUNTRY_NAME"),
120 (jurisdiction_locality_name, "JURISDICTION_LOCALITY_NAME"),
121 (jurisdiction_state_or_province_name, "JURISDICTION_STATE_OR_PROVINCE_NAME"),
122 (locality_name, "LOCALITY_NAME"),
123 (organizational_unit_name, "ORGANIZATIONAL_UNIT_NAME"),
124 (organization_name, "ORGANIZATION_NAME"),
125 (postal_address, "POSTAL_ADDRESS"),
126 (postal_code, "POSTAL_CODE"),
127 (pseudonym, "PSEUDONYM"),
128 (serial_number, "SERIAL_NUMBER"),
129 (state_or_province_name, "STATE_OR_PROVINCE_NAME"),
130 (street_address, "STREET_ADDRESS"),
131 (surname, "SURNAME"),
132 (title, "TITLE"),
133 (user_id, "USER_ID"),
134 (x500_unique_identifier, "X500_UNIQUE_IDENTIFIER"),
135 ]
136 name_parts = []
137 for val, k in pairs:
138 if val:
139 name_parts.append(x509.NameAttribute(getattr(x509.oid.NameOID, k), val))
141 builder = x509.CertificateSigningRequestBuilder()
142 builder = builder.subject_name(x509.Name(name_parts))
143 backend = cryptography.hazmat.backends.default_backend()
144 request = builder.sign(private_key, hashes.SHA256(), backend)
145 assert isinstance(request, x509.CertificateSigningRequest)
146 return request.public_bytes(serialization.Encoding.PEM)