瀏覽代碼

feat(certs): Add python script for generating certificate

ding huan 2 年之前
父節點
當前提交
859df2a983

+ 1 - 1
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 <type>` 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 <cert_type> sha384` to create appropriate certificates such as RSA-3072 or p384 EC certificates.
 
 The steps to create new certificates are given below.
 

+ 0 - 117
examples/wifi/wifi_enterprise/generate_certs/example-ca-openssl.cnf

@@ -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

+ 152 - 0
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()

+ 0 - 126
examples/wifi/wifi_enterprise/generate_certs/generate_certs.sh

@@ -1,126 +0,0 @@
-#!/bin/bash
-
-help_text="
-Usage: generate_certs.sh <cert_type>\n
-<cert_type> 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

+ 0 - 1
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