# Netflix Widevine DRM License Exchange - Reference Data > Android アプリ (v9.57.0 / build 63928) から MSL 経由で取得した実データ。 > 2026-03-13 にキャプチャ。デバイス: Pixel 4a (5G) / Android 14 / Widevine CDM v17.0.0 > **環境**: L3 (ソフトウェア) 強制。レスポンスは Widevine decrypt → CBOR → GZIP → JSON で復元。 --- ## 概要 Netflix Android アプリは **Widevine CDM** DRM を使用してコンテンツを保護している。 iOS 版と異なり、マニフェスト取得と初回ライセンス取得が `/licensedManifest` として統合されている: 1. **licensedManifest (PRE_FETCH)** → ストリーム一覧 + CDN URL + 初回 Widevine ライセンス (limited) を一括取得 2. **License Request (standard)** → 再生開始時に standard ライセンスを取得 (Widevine CDM protobuf チャレンジ) 3. **License Response** → Widevine ライセンスバイナリを受信、コンテンツ鍵を取得 4. **Release License** → 再生終了時にライセンスを解放 ライセンスリクエスト/レスポンスはすべて **Widevine CryptoContext** で暗号化された MSL ペイロードとして送信される。以下のデータはアプリ内部で MSL 暗号化される前/復号後の平文。 --- ## エンドポイント ``` POST /nq/androidui/samurai/~9.0.0/api ``` MSL URL としては: ``` /licensedManifest (マニフェスト + 初回ライセンス一括) /license?licenseType={standard|limited}&playbackContextId={id}&esn={esn}&drmContextId={id} ``` ### URL パラメータ | パラメータ | 説明 | |---|---| | `licenseType` | `standard` (再生開始時) or `limited` (PRE_FETCH / LDL) | | `playbackContextId` | licensedManifest レスポンスで取得した再生セッション ID (Base64url-like) | | `esn` | Netflix デバイス ESN。`NFANDROID1-PRV-P-{L3}-{MODEL}-{userId}-{fingerprint}` 形式 | | `drmContextId` | DRM コンテキスト ID (数値)。licensedManifest レスポンスの `packageId` と対応 | --- ## 1. licensedManifest Request (PRE_FETCH) コンテンツ選択時に送信される。iOS の `/manifest` + `/license` を統合した Android 固有の API。 バッチリクエストにより複数 viewableId を同時に指定可能。 ```json { "version": 2, "url": "/licensedManifest", "languages": ["en-JP"], "common": { "challenge": "<共通 Widevine CDM protobuf>" }, "params": [ { "viewableId": "81756595", "method": "licensedManifest", "flavor": "PRE_FETCH", "drmType": "widevine", "manifestVersion": "v2", "licenseType": "limited", "challenges": { "primary": [{ "challengeBase64": "", "drmSessionId": 1, "clientTime": 1773373148 }] }, "profiles": ["playready-h264mpl30-dash", "playready-h264hpl22-dash", "..."], "profileGroups": [{"name": "primary", "profiles": ["..."]}] } ] } ``` ### iOS との違い | 項目 | iOS (`/manifest` + `/license`) | Android (`/licensedManifest`) | |---|---|---| | API パス | 分離 (2 リクエスト) | 統合 (1 リクエスト) | | バッチ | 1 viewableId / リクエスト | 複数 viewableId / リクエスト | | DRM チャレンジ | FairPlay SPC (個別) | `common.challenge` + 個別 `challenges.primary` | | DRM タイプ | `fairplay` | `widevine` | | チャレンジ形式 | FairPlay SPC (JSON: CHALLENGES[].PAYLOAD) | Widevine CDM protobuf (バイナリ) | --- ## 2. licensedManifest Response L3 Widevine decrypt → CBOR (key 62) → GZIP 展開で復元。8 チャンク、合計 456KB。 ```json { "id": 1, "version": 2, "serverTime": 1773373149628, "result": [ { "movieId": "81756595", "packageId": "2596051", "duration": 8523000, "drmContextId": "2596051", "playbackContextId": "E3-Bgj5tevc...", "video_tracks": [{"..."}], "audio_tracks": [{"..."}], "timedtexttracks": [{"..."}], "servers": [{"..."}], "links": { "events": {"href": "/events?playbackContextId=...&esn=..."}, "ldl": {"href": "/license?licenseType=limited&..."}, "license": {"href": "/license?licenseType=standard&..."} } } ] } ``` ### result フィールド解説 | フィールド | 型 | 説明 | |---|---|---| | `movieId` | string | コンテンツ ID | | `packageId` | string | DRM パッケージ ID (`drmContextId` と対応) | | `duration` | number | 再生時間 (ミリ秒)。`8523000` = 2時間22分3秒 | | `drmContextId` | string | DRM コンテキスト ID | | `playbackContextId` | string | 再生セッション ID (後続の `/license`, `/events` URL に埋め込み) | | `video_tracks` | array | 映像トラック一覧 (複数ビットレート/解像度) | | `audio_tracks` | array | 音声トラック一覧 (多言語 × 複数ビットレート) | | `timedtexttracks` | array | 字幕トラック一覧 (IMSC 1.1) | | `servers` | array | CDN サーバー一覧 (Open Connect Appliance) | | `links` | object | 後続 API エンドポイント (`events`, `ldl`, `license`) | ### video_tracks の構造 ```json { "trackType": "PRIMARY", "new_track_id": "V:2:1;2;;primary;-1;none;-1;", "dimensionsLabel": "2D", "streams": [ { "content_profile": "playready-h264hpl30-dash", "bitrate": 1050, "peakBitrate": 2250, "res_w": 960, "res_h": 540, "framerate_value": 24000, "framerate_scale": 1001, "size": 1181780966, "downloadable_id": "1496730611", "vmaf": 87, "isDrm": true } ] } ``` L3 環境での最大解像度は **960x540** (SD)。7 ストリーム (80〜1050 kbps)。 ### servers の構造 ```json { "id": 140368, "name": "c062.osa001.ix.nflxvideo.net", "rank": 1, "type": "OPEN_CONNECT_APPLIANCE", "dns": { "host": "ipv4-c062-osa001-ix.1.oca.nflxvideo.net", "ipv4": "45.57.82.139", "ipv6": null } } ``` 3 台の Open Connect Appliance (大阪: osa001, osa003)。`rank` でフェイルオーバー優先度を指定。 ### iOS manifest レスポンスとの比較 | 項目 | iOS (`/manifest`) | Android (`/licensedManifest`) | |---|---|---| | レスポンス取得 | **未取得** | **復元済み** (L3 decrypt 経由) | | result 構造 | 単一オブジェクト (推定) | 配列 (バッチ対応) | | links | events, license (推定) | events, ldl, license | | servers | CDN URL 一覧 (推定) | Open Connect Appliance (id, name, rank, dns) | | video_tracks | 不明 | streams[] (bitrate, res, vmaf, downloadable_id) | --- ## 3. Standard License Request 再生開始時に送信される。licensedManifest レスポンスの `links.license.href` を使用。 ```json { "version": 2, "url": "/license?licenseType=standard&playbackContextId=E3-Bgj5tevc...&esn=NFANDROID1-PRV-P-L3-GOOGLPIXEL%3D4A%3D%3D5G%3D-22594-...&drmContextId=2596051", "params": { "clientTime": 1773373168, "challengeBase64": "", "xid": "7616579701369171345" }, "languages": ["en-JP"] } ``` ### params フィールド解説 | フィールド | 型 | 説明 | |---|---|---| | `clientTime` | number | クライアント時刻 (Unix epoch, seconds) | | `challengeBase64` | string | Base64 エンコードされた Widevine CDM protobuf ライセンスチャレンジ | | `xid` | string | リクエスト ID (トランザクション ID) | ### challengeBase64 の内容 iOS の FairPlay SPC が JSON (`CHALLENGES[].PAYLOAD`) であるのに対し、Widevine は **protobuf バイナリ**。 内部に以下の情報が含まれる: | フィールド | 値 | 説明 | |---|---|---| | `esn` | `NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G=-22594-...` | デバイス ESN (L3 マーカー含む) | | `movieid` | `"81756595"` | コンテンツ ID | | `issuetime` | `1773373148` | 発行時刻 | | `salt` | `"35982634558192635..."` | ランダムソルト | | `oem_crypto_build_information` | `"OEMCrypto Level3 Code"` | OEMCrypto セキュリティレベル | | `widevine_cdm_version` | `"17.0.0"` | CDM バージョン | | `device_name` | `"bramble"` | デバイスコードネーム | | `architecture_name` | `"arm64-v8a"` | CPU アーキテクチャ | --- ## 4. Standard License Response ```json { "id": 1, "version": 2, "serverTime": 1773373169221, "result": { "licenseResponseBase64": "", "secureStopExpected": false, "links": { "releaseLicense": { "rel": "releaseLicense", "href": "/releaseLicense?drmLicenseContextId=E3-Bgj5tevc...;EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED;STANDARD;1773373169202" } }, "drmGroupId": "132", "licenseType": "standard", "expiration": 1773373168000 }, "common": {}, "from": "playapi" } ``` ### result フィールド解説 | フィールド | 型 | 説明 | |---|---|---| | `licenseResponseBase64` | string | Base64 エンコードされた Widevine ライセンスバイナリ (protobuf)。`provideKeyResponse()` に渡す | | `secureStopExpected` | boolean | Secure Stop (再生終了通知) が必要か | | `links.releaseLicense.href` | string | ライセンス解放用 URL。再生終了時にこの URL にリクエストを送る | | `drmGroupId` | string | DRM グループ ID。`"132"` (iOS は `"191-192"` のようにハイフン区切り) | | `licenseType` | string | `"standard"` | | `expiration` | number | ライセンス有効期限 (Unix epoch, milliseconds) | ### licenseResponseBase64 の内容 iOS の FairPlay CKC が JSON (`VERSION`, `MEDIASESSIONID`, `RESPONSES[].PAYLOAD`) であるのに対し、 Widevine のライセンスレスポンスは **protobuf バイナリ**。内部には: - **暗号化されたコンテンツ鍵**: Widevine CDM のセッション鍵で暗号化 - **埋め込み JSON**: `version`, `esn`, `issuetime`, `movieid`, `salt` - **鍵 ID**: `CE66E4B447F964AC19000000000000000` - **ライセンスメタデータ**: security_level, expiry_duration `android.media.MediaDrm.provideKeyResponse(sessionId, licenseResponseBase64)` に渡すことで コンテンツ復号鍵が Widevine CDM にインストールされる。 --- ## 5. Release License (再生終了) 再生終了時、サーバーに通知する。`links.releaseLicense.href` を使用。 iOS と同じく、レスポンスは空の `actions` オブジェクト (推定): ```json { "result": [ { "actions": {} } ], "id": 1, "common": {}, "from": "playapi", "serverTime": 1773373xxx000, "version": 2 } ``` --- ## 6. drmSessionId / videoTrackName フォーマット ``` V:2:1;2;;primary;-1;none;-1; ``` セミコロン区切り。推定される構造: - `V:2:1` — ビデオトラック識別 (V=Video, 以降はインデックス) - `2` — DRM グループ数 - (空) — 予約 - `primary` — プロファイルグループ (iOS は `ce4` = HEVC) - `-1` — 品質レベル (-1 = auto) - `none` — HDR タイプ (none = SDR) - `-1` — 予約 --- ## 7. ESN (Entertainment Service Name) フォーマット ``` NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G=-22594-3E369F1C9B189ED664E13DEEEE9BACB67684B4FE6283537D1A67810BFE73E371 ``` - `NFANDROID1` — Netflix Android プラットフォーム (iOS は `NFAPPL`) - `PRV` — プロビジョニングタイプ - `P` — 製品タイプ - `L3` — Widevine セキュリティレベル (L3=ソフトウェア、L1=TEE)。iOS には存在しない - `GOOGLPIXEL=4A==5G=` — デバイスモデル (Pixel 4a (5G)、`=` は `,` のエスケープ) - `22594` — ユーザー ID - 末尾 64 文字 — デバイス固有ハッシュ (SHA-256) ### iOS ESN との比較 | 項目 | iOS | Android | |---|---|---| | プレフィックス | `NFAPPL` | `NFANDROID1` | | DRM バージョン | `02` (FairPlay v2) | なし | | セキュリティレベル | なし | `L3` or `L1` | | モデル | `IPHONE9=1` | `GOOGLPIXEL=4A==5G=` | | ユーザー ID | なし | あり (`22594`) | --- ## 8. データの関連性まとめ ``` licensedManifest Response ├─ playbackContextId ──→ /license Request URL ├─ drmContextId / packageId ──→ /license Request URL ├─ video_tracks / audio_tracks ──→ CDN ストリーム URL ├─ servers[] ──→ CDN サーバー (Open Connect Appliance) └─ links ├─ events ──→ /events URL (再生イベント報告) ├─ ldl ──→ /license?licenseType=limited (LDL) └─ license ──→ /license?licenseType=standard License Request └─ challengeBase64 (Widevine protobuf) ──→ License Response.licenseResponseBase64 License Response ├─ licenseResponseBase64 ──→ MediaDrm.provideKeyResponse() → コンテンツ鍵 ├─ links.releaseLicense.href ──→ Release License Request URL └─ expiration ──→ standard: ライセンス有効期限 ```