Skip to content

Instantly share code, notes, and snippets.

@t-okkn
Last active January 23, 2022 18:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t-okkn/658d78245e9758f7cf69e6ef316d52e2 to your computer and use it in GitHub Desktop.
Save t-okkn/658d78245e9758f7cf69e6ef316d52e2 to your computer and use it in GitHub Desktop.
オレオレ認証局構築メモ

オレオレ認証局構築メモ

■前提

Arch Linux にて検証した手順です。
※「{{ }}」にて囲まれている部分は変数を表します(sedやJinja2などのテンプレートエンジンで一括置換してください)。
※「myCA」についてはテンプレート化していませんが、お好きな名前にしていただいても結構です。

■ざっくり仕様

  • 証明書ごとにフォルダで分けられるように設計しています
  • HOST名には【*】(アスタリスク)と【.】(ドット)と【-】(ハイフン)以外の記号は使用しないでください
  • USER名には【_】(アンダーバー)と【-】(ハイフン)以外の記号は使用しないでください
  • HOST名やUSER名に「CA」の名称を使用することはできません
  • Makefileの細かいルールを呼び出すことも敢えて可能なようにしてありますが、なるべく以下のコマンドのみで証明書発行操作を行ってください
  • CA認証局は36500日、各種証明書は800日の期限をデフォルトとしています

■最終的な証明書配置

/etc/ssl/myCA/
├ Makefile
├ ca.conf
├ server.conf
├ client.conf

├ CA/
│ ├ inca.pem
│ ├ inca.key
│ │
│ ├ db/
│ │ ├ index.txt
│ │ └ serial
│ │
│ ├ crl/
│ │ ├ crl.pem
│ └ ┴ crlserial

├ {{ hostname }}/
│ ├ server.pem
│ └ server.key

├ {{ username }}/
│ ├ client.pem
└ ┴ client.key

■初期構築手順

  1. まずはrootになる

    $ su -
    # cd /etc/ssl
    
  2. ディレクトリを作成し、移動

    # mkdir myCA
    # cd myCA
    
  3. コンフィグとMakefileをダウンロード

    # curl -sSL https://gist.githubusercontent.com/t-okkn/658d78245e9758f7cf69e6ef316d52e2/raw/c1cae48a9f0ae57984224798608723714bee2f16/ca.conf | tee ca.conf
    # curl -sSL https://gist.githubusercontent.com/t-okkn/658d78245e9758f7cf69e6ef316d52e2/raw/c1cae48a9f0ae57984224798608723714bee2f16/server.conf | tee server.conf
    # curl -sSL https://gist.githubusercontent.com/t-okkn/658d78245e9758f7cf69e6ef316d52e2/raw/c1cae48a9f0ae57984224798608723714bee2f16/client.conf | tee client.conf
    # curl -sSL https://gist.githubusercontent.com/t-okkn/658d78245e9758f7cf69e6ef316d52e2/raw/c1cae48a9f0ae57984224798608723714bee2f16/Makefile | tee Makefile
    # chmod 644 *
    
  4. コンフィグの修正を行う
    以下の項目について、修正を行わなくても証明書は発行できますが、各々好きな値にしていただいたほうが良いと思います。

    # vi ca.conf
    =====
    stateOrProvinceName    = Tokyo
    localityName           = Chuou-ku
    0.organizationName     = Example Ltd.
    organizationalUnitName = PrivateCA
    commonName             = example.com
    emailAddress           = admin@example.com
    =====
    
    # vi server.conf
    =====
    stateOrProvinceName    = Tokyo
    localityName           = Chuou-ku
    0.organizationName     = Example Ltd.
    organizationalUnitName = Server
    commonName             = example.com
    emailAddress           = admin@example.com
    =====
    
    # vi client.conf
    =====
    stateOrProvinceName    = Tokyo
    localityName           = Chuou-ku
    0.organizationName     = Example Ltd.
    organizationalUnitName = Client
    commonName             = example.com
    emailAddress           = admin@example.com
    =====
    
  5. 初期環境構築を行う

    # make
    
  6. CA証明書を発行する

    # make ca.new
    
  7. 持ち出し用のファイルを作成
    [/etc/ssl/myCA] の直下に [myCA_ca.p12] ファイルが作成されます。

    # make ca.out
    

