プライベートSSL証明書/証明局(オレオレ証明書/証明局)の作り方

開発をしていてSSL証明書がほしくなることがあると思います。
今となってはLet's Encryptがあるので、オレオレ証明書/オレオレ証明局を作る機会は減ってきてはいると思いますが、いまだにクローズドネットワークでの開発だったり、フリーのSSL証明書が使えなかったり、小規模のサービスなのでオレオレ証明でもいいかという方は多いかと思いましたので、一括でオレオレ証明書/証明局/クライアント認証用証明書を発行するシェルを作ってみました。

SSL証明書で試行錯誤しているSEさんたちの助けになれば…幸いです

- 初めに

Apache + OpenSSLでの利用を想定して作っています。Nginxを使っている方は証明書を結合して使ってください。

- 準備:SSL証明書置き場を作る

Redhat系を想定しているので、Apacheの設定が入っているパスにprivatesslのディレクトリを生成します

```
mkdir -p /etc/httpd/privatessl
```

- 準備:シェルスクリプトを書きます

```
vi /etc/httpd/privatessl/make_ssl.sh
```

```shell-session:make_ssl.sh
#!/bin/sh

#オレオレで作ったCAをWindows7/8以降に取り込むときは
#証明書のインストール->証明書をすべて次のストアに配置する
#->物理ストアを表示するにチェック->信頼されたユーザー-ローカルコンピュータ
#※物理ストアにレジストリしか見えない場合はcmdを管理者で実行して
#コマンドプロンプトからcrt/pemファイルを実行すると証明書のインストーラが開く
###################ここ以下を任意に書き換え###################
#住所系
CCVAL="JP"
#都道府県
SOVAL="Miyagi"
#住所
CTVAL="Sendai"

#証明局名
ONVAL="Private Office CA"

#企業名
OZUVAL="Private Office"

#証明局・証明書
OUVAL="PrivateCA"
CNVAL="PrivateCA"

#サーバ名(FQDN)
HOSTN="centos7.local"

#CA管理者
AMAIL="adminssl@${HOSTN}"
#サーバ管理者
SMAIL="root@${HOSTN}"

#クライアント証明デフォルトユーザ
DEFUSR="user"
UMAIL="user@${HOSTN}"

#キーのbit長
BITLENGTH=2048

#有効期限
ACTIVATESSL=3650
######################書き換えここまで#######################

FUNCmakecaconfig(){

cat << CAEOF > ./ca.conf

[ ca ]
default_ca      = CA_default

[ CA_default ]
dir             = ./CA
certs           = \$dir/certs
crl_dir         = \$dir/crl
database        = \$dir/index.txt
new_certs_dir   = \$dir/newcerts
certificate     = \$dir/cacert.pem
serial          = \$dir/serial
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

default_days    = ${ACTIVATESSL}
default_crl_days= 30
default_md      = sha256
preserve        = no
policy          = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = ${BITLENGTH}
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

# パスフレーズはここで設定する
input_password = 123456789
output_password = 123456789
string_mask = nombstr

# CA 情報はここで設定しておく( 以下の赤字の部分を追加）
[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_value               = ${CCVAL}

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_value       = ${SOVAL}

localityName                    = Locality Name (eg, city)
localityName_value              = ${CTVAL}

0.organizationName              = Organization Name (eg, company)
0.organizationName_value        = ${ONVAL}

organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_value    = ${OUVAL}

commonName                      = Common Name (eg, YOUR name)
commonName_value                = ${CNVAL}

emailAddress                    = Email Address
emailAddress_value              = ${AMAIL}

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_value         = # omit
unstructuredName                = An optional company name
unstructuredName_value          = # omit

[ usr_cert ]
basicConstraints=CA:FALSE
# This will be displayed in Netscape's comment listbox.
nsComment                       = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true

[ crl_ext ]
authorityKeyIdentifier=keyid:always,issuer:always

CAEOF
}

FUNCmakecareqconfig(){
cat << CAREQEOF > ./careq.conf

[ ca ]
default_ca      = CA_default

[ CA_default ]

dir             = .
certs           = \$dir/certs
crl_dir         = \$dir/crl
database        = \$dir/index.txt
new_certs_dir   = \$dir/newcerts
certificate     = \$dir/cacert.pem
serial          = \$dir/serial
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

default_days    = ${ACTIVATESSL}
default_crl_days= 30
default_md      = sha256
preserve        = no
policy          = policy_match

[ policy_match ]
countryName             = optional
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

####################################################################
[ req ]
default_bits            = ${BITLENGTH}
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

# パスフレーズはここで設定する（後で無効にするので何でも構わない）
input_password = 123456789
output_password = 123456789
string_mask = nombstr

# CA 情報はここで設定しておく
[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_value               = ${CCVAL}

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_value       = ${SOVAL}

localityName                    = Locality Name (eg, city)
localityName_value              = ${CTVAL}

0.organizationName              = Organization Name (eg, company)
0.organizationName_value        = ${ONVAL}

organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_value    = ${OZUVAL}

commonName                      = Common Name (eg, YOUR name)
commonName_value                = ${HOSTN}

emailAddress                    = Email Address
emailAddress_value              = ${SMAIL}

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_value         = # omit
unstructuredName                = An optional company name
unstructuredName_value          = # omit

[ usr_cert ]
basicConstraints=CA:FALSE
# This will be displayed in Netscape's comment listbox.
nsComment                       = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true

[ crl_ext ]
authorityKeyIdentifier=keyid:always,issuer:always

CAREQEOF
}

FUNCmakeclientconfig(){
cat << CLIENTEOF > ./client.conf
[ ca ]
default_ca      = CA_default            # The default ca section

[ CA_default ]

dir             = .
certs           = \$dir/certs
crl_dir         = \$dir/crl
database        = \$dir/index.txt
new_certs_dir   = \$dir/newcerts
certificate     = \$dir/cacert.pem
serial          = \$dir/serial
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

default_days    = ${ACTIVATESSL}
default_crl_days= 30
default_md      = sha256
preserve        = no
policy          = policy_match

[ policy_match ]
countryName             = optional
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

####################################################################
[ req ]
default_bits            = ${BITLENGTH}
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

# パスフレーズはここで設定する（後で無効にするので何でも構わない）
input_password = 123456789
output_password = 123456789
string_mask = nombstr

# CA 情報はここで設定しておく
[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_value               = ${CCVAL}

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_value       = ${SOVAL}

localityName                    = Locality Name (eg, city)
localityName_value              = ${CTVAL}

0.organizationName              = Organization Name (eg, company)
0.organizationName_value        = ${ONVAL}

organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_value    = ${OZUVAL}

commonName                      = Common Name (eg, YOUR name)
commonName_value                = ${DEFUSR}

emailAddress                    = Email Address
emailAddress_value              = ${UMAIL}

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_value         = # omit
unstructuredName                = An optional company name
unstructuredName_value          = # omit

[ usr_cert ]
basicConstraints=CA:FALSE
# This will be displayed in Netscape's comment listbox.
nsComment                       = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true

[ crl_ext ]
authorityKeyIdentifier=keyid:always,issuer:always

CLIENTEOF
}

#Config作成
FUNCmakecaconfig
FUNCmakecareqconfig
FUNCmakeclientconfig

echo "ディレクトリの用意"
mkdir ./CA
mkdir ./CA/certs
mkdir ./CA/crl
mkdir ./CA/newcerts
touch ./CA/index.txt
echo "01" > ./CA/serial
mkdir ./CA/private
chmod 700 ./CA/private
cp *.conf ./CA/
cd CA

echo "キーの生成"
openssl genrsa -out ca.key ${BITLENGTH}
openssl req -config ca.conf -batch -new -days ${ACTIVATESSL} -key ca.key -out ca.csr

echo "CA証明書への自己署名と発行"
openssl x509 -days ${ACTIVATESSL} -req -in ca.csr -signkey ca.key -out ca.crt

echo "CA証明書をrootCAに追加"
openssl x509 -days ${ACTIVATESSL} -in ca.crt -outform DER -out ca.der

echo 然るべき位置にコピー
cp ./ca.key ./private/cakey.pem
cp ./ca.crt ./cacert.pem

cd ..

echo "サーバ秘密鍵の作成"
openssl genrsa -out server.key ${BITLENGTH}

echo "秘密鍵からサーバのx509証明書発行要求(CSR)を作成"
openssl req -config careq.conf -batch -new -days ${ACTIVATESSL} -key server.key -out server.csr

cp ./server.csr ./CA/
cd CA
echo "CSRにCAが署名してサーバ証明書を発行"
openssl ca -config careq.conf -batch -out server-ca.crt -infiles server.csr
cd ..

#ここ以下はクライアント証明のための領域

echo "クライアント秘密鍵の作成"
openssl genrsa -out client.key ${BITLENGTH}

echo "秘密鍵からx509証明書発行要求(CSR)を作成"
openssl req -config client.conf -batch -new -days ${ACTIVATESSL} -key client.key -out client.csr

cp ./client.csr ./CA/
cp ./client.key ./CA/
cd CA

echo "CSRにCAが署名してクライアント証明書を発行"
openssl ca -config client.conf -batch -out client-ca.crt -infiles client.csr

echo "クライアント秘密鍵とクライアント証明書のペアをpkcs12でexportする"
cat client.key client-ca.crt | openssl pkcs12 -export -out client-default.p12 -name "default key"

#複数生成する場合はDEFUSRとUMAILを変更してガシガシ作る
#DEFUSR="user01"
#UMAIL="user01@${HOSTN}"
#FUNCmakeclientconfig
#openssl req -config client.conf -batch -new -days ${ACTIVATESSL} -key client.key -out client.csr
#openssl ca -config client.conf -batch -out client-ca.crt -infiles client.csr
#cat client.key client-ca.crt | openssl pkcs12 -export -out client-${DEFUSR}.p12 -name "${DEFUSR} key"

cd ..

rm -rf client.csr client.key server.csr

cd CA
mv *.p12 ../
mv client-ca.crt ../
mv client.csr ../
mv client.key ../

mv server-ca.crt ../
mv server.csr ../

#mv ca.crt ../
#mv ca.der ../
rm -rf ca.conf client.conf careq.conf
cd ..
rm -rf ca.conf client.conf careq.conf

echo "自分のサーバ証明書の場所を指定"
echo "###########################################################"
echo "SSLCertificateFile       `pwd`/server-ca.crt"
echo "SSLCertificateKeyFile    `pwd`/server.key"
echo "SSLCertificateChainFile  `pwd`/CA/ca.crt"
echo "###########################################################"
echo "#Client Inport Root Certificate File : [ca.der] or [ca.crt]"
echo "#Client Inport Private Certificate File : [client.p12]"
echo "###########################################################"
```

