プライベートなSSL、オレオレ認証局メモ

SSLのメモ

まず当方の環境。

Server OS: Ubuntu
Http Server: Appache
Client: Windows 10

参考にしたサイト

https://qiita.com/fururun02/items/e22a773516b961a081a5

https://www.golinuxcloud.com/create-certificate-authority-root-ca-linux/

オレオレ証明書作ってみたものの、ブラウザで表示するとERR_CERT_INVALIDの嵐。
ブラウザでSSL系のエラーが出たらキーボードでthisisunsafeを入力すればエラーを無視は出来るのですが、
VSCodeでTypeScriptをデバッグする際にソースマップを読み込めず苦労しました。

    Could not read source map for https://kurage-service/scripts/apps/shiritori/main.js: Unexpected 503 response from https://kurage-service/scripts/apps/shiritori/main.js.map: unable to verify the first certificate

結局オレオレ証明書を成功させないとどうにもならず数日苦戦。

えーと、中間証明書って必須なの? やり方間違ってるの? よくわからない。
原因がわからず試行錯誤した結果、何とか成功したのでメモしておきます。
ただ何が悪かったか理解はしてないです。

作業フォルダは/etc/ssl/mycaにしてます。

設定ファイル

OpenSSLの設定ファイルが/etc/ssl/openssl.cnfにあるのですがその辺はよくわかっておらず、
大体変更した辺りを抜粋します。

####################################################################
[ ca ]
default_ca      = CA_default            # The default ca section

####################################################################
[ CA_default ]

dir             = /etc/ssl/myca/ca      # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
                                        # several certs with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/ca.crt           # The CA certificate - --------------- Change: $dir/ca.crt
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/ca.key   # The private key --------------------- Change: $dir/private/ca.key
RANDFILE        = $dir/private/.rand    # private random number file

x509_extensions = usr_cert              # The extensions to add to the cert
subjectAltName = @alt_names
[ alt_names ]
DNS.3 = oreore

後重要なのはルート証明書、中間証明書は認証局の設定でbasicConstraints=CA:TRUEを指定して、
サーバー証明書はbasicConstraints=CA:FALSEに設定しました。

シェルスクリプトとSAN

コマンド直打ちだと数十回は失敗したのでシェルスクリプトでまとめておきます。

#!/bin/bash

initDirs()
{
    local P="${1}"
    local DIRS=($P/private $P/certs $P/crl $P/newcerts)

    for d in "${DIRS[@]}"; do
        if [ ! -d "$d" ]; then
            mkdir -p "$d"
        fi
    done
}

initFiles()
{
  local DIR="${1}"
  if [[ ! -f $DIR/serial ]]; then
    echo 01 > $DIR/serial
  fi

  if [[ ! -f $DIR/crlnumber ]]; then
    echo 01 > $DIR/crlnumber
  fi

  if [ ! -f $DIR/index.txt ]; then
    touch $DIR/index.txt
  fi

  if [ ! -f $DIR/index.txt.attr ]; then
    touch $DIR/index.txt.attr
  fi
}

initFD()
{
    local P="${1}"
    mkdir $P
    initDirs $P
    initFiles $P

}

