# 機能設計書 34-機密情報管理

## 概要

本ドキュメントは、Jenkinsにおける機密情報管理機能の設計を記載したものである。機密情報管理機能は、パスワード等の機密データの暗号化・保管を担当し、Jenkinsのセキュリティの根幹を支える。

### 本機能の処理概要

本機能は、パスワード、APIキー、トークンなどの機密情報をAES暗号化して安全に保存し、必要時に復号化して使用する仕組みを提供する。

**業務上の目的・背景**：CI/CDパイプラインでは、外部システムへの認証情報（SCMのパスワード、クラウドサービスのAPIキー等）を保持する必要がある。これらの情報を平文で保存することはセキュリティ上許容できないため、暗号化して保存する仕組みが必要である。本機能により、機密情報が設定ファイルやログに露出することを防ぎ、システム全体のセキュリティを確保する。

**機能の利用シーン**：
- ジョブの認証情報（SCMパスワード、デプロイ先の認証情報）の保存
- プラグインが使用するAPIキー、トークンの保存
- ユーザーの認証情報（LDAPバインドパスワード等）の保存
- クレデンシャルプラグインと連携した秘密情報の管理
- 設定ファイルのインポート/エクスポート時の機密情報保護

**主要な処理内容**：
1. マスターキーの生成と安全な保管（ConfidentialStore）
2. Secret（機密値）の暗号化と復号化
3. ConfidentialKeyによる暗号化キーの派生
4. 設定ファイルでの暗号化表現（{AES128}...形式）
5. XStreamシリアライズ時の自動暗号化

**関連システム・外部連携**：ConfidentialStoreが暗号化キーを管理。プラグインはSecretクラスを通じて機密情報を保存。クレデンシャルプラグインはSecretを基盤として構築。

**権限による制御**：Secret値の表示は制限され、SYSTEM認証やJenkins.ADMINISTERを持つユーザーのみがアクセス可能。ただし、暗号化・復号化自体は権限チェックなしで実行される（適切な場所で権限チェックが必要）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 各種設定画面 | 入力/出力画面 | 機密情報の入力と暗号化表示 |

## 機能種別

暗号化処理 / セキュリティ基盤 / データ保護

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| value | String | Yes（暗号化時） | 暗号化対象の平文 | null以外 |
| data | String | Yes（復号時） | 復号対象の暗号文（{...}形式） | 暗号化形式に準拠 |

### 入力データソース

- 設定ファイル（XML）: XStreamによるデシリアライズ
- フォーム入力: StaplerRequestからのパスワードフィールド
- プログラム: Secret.fromString()による明示的な暗号化

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Secret | Secret | 暗号化された値を保持するオブジェクト |
| encryptedValue | String | {AES256:...}または{AES128:...}形式の暗号文 |
| plainText | String | 復号化された平文（getPlainText()で取得） |

### 出力先

- 設定ファイル（XML）: 暗号化形式で保存
- メモリ: 復号化された値は一時的にのみ保持

## 処理フロー

### 処理シーケンス

```
1. 機密情報受信
   └─ フォーム入力またはAPIから平文を受信
2. 暗号化
   └─ Secret.fromString()でAES暗号化
3. 保存
   └─ XStreamで設定ファイルにシリアライズ（{AES256:...}形式）
4. 読み込み
   └─ XStreamでデシリアライズ時に自動的にSecretオブジェクト生成
5. 使用
   └─ getPlainText()で復号化して使用（キャッシュなし）
```

### フローチャート

```mermaid
flowchart TD
    A[平文入力] --> B[Secret.fromString]
    B --> C[CryptoConfidentialKey.encrypt]
    C --> D[AES暗号化]
    D --> E[{AES256:...}形式で保存]

    F[設定ファイル読込] --> G[ConverterImpl.unmarshal]
    G --> H{形式判定}
    H -->|AES256| I[AES256復号]
    H -->|AES128| J[AES128復号]
    H -->|旧形式| K[レガシー復号]
    I --> L[Secretオブジェクト生成]
    J --> L
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | 平文非保存 | 機密情報は平文で永続化しない | 常時 |
| BR-34-02 | マスターキー保護 | マスターキーは$JENKINS_HOME/secretsに安全に保管 | 常時 |
| BR-34-03 | 暗号化形式選択 | AES256を優先、不可能な場合はAES128にフォールバック | 暗号化時 |
| BR-34-04 | 空値処理 | 空文字列はSecret.fromString()でnullを返す | 入力が空の場合 |
| BR-34-05 | toString保護 | Secret.toString()は暗号化された値のみ返す | 常時 |

### 計算ロジック

暗号化形式:
```
暗号文 = {AES256:IV + encrypt(平文, キー)}
IV = 16バイトの乱数
キー = ConfidentialKey.derived256BitKey()から派生
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| キー保存 | $JENKINS_HOME/secrets/master.key | ファイル | マスターキーの永続化 |
| 派生キー保存 | $JENKINS_HOME/secrets/{key-id} | ファイル | 派生キーの永続化 |

### ファイル別操作詳細

#### master.key

| 操作 | 内容 | 備考 |
|-----|------|------|
| CREATE | 初回起動時にランダム生成 | 256ビット |
| READ | 暗号化/復号化のたびに読み込み | メモリにキャッシュ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | IllegalStateException | マスターキーが見つからない | $JENKINS_HOME/secretsディレクトリの権限を確認 |
| - | CryptoException | 復号化失敗（キー不一致等） | 設定ファイルとsecretsディレクトリの整合性を確認 |
| - | NoSuchAlgorithmException | AES非サポート環境 | JVMの暗号化プロバイダを確認 |

