> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 加密登入

> 為 PlayFab 用戶端登入 API 呼叫啟用自訂加密，以保護驗證承載並強化應用程式安全性，防止遭到竄改。

PlayFab 可讓您使用自訂加密保護特定用戶端 API 呼叫，藉此強化應用程式安全性。本教學課程說明如何為您的用戶端啟用加密。

我們將使用的方法可讓您保護*任何*登入 API 呼叫。由於流程*一律*相似，我們只示範如何保護*一個*特定方法 `LoginWithCustomID`。

<Info>
  登入加密的設計是在建立遊戲後套用於*所有*玩家，或完全不使用。這*不是*可以日後再啟用的功能。您必須*從一開始*就使用，否則就完全不使用。特別是，*已加密*的玩家永遠無法以*未加密*方式登入，而*未加密*的玩家也永遠無法成為*已加密*的玩家。
</Info>

在本指南中，我們將：

1. 建立玩家共用秘密。
2. 引進 API 原則規則，以在特定方法上啟用保護。
3. 變更用戶端，使用玩家共用金鑰擷取遊戲公開金鑰並加密承載。

<Note>
  PlayFab 提出以下免責聲明：「我們所有的 API 呼叫都已依照現代標準安全加密，而標準的 API 呼叫加密已足以滿足大多數客戶的需求。此功能代表*額外*的安全層，其目的是讓玩家更難以使用未經授權的用戶端。它*並非*萬無一失，只是提高駭客入侵的難度。對大多數開發人員而言，這種輕微的安全性提升並不值得投入額外的心力。」
</Note>

## 建立玩家共用秘密

PlayFab Admin API 公開了一個方法，可用來管理您的玩家共用秘密。

<Note>
  以特定名稱建立新的共用秘密時，會覆寫同名的現有金鑰 (如果有的話)。此外，您可以在不同名稱下註冊*多個*共用秘密。
</Note>

執行下列程式碼，將玩家共用秘密新增至您的遊戲。

```csharp theme={null}
PlayFabSettings.staticSettings.DeveloperSecretKey = "__DEVELOPER_KEY__";
PlayFabSettings.staticSettings.TitleId = "__TITLE_ID__";
var response = await PlayFabAdminAPI.CreatePlayerSharedSecretAsync(new CreatePlayerSharedSecretRequest()
{
    FriendlyName = "__KEY_NAME__"
});

if (response.Error != null)
{
    Console.WriteLine(response.Error.GenerateErrorReport());
}
else
{
    Console.WriteLine(response.Result.SecretKey);
}
```

若要執行此程式碼，您需要開發人員秘密金鑰。如需秘密金鑰的詳細資訊，請參閱[秘密金鑰管理](/zh-TW/services/playfab/live-service-management/gamemanager/secret-key-management)。

此應用程式應該會列印新建立的玩家共用秘密。*請務必將其儲存*。如果遺失，您將必須再次執行應用程式以產生新的秘密。

秘密看起來像這樣：

`QC953WQ3TU6ZJTZMAT1FNJQIKR92FPUQTISW4Q6WD8SY841MQQ`

## 更新原則

我們已建立新的共用秘密。現在我們需要告訴 PlayFab 要保護哪些 API 呼叫。

執行下列程式碼來保護 `LoginWithCustomId` API 呼叫，並*取消保護*其餘的 API 呼叫。

```csharp theme={null}
// Set development key and title id
PlayFabSettings.DeveloperSecretKey = "__DEVELOPER_KEY__";
PlayFabSettings.TitleId = "__TITLE_ID__";

public static async Task SetApiPermission(bool restrictCustomId)
{
    // The first statement denies every call to LoginWithCustomID that is not properly encrypted
    var filterCustom = new PermissionStatement
    {
        // Statement effects any action
        Action = "*",
        // Filter the case where there is no signature and payload is not encrypted
        ApiConditions = new ApiCondition()
        {
            HasSignatureOrEncryption = Conditionals.False
        },
        Comment = "Deny every request to LoginWithCustomID that is not properly encrypted",
        // Specify the resource name
        Resource = "pfrn:api--/Client/LoginWithCustomID", // Resource name
        // Deny any of such requests
        Effect = EffectType.Deny,
        // For any user
        Principal = "*"
    };
    // The second statement allows every other API call
    var filterNothing = new PermissionStatement()
    {
        // Statement effects any action
        Action = "*",
        Comment = "Allow the rest API calls",
        // For any resource name
        Resource = "pfrn:api--*",
        // Allow any request
        Effect = EffectType.Allow,
        // For any user
        Principal = "*"
    };

    // Update the policy
    var request = new UpdatePolicyRequest()
    {
        // ApiPolicy controls access to API methods
        PolicyName = "ApiPolicy",
        // In this example we overwrite the policy. Consider appending to the existing policy instead.
        OverwritePolicy = true,
        // Introduce policy statements
        Statements = new List<PermissionStatement> { filterNothing }
    };
    if (restrictCustomId)
        request.Statements.Add(filterCustom);
    var result = await PlayFabAdminAPI.UpdatePolicyAsync(request);

    // Handle possible errors
    if (result.Error != null)
        Console.WriteLine(result.Error.GenerateErrorReport());
    else
        Console.WriteLine("Policy updated");
}
```

