blob: 3be52841150c0f549ade2ecf4ac018d89571fd50 [file] [log] [blame]
Wei-Yu Chen49950b92021-11-08 19:19:18 +08001"""
2Copyright 2020 The Magma Authors.
3
4This source code is licensed under the BSD-style license found in the
5LICENSE file in the root directory of this source tree.
6
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12"""
13
14import base64
15
16from cryptography import x509
17from cryptography.hazmat.backends import default_backend
18from cryptography.hazmat.primitives import hashes, serialization
19from cryptography.x509.oid import NameOID
20from common.serialization_utils import write_to_file_atomically
21
22
23def load_key(key_file):
24 """Load a private key encoded in PEM format
25
26 Args:
27 key_file: path to the key file
28
29 Returns:
30 RSAPrivateKey or EllipticCurvePrivateKey depending on the contents of key_file
31
32 Raises:
33 IOError: If file cannot be opened
34 ValueError: If the file content cannot be decoded successfully
35 TypeError: If the key_file is encrypted
36 """
37 with open(key_file, 'rb') as f:
38 key_bytes = f.read()
39 return serialization.load_pem_private_key(
40 key_bytes, None, default_backend(),
41 )
42
43
44def write_key(key, key_file):
45 """Write key object to file in PEM format atomically
46
47 Args:
48 key: RSAPrivateKey or EllipticCurvePrivateKey object
49 key_file: path to the key file
50 """
51 key_pem = key.private_bytes(
52 serialization.Encoding.PEM,
53 serialization.PrivateFormat.TraditionalOpenSSL,
54 serialization.NoEncryption(),
55 )
56 write_to_file_atomically(key_file, key_pem.decode("utf-8"))
57
58
59def load_public_key_to_base64der(key_file):
60 """Load the public key of a private key and convert to base64 encoded DER
61 The return value can be used directly for device registration.
62
63 Args:
64 key_file: path to the private key file, pem encoded
65
66 Returns:
67 base64 encoded public key in DER format
68
69 Raises:
70 IOError: If file cannot be opened
71 ValueError: If the file content cannot be decoded successfully
72 TypeError: If the key_file is encrypted
73 """
74
75 key = load_key(key_file)
76 pub_key = key.public_key()
77 pub_bytes = pub_key.public_bytes(
78 encoding=serialization.Encoding.DER,
79 format=serialization.PublicFormat.SubjectPublicKeyInfo,
80 )
81 encoded = base64.b64encode(pub_bytes)
82 return encoded
83
84
85def create_csr(
86 key, common_name,
87 country=None, state=None, city=None, org=None,
88 org_unit=None, email_address=None,
89):
90 """Create csr and sign it with key.
91
92 Args:
93 key: RSAPrivateKey or EllipticCurvePrivateKey object
94 common_name: common name
95 country: country
96 state: state or province
97 city: city
98 org: organization
99 org_unit: organizational unit
100 email_address: email address
101
102 Returns:
103 csr: x509.CertificateSigningRequest
104 """
105 name_attrs = [x509.NameAttribute(NameOID.COMMON_NAME, common_name)]
106 if country:
107 name_attrs.append(x509.NameAttribute(NameOID.COUNTRY_NAME, country))
108 if state:
109 name_attrs.append(
110 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
111 )
112 if city:
113 name_attrs.append(x509.NameAttribute(NameOID.LOCALITY_NAME, city))
114 if org:
115 name_attrs.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, org))
116 if org_unit:
117 name_attrs.append(
118 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, org_unit),
119 )
120 if email_address:
121 name_attrs.append(
122 x509.NameAttribute(NameOID.EMAIL_ADDRESS, email_address),
123 )
124
125 csr = x509.CertificateSigningRequestBuilder().subject_name(
126 x509.Name(name_attrs),
127 ).sign(key, hashes.SHA256(), default_backend())
128
129 return csr
130
131
132def load_cert(cert_file):
133 """Load certificate from a file
134
135 Args:
136 cert_file: path to file storing the cert in PEM format
137
138 Returns:
139 cert: an instance of x509.Certificate
140
141 Raises:
142 IOError: If file cannot be opened
143 ValueError: If the file content cannot be decoded successfully
144 """
145 with open(cert_file, 'rb') as f:
146 cert_pem = f.read()
147 cert = x509.load_pem_x509_certificate(cert_pem, default_backend())
148 return cert
149
150
151def write_cert(cert_der, cert_file):
152 """Write DER encoded cert to file in PEM format
153
154 Args:
155 cert_der: certificate encoded in DER format
156 cert_file: path to certificate
157 """
158 cert = x509.load_der_x509_certificate(cert_der, default_backend())
159 cert_pem = cert.public_bytes(serialization.Encoding.PEM)
160 write_to_file_atomically(cert_file, cert_pem.decode("utf-8"))