shou2017.com
JP / EN

CFSSLを使って自己証明書を作成する

Sat May 3, 2025
Sat May 3, 2025

Let’s Encryptなどを使えば無料で証明書ができる便利な時代ですが、証明書が期限が切れた場合や鍵長が違った場合、暗号が違った場合など様々な検証などをやるには便利な自己証明書(オレオレ証明書)です。

opensslでもいいのですが、少々コマンドが面倒なのでcfsslを使って自己証明書を作成することにしました。

サックと作成できるとかと思ったのですが想像以上に証明書が複雑で知らないことが多かったのでメモしておきます。正直、いまでもわからないことがちらほらあるので間違っていたら教えてくださいm(_ _)m

cfsslのインストール

cfsslはhomebrewでインストールできます。

brew install cfssl

順序

単にサーバー証明書だけを作成するだけならもっと簡単にできるのですが、それでは証明書検証の際にエラーになるのできちんとした順序で作成する必要ありました。自己証明書を作成するには以下の順序で行います。

  1. CA証明書の作成
  2. 中間CA証明書の作成
  3. 中間CA証明書の署名
  4. サーバ証明書の作成

CA証明書の作成

CA証明書を作成するには以下のコマンドを実行します。便宜上、フォルダ構成を変えているので実行する際は適宜変更してください。これ以降のコマンドも同様です。

cfssl gencert -initca \
./ca-csr.json | \
cfssljson -bare ../certificates/${DOMAIN}/ca

ca-csr.jsonは以下のように作成します。

{
  "CN": "Root CA",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "ST": "CA",
      "L": "San Francisco"
    }
  ]
}

デフォルトではecdsaになっていますが私はrsaで作成したいのでalgoをrsaにしています。

中間CA証明書の作成

ルートCA証明書をもとに中間CA証明書を作成します。中間CA証明書はルートCA証明書の署名を受けて作成されます。

cfssl gencert \
-ca ../certificates/${DOMAIN}/ca.pem \
-ca-key ../certificates/${DOMAIN}/ca-key.pem \
-config ./ca-config.json \
-profile intermediate ./intermediate-csr.json | \
cfssljson -bare ../certificates/${DOMAIN}/chain

-configでca-config.jsonを指定します。ca-config.jsonは以下のように作成します。ここで証明書の有効期限や署名のプロファイルを指定します。is_caはCA証明書の署名を受けるかどうかのフラグらしいです。

// ca-config.json
{
  "signing": {
    "default": {
      "expiry": "168h"
    },
    "profiles": {
      "ca": {
        "expiry": "26280h",
        "usages": [
          "cert sign",
          "crl sign"
        ],
        "ca_constraint": {
          "is_ca": true
        }
      },
      "www": {
        "expiry": "8760h",
        "usages": [
          "signing",
          "key encipherment",
          "server auth"
        ]
      },
      "client": {
        "expiry": "8760h",
        "usages": [
          "signing",
          "key encipherment",
          "client auth"
        ]
      },
      "server": {
        "expiry": "8760h",
        "usages": [
          "signing",
          "key encipherment",
          "server auth",
          "client auth"
        ]
      },
      "intermediate": {
        "expiry": "26280h",
        "usages": [
          "signing",
          "cert sign",
          "crl sign",
          "client auth",
          "server auth"
        ],
        "ca_constraint": {
          "is_ca": true
        }
      }
    }
  }
}

intermediate-csr.jsonは以下のように作成します。

// intermediate-csr.json
{
  "CN": "example.net",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "ST": "CA",
      "L": "San Francisco"
    }
  ]
}

中間CA証明書の署名

中間CA証明書を署名します。中間CA証明書はルートCA証明書の署名を受けて作成されます。

	cfssl sign \
	-ca ../certificates/${DOMAIN}/ca.pem \
	-ca-key ../certificates/${DOMAIN}/ca-key.pem \
	-config ./ca-config.json \
	-profile intermediate ../certificates/${DOMAIN}/chain.csr | \
	cfssljson -bare ../certificates/${DOMAIN}/chain

サーバ証明書の作成