## 設定用戶端

現在原則已更新，您就無法再直接呼叫 `LoginWithCustomID` API。請參考下列程式碼。

```csharp theme={null}
var result = await PlayFabClientAPI.LoginWithCustomIDAsync(new LoginWithCustomIDRequest()
{
    CreateAccount = true,
    CustomId = "Some_Custom_Id"
});

if (result.Error != null)
{
    Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
    Console.WriteLine(result.Result.PlayFabId);
}
```

一般而言，這會順利登入使用者。不過，*現在*此 API 呼叫已受到*保護*，而程式碼將產生 `Not Authorized` 錯誤 (請勿與 `Not Authenticated` 混淆)。

我們需要修改用戶端，以正確加密呼叫承載。這分為 2 個步驟：

1. 使用玩家共用秘密擷取遊戲公開金鑰。
2. 使用遊戲公開金鑰加密承載。

下列程式碼示範了這一點。

```csharp theme={null}
public static async Task DoEncryptedLogin()
{
    Console.WriteLine("Begin DoEncryptedLogin");

    // Use Player Shared Secret to get Title Public Key
    var titleKeyResult = await PlayFabClientAPI.GetTitlePublicKeyAsync(new GetTitlePublicKeyRequest
    {
        TitleId = TITLE_ID,
        TitleSharedSecret = CLIENT_SECRET_KEY
    });

    Console.WriteLine("Encrypt request");
    // Convert public key to bytes
    var cspBlob = Convert.FromBase64String(titleKeyResult.Result.RSAPublicKey);

    // Serialize certain part of the model into string (this will be encrypted).
    var encryptionModel = JsonWrapper.SerializeObject(new LoginWithCustomIDRequest { CustomId = "SOME_PLAYER_ID_ENCRYPTED" });
    string encryptedPayload;

    // RSA encryption
    using (var rsa = new RSACryptoServiceProvider())
    {
        rsa.ImportCspBlob(cspBlob);
        var bytesToEncrypt = Encoding.UTF8.GetBytes(encryptionModel);
        var encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
        encryptedPayload = Convert.ToBase64String(encryptedBytes);
    }

    // Use encrypted payload to construct a model
    var model = new LoginWithCustomIDRequest
    {
        EncryptedRequest = encryptedPayload,
        PlayerSecret = CLIENT_SECRET_KEY,
        CreateAccount = true
    };

    Console.WriteLine("Call LoginWithCustomIDAsync");
    // Finally execute the call
    var result = await PlayFabClientAPI.LoginWithCustomIDAsync(model);
    Console.WriteLine("LoginWithCustomIDAsync done");
    bool successful = result.Error == null && result.Result != null;
    if (!successful)
        Console.WriteLine(result.Error.GenerateErrorReport());
    else
        Console.WriteLine("Login Successful" + result.Result.PlayFabId);
}
```

執行程式碼後，您應該就能登入。請記住，一旦建立玩家共用秘密，就必須將其硬式編碼至用戶端程式碼中，因為*無法*透過任何 API 呼叫擷取或要求它。


## Related topics

- [PlayFab 2017 版本資訊](/zh-TW/services/playfab/release-notes/2017.md)
- [玩家加密服務](/zh-TW/services/playfab/identity/player-identity/encryption/player-encryption-services.md)
- [IXtfUserClient::AddUser 方法](/zh-TW/reference/tools/xtf/xtfuser/classes/IXtfUserClient/methods/adduser-ixtfuserclient-xtfuser-xbox-windows-m.md)
- [IXtfUserClient::SigninUser](/zh-TW/reference/tools/xtf/xtfuser/classes/IXtfUserClient/methods/signinuser-ixtfuserclient-xtfuser-xbox-windows-m.md)
- [IXtfUserClient::SigninUserId](/zh-TW/reference/tools/xtf/xtfuser/classes/IXtfUserClient/methods/signinuserid-ixtfuserclient-xtfuser-xbox-windows-m.md)
