LoginSignup
1
0

More than 1 year has passed since last update.

JS7® JobScheduler https接続用KeyStore、TrustStore準備

Last updated at Posted at 2022-02-18

概要

JS7 各コンポーネント間の通信を https で行う際に必要となるKeyStore、TrustStore の作成方法についてのメモ。
2パターン書きますが、どちらでも通信できることは確認済みです。

基本は手段1のRoot CA + サーバー毎の証明書で対応。
ただし各ホストの宛先をドメイン名で指定可能ならば手段2がお手軽。

動作確認環境

JS7 JobScheduler 2.5.3
OpenSSL 1.0.2k-fips
openjdk 11.0.18 2023-01-17 LTS

手段1 Root CA のTrustStore、サーバー毎のKeyStoreを生成

各サーバーの証明書は、Root CA が発行する。
またSANに、宛先として指定する値(ホスト名、ドメイン名、IPアドレス)を登録。
Root CA の証明書をTrustStoreに格納することで、各サーバー証明書のチェックが可能となる。

Root CA 証明書を管理しておけば、後から追加でサーバー証明書を作成することも可能。

Root CA 証明書(自己署名)作成

各証明書発行用のRoot CA 証明書を自己署名で作成
あわせて TrustStore用に、PKCS#12形式証明書を作成
※要keytoolコマンド (openjdkインストールで利用可)

cn="JS7 Root CA"
company="Saitamano KUSA Inc."
country="JP"
crtfile="js7_ca.crt"
keyfile="js7_ca.key"
truststore="https-truststore.p12"
p12pass="jobscheduler"
p12alias="$cn"

subject_text="/C=${country}/O=${company}/CN=${cn}"
# ※最小はCNのみ
subject_text="/CN=${cn}"

# 自己署名証明書、秘密鍵を作成
openssl req -new -x509 -days 3650 -newkey rsa:2048 -nodes -out $crtfile -keyout $keyfile -subj "$subject_text"

# 上記証明書より、TrustStore用にPKCS#12形式証明書を作成  ※要openjdk
[ -f $truststore ] && rm $truststore
keytool -importcert -noprompt -file $crtfile -alias "$p12alias" -keystore $truststore -storepass $p12pass -storetype PKCS12 -trustcacerts

個々のサーバー証明書を作成

各サーバー用の証明書を発行。認証用にSANを追加する。
あわせてKeyStore用に、PKCS#12形式証明書を作成

#!/usr/bin/env bash

gen_keystore() {
    # SAN 対応のPEM形式証明書を作成し、CA証明書で署名。
    # あわせてKeyStore用のPKCS#12形式証明書を作成
    # $1 でCommon Name を指定(ホスト名、ドメイン名)
    # --domain , --ip でSANに登録する値を指定可能

    declare -a ip_ranges=()
    declare ip_range
    declare -a domain_names=()
    declare domain_name
    declare -a san_values=()
    declare san_value
    declare common_name
    declare subject_text

    declare keyfile
    declare csrfile
    declare crtfile
    declare keystore
    declare p12alias
    declare p12pass="jobscheduler"
    # あらかじめ用意しておいた Root CA 証明書、秘密鍵
    declare ca_keyfile="js7_ca.key"
    declare ca_crtfile="js7_ca.crt"

    options=$(getopt --options i:d:h \
        --longoptions ip:,domain:,help \
        -- "$@") || return 1
    eval set -- "$options"

    while true; do
        case "$1" in
        -i | --ip)
            [ "${2:0:1}" == "-" ] || [ ! "$2" ] && { echo "ip が指定されていません" >&2 && return 1; }
            ip_ranges+=("$2")
            shift 2
            ;;
        -d | --domain)
            [ "${2:0:1}" == "-" ] || [ ! "$2" ] && { echo "domain が指定されていません" >&2 && return 1; }
            domain_names+=("$2")
            shift 2
            ;;
        -h | --help)
            # usage (省略)
            exit
            ;;
        --)
            shift
            break
            ;;
        *)
            echo Error
            return 1
            ;;
        esac
    done

    shift $((OPTIND - 1))

    common_name=$1
    if echo "$common_name" | grep -E '^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.?)+([a-zA-Z]{2,})?$' >/dev/null; then
        subject_text="/CN=${common_name}"
        san_values+=("DNS:${common_name}")
    else
        echo "CNの指定に問題あり : ${common_name}"
        return 1
    fi

    # 各種出力ファイルはCN名ベースとする
    keyfile="${common_name}.key"
    csrfile="${common_name}.csr"
    crtfile="${common_name}.crt"
    keystore="${common_name}-keystore.p12"
    p12alias="${common_name}"

    # SNI対応用にSAN値を生成
    for domain_name in "${domain_names[@]}"; do
        if echo "$domain_name" | grep -E '^(\*\.)?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.?)+([a-zA-Z]{2,})?$' >/dev/null; then
            [ "$domain_name" != "$common_name" ] && san_values+=("DNS:${domain_name}")
        else
            echo "ドメイン名またはホスト名ではありません : ${domain_name}"
            return 1
        fi
    done

    for ip_range in "${ip_ranges[@]}"; do
        if echo "$ip_range" | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$' >/dev/null; then
            san_values+=("IP:${ip_range}")
        else
            echo "CIDR表記ではありません : ${ip_range}"
            return 1
        fi
    done

    san_value=$(
        IFS=","
        echo "${san_values[*]}"
    )
    san_value="subjectAltName = $san_value"

    # 秘密鍵、CSR生成
    openssl req -new -newkey rsa:2048 -nodes -out "$csrfile" -keyout "$keyfile" -subj "$subject_text"

    # CSRより証明書をPEM形式で作成、CA証明書で署名  あわせてSANも追加する
    openssl x509 -req -in "$csrfile" -out "$crtfile" -days 3650 -CA $ca_crtfile -CAkey $ca_keyfile -CAcreateserial -extfile <(echo "$san_value")

    # 上記証明書と秘密鍵を使ってPKCS#12 形式で出力(KeyStore用)
    openssl pkcs12 -export -inkey "$keyfile" -in "$crtfile" -out "$keystore" -name "$p12alias" -password "pass:$p12pass"

}

# 証明書作成  --domain, --ip でSANに登録する値を指定
gen_keystore js7joc        --domain "*.local" --domain "*.mshome.net" --domain "localhost" --ip "192.168.11.101" --ip "127.0.0.1"
gen_keystore js7controller --domain "*.local" --domain "*.mshome.net" --domain "localhost" --ip "192.168.11.102" --ip "127.0.0.1"
gen_keystore js7agent      --domain "*.local" --domain "*.mshome.net" --domain "localhost" --ip "192.168.11.103" --ip "127.0.0.1"

これにより作られた https-truststore.p12をTrustStoreとして、
js7joc-keystore.p12 js7controller-keystore.p12 js7agent-keystore.p12 を各サーバーのKeyStoreとして使用する。

この手段を用いた場合は、Root CA の証明書、秘密鍵を適切に管理してください。

手段2 各サーバー共通のKeyStore、TrustStoreを生成

TrustStore、KeyStoreを、共通の証明書ファイルとするやり方

SANの登録値にワイルドカードドメイン名を指定。(alt_names に記載)
※controller, agent 登録時のURLは、このドメイン名で指定する必要あり
(IP指定は非推奨。IPが変更となった場合に、証明書の作り直し、全差し替えが発生する)

内部DNSやmDNS利用で、同一ドメインで名前解決ができるならば、1つの証明書のみで、お手軽に済ませられる。

下記のコマンド例は、以下の環境で動作確認済みです
openssl 1.1.1 および 3.0
openjdk 11 および 17

cn="JobScheduler"
crtfile="https-keystore.crt"
keyfile="https-keystore.key"
keystore="https-keystore.p12"
truststore="https-truststore.p12"
p12pass="jobscheduler"
p12alias="$cn"


# 自己署名でSANに vault-cluster を含めた証明書を生成
openssl req -x509 -days 3650 -nodes -newkey rsa:2048 -out "$crtfile" -keyout "$keyfile" -config - <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = $cn

[v3_req]
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
DNS.2 = *.local
DNS.3 = *.mshome.net
IP.1 = 127.0.0.1
EOF

# 上記証明書と秘密鍵を使ってPKCS#12 形式で出力(KeyStore用)
openssl pkcs12 -export -inkey "$keyfile" -in "$crtfile" -out "$keystore" -name "$p12alias" -password "pass:$p12pass"
# 上記証明書を使ってPKCS#12 形式で出力(TrustStore用)
keytool -importcert -noprompt -file $crtfile -alias "$p12alias" -keystore $truststore -storepass $p12pass -storetype PKCS12 -trustcacerts

これにより作られた https-keystore.p12をKeyStore https-truststore.p12をTrustStoreとして使用する。
(上記の例では、宛先に localhost、xxx.local、 xxx.mshome.net、127.0.0.1 のいずれかの指定ならば対応可能)

用語

KeyStore

証明書+秘密鍵を PKCS#12形式で保存したもの
自サーバーの情報を表すために使用

TrustStore

発行者の証明書を PKCS#12形式で保存したもの
証明書認証時に、接続相手サーバーの証明書が信頼できることを確認するために使用

SAN(Subject Alternative Name)

CommonNameの別名として、自サーバーの情報を表す。
ドメイン、ホスト名、IPアドレスなどの情報を格納できる。
JOC、Controller、Agentとの接続時に、接続先URLとして指定した値(IP、ホスト名など)と、接続先サーバー証明書上のSANに記載された情報が一致する必要がある。

本例では、SANにホスト名、ドメイン名(ワイルドカード使用可)、IPアドレスを指定する

※接続先がSANに含まれていない場合、認証エラーとなる。
※接続時に常にドメイン名を使う場合は、CommonNameにワイルドカード付ドメイン名などを指定することで、SANは指定しなくても接続可能。ただしこの場合、IP指定での接続はできない

keytool

証明書の生成、管理を行うコマンド
TrustStore 生成時に必須 ( openssl コマンドでは不可 )
OpenJDK インストールで利用可能
※JS7 インストール環境であれば、OpenJDK が入っている

KeyStore Explorer

https://keystore-explorer.org/
GUIでの証明書管理が可能。
KeyStore 、TrustStore の内容を確認する際に、インストールしてあると便利。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0