サーバ証明書を作成します。サーバ証明書は中間CA証明書の署名を受けて作成されます。

cfssl gencert \
	-ca ../certificates/${DOMAIN}/chain.pem \
	-ca-key ../certificates/${DOMAIN}/chain-key.pem \
	-config ./ca-config.json \
	-profile server ./server-csr.json | \
	cfssljson -bare ../certificates/${DOMAIN}/cert
	mv ../certificates/${DOMAIN}/cert-key.pem ../certificates/${DOMAIN}/privkey.pem

サーバ証明書は以下のように作成します。

// server-csr.json
{
  "CN": "example.net",
  "hosts": [
    "example.net",
    "www.example.net"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "ST": "CA",
      "L": "San Francisco"
    }
  ]
}

これで完成です。let’s encryptなどの証明書と同じようにcert.pemprivkey.pemという名前にしたかったのでコマンドを追加しています。

やってみて思ったのですが証明書はだいぶ複雑でした。なので今後のためにも証明書関連の便利ツール集を作成してみました。

cert-manager-tools

cert-manager-tools

cert-manager-toolsは証明書関連でよく使うであろうスクリプトなどをまとめたものです。cfsslを使った証明書の作成やcertbotを使用してlet’s encryptの証明書を作成するスクリプトなどをまとめています。そのほかにもgo言語で作成した証明書の検証ツールなども作ってみました。

let’s encrypt

certbotを使用してlet’s encryptの証明書を作成するスクリプトをまとめてます。検証用に使用するためのものなので指定したディレクトリに証明書を配置するようにしています。 リポジトリを見て貰えばわかると思いますが、certbotのコマンドを実行するだけのスクリプトです。なので特に難しいことはありません。

こん感じのを作成してます。

.PHONY: create-rsa-cert
create-rsa-cert:
	sudo certbot certonly --manual \
		--server https://acme-v02.api.letsencrypt.org/directory \
		--preferred-challenges dns \
		-m ${EMAIL} \
		-d ${DOMAIN} \
		--agree-tos \
		--key-type rsa \
		--rsa-key-size ${KEY_SIZE} \
		--deploy-hook "./script.sh"

なんの変哲もないですが、たまに使うことがあるわりによく迷うのでまとめておきました。let’s encryptで証明書を作成する際のディレクトリは指定できないのでscript.shを使用して、それをカバーしてます。

作成した証明書の検証

作成した証明書を検証するためのツールを作成しました。 もちろんopensslやcfsslでも検証できるのですが、実際の本番環境ではopensslやcfsslはOSコマンドインジェクションの危険性があるのでプログラミング言語で作成したものを使用しています。

というわけでssl/tlsをネイティブにサポートしているgo言語で作成しました。go言語は初めてチャレンジしてみましたが、便利ですね。そこそこ新しい言語だけあってネイティブサポートしているものが今の時代に役に立ちそうなものが多い印象でした。

というわけで以下のコマンドを実行するとgoが実行されます。

make verify DOMAIN=example.net

検証が実行されます。

CFSSLを使って自己証明書を作成する

まとめ

証明書は難しい。

単に発行するだけならクラウドサービスとか使えばすぐできるが自分で作成するとなると証明書の構造や署名の仕組みなどを理解する必要があるので難しかった。それに暗号の読み方とか同じなのに違った読み方するのが多くて混乱しました。

証明書の有効期限が短くなってきていますが、みなさんはどうするんでしょうね。AWSは組織認証(OV)や拡張認証(EV)には対応してませんし(2025年5月現在)、これらの証明書を使っている場合はなんらかの方法で自動化をする必要があるでしょうね。 それとも手動でやるのかな?

ちなみに私がちょっと驚いたのはacmは証明書の整合性がとれていなくても登録もできるし、コモンネームが更新の際にまったく違うものでも更新できるということを知ったことですかね。

awsがよしなしにやってくれてるだろうと思っていたのですが、意外と適当でした。なので証明書は気をつけましょう。サイトが見れなくなりますからね。

SSL/TLS実践入門-Webの安全性を支える暗号化技術の設計思想