### リトライ仕様

暗号化/復号化は即座に結果を返すため、リトライは不要。復号化失敗時は新たな値を設定する必要がある。

## トランザクション仕様

暗号化/復号化はステートレスな処理。設定ファイルへの保存はJenkinsの設定保存メカニズムに依存。

## パフォーマンス要件

- 暗号化/復号化は頻繁に実行されるため、高速な応答が必要
- マスターキーはメモリにキャッシュして毎回のディスクアクセスを回避
- AES暗号化は十分高速（マイクロ秒オーダー）

## セキュリティ考慮事項

- マスターキーはファイルシステムの権限で保護（600推奨）
- 平文はメモリ上のみに存在し、シリアライズ時は常に暗号化
- ログへの出力時はSecret.toString()で暗号化文字列のみ表示
- JENKINS_HOME全体のバックアップにはsecrets/も含める必要あり
- secretsディレクトリなしでの復元では機密情報が復号不可

## 備考

- SecretはimmutableでありスレッドセーフMODEL_CLASS
- ConfidentialKeyはプラグインが独自の暗号化キーを定義する際に使用
- レガシー形式（Base64エンコードのみ）からの自動マイグレーションをサポート
- OEM配布ではカスタムConfidentialStore実装を提供可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: Secretクラスを理解する

機密情報を保持するValueオブジェクトの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Secret.java | `core/src/main/java/hudson/util/Secret.java` | 機密情報の暗号化・復号化 |

**主要処理フロー**:
- **63-95行目**: クラス定義とフィールド（value, iv, cipher）
- **104-114行目**: getPlainText() - 復号化して平文を取得
- **121-157行目**: getEncryptedValue() - 暗号化文字列を取得
- **195-245行目**: fromString() - 文字列からSecretを生成（暗号文の復号または平文の暗号化）
- **303-328行目**: ConverterImpl - XStreamでのシリアライズ/デシリアライズ

**読解のコツ**: SecretはValueオブジェクトであり、暗号化と復号化の両方を担当する。fromString()は入力が暗号文か平文かを自動判別する。

#### Step 2: ConfidentialStoreを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ConfidentialStore.java | `core/src/main/java/jenkins/security/ConfidentialStore.java` | マスターキーの保管と管理 |

**主要処理フロー**:
- **45-50行目**: store() - キーデータの永続化（抽象メソッド）
- **58行目**: load() - キーデータの読み込み（抽象メソッド）
- **68行目**: randomBytes() - 安全な乱数生成
- **73-102行目**: get() - シングルトンインスタンスの取得（ServiceLoaderまたはデフォルト）

#### Step 3: CryptoConfidentialKeyを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ConfidentialKey.java | `core/src/main/java/jenkins/security/ConfidentialKey.java` | 派生キーの管理基底クラス |

**主要処理フロー**:
- **44行目**: getId() - キー識別子
- **63-76行目**: getPayload() - 保存されたキーデータの取得または新規生成

### プログラム呼び出し階層図

```
Secret.fromString(plainText)
    │
    ├─ [暗号文入力の場合]
    │      └─ tryDecrypt() 復号化試行
    │             ├─ decrypt256() AES256復号
    │             └─ decrypt128() AES128復号
    │
    └─ [平文入力の場合]
           └─ new Secret(plainText)
                  │
                  └─ getEncryptedValue() [保存時]
                         │
                         └─ CryptoConfidentialKey.encrypt()
                                │
                                ├─ ConfidentialKey.getPayload()
                                │      └─ ConfidentialStore.load/store()
                                │
                                └─ Cipher.doFinal() AES暗号化
```

### データフロー図

```
[入力]                      [処理]                           [出力]

平文 ─────────────────▶ Secret.fromString() ──────────────▶ Secretオブジェクト
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
              暗号文判定            平文として処理
                    │                   │
                    ▼                   ▼
               tryDecrypt()       new Secret()
                    │                   │
                    ▼                   ▼
            CryptoConfidentialKey   Secret
            .decrypt()              オブジェクト
                    │
         ┌─────────┴─────────┐
         ▼                   ▼
    AES256復号          AES128復号
         │                   │
         └─────────┬─────────┘
                   ▼
            Secret オブジェクト

[保存時]

Secretオブジェクト ───▶ getEncryptedValue() ───▶ {AES256:...}形式
                              │
                              ▼
                     CryptoConfidentialKey
                     .encrypt()
                              │
                              ▼
                     Cipher.doFinal()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Secret.java | `core/src/main/java/hudson/util/Secret.java` | ソース | 機密情報のValueオブジェクト |
| ConfidentialStore.java | `core/src/main/java/jenkins/security/ConfidentialStore.java` | ソース | マスターキーの保管 |
| ConfidentialKey.java | `core/src/main/java/jenkins/security/ConfidentialKey.java` | ソース | 派生キーの管理基底クラス |
| CryptoConfidentialKey.java | `core/src/main/java/jenkins/security/CryptoConfidentialKey.java` | ソース | AES暗号化キーの実装 |
| DefaultConfidentialStore.java | `core/src/main/java/jenkins/security/DefaultConfidentialStore.java` | ソース | デフォルトのConfidentialStore実装 |
| HexStringConfidentialKey.java | `core/src/main/java/jenkins/security/HexStringConfidentialKey.java` | ソース | 16進文字列キーの実装 |
