Init commit for standalone enodebd
Change-Id: I88eeef5135dd7ba8551ddd9fb6a0695f5325337b
diff --git a/common/cert_utils.py b/common/cert_utils.py
new file mode 100644
index 0000000..3be5284
--- /dev/null
+++ b/common/cert_utils.py
@@ -0,0 +1,160 @@
+"""
+Copyright 2020 The Magma Authors.
+
+This source code is licensed under the BSD-style license found in the
+LICENSE file in the root directory of this source tree.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import base64
+
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.x509.oid import NameOID
+from common.serialization_utils import write_to_file_atomically
+
+
+def load_key(key_file):
+ """Load a private key encoded in PEM format
+
+ Args:
+ key_file: path to the key file
+
+ Returns:
+ RSAPrivateKey or EllipticCurvePrivateKey depending on the contents of key_file
+
+ Raises:
+ IOError: If file cannot be opened
+ ValueError: If the file content cannot be decoded successfully
+ TypeError: If the key_file is encrypted
+ """
+ with open(key_file, 'rb') as f:
+ key_bytes = f.read()
+ return serialization.load_pem_private_key(
+ key_bytes, None, default_backend(),
+ )
+
+
+def write_key(key, key_file):
+ """Write key object to file in PEM format atomically
+
+ Args:
+ key: RSAPrivateKey or EllipticCurvePrivateKey object
+ key_file: path to the key file
+ """
+ key_pem = key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.NoEncryption(),
+ )
+ write_to_file_atomically(key_file, key_pem.decode("utf-8"))
+
+
+def load_public_key_to_base64der(key_file):
+ """Load the public key of a private key and convert to base64 encoded DER
+ The return value can be used directly for device registration.
+
+ Args:
+ key_file: path to the private key file, pem encoded
+
+ Returns:
+ base64 encoded public key in DER format
+
+ Raises:
+ IOError: If file cannot be opened
+ ValueError: If the file content cannot be decoded successfully
+ TypeError: If the key_file is encrypted
+ """
+
+ key = load_key(key_file)
+ pub_key = key.public_key()
+ pub_bytes = pub_key.public_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo,
+ )
+ encoded = base64.b64encode(pub_bytes)
+ return encoded
+
+
+def create_csr(
+ key, common_name,
+ country=None, state=None, city=None, org=None,
+ org_unit=None, email_address=None,
+):
+ """Create csr and sign it with key.
+
+ Args:
+ key: RSAPrivateKey or EllipticCurvePrivateKey object
+ common_name: common name
+ country: country
+ state: state or province
+ city: city
+ org: organization
+ org_unit: organizational unit
+ email_address: email address
+
+ Returns:
+ csr: x509.CertificateSigningRequest
+ """
+ name_attrs = [x509.NameAttribute(NameOID.COMMON_NAME, common_name)]
+ if country:
+ name_attrs.append(x509.NameAttribute(NameOID.COUNTRY_NAME, country))
+ if state:
+ name_attrs.append(
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
+ )
+ if city:
+ name_attrs.append(x509.NameAttribute(NameOID.LOCALITY_NAME, city))
+ if org:
+ name_attrs.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, org))
+ if org_unit:
+ name_attrs.append(
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, org_unit),
+ )
+ if email_address:
+ name_attrs.append(
+ x509.NameAttribute(NameOID.EMAIL_ADDRESS, email_address),
+ )
+
+ csr = x509.CertificateSigningRequestBuilder().subject_name(
+ x509.Name(name_attrs),
+ ).sign(key, hashes.SHA256(), default_backend())
+
+ return csr
+
+
+def load_cert(cert_file):
+ """Load certificate from a file
+
+ Args:
+ cert_file: path to file storing the cert in PEM format
+
+ Returns:
+ cert: an instance of x509.Certificate
+
+ Raises:
+ IOError: If file cannot be opened
+ ValueError: If the file content cannot be decoded successfully
+ """
+ with open(cert_file, 'rb') as f:
+ cert_pem = f.read()
+ cert = x509.load_pem_x509_certificate(cert_pem, default_backend())
+ return cert
+
+
+def write_cert(cert_der, cert_file):
+ """Write DER encoded cert to file in PEM format
+
+ Args:
+ cert_der: certificate encoded in DER format
+ cert_file: path to certificate
+ """
+ cert = x509.load_der_x509_certificate(cert_der, default_backend())
+ cert_pem = cert.public_bytes(serialization.Encoding.PEM)
+ write_to_file_atomically(cert_file, cert_pem.decode("utf-8"))