blob: 35c2bd6005e062d1280777b99e1690d41f65e7b2 [file] [log] [blame]
Wei-Yu Chenad55cb82022-02-15 20:07:01 +08001# SPDX-FileCopyrightText: 2020 The Magma Authors.
2# SPDX-FileCopyrightText: 2022 Open Networking Foundation <support@opennetworking.org>
3#
4# SPDX-License-Identifier: BSD-3-Clause
Wei-Yu Chen49950b92021-11-08 19:19:18 +08005
6import base64
7
8from cryptography import x509
9from cryptography.hazmat.backends import default_backend
10from cryptography.hazmat.primitives import hashes, serialization
11from cryptography.x509.oid import NameOID
12from common.serialization_utils import write_to_file_atomically
13
14
Wei-Yu Chen678f0a52021-12-21 13:50:52 +080015def load_key_bytes(key_file):
16 """Load a private key encoded in PEM format
17
18 Args:
19 key_file: path to the key file
20
21 Returns:
22 Bytes
23
24 Raises:
25 IOError: If file cannot be opened
26 ValueError: If the file content cannot be decoded successfully
27 TypeError: If the key_file is encrypted
28 """
29 with open(key_file, 'rb') as f:
30 key_bytes = f.read()
31
32 return key_bytes
33
Wei-Yu Chen49950b92021-11-08 19:19:18 +080034def load_key(key_file):
35 """Load a private key encoded in PEM format
36
37 Args:
38 key_file: path to the key file
39
40 Returns:
41 RSAPrivateKey or EllipticCurvePrivateKey depending on the contents of key_file
42
43 Raises:
44 IOError: If file cannot be opened
45 ValueError: If the file content cannot be decoded successfully
46 TypeError: If the key_file is encrypted
47 """
48 with open(key_file, 'rb') as f:
49 key_bytes = f.read()
50 return serialization.load_pem_private_key(
51 key_bytes, None, default_backend(),
52 )
53
54
55def write_key(key, key_file):
56 """Write key object to file in PEM format atomically
57
58 Args:
59 key: RSAPrivateKey or EllipticCurvePrivateKey object
60 key_file: path to the key file
61 """
62 key_pem = key.private_bytes(
63 serialization.Encoding.PEM,
64 serialization.PrivateFormat.TraditionalOpenSSL,
65 serialization.NoEncryption(),
66 )
67 write_to_file_atomically(key_file, key_pem.decode("utf-8"))
68
69
70def load_public_key_to_base64der(key_file):
71 """Load the public key of a private key and convert to base64 encoded DER
72 The return value can be used directly for device registration.
73
74 Args:
75 key_file: path to the private key file, pem encoded
76
77 Returns:
78 base64 encoded public key in DER format
79
80 Raises:
81 IOError: If file cannot be opened
82 ValueError: If the file content cannot be decoded successfully
83 TypeError: If the key_file is encrypted
84 """
85
86 key = load_key(key_file)
87 pub_key = key.public_key()
88 pub_bytes = pub_key.public_bytes(
89 encoding=serialization.Encoding.DER,
90 format=serialization.PublicFormat.SubjectPublicKeyInfo,
91 )
92 encoded = base64.b64encode(pub_bytes)
93 return encoded
94
95
96def create_csr(
97 key, common_name,
98 country=None, state=None, city=None, org=None,
99 org_unit=None, email_address=None,
100):
101 """Create csr and sign it with key.
102
103 Args:
104 key: RSAPrivateKey or EllipticCurvePrivateKey object
105 common_name: common name
106 country: country
107 state: state or province
108 city: city
109 org: organization
110 org_unit: organizational unit
111 email_address: email address
112
113 Returns:
114 csr: x509.CertificateSigningRequest
115 """
116 name_attrs = [x509.NameAttribute(NameOID.COMMON_NAME, common_name)]
117 if country:
118 name_attrs.append(x509.NameAttribute(NameOID.COUNTRY_NAME, country))
119 if state:
120 name_attrs.append(
121 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
122 )
123 if city:
124 name_attrs.append(x509.NameAttribute(NameOID.LOCALITY_NAME, city))
125 if org:
126 name_attrs.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, org))
127 if org_unit:
128 name_attrs.append(
129 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, org_unit),
130 )
131 if email_address:
132 name_attrs.append(
133 x509.NameAttribute(NameOID.EMAIL_ADDRESS, email_address),
134 )
135
136 csr = x509.CertificateSigningRequestBuilder().subject_name(
137 x509.Name(name_attrs),
138 ).sign(key, hashes.SHA256(), default_backend())
139
140 return csr
141
Wei-Yu Chen678f0a52021-12-21 13:50:52 +0800142def load_cert_bytes(cert_file):
143 """Load certificate from a file
144
145 Args:
146 cert_file: path to file storing the cert in PEM format
147
148 Returns:
149 cert: an instance of x509.Certificate
150
151 Raises:
152 IOError: If file cannot be opened
153 ValueError: If the file content cannot be decoded successfully
154 """
155 with open(cert_file, 'rb') as f:
156 cert_pem = f.read()
157
158 return cert_pem
Wei-Yu Chen49950b92021-11-08 19:19:18 +0800159
160def load_cert(cert_file):
161 """Load certificate from a file
162
163 Args:
164 cert_file: path to file storing the cert in PEM format
165
166 Returns:
167 cert: an instance of x509.Certificate
168
169 Raises:
170 IOError: If file cannot be opened
171 ValueError: If the file content cannot be decoded successfully
172 """
173 with open(cert_file, 'rb') as f:
174 cert_pem = f.read()
175 cert = x509.load_pem_x509_certificate(cert_pem, default_backend())
176 return cert
177
178
179def write_cert(cert_der, cert_file):
180 """Write DER encoded cert to file in PEM format
181
182 Args:
183 cert_der: certificate encoded in DER format
184 cert_file: path to certificate
185 """
186 cert = x509.load_der_x509_certificate(cert_der, default_backend())
187 cert_pem = cert.public_bytes(serialization.Encoding.PEM)
188 write_to_file_atomically(cert_file, cert_pem.decode("utf-8"))