- 証明書を作成

以下のように、スクリプトを作ったディレクトリに移動して実行

```
cd /etc/httpd/privatessl
/bin/sh ./make_ssl.sh
```

- 実行結果:以下のようにログが流れるはず

```
ディレクトリの用意
キーの生成
Generating RSA private key, 2048 bit long modulus
...+++
........................................................................................................................................................................................+++
e is 65537 (0x10001)
JP
Miyagi
Sendai
Private Office
PrivateCA
PrivateCA
adminssl@centos7.local


CA証明書への自己署名と発行
Signature ok
subject=/C=JP/ST=Miyagi/L=Sendai/O=Private Office/OU=PrivateCA/CN=PrivateCA/emailAddress=adminssl@centos7.local
Getting Private key
CA証明書をrootCAに追加
然るべき位置にコピー
サーバ秘密鍵の作成
Generating RSA private key, 2048 bit long modulus
.............................................................................................+++
.................+++
e is 65537 (0x10001)
秘密鍵からサーバのx509証明書発行要求(CSR)を作成
JP
Miyagi
Sendai
Private Office
Private Office
centos7.local
root@centos7.local


CSRにCAが署名してサーバ証明書を発行
Using configuration from careq.conf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Apr 15 03:50:35 2017 GMT
            Not After : Apr 13 03:50:35 2027 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Miyagi
            organizationName          = Private Office
            organizationalUnitName    = Private Office
            commonName                = centos7.local
            emailAddress              = root@centos7.local
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                59:B7:E1:13:C1:1D:9A:31:BB:A2:F5:E5:12:A2:D7:3C:D9:83:82:24
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Miyagi/L=Sendai/O=Private Office/OU=PrivateCA/CN=PrivateCA/emailAddress=adminssl@centos7.local
                serial:A0:B6:CB:4B:2C:A6:63:09

Certificate is to be certified until Apr 13 03:50:35 2027 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
クライアント秘密鍵の作成
Generating RSA private key, 2048 bit long modulus
...................+++
.............+++
e is 65537 (0x10001)
秘密鍵からx509証明書発行要求(CSR)を作成
JP
Miyagi
Sendai
Private Office
Private Office
user
user@centos7.local


CSRにCAが署名してクライアント証明書を発行
Using configuration from client.conf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Apr 15 03:50:37 2017 GMT
            Not After : Apr 13 03:50:37 2027 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Miyagi
            organizationName          = Private Office
            organizationalUnitName    = Private Office
            commonName                = user
            emailAddress              = user@centos7.local
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                9B:99:DE:9B:44:AA:6F:D5:83:3C:1B:87:22:58:5F:BE:C0:A6:C3:6D
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Miyagi/L=Sendai/O=Private Office/OU=PrivateCA/CN=PrivateCA/emailAddress=adminssl@centos7.local
                serial:A0:B6:CB:4B:2C:A6:63:09

Certificate is to be certified until Apr 13 03:50:37 2027 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
クライアント秘密鍵とクライアント証明書のペアをpkcs12でexportする
Enter Export Password:
Verifying - Enter Export Password:
```