ca()
{
  local P="/etc/ssl/myca"

 # 実行毎に作業ディレクトリを掃除しておく。
  rm -R $P/*

  echo " ||||| CREATE ROOT CA CERT"
  local CA=$P/ca
  initFD $CA

  echo "CA 鍵作成"
  openssl genrsa -aes256 -out $CA/private/ca.key.pem -passout pass:pass-word 2048

  echo "CSR + 自己証明(-x509を設定すると自己署名)"
  openssl req -new -x509 -days 365 -key $CA/private/ca.key.pem -out $CA/ca.cert.pem -passin pass:pass-word

  echo "----- ルート証明書表示"
  cat $CA/ca.cert.pem

  echo " ||||| CREATE INTER CHAIN CERT"
  local INTER=$P/inter
  initFD $INTER

  echo "中間CA 鍵作成"
  openssl genrsa -aes256 -out $INTER/private/inter.key.pem -passout pass:pass-word 2048

  echo "中間 CSR"
  openssl req -new -key $INTER/private/inter.key.pem -out $INTER/inter.csr.pem -passin pass:pass-word

  echo "中間証明書"
  openssl ca -days 365 -in $INTER/inter.csr.pem -out $INTER/inter.cert.pem -cert $CA/ca.cert.pem -keyfile $CA/private/ca.key.pem -extfile /etc/ssl/san/inter.txt -passin pass:pass-word

  echo "中間証明書のチェック"
  openssl verify -CAfile $CA/ca.cert.pem $INTER/inter.cert.pem

  echo "中間:バンドル"
  cat $INTER/inter.cert.pem $CA/ca.cert.pem > $INTER/inter.bundle.cert.pem

  echo "中間:バンドルチェック"
  openssl verify -CAfile $CA/ca.cert.pem $INTER/inter.bundle.cert.pem

  echo " ||||| CREATE SERVER CERT"
  local SERVER=$P/server
  initFD $SERVER

  echo "サーバ 鍵作成"
  openssl genrsa -aes256 -out $SERVER/private/server.key.pem -passout pass:pass-word 2048
  openssl rsa -in $SERVER/private/server.key.pem -out $SERVER/private/server.key.pem -passin pass:pass-word

  echo "サーバ CSR"
  openssl req -new -key $SERVER/private/server.key.pem -out $SERVER/server.csr.pem -passin pass:pass-word

  echo "サーバ 証明書"
  openssl x509 -req  -days 365 -in $SERVER/server.csr.pem -out $SERVER/server.cert.pem -CA $INTER/inter.bundle.cert.pem -CAkey $INTER/private/inter.key.pem -CAserial $SERVER/serial -extfile /etc/ssl/san/server.txt -passin pass:pass-word

  echo "サーババンドル"
  cat $SERVER/server.cert.pem $INTER/inter.bundle.cert.pem > $SERVER/server.bundle.cert.pem

}

# 関数実行
ca

*注!

無駄なコードが散在します。
certsやnewcertsディレクトリなんて使ってないやん、とかいう箇所がありますが面倒なので放置してます。

では、

中間の外部設定ファイル

/etc/ssl/san/inter.txt

subjectAltName=DNS:inter
basicConstraints=CA:TRUE

サーバの外部設定ファイル

/etc/ssl/san/server.txt

subjectAltName=DNS:kurage, DNS:kurage-service, DNS:wordpress
basicConstraints=CA:FALSE

コマンド周りは、秘密鍵作って、CSR(要求書)発行して、署名するの手順です。
それをルート、中間、サーバとそれぞれ秘密鍵と証明書を作成します。
ルート証明書をブラウザにインストール、サーバの秘密鍵と証明書をサーバ(今回はApache)に設定して使います。

コマンドでちょくちょく出てくるオプション。

-passout pass:pass-word

パスワードを設定します。
このオプションをあらかじめ設定しておくと、コンソールでは求められなくなります。

-passin pass:pass-word

コンソールからパスワードの入力を求められれなくなります。

出力結果

コンソールの出力結果。

 ||||| CREATE ROOT CA CERT
CA 鍵作成
Generating RSA private key, 2048 bit long modulus (2 primes)
........................+++++
...........................................................+++++
e is 65537 (0x010001)
CSR作成
自己証明書
CSR + 自己証明(-x509を設定すると自己署名)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:jp
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:OREORE
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:oreore
Email Address []:
----- ルート証明書表示
-----BEGIN CERTIFICATE-----
  <<秘密だよ>>
-----END CERTIFICATE-----
 ||||| CREATE INTER CHAIN CERT
中間CA 鍵作成
Generating RSA private key, 2048 bit long modulus (2 primes)
.........................................................................................+++++
......................................................................................+++++
e is 65537 (0x010001)
中間 CSR
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:jp
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:INTER
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:inter
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
中間証明書
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Mar 21 02:26:32 2022 GMT
            Not After : Mar 21 02:26:32 2023 GMT
        Subject:
            countryName               = jp
            stateOrProvinceName       = Some-State
            organizationName          = INTER
            commonName                = inter
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:inter
            X509v3 Basic Constraints: 
                CA:TRUE
Certificate is to be certified until Mar 21 02:26:32 2023 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
中間証明書のチェック
/etc/ssl/myca/inter/inter.cert.pem: OK
中間:バンドル
中間:バンドルチェック
/etc/ssl/myca/inter/inter.bundle.cert.pem: OK
 ||||| CREATE SERVER CERT
サーバ 鍵作成
Generating RSA private key, 2048 bit long modulus (2 primes)
.............+++++
..................................+++++
e is 65537 (0x010001)
writing RSA key
サーバ CSR
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:jp
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:SERVER
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:server
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
サーバ 証明書
Signature ok
subject=C = jp, ST = Some-State, O = SERVER, CN = server
Getting CA Private Key
サーババンドル

CSR作成の際注意した点。

Country Name (2 letter code) [AU]:

Organization Name (eg, company) [Internet Widgits Pty Ltd]:

Common Name (e.g. server FQDN or YOUR name) []:

だけ入力しました。

Country Nameはjpに、

他はそれぞれ(Organization Name / Common Name)

ルート: OREORE/oreore
中間: INTER/inter
サーバ: SERVER/server

と入力しました。
ここでの入力は後で意味を知ることになります。

チラ見

 0 s:C = jp, ST = Some-State, O = SERVER, CN = server
   i:C = jp, ST = Some-State, O = INTER, CN = inter
 1 s:C = jp, ST = Some-State, O = INTER, CN = inter
   i:C = jp, ST = Some-State, O = OREORE, CN = oreore
 2 s:C = jp, ST = Some-State, O = OREORE, CN = oreore
   i:C = jp, ST = Some-State, O = OREORE, CN = oreore

Apacheの設定

Apacheの設定ファイル、/etc/apache2/sites-available/default-ssl.confを編集。

SSLCertificateFile      /etc/ssl/myca/server/server.bundle.cert.pem
SSLCertificateKeyFile   /etc/ssl/myca/server/private/server.key.pem

を追加します。

サーバーの証明書と秘密鍵を設定したらApache再起動

sudo service apache2 restart

証明書のインストール

大事なのが証明書のインストール。
今回作成したルート証明書のパスは/etc/ssl/myca/ca/ca.cert.pemです。

証明書作成した側のOS(Ubuntu)で使う場合はその場所を覚えておきます。
別のOSから使うには先にファイルをそのOSにダウンロードしておきます。

出来上がったルート証明書をChromeにインストールします。

Windowsなら設定 -> セキュリティとプライバシー -> セキュリティ -> 証明書の管理 -> インポート の手順でインストールします。
ウィザードが開くので、ここで指示に従ってインストールしますが、重要なのはどこにインストールするかです。

証明書ストアに信頼されたルート証明機関を選びます。

Ubuntuなら設定 -> 詳細設定(スクロールして一番下にある) -> 証明書の管理 -> 認証局 -> インポートの手順でインストールします。

いったんChromeを再起動します!

そしてhttpsにアクセスします。

成功しましたか?

正常に動いているかチェック

SSLが正常に動いているかチェックするには以下のコマンドを使います。
Ubuntuにて、

openssl s_client -connect kurage-service:443

kurage-serviceの部分はドメインで環境に合わせてください。
GoogleとかYahooを覗いてみるといいかもしれません。

もし今回のオレオレ証明書設定だけでは次のエラーが出ます。

Verify return code: 19 (self signed certificate in certificate chain)

成功した場合は

Verify return code: 0 (ok)

とか表示されてます。

これはルート証明書をOS(Ubuntu)にインストールしてないから?
ブラウザにインストールしてもOSにインストールしてなかったらopensslは証明書手に入れようないですよね?

今回はある部分をチェックしたいだけで成功では無くてもいいのでOSにルート証明書をインストールしなくてもいいです。
で、出力結果を見ます。

CONNECTED(00000005)
depth=2 C = jp, ST = Some-State, O = OREORE, CN = oreore
verify return:1
depth=1 C = jp, ST = Some-State, O = INTER, CN = inter
verify return:1
depth=0 C = jp, ST = Some-State, O = SERVER, CN = server
verify return:1
---
Certificate chain
 0 s:C = jp, ST = Some-State, O = SERVER, CN = server
   i:C = jp, ST = Some-State, O = INTER, CN = inter
 1 s:C = jp, ST = Some-State, O = INTER, CN = inter
   i:C = jp, ST = Some-State, O = OREORE, CN = oreore
 2 s:C = jp, ST = Some-State, O = OREORE, CN = oreore
   i:C = jp, ST = Some-State, O = OREORE, CN = oreore
---
Server certificate
-----BEGIN CERTIFICATE-----
  <<秘密だよ!>>
-----END CERTIFICATE-----
subject=C = jp, ST = Some-State, O = SERVER, CN = server

issuer=C = jp, ST = Some-State, O = INTER, CN = inter

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3105 bytes and written 396 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: A5905C77592570908C9F22E17B1D7EC4049DA3EE0764F04AAE0AF86008A64345
    Session-ID-ctx: 
    Resumption PSK: 50A456AE608975B1F3C95D8C91F0466334211AA6C29E7CA8F9F227FA553586F5C95E61139D9EE7807D58B147735789F2
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    <<秘密だよ>>

    Start Time: 1647834600
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 42D7827B4E80C743CA5FF251D70EDA671D614B2235654FFE5843AA6DFE51418E
    Session-ID-ctx: 
    Resumption PSK: 83E700E4AD0503C24164386EF7E9443653A0A0E3C6626C3512E6864F2BA53AD97BD8C8F4DD1737DCD436BB5871064850
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    <<秘密だよ>

    Start Time: 1647834600
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
closed

注目点その1

depth=2 C = jp, ST = Some-State, O = OREORE, CN = oreore
verify return:1
depth=1 C = jp, ST = Some-State, O = INTER, CN = inter
verify return:1
depth=0 C = jp, ST = Some-State, O = SERVER, CN = server
verify return:1

depth(階層)を見ると、0=サーバ, 1=中間, 2=ルート になってることが確認出来ます。

注目点その2

Certificate chain
 0 s:C = jp, ST = Some-State, O = SERVER, CN = server
   i:C = jp, ST = Some-State, O = INTER, CN = inter
 1 s:C = jp, ST = Some-State, O = INTER, CN = inter
   i:C = jp, ST = Some-State, O = OREORE, CN = oreore
 2 s:C = jp, ST = Some-State, O = OREORE, CN = oreore
   i:C = jp, ST = Some-State, O = OREORE, CN = oreore

sがSubject(主体者), iがIssuer(発行者)で、serverはinterに、interはoreoreに、そしてoreoreはoreoreに発行されてることが確認できます。
oreoreがoreoreに、自己署名ですね。

失敗しているとこの辺がおかしいことになってます。

OS(Ubuntu)にインストールする場合

https://qiita.com/msi/items/9cb90271836386dafce3

もうここ見た方が早いです。

certificate Docker Gutenberg Hyper-V openssl PHP React ReduxToolkit REST ubuntu WordPress オレオレ認証局 フレームワーク