■サーバ証明書発行手順

  1. サーバ証明書用の雛形を作成する

    # make HOSTNAME="{{ hostname }}" server.init
    
  2. サーバ証明書用の発行を行う
    必要に応じて「subjectAltName」を編集してください。

    # vi {{ hostname }}/ext.txt
    =====
    subjectAltName = DNS:{{ hostname }},IP:192.168.0.1
    =====
    
    # make HOSTNAME="{{ hostname }}" server.new
    
  3. 持ち出し用のファイルを作成
    [/etc/ssl/myCA] の直下に [{{ hostname }}.p12] ファイルが作成されます。

    # make HOSTNAME="{{ hostname }}" server.out
    

サーバ証明書の更新

1世代だけ「old」フォルダ内にバックアップが残ります(ただし、失効済みです)。

# make HOSTNAME="{{ hostname }}" server.upd

■クライアント証明書発行手順

  1. クライアント証明書用の雛形を作成する

    # make USERNAME="{{ username }}" client.init
    
  2. クライアント証明書用の発行を行う

    # make USERNAME="{{ username }}" client.new
    
  3. 持ち出し用のファイルを作成
    [/etc/ssl/myCA] の直下に [{{ username }}.p12] ファイルが作成されます。

    # make USERNAME="{{ username }}" client.out
    

クライアント証明書の更新

1世代だけ「old」フォルダ内にバックアップが残ります(ただし、失効済みです)。

# make USERNAME="{{ username }}" client.upd

■その他コマンド

証明書閲覧

CA自己署名証明書がテキスト形式で閲覧できます。

# make printca

サーバ証明書がテキスト形式で閲覧できます。

# make HOSTNAME="{{ hostname }}" printsv

クライアント証明書がテキスト形式で閲覧できます。

# make USERNAME="{{ username }}" printcl

DHparamの取得

[/etc/ssl/myCA] の直下に [dh] ファイルが作成されます。

# make dh

CA証明書をDER形式に変換

[/etc/ssl/myCA] の直下に [myCA_ca.der] ファイルが作成されます。

# make ca.der

fullchainサーバ証明書の取得

CA証明書とサーバ証明書を併記した証明書が発行されます。

# make HOSTNAME="{{ hostname }}" server.fullchain

証明書データベースの更新

# make ca.updatedb

証明書データベースのoldファイルを掃除

# make clean

認証局の破壊(リセット)

認証局をリセットする事ができますが、頻繁に実行するコマンドではないと思われます。

# make destroy

参照文献

オレだよオレオレ認証局で証明書つくる