途中入力を求められるので、何も入力せず[Enter]を押す。
※クライアント証明を取り込む際のパスワードになるので必要な場合は入力してもいい

```
自分のサーバ証明書の場所を指定
###########################################################
SSLCertificateFile       /etc/httpd/privatessl/server-ca.crt
SSLCertificateKeyFile    /etc/httpd/privatessl/server.key
SSLCertificateChainFile  /etc/httpd/privatessl/CA/ca.crt
###########################################################
#Client Inport Root Certificate File : [ca.der] or [ca.crt]
#Client Inport Private Certificate File : [client.p12]
###########################################################
```

最後に、ssl.confなどに書き込む設定の３行

```
SSLCertificateFile       /etc/httpd/privatessl/server-ca.crt
SSLCertificateKeyFile    /etc/httpd/privatessl/server.key
SSLCertificateChainFile  /etc/httpd/privatessl/CA/ca.crt
```

が表示されるので、これをSSLの設定記入位置に転記して、Apacheを再起動すればオレオレ証明の導入は完了

- ブラウザで安全なSSL証明書として認識させたい場合

上記の設定で出来上がった、オレオレ認証局の証明書
/etc/httpd/privatessl/CA/ca.crt
をブラウザもしくはOSの証明局小類書に追加する(Windows7以降 or OSX10.8以降ならコピーしてダブルクリックで取り込み画面が出るはず)と、ブラウザでも安全なSSL証明書として認識されるので、テストをする際にChromeなどのブラウザから、安全ではない接続うんぬんで怒られる心配はなくなります

以上、参考になれば幸いです