> ## 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 player shared secret、title 公钥以及用于保护账户创建和客户端登录请求的 RSA 加密注册载荷。

## Player shared secrets

Player shared secrets 是一种新的伪密钥类型，在游戏客户端之间共享。使用 API，它可以换取该 title 的 RSA 公钥，并可用于执行账户注册。

title 可以有多个 player shared 密钥，并可以通过 Admin API 调用 [Create](xref:titleid.playfabapi.com.admin.authentication.createplayersharedsecret)、[Update](xref:titleid.playfabapi.com.admin.authentication.updateplayersharedsecret)、[Delete](xref:titleid.playfabapi.com.admin.authentication.deleteplayersharedsecret)、[List](xref:titleid.playfabapi.com.admin.authentication.getplayersharedsecrets) 随意设置和吊销它们。

Player shared secrets 应烘焙到相应的客户端中，因为没有客户端 API 来检索它们——无论是经过身份验证的还是其他方式。

## Title 公钥

然后将 player shared secret 发送到 [GetTitlePublicKey](xref:titleid.playfabapi.com.client.authentication.gettitlepublickey)。如果密钥有效，该 API 返回一个 Base 64 编码的 RSA CSP blob 字节数组，可以加密 237 字节的数据。

所有允许创建账户的 API 现在都接受将注册请求作为加密载荷发布到 `EncryptedRequest` 字段。

<Note>
  标准字段 **`TitleId`**、**`InfoRequestParameters`** 和 **`CreateAccount`** *不*应包含在加密载荷中。
</Note>

## 使用 title 公钥进行注册

以下是使用 `LoginWithCustomID` 和 title 公钥注册玩家的示例代码。

```csharp theme={null}
var titleKeyResult = PlayFabClientAPI.GetTitlePublicKey(new GetTitlePublicKeyRequest{ TitleId = "TITLE", TitleSharedSecret = "player shared secret" });

var cspBlob = Convert.FromBase64String(titleKeyResult.RSAPublicKey);

var encryptionModel = JsonConvert.SearializeObject(new LoginWithCustomIDRequest

{
    CustomID = "my player's custom id",
    PlayerSecret = "my player's individual secret"
});

string encryptedPayload;

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

var postModel = new LoginWithCustomIDRequest
{
    TitleId = "TITLEID",
    EncryptedRequest = encryptedPayload,
    CreateAccount = True
};

var createAccountResult = PlayFabClientAPI.LoginWithCustomID(postModel);
```

## Player secret

新注册系统的一部分是一个名为 `PlayerSecret` 的新字段。如果设置，它允许你对请求标头进行签名，服务器在调用所有服务（包括登录请求）的 API 时对其进行验证。

Player secret 每个用户每个 title 只能设置*一次*。在同一工作室中有多个 title 的用户需要为每个 title 设置 player secret。

如果 player secret 在注册期间尚未设置，可以通过调用 [SetPlayerSecret](xref:titleid.playfabapi.com.client.authentication.setplayersecret) 来设置它。存在 admin 和 server API，即使 player secret 之前已被设置，也允许将其设置为*新*值。

<Note>
  一旦设置，player secret 应安全地存储在设备上，因为它*无法*恢复（如果丢失），并且*没有* API 可以恢复它。
</Note>

## 使用 player secret 签名 API 请求

以下代码示例构造一个签名标头，可用于签名 API 请求。签名标头的格式如下所示。

`jsonRequestModel.utcTimeStampInISO.playerSecret`

```csharp theme={null}
var postModel = new LoginWithCustomIDRequest

{
    TitleId = "TITLEID",
    CustomID =  "my player's custom id",
    CreateAccount = False
};

string signatureHeader;
string timestampHeader = DateTime.UtcNow.ToString("O");

string playerSecret; // the player secret that was sent during registration.

 using (var hash = new SHA256Managed())
{
    var bytesToHash = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(postModel)+"." + timestampHeader + "." + playerSecret);
    signatureHeader = hash.ComputeHash(bytesToHash);
}

var customHeaders = new Dictionary<string, string>
{
    { "X-PlayFab-Signature", signatureHeader },
    { "X-PlayFab-Timestamp", timestampHeader }
}

var loginResult = PLayFabClientAPI.LoginWithCustomID(postModel, customHeaders);
```

## 使用策略强制执行

[API 策略](https://playfab.com/blog/permission-policies/)现在可用于强制执行这些场景。

* 客户端请求是加密载荷。
* 客户端请求包含已签名的标头。

即使*不使用*策略强制执行，如果发送了加密载荷（或发送了标头），它们也会被验证。如果它们的格式不正确，则会发生错误。

要创建策略要求特定 API 上有标头，请使用 `Deny` 语句。这将创建一个策略，要求在你可以放置的、未被 `Allow` 语句许可的*所有*调用上有标头。

策略语句有一个称为 `ApiConditions` 的属性。`ApiConditions` 包含一个称为 `HasSignatureOrEncryption` 的属性，它是一个具有三个可能值的枚举：

* `Any`
* `True`
* `False`

<Note>
  默认值（如果策略*未*设置）是 **`Any`**。
</Note>

以下示例策略允许对 `LoginWithCustomID` 的*所有* API 调用（未加密或缺少标头的调用除外）。

```json theme={null}
{
    "PolicyName": "ApiPolicy",
    "OverwritePolicy": true,
    "Statements":
    [
        {
            "Comment": "Require Headers on LoginWithCustomID",
            "Action": "*",
            "Principal": "*",
            "Effect": "Deny",
            "Resource": "pfrn:api--/Client/LoginWithCustomID",
            "ApiConditions": { "HasSignatureOrEncryption": "False" }
        },
        {
            "Comment": "Allow the rest policy",
            "Action": "*",
            "Principal": "*",
            "Effect": "Allow",
            "Resource": "pfrn:api--*"
        }
    ]
}
```

上面的 `Deny` 语句包含 `HasSignatureOrEncryption: False`。这意味着所有没有签名或加密的请求都将被拒绝。换句话说，所有*具有*签名标头或加密的请求都将根据 `Allow the rest policy` 被允许。
