panda's tech note

Advent Calendar 2021:自作メッセージングプロトコル

Day 4: CAの準備

昨日は送受信者IDの生成と証明書署名要求を実装しました。今日はCAによるユニーク性の保証(検証)とこの証明書署名要求への署名を実装します。

CAの実装

CAはOpenSSLのスクリプト (CA.sh) をそのまま使ってもよいのですが、CAの署名手順の理解を深めるためにこちらもPythonで実装します。

CA証明書の生成

まずはCA証明書を作ります。CAの証明書は自己署名証明書と呼ばれる、自分の秘密鍵で署名した証明書です。そのため、昨日作ったCSRと同様の手順で証明書署名要求を作成します。ただし、今回作成する証明書はCAの証明書として使うため、X.509形式の証明書拡張として、basicConstraints 拡張フィールドに CA:true という属性を付与します。CA証明書を生成する関数 generate_cacert() を以下のとおり実装しました。

VALID_NOT_BEFORE = 0
VALID_NOT_AFTER = 3600 * 24 * 356 * 10 # 10 years
def generate_cacert(cn, key):
    # Generate a new CSR
    req = OpenSSL.crypto.X509Req()
    # Set the common name
    req.get_subject().CN = cn
    # Set the public key to the request (with SHA256 fingerprint)
    req.set_pubkey(key)
    req.sign(key, 'sha256')
    # Extension for CA
    extensions = ([
        OpenSSL.crypto.X509Extension(b'basicConstraints', False, b'CA:true'),
    ])
    req.add_extensions(extensions)
    # Create a self-signed certificate
    cert = OpenSSL.crypto.X509()
    cert.set_serial_number(1)
    cert.gmtime_adj_notBefore(VALID_NOT_BEFORE)
    cert.gmtime_adj_notAfter(VALID_NOT_AFTER)
    cert.set_issuer(req.get_subject())
    cert.set_subject(req.get_subject())
    cert.set_pubkey(req.get_pubkey())
    cert.sign(key, 'sha256')
    return cert

昨日の generate_csr() 関数との違いは、CSRを作成した後に、自分の鍵 key で有効期限が10年の証明書に署名をして発行している点です。

この generate_cacert() 関数をCAの鍵ペアを作成した後に呼び出します。ここで生成するCA証明書(ルート証明書)の Common NameAdvent Calendar CA Root 2021 とします。

以下のコードのように、鍵ペアを作成してPEM形式のCA証明書 cacert_pem を生成します。なお、秘密鍵は今後IDのCSRへの署名に必要になるので、必ず安全な場所に保存しておきます。

CA_CN = "Advent Calendar CA Root 2021"
# Generate a key pair using 256-bit ECDSA
key = ec.generate_private_key(ec.SECP256R1(), default_backend())
# Private key PEM
key_pem = key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption())
# Public key
pub_pem = key.public_key().public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)

# Convert the private key information to OpenSSL format
okey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, key_pem)

# Generate a self-signed CA certificate
cacert = generate_cacert(CA_CN, okey)
# Dump the CSR as a PEM file
cacert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cacert)

print(cacert_pem.decode('utf-8'))

実装

上記で説明したCA証明書の発行を [github:drpnd/advmsg:prepare_ca.py] に実装しました。このプログラムを実行すると以下のように、秘密鍵を ca/cakey.pem、CA証明書を ca/cacert.pem に保存します。なお、証明書のPEMも表示しています。

$ python3 prepare_ca.py
Generated CA certificate:
        Private key: ca/cakey.pem
        CA Certificate: ca/cacert.pem

-----BEGIN CERTIFICATE-----
MIIBNTCB3AIBATAKBggqhkjOPQQDAjAnMSUwIwYDVQQDDBxBZHZlbnQgQ2FsZW5k
YXIgQ0EgUm9vdCAyMDIxMB4XDTIxMTIwNTA1MDM0NVoXDTMxMDkwNDA1MDM0NVow
JzElMCMGA1UEAwwcQWR2ZW50IENhbGVuZGFyIENBIFJvb3QgMjAyMTBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABH3RsB9gRQs5KLddLhCRV6TMTwmKvc8+dAMQmvv7
+DUVMG6LnSf4zEEI7n2B4lQPZejPNG1AlAAO2HfTmPVA0GUwCgYIKoZIzj0EAwID
SAAwRQIgHxTusyCKCwAjiH3HhwOiNPB37yBCXLhXBmlvOoiiKWMCIQC9Woit8B8G
xpjshBJrG0H+/vuD9xNVuGFLYKiRKB17GA==
-----END CERTIFICATE-----

このCA証明書の中身は openssl コマンドで以下のように確認できます。

$ openssl x509 -in ca/cacert.pem -text
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 1 (0x1)
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=Advent Calendar CA Root 2021
        Validity
            Not Before: Dec  5 05:03:45 2021 GMT
            Not After : Sep  4 05:03:45 2031 GMT
        Subject: CN=Advent Calendar CA Root 2021
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:7d:d1:b0:1f:60:45:0b:39:28:b7:5d:2e:10:91:
                    57:a4:cc:4f:09:8a:bd:cf:3e:74:03:10:9a:fb:fb:
                    f8:35:15:30:6e:8b:9d:27:f8:cc:41:08:ee:7d:81:
                    e2:54:0f:65:e8:cf:34:6d:40:94:00:0e:d8:77:d3:
                    98:f5:40:d0:65
                ASN1 OID: prime256v1
                NIST CURVE: P-256
    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:20:1f:14:ee:b3:20:8a:0b:00:23:88:7d:c7:87:03:
         a2:34:f0:77:ef:20:42:5c:b8:57:06:69:6f:3a:88:a2:29:63:
         02:21:00:bd:5a:88:ad:f0:1f:06:c6:98:ec:84:12:6b:1b:41:
         fe:fe:fb:83:f7:13:55:b8:61:4b:60:a8:91:28:1d:7b:18
-----BEGIN CERTIFICATE-----
MIIBNTCB3AIBATAKBggqhkjOPQQDAjAnMSUwIwYDVQQDDBxBZHZlbnQgQ2FsZW5k
YXIgQ0EgUm9vdCAyMDIxMB4XDTIxMTIwNTA1MDM0NVoXDTMxMDkwNDA1MDM0NVow
JzElMCMGA1UEAwwcQWR2ZW50IENhbGVuZGFyIENBIFJvb3QgMjAyMTBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABH3RsB9gRQs5KLddLhCRV6TMTwmKvc8+dAMQmvv7
+DUVMG6LnSf4zEEI7n2B4lQPZejPNG1AlAAO2HfTmPVA0GUwCgYIKoZIzj0EAwID
SAAwRQIgHxTusyCKCwAjiH3HhwOiNPB37yBCXLhXBmlvOoiiKWMCIQC9Woit8B8G
xpjshBJrG0H+/vuD9xNVuGFLYKiRKB17GA==
-----END CERTIFICATE-----

まとめと明日の予定

今日はCAの準備をしました。明日は3日目で作成したCSRへの署名を実装する予定です。