Fix generate auth certificates script
pyCrypto has removed support for the PKCS12 certificates this script
generates, so this script is broken as-is on any distro from the last
year or two.
Rewrite the script to target python-cryptography instead. While there,
implement TODOs around code formatting, using EC keys, removing the
dependency on the redfish library, using the service root to
determine the correct manager instance to update, and cleaning up the
redfish session after a crash.
Tested: running this script targeting redfish instance shows it runs
to completion and test passes.
Change-Id: Ie1ee1a6f0a548258fe7b7d4c9678a9d55c8b71d1
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/scripts/generate_auth_certificates.py b/scripts/generate_auth_certificates.py
index 47e3592..ebbad18 100755
--- a/scripts/generate_auth_certificates.py
+++ b/scripts/generate_auth_certificates.py
@@ -1,81 +1,232 @@
#!/usr/bin/env python3
+"""
+Script to generate certificates for a CA, server, and client allowing for
+client authentication using mTLS certificates. This can then be used to test
+mTLS client authentication for Redfish and WebUI.
+"""
import argparse
+import datetime
+import errno
+import ipaddress
import os
import socket
-import urllib
+import time
-import requests
+import httpx
+from cryptography import x509
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.serialization import (
+ load_pem_private_key,
+ pkcs12,
+)
+from cryptography.x509.oid import NameOID
-try:
- import redfish
-except ModuleNotFoundError:
- raise Exception("Please run pip install redfish to run this script.")
-try:
- from OpenSSL import crypto
-except ImportError:
- raise Exception("Please run pip install pyOpenSSL to run this script.")
-
-SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
-
-# Script to generate a certificates for a CA, server, and client
-# allowing for client authentication using mTLS certificates.
-# This can then be used to test mTLS client authentication for Redfish
-# and webUI. Note that this requires the pyOpenSSL library to function.
-# TODO: Use EC keys rather than RSA keys.
+replaceCertPath = "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"
-def generateCACert(serial):
- # CA key
- key = crypto.PKey()
- key.generate_key(crypto.TYPE_RSA, 2048)
+class RedfishSessionContext:
+ def __init__(self, client, username="root", password="0penBmc"):
+ self.client = client
+ self.session_uri = None
+ self.x_auth_token = None
+ self.username = username
+ self.password = password
- # CA cert
- cert = crypto.X509()
- cert.set_serial_number(serial)
- cert.set_version(2)
- cert.set_pubkey(key)
+ def __enter__(self):
+ r = self.client.post(
+ "/redfish/v1/SessionService/Sessions",
+ json={
+ "UserName": self.username,
+ "Password": self.password,
+ "Context": f"pythonscript::{os.path.basename(__file__)}",
+ },
+ headers={"content-type": "application/json"},
+ )
+ r.raise_for_status()
+ self.x_auth_token = r.headers["x-auth-token"]
+ self.session_uri = r.headers["location"]
+ return self
- cert.set_notBefore(b"19700101000000Z")
- cert.set_notAfter(b"20700101000000Z")
+ def __exit__(self, type, value, traceback):
+ if not self.session_uri:
+ return
+ r = self.client.delete(self.session_uri)
+ r.raise_for_status()
- caCertSubject = cert.get_subject()
- caCertSubject.countryName = "US"
- caCertSubject.stateOrProvinceName = "California"
- caCertSubject.localityName = "San Francisco"
- caCertSubject.organizationName = "OpenBMC"
- caCertSubject.organizationalUnitName = "bmcweb"
- caCertSubject.commonName = "Test CA"
- cert.set_issuer(caCertSubject)
- cert.add_extensions(
+def generateCA():
+ private_key = ec.generate_private_key(ec.SECP256R1())
+ public_key = private_key.public_key()
+ builder = x509.CertificateBuilder()
+
+ name = x509.Name(
[
- crypto.X509Extension(
- b"basicConstraints", True, b"CA:TRUE, pathlen:0"
- ),
- crypto.X509Extension(b"keyUsage", True, b"keyCertSign, cRLSign"),
- crypto.X509Extension(
- b"subjectKeyIdentifier", False, b"hash", subject=cert
- ),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "OpenBMC"),
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "bmcweb"),
+ x509.NameAttribute(NameOID.COMMON_NAME, "Test CA"),
]
)
- cert.add_extensions(
- [
- crypto.X509Extension(
- b"authorityKeyIdentifier", False, b"keyid:always", issuer=cert
- ),
- ]
+ builder = builder.subject_name(name)
+ builder = builder.issuer_name(name)
+
+ builder = builder.not_valid_before(
+ datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
+ )
+ builder = builder.not_valid_after(
+ datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
+ )
+ builder = builder.serial_number(x509.random_serial_number())
+ builder = builder.public_key(public_key)
+
+ basic_constraints = x509.BasicConstraints(ca=True, path_length=None)
+ builder = builder.add_extension(basic_constraints, critical=True)
+
+ usage = x509.KeyUsage(
+ content_commitment=False,
+ crl_sign=True,
+ data_encipherment=False,
+ decipher_only=False,
+ digital_signature=False,
+ encipher_only=False,
+ key_agreement=False,
+ key_cert_sign=True,
+ key_encipherment=False,
+ )
+ builder = builder.add_extension(usage, critical=False)
+
+ auth_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key)
+
+ builder = builder.add_extension(auth_key, critical=False)
+
+ root_cert = builder.sign(
+ private_key=private_key, algorithm=hashes.SHA256()
)
- # sign CA cert with CA key
- cert.sign(key, "sha256")
- return key, cert
+ return private_key, root_cert
-def generateCertCsr(
- redfishObject, commonName, extensions, caKey, caCert, serial
+def signCsr(csr, ca_key):
+ csr.sign(ca_key, algorithm=hashes.SHA256())
+ return
+
+
+def generate_client_key_and_cert(commonName, ca_cert, ca_key):
+ private_key = ec.generate_private_key(ec.SECP256R1())
+ public_key = private_key.public_key()
+ builder = x509.CertificateBuilder()
+
+ builder = builder.subject_name(
+ x509.Name(
+ [
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
+ x509.NameAttribute(
+ NameOID.STATE_OR_PROVINCE_NAME, "California"
+ ),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "OpenBMC"),
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "bmcweb"),
+ x509.NameAttribute(NameOID.COMMON_NAME, commonName),
+ ]
+ )
+ )
+
+ builder = builder.issuer_name(ca_cert.subject)
+ builder = builder.public_key(public_key)
+ builder = builder.serial_number(x509.random_serial_number())
+ builder = builder.not_valid_before(
+ datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
+ )
+ builder = builder.not_valid_after(
+ datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
+ )
+
+ usage = x509.KeyUsage(
+ content_commitment=False,
+ crl_sign=False,
+ data_encipherment=False,
+ decipher_only=False,
+ digital_signature=True,
+ encipher_only=False,
+ key_agreement=True,
+ key_cert_sign=False,
+ key_encipherment=False,
+ )
+ builder = builder.add_extension(usage, critical=False)
+
+ exusage = x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH])
+ builder = builder.add_extension(exusage, critical=True)
+
+ auth_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key)
+ builder = builder.add_extension(auth_key, critical=False)
+
+ signed = builder.sign(private_key=ca_key, algorithm=hashes.SHA256())
+
+ return private_key, signed
+
+
+def generateServerCert(url, ca_key, ca_cert, csr):
+ builder = x509.CertificateBuilder()
+
+ builder = builder.subject_name(csr.subject)
+ builder = builder.issuer_name(ca_cert.subject)
+ builder = builder.public_key(csr.public_key())
+ builder = builder.serial_number(x509.random_serial_number())
+ builder = builder.not_valid_before(
+ datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
+ )
+ builder = builder.not_valid_after(
+ datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
+ )
+
+ usage = x509.KeyUsage(
+ content_commitment=False,
+ crl_sign=False,
+ data_encipherment=False,
+ decipher_only=False,
+ digital_signature=True,
+ encipher_only=False,
+ key_agreement=False,
+ key_cert_sign=True,
+ key_encipherment=True,
+ )
+ builder = builder.add_extension(usage, critical=True)
+
+ exusage = x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH])
+ builder = builder.add_extension(exusage, critical=True)
+
+ san_list = [x509.DNSName("localhost")]
+ try:
+ value = ipaddress.ip_address(url)
+ san_list.append(x509.IPAddress(value))
+ except ValueError:
+ san_list.append(x509.DNSName(url))
+
+ altname = x509.SubjectAlternativeName(san_list)
+ builder = builder.add_extension(altname, critical=True)
+ basic_constraints = x509.BasicConstraints(ca=False, path_length=None)
+ builder = builder.add_extension(basic_constraints, critical=True)
+
+ builder = builder.add_extension(
+ x509.SubjectKeyIdentifier.from_public_key(ca_key.public_key()),
+ critical=False,
+ )
+ authkeyident = x509.AuthorityKeyIdentifier.from_issuer_public_key(
+ ca_key.public_key()
+ )
+ builder = builder.add_extension(authkeyident, critical=False)
+
+ signed = builder.sign(private_key=ca_key, algorithm=hashes.SHA256())
+
+ return signed
+
+
+def generateCsr(
+ redfish_session,
+ commonName,
+ manager_uri,
):
-
try:
socket.inet_aton(commonName)
commonName = "IP: " + commonName
@@ -90,7 +241,7 @@
"OrganizationalUnit": "",
"State": "CA",
"CertificateCollection": {
- "@odata.id": "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates",
+ "@odata.id": f"{manager_uri}/NetworkProtocol/HTTPS/Certificates",
},
"AlternativeNames": [
commonName,
@@ -99,251 +250,39 @@
],
}
- response = redfishObject.post(
+ response = redfish_session.post(
"/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR",
- body=CSRRequest,
+ json=CSRRequest,
)
+ response.raise_for_status()
- if response.status != 200:
- raise Exception("Failed to create CSR")
-
- csrString = response.dict["CSRString"]
-
- return crypto.load_certificate_request(crypto.FILETYPE_PEM, csrString)
+ csrString = response.json()["CSRString"]
+ csr = x509.load_pem_x509_csr(csrString.encode())
+ if not csr.is_signature_valid:
+ raise Exception("CSR was not valid")
+ return csr
-def generateCert(commonName, extensions, caKey, caCert, serial, csr=None):
- # key
-
- # cert
- cert = crypto.X509()
- cert.set_serial_number(serial)
- cert.set_version(2)
-
- if csr is None:
- key = crypto.PKey()
- key.generate_key(crypto.TYPE_RSA, 2048)
- cert.set_pubkey(key)
- else:
- key = None
- cert.set_subject(csr.get_subject())
- cert.set_pubkey(csr.get_pubkey())
-
- cert.set_notBefore(b"19700101000000Z")
- cert.set_notAfter(b"20700101000000Z")
-
- certSubject = cert.get_subject()
- certSubject.countryName = "US"
- certSubject.stateOrProvinceName = "California"
- certSubject.localityName = "San Francisco"
- certSubject.organizationName = "OpenBMC"
- certSubject.organizationalUnitName = "bmcweb"
- certSubject.commonName = commonName
- cert.set_issuer(caCert.get_issuer())
-
- extensions.extend(
- [
- crypto.X509Extension(
- b"authorityKeyIdentifier", False, b"keyid", issuer=caCert
- ),
- ]
- )
- cert.add_extensions(extensions)
- cert.sign(caKey, "sha256")
- return key, cert
-
-
-def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("--host", help="Host to connect to", required=True)
- parser.add_argument(
- "--username", help="Username to connect with", default="root"
- )
- parser.add_argument(
- "--password",
- help="Password for user in order to install certs over Redfish.",
- default="0penBmc",
- )
- args = parser.parse_args()
- host = args.host
- username = args.username
- password = args.password
- if username == "root" and password == "0penBMC":
- print(
- """Note: Using default username 'root' and default password
- '0penBmc'. Use --username and --password flags to change these,
- respectively."""
- )
- if "//" not in host:
- host = f"https://{host}"
- url = urllib.parse.urlparse(host, scheme="https")
-
- serial = 1000
- certsDir = os.path.join(SCRIPT_DIR, "certs")
- print(f"Writing certs to {certsDir}")
- try:
- print("Making certs directory.")
- os.mkdir(certsDir)
- except OSError as error:
- if error.errno == 17:
- print("certs directory already exists. Skipping...")
- else:
- print(error)
-
- cacertFilename = os.path.join(certsDir, "CA-cert.cer")
- cakeyFilename = os.path.join(certsDir, "CA-key.pem")
- if os.path.exists(cacertFilename):
- with open(cacertFilename, "rb") as cacert_file:
- caCertDump = cacert_file.read()
- caCert = crypto.load_certificate(crypto.FILETYPE_PEM, caCertDump)
- with open(cakeyFilename, "rb") as cakey_file:
- caKeyDump = cakey_file.read()
- caKey = crypto.load_privatekey(crypto.FILETYPE_PEM, caKeyDump)
- else:
-
- caKey, caCert = generateCACert(serial)
- caKeyDump = crypto.dump_privatekey(crypto.FILETYPE_PEM, caKey)
- caCertDump = crypto.dump_certificate(crypto.FILETYPE_PEM, caCert)
- with open(cacertFilename, "wb") as f:
- f.write(caCertDump)
- print("CA cert generated.")
- with open(cakeyFilename, "wb") as f:
- f.write(caKeyDump)
- print("CA key generated.")
- serial += 1
-
- clientExtensions = [
- crypto.X509Extension(
- b"keyUsage",
- True,
- b"""digitalSignature,
- keyAgreement""",
- ),
- crypto.X509Extension(b"extendedKeyUsage", True, b"clientAuth"),
- ]
-
- redfishObject = redfish.redfish_client(
- base_url="https://" + url.netloc,
- username=username,
- password=password,
- default_prefix="/redfish/v1",
- )
- redfishObject.login(auth="session")
-
- clientKey, clientCert = generateCert(
- username, clientExtensions, caKey, caCert, serial
- )
-
- clientKeyDump = crypto.dump_privatekey(crypto.FILETYPE_PEM, clientKey)
- with open(os.path.join(certsDir, "client-key.pem"), "wb") as f:
- f.write(clientKeyDump)
- print("Client key generated.")
- serial += 1
- clientCertDump = crypto.dump_certificate(crypto.FILETYPE_PEM, clientCert)
-
- with open(os.path.join(certsDir, "client-cert.pem"), "wb") as f:
- f.write(clientCertDump)
- print("Client cert generated.")
-
- san_list = [
- b"DNS: localhost",
- b"IP: 127.0.0.1",
- ]
-
- try:
- socket.inet_aton(url.hostname)
- san_list.append(b"IP: " + url.hostname.encode())
- except socket.error:
- san_list.append(b"DNS: " + url.hostname.encode())
-
- serverExtensions = [
- crypto.X509Extension(
- b"keyUsage",
- True,
- b"digitalSignature, keyAgreement",
- ),
- crypto.X509Extension(b"extendedKeyUsage", True, b"serverAuth"),
- crypto.X509Extension(b"subjectAltName", False, b", ".join(san_list)),
- ]
-
- useCSR = True
-
- if useCSR:
- csr = generateCertCsr(
- redfishObject,
- url.hostname,
- serverExtensions,
- caKey,
- caCert,
- serial,
- )
- serverKey = None
- serverKeyDumpStr = ""
- else:
- csr = None
- serverKey, serverCert = generateCert(
- url.hostname, serverExtensions, caKey, caCert, serial, csr=csr
- )
- if serverKey is not None:
- serverKeyDump = crypto.dump_privatekey(crypto.FILETYPE_PEM, serverKey)
- with open(os.path.join(certsDir, "server-key.pem"), "wb") as f:
- f.write(serverKeyDump)
- print("Server key generated.")
- serverKeyDumpStr = serverKeyDump.decode()
- serial += 1
-
- serverCertDump = crypto.dump_certificate(crypto.FILETYPE_PEM, serverCert)
-
- with open(os.path.join(certsDir, "server-cert.pem"), "wb") as f:
- f.write(serverCertDump)
- print("Server cert generated.")
-
- serverCertDumpStr = serverCertDump.decode()
-
- print("Generating p12 cert file for browser authentication.")
- pkcs12Cert = crypto.PKCS12()
- pkcs12Cert.set_certificate(clientCert)
- if clientKey:
- pkcs12Cert.set_privatekey(clientKey)
- pkcs12Cert.set_ca_certificates([caCert])
- pkcs12Cert.set_friendlyname(bytes(username, encoding="utf-8"))
- with open(os.path.join(certsDir, "client.p12"), "wb") as f:
- f.write(pkcs12Cert.export())
- print("Client p12 cert file generated and stored in client.p12.")
- print(
- "Copy this file to a system with a browser and install the "
- "cert into the browser."
- )
- print(
- "You will then be able to test redfish and webui "
- "authentication using this certificate."
- )
- print(
- "Note: this p12 file was generated without a password, so it "
- "can be imported easily."
- )
-
- caCertJSON = {
- "CertificateString": caCertDump.decode(),
+def install_ca_cert(redfish_session, ca_cert_dump, manager_uri):
+ ca_certJSON = {
+ "CertificateString": ca_cert_dump.decode(),
"CertificateType": "PEM",
}
- caCertPath = "/redfish/v1/Managers/bmc/Truststore/Certificates"
- replaceCertPath = "/redfish/v1/CertificateService/Actions/"
- replaceCertPath += "CertificateService.ReplaceCertificate"
+ ca_certPath = f"{manager_uri}/Truststore/Certificates"
print("Attempting to install CA certificate to BMC.")
- response = redfishObject.post(caCertPath, body=caCertJSON)
- if response.status == 500:
+ response = redfish_session.post(ca_certPath, json=ca_certJSON)
+ if response.status_code == 500:
print(
"An existing CA certificate is likely already installed."
" Replacing..."
)
- caCertJSON["CertificateUri"] = {
- "@odata.id": caCertPath + "/1",
+ ca_certJSON["CertificateUri"] = {
+ "@odata.id": ca_certPath + "/1",
}
- response = redfishObject.post(replaceCertPath, body=caCertJSON)
- if response.status == 200:
+ response = redfish_session.post(replaceCertPath, json=ca_certJSON)
+ if response.status_code == 200:
print("Successfully replaced existing CA certificate.")
else:
raise Exception(
@@ -352,46 +291,208 @@
"certificate is already installed, try performing a factory"
"restore to clear such settings."
)
- elif response.status == 200:
- print("Successfully installed CA certificate.")
- else:
- raise Exception("Could not install certificate: " + response.read)
- serverCertJSON = {
- "CertificateString": serverKeyDumpStr + serverCertDumpStr,
+ response.raise_for_status()
+ print("Successfully installed CA certificate.")
+
+
+def install_server_cert(redfish_session, manager_uri, server_cert_dump):
+
+ server_cert_json = {
+ "CertificateString": server_cert_dump.decode(),
"CertificateUri": {
- "@odata.id": "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1",
+ "@odata.id": f"{manager_uri}/NetworkProtocol/HTTPS/Certificates/1",
},
"CertificateType": "PEM",
}
print("Replacing server certificate...")
- response = redfishObject.post(replaceCertPath, body=serverCertJSON)
- if response.status == 200:
+ response = redfish_session.post(replaceCertPath, json=server_cert_json)
+ if response.status_code == 200:
print("Successfully replaced server certificate.")
else:
- raise Exception("Could not replace certificate: " + response.read)
- tlsPatchJSON = {"Oem": {"OpenBMC": {"AuthMethods": {"TLS": True}}}}
+ raise Exception(f"Could not replace certificate: {response.json()}")
+
+ tls_patch_json = {"Oem": {"OpenBMC": {"AuthMethods": {"TLS": True}}}}
print("Ensuring TLS authentication is enabled.")
- response = redfishObject.patch(
- "/redfish/v1/AccountService", body=tlsPatchJSON
+ response = redfish_session.patch(
+ "/redfish/v1/AccountService", json=tls_patch_json
)
- if response.status == 200:
+ if response.status_code == 200:
print("Successfully enabled TLS authentication.")
else:
raise Exception("Could not enable TLS auth: " + response.read)
- redfishObject.logout()
- print("Testing redfish TLS authentication with generated certs.")
- response = requests.get(
- f"https://{url.netloc}/redfish/v1/SessionService/Sessions",
- verify=os.path.join(certsDir, "CA-cert.cer"),
+
+
+def generate_pk12(certs_dir, key, client_cert, username):
+ print("Generating p12 cert file for browser authentication.")
+ p12 = pkcs12.serialize_key_and_certificates(
+ username.encode(),
+ key,
+ client_cert,
+ None,
+ serialization.NoEncryption(),
+ )
+ with open(os.path.join(certs_dir, "client.p12"), "wb") as f:
+ f.write(p12)
+
+
+def test_mtls_auth(url, certs_dir):
+ response = httpx.get(
+ f"https://{url}/redfish/v1/SessionService/Sessions",
+ verify=os.path.join(certs_dir, "CA-cert.cer"),
cert=(
- os.path.join(certsDir, "client-cert.pem"),
- os.path.join(certsDir, "client-key.pem"),
+ os.path.join(certs_dir, "client-cert.pem"),
+ os.path.join(certs_dir, "client-key.pem"),
),
)
response.raise_for_status()
+
+
+def setup_server_cert(
+ redfish_session,
+ ca_cert_dump,
+ certs_dir,
+ client_key,
+ client_cert,
+ username,
+ url,
+ ca_key,
+ ca_cert,
+):
+ service_root = redfish_session.get("/redfish/v1/")
+ service_root.raise_for_status()
+
+ manager_uri = service_root.json()["Links"]["ManagerProvidingService"][
+ "@odata.id"
+ ]
+
+ install_ca_cert(redfish_session, ca_cert_dump, manager_uri)
+ generate_pk12(certs_dir, client_key, client_cert, username)
+
+ csr = generateCsr(
+ redfish_session,
+ url,
+ manager_uri,
+ )
+ serverCert = generateServerCert(
+ url,
+ ca_key,
+ ca_cert,
+ csr,
+ )
+ server_cert_dump = serverCert.public_bytes(
+ encoding=serialization.Encoding.PEM
+ )
+ with open(os.path.join(certs_dir, "server-cert.pem"), "wb") as f:
+ f.write(server_cert_dump)
+ print("Server cert generated.")
+
+ install_server_cert(redfish_session, manager_uri, server_cert_dump)
+
+
+def generate_and_load_certs(url, username, password):
+ certs_dir = os.path.expanduser("~/certs")
+ print(f"Writing certs to {certs_dir}")
+ try:
+ print("Making certs directory.")
+ os.mkdir(certs_dir)
+ except OSError as error:
+ if error.errno != errno.EEXIST:
+ raise
+
+ ca_cert_filename = os.path.join(certs_dir, "CA-cert.cer")
+ ca_key_filename = os.path.join(certs_dir, "CA-key.pem")
+ if not os.path.exists(ca_cert_filename):
+ ca_key, ca_cert = generateCA()
+
+ ca_key_dump = ca_key.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
+ encryption_algorithm=serialization.NoEncryption(),
+ )
+ ca_cert_dump = ca_cert.public_bytes(
+ encoding=serialization.Encoding.PEM
+ )
+
+ with open(ca_cert_filename, "wb") as f:
+ f.write(ca_cert_dump)
+ print("CA cert generated.")
+ with open(ca_key_filename, "wb") as f:
+ f.write(ca_key_dump)
+ print("CA key generated.")
+
+ with open(ca_cert_filename, "rb") as ca_cert_file:
+ ca_cert_dump = ca_cert_file.read()
+ ca_cert = x509.load_pem_x509_certificate(ca_cert_dump)
+
+ with open(ca_key_filename, "rb") as ca_key_file:
+ ca_key_dump = ca_key_file.read()
+ ca_key = load_pem_private_key(ca_key_dump, None)
+
+ client_key, client_cert = generate_client_key_and_cert(
+ username, ca_cert, ca_key
+ )
+ client_key_dump = client_key.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
+ encryption_algorithm=serialization.NoEncryption(),
+ )
+
+ with open(os.path.join(certs_dir, "client-key.pem"), "wb") as f:
+ f.write(client_key_dump)
+ print("Client key generated.")
+ client_cert_dump = client_cert.public_bytes(
+ encoding=serialization.Encoding.PEM
+ )
+
+ with open(os.path.join(certs_dir, "client-cert.pem"), "wb") as f:
+ f.write(client_cert_dump)
+ print("Client cert generated.")
+
+ print(f"Connecting to {url}")
+ with httpx.Client(
+ base_url=f"https://{url}", verify=False, follow_redirects=False
+ ) as redfish_session:
+ with RedfishSessionContext(
+ redfish_session, username, password
+ ) as rf_session:
+ redfish_session.headers["X-Auth-Token"] = rf_session.x_auth_token
+ setup_server_cert(
+ redfish_session,
+ ca_cert_dump,
+ certs_dir,
+ client_key,
+ client_cert,
+ username,
+ url,
+ ca_key,
+ ca_cert,
+ )
+
+ print("Testing redfish TLS authentication with generated certs.")
+
+ time.sleep(2)
+ test_mtls_auth(url, certs_dir)
print("Redfish TLS authentication success!")
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--username",
+ help="Username to connect with",
+ default="root",
+ )
+ parser.add_argument(
+ "--password",
+ help="Password for user in order to install certs over Redfish.",
+ default="0penBmc",
+ )
+ parser.add_argument("host", help="Host to connect to")
+
+ args = parser.parse_args()
+ generate_and_load_certs(args.host, args.username, args.password)
+
+
if __name__ == "__main__":
main()