リモートデスクトップサービスに割り当てた正しい SSL の更新・新規割当て方

**※ 本ドキュメントは一般公開用に一部値を置き換えたものです。**

リモートデスクトップを何も気にせずに設定するといわゆるオレオレ証明書と呼ばれる認証局の認証のない自己署名証明書が使われるため、リモートデスクトップに接続するとこのサーバはほんとに接続していいサーバーですか？というような内容の警告ダイアログが出ます（無視することもできます）。それを正しい正規の認証局の認証のあるSSLを設定・更新する方法です。

作業環境・前提は以下のとおりです

-   Windows 11 Pro。IISがない環境を想定しています。
-   IT 中級者以上向け。レジストリを直接操作したり Powershell を使ったりするためです。
-   管理者権限必須。PowerShell はすべて管理者権限で実行してください。
-   事前にサーバー証明書、中間証明書、秘密鍵が取得済みであること。さくらのサーバ等で取得したサーバー用 SSL が使えます。
-   リモートデスクトップ経由ではなく、ローカルマシンで直接作業してください。リモートデスクトップ経由の場合、予期せぬ接続切断により作業ができなくなり最悪OSのインストールからやり直す必要の可能性がありえます。すなわち**仮想マシン上の作業の場合**、そのリスクと常に背中合わせな危険な作業です（何度やらかしたことやら）。

## 秘密鍵・中間証明書・サーバ証明書を一つにした pfx を生成する

サーバー証明書の取得は前提として、以下の手順で pfx ファイルを生成します。

-   サーバー証明書: `server.crt`
-   中間証明書: `ca.crt`
-   秘密鍵: `private.key`

SHA256 を使用して pfx ファイルを生成します。SHA1 はすでに脆弱性が指摘されているため、使用しないでください。

