# スマートフォンのマイナンバーカードAPI

<br>

## **概要**

スマートフォンのマイナンバーカードAPIは、iPhoneのAppleウォレットに格納されたマイナンバーカードから提示されたmdoc（mobile document／モバイル・ドキュメント）を、ISO/IEC 18013-5規格に準拠して検証するためのAPIです。 オンラインで受け取った発行者署名とデバイス署名を検証することで、提示されたデータが「正当な発行機関によって作成されたものであること」および「現在の所持者のデバイスで生成されたものであること」を確認します。

また、本方式ではデータ提供時に利用者自身のデバイス上で生体認証（Face ID等）を行うため、なりすましを強力に防止します。さらに、利用者が提示する情報の項目（氏名、住所、生年月日等）を自ら確認できるため、必要最小限のデータのみを安全に受け渡し可能です。 これにより、発行元の公的機関による真正性が厳密に担保され、偽造や改ざんが極めて困難な、最堅牢かつプライバシーに配慮したデジタル本人確認／属性証明プラットフォームを提供します。

:::info[]
情報
Androidのマイナンバーカードの対応は、2026年秋頃を予定しております。
デジタル庁：[2026年秋頃に「Androidのマイナンバーカード」へ刷新します](https://services.digital.go.jp/mynumbercard-android/news/0cfe138d7fb5927e4dc6d/)
:::

## **取得できる項目**
<AccordionGroup>
    <Accordion title="iPhoneのマイナンバーカードから取得可能な属性項目">
        - 氏名
        - 住所
        - 生年月日
        - 性別
        - 個人番号（マイナンバー）
        - 顔写真
        - 市区町村コード（全国地方公共団体コード）
    </Accordion>
</AccordionGroup>
    
## **アクセスコントロール**
<AccordionGroup>
    <Accordion title="iPhoneのマイナンバーカード" >
        端末認証（Face ID、Touch ID）
    </Accordion>
</AccordionGroup>


## **処理の流れ**
<Tabs>
    <Tab title="対面">
        <Steps>
            <Step title="乱数の取得（サービスプロバイダ事業者）">
            本人確認セッションを開始し、mdoc検証サーバーからリプレイアタック（再送攻撃）を防止するためのNonce（使い捨ての乱数）を取得する。
            </Step>

            <Step title="属性項目の確認と承認（利用者）">
            要求された属性項目を画面上で確認し、掲示を承認します。生体認証（Face ID/Touch ID）を実行することで、端末内の秘密鍵によるデータ署名を許可します。
            </Step>

            <Step title="デバイス署名付mdocの生成（SDK）">
            取得済みのNonceと属性情報を組み合わせ、端末固有の鍵で署名（Device Auth）を付与します。これにより、検証サーバーのみが復号・検証可能な暗号化された提示用データ（DeviceResponse）を構成します。
            </Step>

            <Step title="復号と署名検証（プラットフォーム事業者）">
            暗号化データを復号し、Nonceの一致確認、発行元署名による改ざん検知、およびデバイス署名による所有証明を厳格に行います。すべての検証を通過したのち、承諾された属性のみをJSON形式で取り出します。
            </Step>
        </Steps>

    </Tab>
    <Tab title="非対面">
        <Steps>
            <Step title="乱数の取得（サービスプロバイダ事業者）">
            本人確認セッションを開始し、mdoc検証サーバーからリプレイアタック（再送攻撃）を防止するためのNonce（使い捨ての乱数）を取得する。
            </Step>

            <Step title="端末間セッションの確立（SDK）">
              ISO/IEC 18013-5規格に基づき、NFCやQRコードを介したエンゲージメント（鍵交換）を行い、店舗端末と利用者のスマートフォン間でエンドツーエンドの暗号化セッションを確立します。
            </Step>

            <Step title="属性項目の確認と承認（利用者）">
            要求された属性項目を画面上で確認し、掲示を承認します。生体認証（Face ID/Touch ID）を実行することで、端末内の秘密鍵によるデータ署名を許可します。
            </Step>

            <Step title="デバイス署名付mdocの生成（SDK）">
            取得済みのNonceと属性情報を組み合わせ、端末固有の鍵で署名（Device Auth）を付与します。これにより、検証サーバーのみが復号・検証可能な暗号化された提示用データ（DeviceResponse）を構成します。
            </Step>

            <Step title="復号と署名検証（プラットフォーム事業者）">
            暗号化データを復号し、Nonceの一致確認、発行元署名による改ざん検知、およびデバイス署名による所有証明を厳格に行います。すべての検証を通過したのち、承諾された属性のみをJSON形式で取り出します。
            </Step>
        </Steps>
    </Tab>
</Tabs>
 ## **シーケンス図**

<Tabs>
    <Tab title="対面">
    ```mermaid
%%{init:{'theme':'natural'}}%%
sequenceDiagram
    participant 利用者
    participant SDK
    participant SP as サービスプロバイダ事業者
    participant PF as プラットフォーム事業者
    participant IACA as mdoc発行機関

    loop スケジューリング実行
        PF->>IACA: ルート証明書<br>失効リストの要求
        IACA-->>PF: 失効リストの返却
    end

    利用者->>SP: 申し込み情報の入力
    SP->>PF: 乱数の要求
    PF->>PF: 乱数の生成
    PF-->>SP: 乱数の返却

    SP->>SDK: スマートフォンの<br>マイナンバーカード読み取り要求
    SDK->>利用者: 読み取り待機画面表示
    利用者-->>SDK: スマートフォンをかざす
    SDK->>利用者: 属性項目・同意画面表示
    利用者-->>SDK: 生体認証
    SDK->>SDK: デバイス署名付mdocの生成
    SDK-->>SP: デバイス署名付mdocの返却
    
    SP->>PF: 署名検証・有効性確認の要求
    PF->>IACA: デバイス証明書<br>失効リストの要求
    IACA-->>PF: 失効リストの返却
    PF->>PF: 署名検証・有効性確認
    PF-->>SP: 署名検証結果・属性情報の返却
    SP-->>利用者: 本人確認・申し込み完了画面の表示
    ```
    </Tab>
    <Tab title="非対面">
    ```mermaid
%%{init:{'theme':'natural'}}%%
        sequenceDiagram
        participant 利用者
        participant SDK
        participant SP as サービスプロバイダ事業者
        participant PF as プラットフォーム事業者
        participant IACA as mdoc発行機関

    loop スケジューリング実行
        PF->>IACA: ルート証明書の<br>失効リストの要求
        IACA-->>PF: 失効リストの返却
    end

        利用者->>SP: 申し込み情報の入力
        SP->>PF: 乱数の要求
        PF->>PF: 乱数の生成
        PF-->>SP: 乱数の返却

        SP->>SDK: スマートフォンの<br>マイナンバーカード読み取り要求
        SDK-->>利用者: 属性項目・同意画面表示
        利用者->>SDK: 生体認証
        SDK->>SDK: デバイス署名付mdocの生成

        SDK->>SP: デバイス署名付mdocの返却
        SP->>PF: 署名検証・有効性確認の要求
        
        %% 検証時にデバイス証明書の失効確認も行う場合
        PF->>IACA: デバイス証明書の<br>失効リストの要求
        IACA-->>PF: 失効リストの返却
        
        PF->>PF: 署名検証・有効性確認
        PF-->>SP: 署名検証結果・属性情報の送信

        SP-->>利用者: 本人確認・申し込み完了画面の表示
    ```
    </Tab>
</Tabs>

## **実装例**

<Tabs>
    <Tab title="iOS">
```swift
Task {
    do {
        let session = URLSession.shared
        
        // 1. 乱数の取得
        var nonceRequest = URLRequest(url: URL(string: NONCE_ENDPOINT)!)
        nonceRequest.httpMethod = "POST"
        nonceRequest.httpBody = "{}".data(using: .utf8)
        nonceRequest.addValue(MEDIA_TYPE, forHTTPHeaderField: "Content-Type")
        nonceRequest.addValue(API_KEY, forHTTPHeaderField: "X-PTS-API-Key")
        
        let (nonceData, _) = try await session.data(for: nonceRequest)
        let nonceResponse = String(data: nonceData, encoding: .utf8) ?? ""
        
        // 取得する属性項目を指定 (基本4情報の場合)
        let requestedElements: [String: [String: Bool]] = [
            "org.iso.23220.1.jp": [
                "full_name_unicode": true,
                "birth_date_unicode": true,
                "resident_address_unicode": true,
                "sex_unicode": true
            ]
        ]
        
        // 2. 外部デバイスへmdoc読み取り要求 (NFC・BLE) ※ここからNFC待機状態になります
        let verifyBody = try await MdocReaderSDK.requestMdocFromExternalDevice(
            viewController: self,
            nonceResponse: nonceResponse,
            elements: requestedElements
        )
        
        // 3. 署名検証・有効性確認の要求
        let verifyJsonData = try JSONSerialization.data(withJSONObject: verifyBody)
        
        var verifyRequest = URLRequest(url: URL(string: VERIFY_ENDPOINT)!)
        verifyRequest.httpMethod = "POST"
        verifyRequest.httpBody = verifyJsonData
        verifyRequest.addValue(MEDIA_TYPE, forHTTPHeaderField: "Content-Type")
        verifyRequest.addValue(API_KEY, forHTTPHeaderField: "X-PTS-API-Key")
        
        let (verifyData, _) = try await session.data(for: verifyRequest)
        let verifyResponse = String(data: verifyData, encoding: .utf8) ?? ""
        
        // 検証結果の確認
        
    } catch {
        print("エラー発生: \(error)")
    }
}
```
    </Tab>
    <Tab title="Android">
```kotlin
lifecycleScope.launch(Dispatchers.IO) {
    val client = OkHttpClient()

    try {
        // 1. 乱数（Nonce）の取得
        val nonceRequest = Request.Builder()
            .url(NONCE_ENDPOINT)
            .post("{}".toRequestBody(MEDIA_TYPE.toMediaType()))
            .addHeader("Content-Type", MEDIA_TYPE)
            .addHeader("X-PTS-API-Key", API_KEY)
            .build()   
        
        val nonceResponse = client.newCall(nonceRequest).execute()
                                .body?.string() ?: ""
       
        // 取得する属性項目を指定
        val requestedElements = mapOf(
            "org.iso.23220.1.jp" to mapOf(
                "full_name_unicode" to true,
                "birth_date_unicode" to true,
                "resident_address_unicode" to true,
                "sex_unicode" to true
            )
        )

        // 2. 外部デバイスへmdoc読み取り要求 ※ここからNFC待機状態になります
        val verifyBody: String = MdocReaderSDK.requestMdocFromExternalDevice(
            activity = requireActivity(),
            nonceResponse = nonceResponse,
            elements = requestedElements
        )
        
        // 3. 署名検証・有効性確認の要求
        val verifyRequest = Request.Builder()
            .url(VERIFY_ENDPOINT)
            .post(verifyBody.toRequestBody(MEDIA_TYPE.toMediaType()))
            .addHeader("Content-Type", MEDIA_TYPE)                             
            .addHeader("X-PTS-API-Key", API_KEY)
            .build()   

        val verifyResponse = client.newCall(verifyRequest).execute()
                                 .body?.string() ?: ""
        
        // 検証結果の確認
        
    } catch (e: Exception) {
        e.printStackTrace()
    }
}
```
    </Tab>
    <Tab title="React Native">
```TypeScript
const startMdocProcess = async () => {
  try {
    // 1. 乱数の取得
    const nonceResponse = await fetch(NONCE_ENDPOINT, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-PTS-API-Key': API_KEY,
      },
      body: JSON.stringify({}),
    });
    const nonceData = await nonceResponse.text();

    // 取得する属性項目を指定 (基本4情報の場合)
    const requestedElements = {
      "org.iso.23220.1.jp": {
        "full_name_unicode": true,
        "birth_date_unicode": true,
        "resident_address_unicode": true,
        "sex_unicode": true
      }
    };

    // 2. 外部デバイスへmdoc読み取り要求 (NFC・BLE) ※ここからNFC待機状態になります
    const verifyBody = await MdocReaderSDK.requestMdocFromExternalDevice(
      nonceData, 
      requestedElements
    );

    // 3. 署名検証・有効性確認の要求
    const verifyResponse = await fetch(VERIFY_ENDPOINT, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-PTS-API-Key': API_KEY,
      },
      body: JSON.stringify(verifyBody),
    });
    const verifyResultJson = await verifyResponse.json();

    // 検証結果の確認

  } catch (error) {
    console.error("エラー発生:", error);
  }
};
```
    </Tab>
</Tabs>

:::warning[]
注意
本実装例では解説のためアプリから直接通信していますが、商用環境ではAPIキーの露出や盗用を防ぐため、必ず自社サーバーを介してAPIキーを秘匿する構成にしてください。
:::

<br>
