タグ: openSSl

ChatGPTによるシェルスクリプト(SSL証明書の更新)

Let's Encryptで更新しているワイルドカード証明書。

他のサーバにも適用するのを更に簡便にするため、自動化するスクリプトを出力しました。

動作を確認した環境

  • Ubuntu 20.o4
  • Apache2.4

要件

1. 証明書と鍵ペアがあるディレクトリを引数にしてコマンドを実行
 → ファイルがなければエラーを返す
 → ファイルが次の形式でなくてもエラーを返す
 ・hoge.example.com.crt.$(date +%Y%m) ← hoge.example.com.crtの部分を変数化
 ・hoge.example.com.key.$(date +%Y%m) ← hoge.example.com.keyの部分を変数化
2. 引数内のディレクトリのcrt.$(date +%Y%m)を対象に、以下のコマンドを実行する
  2.1 次のハッシュ値をそれぞれ参照し合っていれば後続
  openssl x509 -pubkey -in hoge.example.com.crt.$(date +%Y%m) -noout | openssl md5
  openssl pkey -pubout -in hoge.example.com.key.$(date +%Y%m)  | openssl md5
  2.2 次のハッシュ値をそれぞれ参照し合っていれば後続
  openssl x509 -issuer_hash -noout -in hoge.example.com.crt.$(date +%Y%m)
  sed -n -e'1d' -e'/BEGIN/,$p' hoge.example.com.crt.$(date +%Y%m) | openssl x509 -subject_hash -noout
3. openssl x509 -noout -dates -subject -in hoge.example.com.crt.$(date +%Y%m)
 のnotAfterを参照し、
 「ドメイン『hoge.example.com』有効期限:notAfterで出力した値 で更新します。よろしいですか?」
 を出力。y/nで後続判断
4. 整合性を確認したファイルを以下のディレクトリにコピー
 ・hoge.example.com.crt.$(date +%Y%m) → /etc/certs
 ・hoge.example.com.key.$(date +%Y%m) → /etc/private
 コピー完了の出力をする
5. 証明書のシンボリックリンクを貼り替える
  5.1 cd /etc/certs
  5.2 sudo ln -sf hoge.sample.com.crt.$(date +%Y%m) hoge.sample.com.crt
  5.3 1のコピーとリンクしたファイルのハッシュ値が同じであることを確認
6. 秘密鍵のシンボリックリンクを貼り替える
  6.1 cd /etc/private
  6.2 sudo ln -sf hoge.sample.com.key.$(date +%Y%m) hoge.sample.com.key
  6.3 1のコピーとリンクしたファイルのハッシュ値が同じであることを確認
7. 「Webサービスを再起動しますか?」y/n
 → yの場合は systemctl restart apache2.serviceして後続
 → nの場合はスクリプトを抜ける
8. 「証明書更新が完了しました」のメッセージを出してスクリプト終了

結果

出て来たのはこちら。

ssl_update.sh

#!/bin/bash

# 変更するドメイン
domain="hoge.example.com"

# OpenSSLのパス
openssl_path="/usr/local/ssl/bin/openssl"

# 証明書ファイルの格納位置
cert_dir="/etc/certs"

# 秘密鍵ファイルの格納位置
private_dir="/etc/private"

# 引数の数を確認
if [ "$#" -ne 1 ]; then
  echo "Usage: $0 <directory>"
  exit 1
fi

# ディレクトリが存在するか確認
directory="$1"
if [ ! -d "$directory" ]; then
  echo "Error: Directory not found"
  exit 1
fi

# 証明書と鍵のファイルパスを設定
cert_file="$directory/$domain.crt.$(date +%Y%m)"
key_file="$directory/$domain.key.$(date +%Y%m)"

# ファイルの存在を確認
if [ ! -e "$cert_file" ] || [ ! -e "$key_file" ]; then
  echo "Error: Certificate or key file not found"
  exit 1
fi

# ハッシュの確認
cert_hash=$("$openssl_path" x509 -pubkey -in "$cert_file" -noout | "$openssl_path" md5)
key_hash=$("$openssl_path" pkey -pubout -in "$key_file" | "$openssl_path" md5)

if [ "$cert_hash" != "$key_hash" ]; then
  echo "Error: Certificate and key do not match"
  exit 1