#
# OpenSSL configuration file.
#
####################################################################
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /etc/ssl/myCA
certificate = $dir/CA/inca.pem
private_key = $dir/CA/inca.key
database = $dir/CA/db/index.txt
serial = $dir/CA/db/serial
crl_dir = $dir/CA/crl
crl = $dir/CA/crl/crl.pem
crlnumber = $dir/CA/crl/crlserial
certs = $dir
new_certs_dir = $dir
RANDFILE = $dir/.rand
name_opt = ca_default
cert_opt = ca_default
default_days = 36500
default_crl_days = 36500
default_md = sha256
preserve = no
x509_extensions = v3_ca
copy_extensions = copy
policy = policy_default
[ policy_default ]
countryName = supplied
stateOrProvinceName = supplied
localityName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
prompt = no
default_bits = 2048
default_keyfile = private.key
string_mask = utf8only
distinguished_name = server
[ server ]
countryName = JP
stateOrProvinceName = Tokyo
localityName = Chuou-ku
0.organizationName = Example Ltd.
organizationalUnitName = PrivateCA
commonName = example.com
emailAddress = admin@example.com
[ v3_ca ]
basicConstraints = CA:TRUE
nsCertType = sslCA, emailCA, objCA
nsComment = "OpenSSL Generated Certificate"
keyUsage = digitalSignature, cRLSign, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
#
# OpenSSL configuration file.
#
####################################################################
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /etc/ssl/myCA
certificate = $dir/CA/inca.pem
private_key = $dir/CA/inca.key
database = $dir/CA/db/index.txt
serial = $dir/CA/db/serial
crl_dir = $dir/CA/crl
crl = $dir/CA/crl/crl.pem
crlnumber = $dir/CA/crl/crlserial
certs = $dir
new_certs_dir = $dir
RANDFILE = $dir/.rand
name_opt = ca_default
cert_opt = ca_default
default_days = 800
default_crl_days = 800
default_md = sha256
preserve = no
copy_extensions = copy
policy = policy_default
[ policy_default ]
countryName = supplied
stateOrProvinceName = supplied
localityName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
prompt = no
default_bits = 2048
default_keyfile = private.key
string_mask = utf8only
distinguished_name = server
x509_extensions = v3_req
[ server ]
countryName = JP
stateOrProvinceName = Tokyo
localityName = Chuou-ku
0.organizationName = Example Ltd.
organizationalUnitName = Client
commonName = example.com
emailAddress = admin@example.com
[ v3_req ]
basicConstraints = CA:FALSE
nsCertType = client, email, objsign
nsComment = "OpenSSL Generated Certificate"
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
######################################################################
#
# Let's create certificates easily.
#
######################################################################
HOSTNAME =
USERNAME =
openssl := openssl
dhKeySize := 2048
caConf := ca.conf
svConf := server.conf
clConf := client.conf
basedir := $(shell grep -E '^dir +=' $(caConf) | sed -e 's/.*=//' -e 's/^ *//')
caDays := $(shell grep default_days $(caConf) | sed -e 's/.*=//' -e 's/^ *//')
caDir := $(basedir)/CA
caKey := $(caDir)/inca.key
caCsr := $(caDir)/inca.csr
caPem := $(caDir)/inca.pem
caExt := v3_ca
dbDir := $(caDir)/db
crlDir := $(caDir)/crl
dbIndex := $(dbDir)/index.txt
dbSerial := $(dbDir)/serial
crlSerial := $(crlDir)/crlserial
crlPem := $(crlDir)/crl.pem
HOST_DIR = $(basedir)/$(subst *,_,$(HOSTNAME))
HOST_KEY = $(HOST_DIR)/server.key
HOST_CSR = $(HOST_DIR)/server.csr
HOST_PEM = $(HOST_DIR)/server.pem
HOST_FULLPEM = $(HOST_DIR)/fullchain.pem
HOST_EXT = $(HOST_DIR)/ext.txt
HOST_P12 = $(basedir)/$(addsuffix .p12,$(subst *,_,$(HOSTNAME)))
USER_DIR = $(basedir)/$(USERNAME)
USER_KEY = $(USER_DIR)/client.key
USER_CSR = $(USER_DIR)/client.csr
USER_PEM = $(USER_DIR)/client.pem
distName := server
svSbj := $(shell grep -A7 '\[ $(distName) \]' $(svConf) | sed 's/ //g')
svC := $(word 2,$(subst =, ,$(filter countryName=%,$(svSbj))))
svST := $(word 2,$(subst =, ,$(filter stateOrProvinceName=%,$(svSbj))))
svL := $(word 2,$(subst =, ,$(filter localityName=%,$(svSbj))))
svO := $(word 2,$(subst =, ,$(filter 0.organizationName=%,$(svSbj))))
svOU := $(word 2,$(subst =, ,$(filter organizationalUnitName=%,$(svSbj))))
svEA := $(word 2,$(subst =, ,$(filter emailAddress=%,$(svSbj))))
SV_SUBJ = "/emailAddress=$(svEA)/C=$(svC)/ST=$(svST)/L=$(svL)/O=$(svO)/OU=$(svOU)/CN=$(HOSTNAME)"
clSbj := $(shell grep -A7 '\[ $(distName) \]' $(clConf) | sed 's/ //g')
clC := $(word 2,$(subst =, ,$(filter countryName=%,$(clSbj))))
clST := $(word 2,$(subst =, ,$(filter stateOrProvinceName=%,$(clSbj))))
clL := $(word 2,$(subst =, ,$(filter localityName=%,$(clSbj))))
clO := $(word 2,$(subst =, ,$(filter 0.organizationName=%,$(clSbj))))
clOU := $(word 2,$(subst =, ,$(filter organizationalUnitName=%,$(clSbj))))
clEA := $(word 2,$(subst =, ,$(filter emailAddress=%, $(clSbj))))
CL_SUBJ = "/emailAddress=$(clEA)/C=$(clC)/ST=$(clST)/L=$(clL)/O=$(clO)/OU=$(clOU)/CN=$(USERNAME)"
######################################################################
#
# Make the each certificates
#
######################################################################
init:
@[ -d $(caDir) ] || mkdir $(caDir)
@[ -d $(dbDir) ] || mkdir $(dbDir)
@[ -d $(crlDir) ] || mkdir $(crlDir)
@$(MAKE) __require_db
.PHONY: ca.new
ca.new: ca.key ca.csr ca.pem
.PHONY: ca.out
ca.out: ca.p12
.PHONY: server.init
server.init: server.dir
@echo "subjectAltName = DNS:$(HOSTNAME)" > $(HOST_EXT)
.PHONY: server.new
server.new: server.check.key server.key server.csr server.check.pem server.pem server.verify
.PHONY: server.upd
server.upd: server.revoke server.key server.csr server.pem server.verify
.PHONY: server.out
server.out: server.p12
.PHONY: client.init
client.init: client.dir
.PHONY: client.new
client.new: client.check.key client.key client.csr client.check.pem client.pem client.verify
.PHONY: client.upd
client.upd: client.revoke client.key client.csr client.pem client.verify
.PHONY: client.out
client.out: client.p12
######################################################################
#
# Miscellaneous rules.
#
######################################################################
__require_HOSTNAME:
@[ "$(HOSTNAME)" != "" ] || \
(echo -e "HOSTNAME is not defined.\n# make HOSTNAME=xxx <target>\n"; exit 127)
@[ "$(HOSTNAME)" != "CA" ] || (echo "Unacceptable value: $(HOSTNAME)"; exit 125)
__require_USERNAME:
@[ "$(USERNAME)" != "" ] || \
(echo -e "USERNAME is not defined.\n# make USERNAME=xxx <target>\n"; exit 126)
@[ "$(USERNAME)" != "CA" ] || (echo "Unacceptable value: $(USERNAME)"; exit 125)
__require_db:
@[ -f $(dbIndex) ] || touch $(dbIndex)
@[ -f $(dbSerial) ] || echo '01' > $(dbSerial)
@[ -f $(crlSerial) ] || echo '01' > $(crlSerial)
printsv: __require_HOSTNAME
$(openssl) x509 -text -in $(HOST_PEM)
printcl: __require_USERNAME
$(openssl) x509 -text -in $(USER_PEM)
printca:
$(openssl) x509 -text -in $(caPem)
clean:
@rm -f $(dbDir)/*old $(crlDir)/*old
#
# Make a target that people won't run too often.
#
destroy:
@find . -mindepth 1 -maxdepth 1 -not -name 'Makefile' -a -not -name '*.conf' | xargs rm -rf
@$(MAKE) init
######################################################################
#
# Diffie-Hellman parameters
#
######################################################################
.PHONY: dh
dh:
$(openssl) dhparam -out dh -2 $(dhKeySize)
######################################################################
#
# Create a new self-signed CA certificate
#
######################################################################
.PHONY: ca.key
ca.key:
@[ ! -f $(caKey) ] || (echo "$(caKey) already exists."; exit 1)
$(openssl) ecparam -genkey -name prime256v1 -out $(caKey)
@chmod 600 $(caKey)
.PHONY: ca.csr
ca.csr: $(caConf) $(caKey)
$(openssl) req -new -sha256 -key $(caKey) -config $(caConf) -out $(caCsr)
.PHONY: ca.pem
ca.pem: __require_db $(caConf) $(caCsr)
@[ ! -f $(caPem) ] || (echo "$(caPem) already exists."; exit 2)
$(openssl) x509 -req -in $(caCsr) -signkey $(caKey) -days $(caDays) \
-extensions $(caExt) -extfile $(caConf) -out $(caPem)
@chmod g+r $(caPem)
@rm -f $(caCsr)
.PHONY: ca.der
ca.der: $(caPem)
$(openssl) x509 -inform PEM -outform DER -in $(caPem) -out $(notdir $(basedir))_ca.der
@chmod g+r $(notdir $(basedir))_ca.der
.PHONY: ca.p12
ca.p12: $(caPem)
$(openssl) pkcs12 -export -in $(caPem) -inkey $(caKey) -out $(notdir $(basedir))_ca.p12
@chmod g+r $(notdir $(basedir))_ca.p12
.PHONY: ca.updatedb
ca.updatedb: $(caConf)
$(openssl) ca -config $(caConf) -updatedb
######################################################################
#
# Create a new server certificate, signed by the above CA.
#
######################################################################
.PHONY: server.dir
server.dir: __require_HOSTNAME
@[ -d $(HOST_DIR) ] || mkdir $(HOST_DIR)
@[ -f $(HOST_EXT) ] || touch $(HOST_EXT)
.PHONY: server.check.key
server.check.key: server.dir
@[ ! -f $(HOST_KEY) ] || (echo "$(HOST_KEY) already exists."; exit 3)
.PHONY: server.check.pem
server.check.pem: server.dir
@[ ! -f $(HOST_PEM) ] || (echo "$(HOST_PEM) already exists."; exit 4)
.PHONY: server.check.revoke
server.check.revoke: server.dir
@[ -f $(HOST_PEM) ] || (echo "$(HOST_PEM) does not exist."; exit 5)
.PHONY: server.key
server.key: server.dir
$(openssl) ecparam -genkey -name prime256v1 -out $(HOST_KEY)
@chmod 600 $(HOST_KEY)
.PHONY: server.csr
server.csr: server.dir $(svConf) $(HOST_KEY)
$(openssl) req -new -sha256 -key $(HOST_KEY) -subj $(SV_SUBJ) -config $(svConf) -out $(HOST_CSR)
.PHONY: server.pem
server.pem: __require_db server.dir $(svConf) $(caKey) $(caPem) $(HOST_CSR)
$(openssl) ca -in $(HOST_CSR) -cert $(caPem) -keyfile $(caKey) -extfile $(HOST_EXT) \
-config $(svConf) -subj $(SV_SUBJ) -outdir $(HOST_DIR) -out $(HOST_PEM)
@chmod g+r $(HOST_PEM)
@rm -f $(HOST_CSR)
.PHONY: server.p12
server.p12: server.dir $(HOST_PEM)
$(openssl) pkcs12 -export -in $(HOST_PEM) -inkey $(HOST_KEY) -out $(HOST_P12)
@chmod g+r $(HOST_P12)
.PHONY: server.fullchain
server.fullchain: server.dir $(caPem) $(HOST_PEM)
@cat $(caPem) > $(HOST_FULLPEM)
@cat $(HOST_PEM) | sed -n '/-----BEGIN CERTIFICATE-----/,$$p' >> $(HOST_FULLPEM)
@chmod g+r $(HOST_FULLPEM)
.PHONY: server.revoke
server.revoke: __require_db server.check.revoke $(caConf)
@[ -d $(HOST_DIR)/old ] || mkdir $(HOST_DIR)/old
$(openssl) ca -config ca.conf -gencrl -revoke $(HOST_PEM) -out $(crlPem)
@[ ! -f $(HOST_KEY) ] || command mv -f $(HOST_KEY) $(HOST_DIR)/old/
@[ ! -f $(HOST_PEM) ] || command mv -f $(HOST_PEM) $(HOST_DIR)/old/
@[ ! -f $(HOST_FULLPEM) ] || command rm -f $(HOST_FULLPEM)
.PHONY: server.verify
server.verify: __require_HOSTNAME $(caPem) $(HOST_PEM)
$(openssl) verify -CAfile $(caPem) $(HOST_PEM)
######################################################################
#
# Create a new client certificate, signed by the the above CA.
#
######################################################################
.PHONY: client.dir
client.dir: __require_USERNAME
@[ -d $(USER_DIR) ] || mkdir $(USER_DIR)
.PHONY: client.check.key
client.check.key: client.dir
@[ ! -f $(USER_KEY) ] || (echo "$(USER_KEY) already exists."; exit 6)
.PHONY: client.check.pem
client.check.pem: client.dir
@[ ! -f $(USER_PEM) ] || (echo "$(USER_PEM) already exists."; exit 7)
.PHONY: client.check.revoke
client.check.revoke: client.dir
@[ -f $(USER_PEM) ] || (echo "$(USER_PEM) does not exist."; exit 8)
.PHONY: client.key
client.key: client.dir
$(openssl) ecparam -genkey -name prime256v1 -out $(USER_KEY)
@chmod 600 $(USER_KEY)
.PHONY: client.csr
client.csr: client.dir $(clConf) $(USER_KEY)
$(openssl) req -new -sha256 -key $(USER_KEY) -subj $(CL_SUBJ) -config $(clConf) -out $(USER_CSR)
.PHONY: client.pem
client.pem: __require_db client.dir $(clConf) $(caKey) $(caPem) $(USER_CSR)
$(openssl) ca -in $(USER_CSR) -cert $(caPem) -keyfile $(caKey) -config $(clConf) \
-subj $(CL_SUBJ) -outdir $(USER_DIR) -out $(USER_PEM)
@chmod g+r $(USER_PEM)
@rm -f $(USER_CSR)
.PHONY: client.p12
client.p12: client.dir $(USER_PEM)
$(openssl) pkcs12 -export -in $(USER_PEM) -inkey $(USER_KEY) -out $(USERNAME).p12
@chmod g+r $(USERNAME).p12
.PHONY: client.revoke
client.revoke: __require_db client.check.revoke $(caConf)
@[ -d $(USER_DIR)/old ] || mkdir $(USER_DIR)/old
$(openssl) ca -config $(caConf) -gencrl -revoke $(USER_PEM) -out $(crlPem)
@[ ! -f $(USER_KEY) ] || command mv -f $(USER_KEY) $(USER_DIR)/old/
@[ ! -f $(USER_PEM) ] || command mv -f $(USER_PEM) $(USER_DIR)/old/
.PHONY: client.verify
client.verify: __require_USERNAME $(caPem) $(USER_PEM)
$(openssl) verify -CAfile $(caPem) $(USER_PEM)
#
# OpenSSL configuration file.
#
####################################################################
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /etc/ssl/myCA
certificate = $dir/CA/inca.pem
private_key = $dir/CA/inca.key
database = $dir/CA/db/index.txt
serial = $dir/CA/db/serial
crl_dir = $dir/CA/crl
crl = $dir/CA/crl/crl.pem
crlnumber = $dir/CA/crl/crlserial
certs = $dir
new_certs_dir = $dir
RANDFILE = $dir/.rand
name_opt = ca_default
cert_opt = ca_default
default_days = 800
default_crl_days = 800
default_md = sha256
preserve = no
copy_extensions = copy
policy = policy_default
[ policy_default ]
countryName = supplied
stateOrProvinceName = supplied
localityName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
prompt = no
default_bits = 2048
default_keyfile = private.key
string_mask = utf8only
distinguished_name = server
x509_extensions = v3_req
[ server ]
countryName = JP
stateOrProvinceName = Tokyo
localityName = Chuou-ku
0.organizationName = Example Ltd.
organizationalUnitName = Server
commonName = example.com
emailAddress = admin@example.com
[ v3_req ]
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Certificate"
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage = serverAuth, clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment