From 859df2a983d79929f4f77fcf7eead0b67d587ca5 Mon Sep 17 00:00:00 2001 From: ding huan Date: Fri, 16 Jun 2023 14:33:35 +0800 Subject: [PATCH] feat(certs): Add python script for generating certificate --- examples/wifi/wifi_enterprise/README.md | 2 +- .../generate_certs/example-ca-openssl.cnf | 117 -------------- .../generate_certs/generate_certs.py | 152 ++++++++++++++++++ .../generate_certs/generate_certs.sh | 126 --------------- tools/ci/executable-list.txt | 1 - 5 files changed, 153 insertions(+), 245 deletions(-) delete mode 100644 examples/wifi/wifi_enterprise/generate_certs/example-ca-openssl.cnf create mode 100644 examples/wifi/wifi_enterprise/generate_certs/generate_certs.py delete mode 100755 examples/wifi/wifi_enterprise/generate_certs/generate_certs.sh diff --git a/examples/wifi/wifi_enterprise/README.md b/examples/wifi/wifi_enterprise/README.md index 5de19a2692..1360163ff5 100644 --- a/examples/wifi/wifi_enterprise/README.md +++ b/examples/wifi/wifi_enterprise/README.md @@ -15,7 +15,7 @@ This example shows how ESP32 connects to AP with Wi-Fi enterprise encryption. Th *Note:* 1. The certificates currently are generated and are present in examples/wifi/wifi_enterprise/main folder. 2. The expiration date of the certificates is 2027/06/05. -3. In case using suite-b, please go into `generate_certs` directory, then execute the script as `sh generate_certs.sh ` to create appropriate certificates such as RSA-3072 or p384 EC certificates. +3. In case using suite-b, please go into `generate_certs` directory, then execute the script as `python generate_certs.py sha384` to create appropriate certificates such as RSA-3072 or p384 EC certificates. The steps to create new certificates are given below. diff --git a/examples/wifi/wifi_enterprise/generate_certs/example-ca-openssl.cnf b/examples/wifi/wifi_enterprise/generate_certs/example-ca-openssl.cnf deleted file mode 100644 index 084ddbaf4b..0000000000 --- a/examples/wifi/wifi_enterprise/generate_certs/example-ca-openssl.cnf +++ /dev/null @@ -1,117 +0,0 @@ -# OpenSSL configuration file - -HOME = . -RANDFILE = $ENV::HOME/.rnd -oid_section = new_oids - -[ new_oids ] - -[ ca ] -default_ca = CA_default - -[ CA_default ] - -dir = ./ca -certs = $dir/certs -crl_dir = $dir/crl -database = $dir/index.txt -unique_subject = no -new_certs_dir = $dir/newcerts -certificate = $dir/cacert.pem -serial = $dir/serial -crlnumber = $dir/crlnumber -crl = $dir/crl.pem -private_key = $dir/private/cakey.pem -RANDFILE = $dir/private/.rand - -x509_extensions = usr_cert - -name_opt = ca_default -cert_opt = ca_default - -copy_extensions = copy - -default_days = 3650 -default_crl_days= 30 -default_md = default -preserve = no - -policy = policy_match - -[ policy_match ] -countryName = match -stateOrProvinceName = optional -organizationName = match -organizationalUnitName = optional -commonName = supplied -#emailAddress = optional - -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -#emailAddress = optional - -[ req ] -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca - -string_mask = utf8only - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = CN -countryName_min = 2 -countryName_max = 2 - -localityName = Locality Name (eg, city) -localityName_default = Shanghai - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = espressif - -commonName = Common Name (e.g. server FQDN or YOUR name) -#@CN@ -commonName_max = 64 - -[ req_attributes ] - -[ v3_ca ] - -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid:always,issuer -basicConstraints = critical, CA:true -#keyUsage = critical, cRLSign, keyCertSign - -[ crl_ext ] - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ usr_cert ] -basicConstraints=CA:FALSE -nsComment = "OpenSSL Generated Certificate" -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid:issuer - -[ v3_req ] -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ ext_client ] -extendedKeyUsage = 1.3.6.1.5.5.7.3.2 -basicConstraints=CA:FALSE -subjectKeyIdentifier = hash -nsComment = "OpenSSL Generated Certificate" -authorityKeyIdentifier = keyid:always, issuer - -[ ext_server ] -extendedKeyUsage = 1.3.6.1.5.5.7.3.1 -basicConstraints=CA:FALSE -subjectKeyIdentifier = hash -nsComment = "OpenSSL Generated Certificate" -authorityKeyIdentifier = keyid:always, issuer diff --git a/examples/wifi/wifi_enterprise/generate_certs/generate_certs.py b/examples/wifi/wifi_enterprise/generate_certs/generate_certs.py new file mode 100644 index 0000000000..78ea617efb --- /dev/null +++ b/examples/wifi/wifi_enterprise/generate_certs/generate_certs.py @@ -0,0 +1,152 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +import argparse +import datetime +import os + +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat +from cryptography.x509.oid import ExtendedKeyUsageOID + +CERT_KEYS = ['2048', '4096', '3072', 'p384'] +CERT_EXPIRY_DAYS = 3650 + + +class GenCerts: + def __init__(self, cert_type: str, message_digest: str, file_path: str) -> None: + self.cert_type = cert_type + self.message_digest = message_digest + self.file_path = file_path + self.digest = hashes.SHA256() + self.init_param() + + def init_param(self) -> None: + + if self.message_digest == 'sha256': + self.digest = hashes.SHA256() + else: + self.digest = hashes.SHA384() + + def generate_ca_certificate(self) -> None: + + if self.cert_type == 'p384': + private_key = ec.generate_private_key( + ec.SECP384R1(), + default_backend() + ) + else: + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=int(self.cert_type) + ) + + public_key = private_key.public_key() + subject = issuer = x509.Name([ + x509.NameAttribute(x509.NameOID.COMMON_NAME, 'example root CA') + ]) + + ca_crt_builder = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer).public_key(public_key) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.datetime.utcnow()) + .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=CERT_EXPIRY_DAYS)) + .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) + .add_extension(x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False) + ) + + self.certificate = ca_crt_builder.sign(private_key, self.digest) + + self.ca_pem = os.path.join(self.file_path, 'ca.pem') + self.ca_key = os.path.join(self.file_path, 'ca.key') + with open(self.ca_pem, 'wb') as f: + f.write(self.certificate.public_bytes(Encoding.PEM)) + with open(self.ca_key, 'wb') as f: + f.write(private_key.private_bytes( + encoding=Encoding.PEM, + format=PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=NoEncryption() + )) + + def generate_user_certificate(self, role: str) -> None: + + if role == 'server': + key_usage = [ExtendedKeyUsageOID.SERVER_AUTH] + elif role == 'client': + key_usage = [ExtendedKeyUsageOID.CLIENT_AUTH] + + with open(self.ca_key, 'rb') as f: + ca_key = serialization.load_pem_private_key(f.read(), password=None) + with open(self.ca_pem, 'rb') as f: + ca_cert = x509.load_pem_x509_certificate(f.read()) + + if self.cert_type == 'p384': + user_private_key = ec.generate_private_key( + ec.SECP384R1(), + default_backend() + ) + else: + user_private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=int(self.cert_type) + ) + + user_key = os.path.join(self.file_path, f'{role}.key') + with open(user_key, 'wb') as f: + f.write(user_private_key.private_bytes( + encoding=Encoding.PEM, + format=PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=NoEncryption() + )) + + subject = x509.Name([ + x509.NameAttribute(x509.NameOID.COMMON_NAME, role) + ]) + + user_csr = x509.CertificateSigningRequestBuilder().subject_name( + subject + ).sign(user_private_key, self.digest) + + user_crt_builder = ( + x509.CertificateBuilder() + .subject_name(user_csr.subject) + .issuer_name(ca_cert.subject) + .public_key(user_csr.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.datetime.utcnow()) + .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=CERT_EXPIRY_DAYS)) + .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=False) + .add_extension(x509.ExtendedKeyUsage(key_usage), critical=False) + .add_extension(x509.SubjectKeyIdentifier.from_public_key(user_csr.public_key()), critical=False) + ) + user_crt = user_crt_builder.sign(ca_key, self.digest) + + user_pem = os.path.join(self.file_path, f'{role}.pem') + user_cert = os.path.join(self.file_path, f'{role}.crt') + with open(user_pem, 'wb') as f: + f.write(user_crt.public_bytes(Encoding.PEM)) + with open(user_cert, 'wb') as f: + f.write(user_crt.public_bytes(Encoding.PEM)) + + +def main() -> None: + + parser = argparse.ArgumentParser() + parser.add_argument('cert_type', choices=['2048', '3072', '4096', 'p384'], help='select cert type') + parser.add_argument('message_digest', choices=['sha256', 'sha384'], help='select message digest, suiteB should be sha384') + file_path = os.path.dirname(__file__) + parser.add_argument('--output', '-o', default=file_path, help='path to store certificates') + args = parser.parse_args() + gencerts = GenCerts(args.cert_type, args.message_digest, args.output) + gencerts.generate_ca_certificate() + gencerts.generate_user_certificate('server') + gencerts.generate_user_certificate('client') + + +if __name__ == '__main__': + main() diff --git a/examples/wifi/wifi_enterprise/generate_certs/generate_certs.sh b/examples/wifi/wifi_enterprise/generate_certs/generate_certs.sh deleted file mode 100755 index fd1e175025..0000000000 --- a/examples/wifi/wifi_enterprise/generate_certs/generate_certs.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash - -help_text=" -Usage: generate_certs.sh \n - only support p384, 2048, 3072, 4096\n -example:\n -sh generate_certs.sh p384\n -sh generate_certs.sh 2048\n -sh generate_certs.sh 3072\n -sh generate_certs.sh 4096\n -" - -DIGEST="-sha256" -DIGEST_CA="-md sha256" -CERT_TYPE="2048" -CERT="2048-ca" - -show_help() { - echo -e $help_text -} - -init_param() { - if [ $(basename "$(pwd)") != "generate_certs" ]; then - echo "path is incorrect, please go into generate_certs directory" - exit - fi - - CERT_TYPE=$1 - CERT=${CERT_TYPE}-ca - - if [ -d "$CERT" ]; then - rm -rf "$CERT" - fi - - if [ $1 = "p384" ] || [ $1 = "3072" ]; then - DIGEST="-sha384" - DIGEST_CA="-md sha384" - elif [ $1 = "2048" ] || [ $1 = "4096" ]; then - DIGEST="-sha256" - DIGEST_CA="-md sha256" - else - echo "parameter error" - exit - fi -} - -create_ca() { - echo - echo "---[ Root CA ]----------------------------------------------------------" - - if [ -d $CERT ]; then - rm $CERT - fi - - mkdir -p $CERT - cat example-ca-openssl.cnf | - sed "s/#@CN@/commonName_default = Root CA/" | - sed s%\./ca$%./$CERT% \ - > ${CERT}-openssl.cnf.tmp - mkdir -p $CERT/certs $CERT/crl $CERT/newcerts $CERT/private - case "$CERT_TYPE" in - "p384") openssl ecparam -out $CERT/ca.key -name secp384r1 -genkey;; - "2048") openssl genrsa -out $CERT/ca.key 2048;; - "3072") openssl genrsa -out $CERT/ca.key 3072;; - "4096") openssl genrsa -out $CERT/ca.key 4096;; - esac - openssl req -config ${CERT}-openssl.cnf.tmp -batch -new -x509 -key $CERT/ca.key -out $CERT/ca.pem $DIGEST - touch $CERT/index.txt - rm ${CERT}-openssl.cnf.tmp -} - -create_certs() { - echo - echo "---[ Server ]-----------------------------------------------------------" - echo - - cat example-ca-openssl.cnf | - sed "s/#@CN@/commonName_default = $CERT_TYPE.$1/" | - sed s%\./ca$%./$CERT% \ - > ${CERT}-openssl.cnf.tmp - echo "---[ Generate $1 Key]----------------------------------------------" - case "$CERT_TYPE" in - "p384") openssl ecparam -out $CERT/$1.key -name secp384r1 -genkey;; - "2048") openssl genrsa -out $CERT/$1.key 2048;; - "3072") openssl genrsa -out $CERT/$1.key 3072;; - "4096") openssl genrsa -out $CERT/$1.key 4096;; - esac - echo "---[ Generate $1 Req]----------------------------------------------" - openssl req -config ${CERT}-openssl.cnf.tmp -batch -new -key $CERT/$1.key -out $CERT/$1.req $DIGEST - openssl ca -config ${CERT_TYPE}-ca-openssl.cnf.tmp -batch -keyfile $CERT/ca.key -cert $CERT/ca.pem -create_serial -in $CERT/$1.req -out $CERT/$1.pem -extensions ext_$1 ${DIGEST_CA} - cp $CERT/$1.pem $CERT/$1.crt - rm ${CERT_TYPE}-ca-openssl.cnf.tmp -} - -verify() { - echo - echo "---[ Verify ]-----------------------------------------------------------" - echo - - openssl verify -CAfile $CERT/ca.pem $CERT/server.pem - openssl verify -CAfile $CERT/ca.pem $CERT/server.crt - openssl verify -CAfile $CERT/ca.pem $CERT/client.pem - openssl verify -CAfile $CERT/ca.pem $CERT/client.crt -} - -clean() { - rm $CERT/*.req - rm $CERT/index* - rm $CERT/serial* - rm -rf $CERT/certs - rm -rf $CERT/newcerts - rm -rf $CERT/private - rm -rf $CERT/crl -} - -if [ "$1" = "--help" ] || [ "$1" = "-h" ] || [ -z "$1" ]; then - show_help - exit 0 -else - init_param $1 - create_ca - create_certs "server" - create_certs "client" - verify - clean -fi diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 98a82aad8c..1e8f299e71 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -43,7 +43,6 @@ examples/storage/parttool/parttool_example.sh examples/system/ota/otatool/get_running_partition.py examples/system/ota/otatool/otatool_example.py examples/system/ota/otatool/otatool_example.sh -examples/wifi/wifi_enterprise/generate_certs/generate_certs.sh install.fish install.sh tools/check_python_dependencies.py