fi

issuer_hash=$("$openssl_path" x509 -issuer_hash -noout -in "$cert_file")
subject_hash=$(sed -n -e '1d' -e '/BEGIN/,$p' "$cert_file" | "$openssl_path" x509 -subject_hash -noout)

if [ "$issuer_hash" != "$subject_hash" ]; then
  echo "Error: Issuer hash and subject hash do not match"
  exit 1
fi

# 有効期限の確認
expiration_date=$("$openssl_path" x509 -noout -dates -subject -in "$cert_file" | grep "notAfter" | cut -d "=" -f 2)

echo "ドメイン『$domain』有効期限:$expiration_date で更新します。よろしいですか? (y/n)"
read response

if [ "$response" != "y" ]; then
  echo "証明書更新がキャンセルされました"
  exit 1
fi

# ファイルをコピー
sudo cp "$cert_file" "$cert_dir"
sudo cp "$key_file" "$private_dir"

# シンボリックリンクを貼り替え
cd "$cert_dir"
sudo ln -sf "$(basename $cert_file)" "$domain.crt"
cd "$private_dir"
sudo ln -sf "$(basename $key_file)" "$domain.key"

# リンクとコピーのハッシュが一致しているか確認
if [ "$("$openssl_path" x509 -pubkey -in "$cert_dir/$domain.crt" -noout | "$openssl_path" md5)" != "$cert_hash" ]; then
  echo "Error: Certificate hash mismatch after linking"
  exit 1
fi

if [ "$("$openssl_path" pkey -pubout -in "$private_dir/$domain.key" | "$openssl_path" md5)" != "$key_hash" ]; then
  echo "Error: Key hash mismatch after linking"
  exit 1
fi

echo "Webサービスを再起動しますか? (y/n)"
read restart_response

if [ "$restart_response" == "y" ]; then
  sudo systemctl restart apache2.service
  echo "Webサービスが再起動しました"
fi

echo "証明書更新が完了しました"
exit 0

作成後、

sudo chmod 744 ssl_update.sh

使う前の準備

  1. 変数を適度に修正します。
  2. 任意のディレクトリにドメインの条件に沿った証明書を格納します。

使い方

sudo bash ssl_update.sh [証明書を格納したディレクトリへのパス]

この後、各種を確認して

証明書更新が完了しました

まで出してくれます。

Ubuntu20.04のOpenSSLを1.1.1から3.1.1にアップデート。

概要

2023/09/11にサポート終了を迎えるOpenSSL1.1.1。

2023年6月現在の最新安定版である3.1.1にアップデートを行います。

https://www.openssl.org/blog/blog/2023/06/15/1.1.1-EOL-Reminder/

環境

  • OS:Ubuntu 20.04
openssl version -a
OpenSSL 1.1.1f  31 Mar 2020
built on: Wed May 24 17:14:51 2023 UTC
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(int) blowfish(ptr) 
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-mSG92N/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1"
Seeding source: os-specific

参考とした手順

さっくりとした手順

  1. システム全体のバックアップ
  2. 必要なライブラリをインストールします。
  3. githubレポジトリから最新安定版のソースコードをダウンロードします。
  4. ソースからインストールしていきます。
  5. 設定を行います。(コンフィグを反映させ、パスを通します)
  6. バージョンアップを確認します。

実施した手順

全体のバックアップを取得します。

  • Webアクセスの根幹となるプログラムであること
  • 重要なデータが格納されている

ことから、AWS Lightsailのスナップショットを利用して全体のバックアップを取りました。

必要なライブラリのインストール

sudo aptitude install build-essential checkinstall zlib1g-dev
# 筆者はaptitudeを用いています。必要に応じてaptを使ってください。

ソースコードの取得

sudo su -
# 以下、管理者権限で実施します

cd /hoge
# 任意のディレクトリを指定します

git clone https://github.com/openssl/openssl -b openssl-3.1.1
# 2023/06/20時点での最新安定版を指定します

cd openssl

ソースからインストール

./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib

make
# makeは時間がかかります。状況を時折確認しながら待ちましょう。

make test

make install

インストール後の設定

  • 設定ファイル追記
cat <<- __EOF__ | tee -a /etc/ld.so.conf.d/openssl-3.1.1.conf
/usr/local/ssl/lib64
__EOF__
  • 設定反映
ldconfig -v
  • 既存プログラムの退避
mv /usr/bin/c_rehash /path/to/backup/c_rehash.$(date +%Y%m%d)

mv /usr/bin/openssl /path/to/backup/openssl.$(date +%Y%m%d)
# 任意の退避ディレクトリを指定します
  • パスを通す
cat <<- __EOF__ | tee -a /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/ssl/bin"
__EOF__
  • 通したパスを反映
source /etc/environment

echo $PATH
# PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/ssl/bin"
# と表示されることを確認します

バージョンアップ後の確認

openssl version -a
OpenSSL 3.1.1 30 May 2023 (Library: OpenSSL 3.1.1 30 May 2023)
built on: Tue Jun 20 01:47:24 2023 UTC
platform: linux-x86_64
options:  bn(64,64)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -O3 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DZLIB -DNDEBUG
OPENSSLDIR: "/usr/local/ssl"
ENGINESDIR: "/usr/local/ssl/lib64/engines-3"
MODULESDIR: "/usr/local/ssl/lib64/ossl-modules"
Seeding source: os-specific
CPUINFO: OPENSSL_ia32cap=0x7ffaf3ffffebffff:0x27ab

これで、Ubuntu20.04でもOpenSSL3.1.1を利用することが可能になりました。

必要に応じて

  • システムの再起動を行います。
  • 既存サービスが正常に動くことを確認します。

作業日

2023/06/22

Let’s Encryptの証明書整合でハマったこと。(ECDSA方式での整合性確認)

概要

更新サイクルが3ヶ月と短いものの、無料で利用できるということで愛用しているLet's Encrypt。
デフォルトの暗号化形式がRSAからECDSA方式に変わったようで確認方法でハマりました。

なんとか解決したのでメモを残します。

従来の確認方法

RSA方式での証明書は、以下の

openssl x509 -in /etc/certs/hoge.example.com.crt -noout -modulus | md5sum
# SSL証明書ファイル

openssl rsa -in /etc/private/hoge.example.com.key -noout -modulus | md5sum
# 秘密鍵ファイル

を実行し、それぞれのハッシュ値が合致していることで証明書と秘密鍵の整合性の確認を取ることができています。

ECDSA方式に変わったことでの問題

2023年4月時点で、Let's Encryptのデフォルト暗号化形式がRSA→ECDSA方式に変わっていました。

openssl rsa -in /etc/private/hoge.example.com.key -noout -modulus | md5sum
# 秘密鍵ファイル

を実行すると

139945929635648:error:0607907F:digital envelope routines:EVP_PKEY_get0_RSA:expecting an rsa key:crypto/evp/p_lib.c:469:

といったエラーが出ます。

これでは証明書と秘密鍵の整合性が確認できません。

しばらくGoogleと格闘し、なんとか解決策を見つけました。

https://security.stackexchange.com/questions/73127/how-can-you-check-if-a-private-key-and-certificate-match-in-openssl-with-ecdsa

ECDSA方式でも証明書と秘密鍵の整合性が確認できるコマンド

まず、鍵がECDSA方式であることを確認。

  • 確認コマンド
openssl ec -in /etc/private/hoge.example.com.key -text -noout
# 秘密鍵のパスを指定します
  • 確認結果
(略)
ASN1 OID: prime256v1
NIST CURVE: P-256
# 上記が表示されればECDSA方式であると確認できます。

次に、以下のようにして公開鍵を抽出してハッシュ値を割り出します。

openssl x509 -pubkey -in /etc/certs/hoge.example.com.crt -noout | openssl md5
(stdin)= ハッシュ値
# SSL証明書ファイル

openssl pkey -pubout -in /etc/private/hoge.example.com.key | openssl md5
(stdin)= ハッシュ値
# 秘密鍵ファイル

### 2つのハッシュ値が合っていれば証明書と秘密鍵の整合性は取れています

以上、ECDSA方式でも整合性を確認することができました。

余談

この、「鍵を用いて鍵を取り出す」って、『ライザのアトリエ3』で無垢の鍵から秘密の鍵を抽出しているようだなと益体もないことが脳裏をよぎりました。

Powered by WordPress & Theme by Anders Norén