```Powershell
openssl pkcs12 -export -in server.crt -inkey private.key -certfile ca.crt -out server.pfx -aes256 -macalg SHA256
```
opensslがインストールされていない場合はこちらを参考にしてください。参考: [Win32 opensslのインストール](https://rms.ne.jp/sslserver/basis/openssl_on_windows/)

任意の証明書用パスワードの入力が求められますので、設定してください。

## 生成した証明書をインストールする

生成した pfx ファイルをローカルマシンにインストールします。証明書のインストールウィザードを進め、「すべての証明書を次のストアに配置する」を選択し、`Cert:\LocalMachine\My` にインストールしてください。

## 証明書を割り当てる

以下の PowerShell スクリプトを、管理者権限で起動した PowerShell 上で実行し、秘密鍵を持つサーバー証明書を検索します。
ここで、`$dnsName` にはリモートデスクトップ接続時に使用するサーバー名を指定してください。すなわち`rdp.example.com` は、実際のリモートデスクトップ接続に使用するDNS名に置き換えてください。

なお、SHA256 で証明書 pfx を生成しても、拇印（サムプリント）は SHA1 で扱われるため、SHA1 の拇印を取得してリモートデスクトップの接続先の証明書として指定します。

- 参考: [Signature Algorithm shows "sha256" but thumbprint algorithm still says "sha1"](https://learn.microsoft.com/en-us/archive/msdn-technet-forums/9543cd5b-c3b3-4d13-a9c4-46b97f2c6c18)
- 参考: [SHA-2 証明書の「拇印アルゴリズム」が「SHA-1」と表示されます。](https://www.cybertrust.co.jp/sureserver/support/faq/e86ks5r7frgv.html)

```powershell
$dnsName = "rdp.example.com"  # ここを実際のリモートデスクトップサーバーのDNS名に置き換えてください (例: rdp.mydomain.com)

Get-ChildItem -Recurse Cert:\LocalMachine\ |
    Where-Object {$_.HasPrivateKey} |
    Where-Object {
        try {
            Test-Certificate $_ -DNSName $dnsName -EKU 1.3.6.1.5.5.7.3.1 -ErrorAction Stop
            $true
        }
        catch {
            $false
        }
    } |
    Where-Object {
        if ($_.Subject -match "CN=(?<SubjectCN>[a-z0-9\.\-]+\??)") {
            $subjectCN = $Matches.SubjectCN
        }
        if ($_.Issuer -match "CN=(?<IssuerCN>[a-z0-9\.\-]+\??)") {
            $issuerCN = $Matches.issuerCN
        }
        $subjectCN -ne $issuerCN
    } |
    Sort-Object NotAfter |
    ForEach-Object {
        $latestCert = $_
        $_ | Format-List -Property Subject, FriendlyName, NotAfter, Thumbprint
    }
```

証明書が見つからない場合は次を確認してください。

-   pfx ファイルではなく `server.crt` を直接インストールしていないか確認してください。秘密鍵が含まれていない場合、このスクリプトでは証明書を検出できません。
-   証明書の CN と接続先サーバー名が一致しているか確認してください。ワイルドカードドメイン証明書も使用できます。

証明書が次のように表示されたら、次の手順に進んでください。表示されるSHA1拇印 `AABBCCDDEEFF00112233445566778899AABBCCDD` は、後の手順で使用します。

```
Subject         : CN=*.example.com  # 証明書のサブジェクト (例)
FriendlyName    :
NotAfter        : 2025/12/31 23:59:59
Thumbprint      : AABBCCDDEEFF00112233445566778899AABBCCDD # 証明書の拇印 (例)
```

## 証明書の割り当て（レジストリへの登録）

上記スクリプトで表示された拇印（サムプリント）を、以下のスクリプトの **`$sha1`** 変数に **大文字** でスペースや `:` なしで入力し、PowerShell で実行します。

```powershell
function Format-SHA1 {
    param(
        [Parameter(Mandatory = $true)]
        [string]$SHA1Hash
    )

    if ($SHA1Hash.Length -ne 40) {
        throw "SHA1 hash must be 40 characters long."
    }

    $bytes = @()
    for ($i = 0; $i -lt 40; $i += 2) {
        $bytes += $SHA1Hash.Substring($i, 2)
    }

    return $bytes -join ","
}

# SHA1ハッシュ値を入力。 <certificate_thumbprint> を上記で取得した拇印に置き換えてください (例: AABBCCDDEEFF00112233445566778899AABBCCDD)
$sha1 = "AABBCCDDEEFF00112233445566778899AABBCCDD" #  拇印の例に合わせて、スペースや:を削除し大文字に

$formattedSHA1 = Format-SHA1 -SHA1Hash $sha1
Write-Host $formattedSHA1
```

上記スクリプトで出力されたフォーマット済みの SHA1 ハッシュ値を、以下のレジストリファイル (`.reg`) に **`<formatted_sha1_hash>`** の箇所を置き換えて保存し、実行してレジストリに登録します。

```ini
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]
"SSLCertificateSHA1Hash"=hex:<formatted_sha1_hash> # ここをPowerShellで出力された値に置き換えてください (例: aa,bb,cc,dd,ee,ff,00,11,22,33,44,55,66,77,88,99,aa,bb,cc,dd) # 例を修正！コロン区切りではなくカンマ区切り
```

レジストリの登録がうまくいかないときは次を確認してください。

-   **`<formatted_sha1_hash>`** が、2 桁区切りで`,`で区切られた 16 進数で書かれているか確認してください。
-   **`<certificate_thumbprint>`** をPowerShellスクリプトに設定する際、**大文字** でスペースや `:` なしで入力したか確認してください。
-   **`SSLCertificateSHA1Hash`** レジストリ値には、**コロン区切り (例: `AA:BB:CC:DD...`) ではなく、カンマ区切り (例: `aa,bb,cc,dd...`) の16進数表記** を設定する必要があります。PowerShellスクリプト `Format-SHA1` が出力する形式であることを確認してください。 参考:  [Force Remote Desktop Services in Windows 7 to use a custom server authentication certificate for TLS](https://learn.microsoft.com/en-us/troubleshoot/windows-server/remote/custom-server-authentication-certificate-for-tls)

## 割り当ての確認

以下の PowerShell コマンドで、証明書が正しく割り当てられたか確認します。

```powershell
Get-CimInstance -Namespace root\cimv2\terminalservices -ClassName Win32_TSGeneralSetting | Format-List -Property *
```

結果の中に `SSLCertificateSHA1Hash` が登録した値 (`<formatted_sha1_hash>` に設定した値) と一致していれば、証明書の割り当ては成功です。

## リモートデスクトップサービスの再起動

最後に、リモートデスクトップサービスを再起動します。これを行わないと、**リモートデスクトップへの接続ができなくなります。**

```powershell
# TermService の依存サービスを取得
$dependentServices = Get-Service TermService | Select-Object -ExpandProperty DependentServices

# 依存サービスを停止
Stop-Service $dependentServices -Force

# TermService を再起動
Restart-Service TermService

# 依存サービスを開始 (必要であれば)
Start-Service $dependentServices
```

---

## 注意事項

-   **`rdp.example.com`** 、 **`AABBCCDDEEFF00112233445566778899AABBCCDD`** 、 **`<formatted_sha1_hash>`** は、実際の設定に合わせて置き換えてください。特に **`rdp.example.com`** は、実際のリモートデスクトップサーバーのDNS名に置き換える必要があります。
-   レジストリの操作は誤るとシステムに影響を与える可能性があります。操作は慎重に行い、自己責任で実施してください。
-   本手順は、サーバー証明書、中間証明書、秘密鍵が正しく取得できていることを前提としています。証明書の取得方法については、ご利用の認証局のドキュメントなどを参照してください。
-   今どき外部からRDPを悪用した攻撃が多いのでそもそもRDPを使用すべきかどうかという懸念はあります。攻撃により御社の機密情報が漏れたりデータの身代金を要求されることもありえます。