SSL/TLSでDICOM通信を体験しよう！

# はじめに

研究でDICOMデータを保存・管理するために、研究用のDICOMサーバを構築して利用している。
研究用サーバということで、ローカル環境での利用のみであったこともあり、SSL/TLSは活用してこなかった。
そろそろ、セキュアに通信することにも気を配ってみようと思い、取り組んだことをメモする。

# 実験環境

- windows10/11

# 必要なもの

- Java-JDK 8 current version
- dcm4che 5.29.0
- [openssl 3.0.5](https://slproweb.com/products/Win32OpenSSL.html)
- [サンプルDICOMデータ](https://www.jira-net.or.jp/dicom/dicom_data_01_02.html) : なんでも大丈夫。はじめは圧縮されていないimplicit little endian形式がお勧め）

# パスを通す

以下のディレクトリパスまで環境変数パスを通す。

- jdkフォルダ/bin
- dcm4cheフォルダ/bin
- opensslフォルダ/bin

パスは、JAVA_HOME, DCM4CHE_HOMEを通して、そのあとに、各binディレクトリへのパスを通す。

- jdk-versionディレクトリまでを、JAVA_HOME、
- dcm4cheディレクトリまでを、DCM4CHE_HOME

として、%JAVA_HOME%\bin;%DCM4CHE_HOME%\bin;をPathに追加。

# SSL/TLS

Wikipedia (2022/10/25 access)より

"Transport Layer Security（トランスポート・レイヤー・セキュリティ、TLS）は、インターネットなどのコンピュータネットワークにおいてセキュリティを要求される通信を行うためのプロトコルである。主な機能として、通信相手の認証、通信内容の暗号化、改竄の検出を提供する。TLSはIETFによって策定された。

当プロトコルは（特に区別する場合を除いて）SSL (Secure Sockets Layer) と呼ばれることも多い。これは、TLSの元になったプロトコルがSSLであり、そのSSLという名称が広く普及していることによる。

2022年現在の最新版はTLS 1.3である。"

# dcm4cheで利用できるtlsプロトコル
- tls-protocol SSLv2Hello 一般的には、非推奨
- tls-protocol SSLv3 一般的には、非推奨
- tls-protocol TLSv1
- tls-protocol TLSv1.1
- tls-protocol TLSv1.2 現状の推奨

# SSL/TLSに必要なファイル

SSL/TLSを実現するためには、一般的に、署名済みキーファイルが必要。
これらは、次のような流れの中で利用される。

※キーの利用の流れを簡単に
１．Aが秘密鍵から公開鍵を作成しBに渡す
２．Bが、その公開鍵を使って通信内容を暗号化する
３．暗号化された文書をAが受け取る
４．Aが秘密鍵を用いて復号し中身を確認する

ただし、今回はDICOM通信をDCM4CHEでテストするために、次の２つのファイルを作成する。

- 署名済みキーファイル（key）
- 認証ファイル（trusted certificate）

先に、DCM4CHEデフォルトのこれらのファイルを確認する。
dcm4cheライブラリ/etc/certsに、キーと認証ファイルが保管されている。
デフォルトでは、javaの仕様に合わせて、両方ともp12拡張子のファイルにまとめられている。

【dcm4che key.p12の内容】
```
C:\Users\tatsu\Desktop>keytool -list -keystore ./certs/key.p12 -storepass secret
キーストアのタイプ: PKCS12
キーストア・プロバイダ: SUN

キーストアには1エントリが含まれます

dcm4che,2022/10/26, PrivateKeyEntry,
証明書のフィンガプリント(SHA-256): BD:60:1C:19:D4:ED:87:18:B3:EC:F6:53:52:91:00:C8:A2:70:21:0F:04:87:E6:B7:ED:15:23:A7:97:D8:28:AC

Warning:
<dcm4che> #1 / 2 uses a 1024ビットRSA鍵 which is considered a security risk. This key size will be disabled in a future update.
```
（最近では2048bitがデフォルトなので、短いよと注意されている）

【dcm4che cacerts.p12の内容】
```
C:\Users\tatsu\Desktop>keytool -list -keystore ./certs/cacerts.p12 -storepass secret
キーストアのタイプ: PKCS12
キーストア・プロバイダ: SUN

キーストアには1エントリが含まれます

mykey,2022/10/26, trustedCertEntry,
証明書のフィンガプリント(SHA-256): 21:EB:CA:86:4A:08:E9:A2:D2:1F:6E:84:37:8D:60:BB:14:92:4D:1B:B0:DD:B0:DC:75:03:0C:2E:F3:B2:6E:DD
```

ここでは、これらのファイルを自作のファイルに置き換えてみる。
自己署名による各ファイルの作成方法をメモする。
[実は、認証ファイルは、PKCS12形式で扱うことが非推奨になっている？](https://stackoverflow.com/questions/42766935/creating-p12-truststore-with-openssl)

TLSプロトコルには、対応できる暗号スイートが決まっている。
さらに、各暗号スイートは、TLSプロトコルとの相性がある。

dcm4cheで利用できる暗号スイート（tls-cipher）、
- SSL_RSA_WITH_NULL_SHA //SSL v3.0
- TLS_RSA_WITH_AES_128_CBC_SHA //TLS1.0
- SSL_RSA_WITH_3DES_EDE_CBC_SHA //SSL v3.0

これらの暗号スイートとプロトコルとの対応は、[ここで確認](https://www.openssl.org/docs/man1.0.2/man1/ciphers.html)
あまり難しいことをしなければ、TLS1.2を使っておけばOK。

dcm4qrscpは、--tlsオプションで上記3つのデフォルトの暗号スイートを設定できる。
複数の暗号スイートがサーバーに登録されていれば、SSL/TLSハンドシェイクの際に、サーバー側と送信側とで一致する暗号スイートが自動で選択される。

ここでは、AESの方式である、TLS_RSA_WITH_AES_128_CBC_SHAの暗号スイートを使う前提で進める。
（DESや3DESは古いアルゴリズムであり、推奨されない。）

# Method1:全部作ってみる 

秘密鍵をRSA方式で作成
```
>openssl genrsa -aes128 -out tutorial.key 2048
```
- openssl:openssl コマンド
- genrsa :RSA秘密鍵作成コマンド
- out :出力先指定
- 2048 :keyの長さ。2048以上が近年（2022年）の推奨
- pass phrase :secretにしてみた。

（補足:必須ではない）秘密鍵から公開鍵を作成
```
>openssl rsa -in tutorial.key -pubout -out tutorial_pub.key
```

秘密鍵を使ってCSR（Certificate Signing Request）を作成。（SSL/TLS サーバー証明書を発行するための証明書署名要求）
```
>openssl req -new -key tutorial.key -out tutorial.csr

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Tokyo
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Visionary Imaging Services, Inc.
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:実可動するサーバーのドメイン。ブランクにしておく。（そのままEnter）
Email Address []:（そのままEnter）

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:（そのままEnter）
An optional company name []:（そのままEnter）
```

CSRの中身を検証する（スキップしない）
```
>openssl req -text -in tutorial.csr -noout -verify
```

自身のOpenSSLを認証局として、CSRから自己署名証明書（CRT）を作成
```
>openssl x509 -in tutorial.csr -out tutorial.crt -req -signkey tutorial.key -days 36500
```
- signkey :自己署名オプション
- days :証明書有効期限

（補足：必須ではない）crtをPEM形式に変換
```
>openssl x509 -in tutorial.crt -out tutorial.pem -outform PEM
```

ここまでで、以下のファイルができる。
- tutorial.key
- tutorial.csr
- tutorial.crt
- (必須でない) tutorial.pem
- (必須でない) tutorial_pub.key

秘密鍵と証明書から、key.p12を作成
```
>openssl pkcs12 -export -in tutorial.crt -inkey tutorial.key -out mykey.p12
Enter Export Password :（この例では、secret）
Verifying - Enter Export Password :（この例では、secret
```

証明書からcacerts.p12を作成
DCM4CHEは、証明書をp12で扱うため、特殊な操作が必要。
JDKのkeytoolコマンドで操作する。
```
>keytool -importcert -storetype PKCS12 -keystore truststore.p12 -storepass secret -alias ca -file tutorial.crt -noprompt
```
（補足：NGな方法。PrivateKeyEntryになる）>openssl pkcs12 -export -nokeys -in tutorial.crt -out mycacert2.p12

これを行うことで、-keystoreで指定したファイル場所にp12形式の認証ファイルが作成され、自身のPCに、-fileで指定したCRT形式の認証ファイルが、-aliasで指定したニックネームで登録される。

## TEST

サーバーとユーザーとに、同じキーと認証ファイルを指定する。
(TLS12がデフォルトかつ推奨)

サーバー側
```
>dcmqrscp -b DCMQRSCP:11112 --dicomdir ./tls-test/DICOMDIR --key-pass secret --key-store mykey.p12 --key-store-pass secret --trust-store truststore.p12 --trust-store-pass secret --tls --tls12
```

ユーザー側
```
>"C:\Program Files\dcm4che-5.29.0\bin\storescu" -c DCMQRSCP@localhost:11112 C:\Users\tatsu\Desktop\sample --tls12 --tls --key-pass secret --key-store mykey.p12 --key-store-pass secret --trust-store truststore.p12 --trust-store-pass secret
```

## 経験的なメモ
「--tls13」プロトコルオプションは利用不可。
総じて、--tls-aes暗号化と、tls12プロトコルを使うとよさそう。
「--tls」としておけば、自動でプロトコルを選んでくれる（AESがデフォルト）
--tls-3desオプションを使うとエラーになる。これはデフォルトのキーがAESだからだろう。

# Method2:dcm4cheデフォルトの秘密鍵のパスワードを変更する

dcm4cheをデフォルト設定で使っていれば、dcm4che同士であれば通信できてしまう。
秘密鍵のパスワードを変更して、デフォルトのdcm4cheで通信できなくすることでセキュアにできる。
認証ファイルは変更する必要なし。（のはず）

dcm4che/ext/certs内にあるキー（pem）と認証ファイル(pem)を使って、p12ファイルを再作成する。
```
>openssl pkcs12 -export -in ./certs/cert.pem -inkey ./certs/key.pem -out mykey.p12
Warning: -chain option ignored with -nocerts
Enter Export Password:（ここでパスワードフレーズを設定, secret2）
Verifying - Enter Export Password:（ここでパスワードフレーズを確認, secret2）
```

## TEST
サーバー側
（trust-storeはデフォルトのままでよい。キーファイルとキーのパスワードを変更したものにあわせる）
```
>dcmqrscp -b DCMQRSCP:11112 --dicomdir ./tls-test/DICOMDIR --tls --key-pass secret2 --key-store mykey.p12 --key-store-pass secret2
```
ユーザー側
```
>"C:\Program Files\dcm4che-5.29.0\bin\storescu" -c DCMQRSCP@localhost:11112 C:\Users\tatsu\Desktop\sample --tls --key-pass secret2 --key-store mykey.p12 --key-store-pass secret2
```

# リファレンス
- https://qiita.com/kunichiko/items/3e2ec27928a95630a73a
- https://www.baeldung.com/openssl-self-signed-cert
- https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/#creating-ca-signed-certificates
- https://go-journey.club/archives/4038

2022/10/26 初稿