> ## 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);
}
```

이 코드를 실행하려면 개발자 시크릿 키가 필요합니다. 시크릿 키에 대한 자세한 내용은 [시크릿 키 관리](/services/playfab/live-service-management/gamemanager/secret-key-management)를 참조하세요.

이 애플리케이션은 새로 생성된 플레이어 공유 시크릿을 인쇄해야 합니다. *반드시 저장하세요*. 잃어버리면 애플리케이션을 다시 실행하여 새 시크릿을 생성해야 합니다.

시크릿은 다음과 같이 보입니다.

`QC953WQ3TU6ZJTZMAT1FNJQIKR92FPUQTISW4Q6WD8SY841MQQ`

## 정책 업데이트

새 공유 시크릿이 생성되었습니다. 이제 어떤 API 호출을 보호할지 PlayFab에 알려야 합니다.

아래 코드를 실행하여 `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

- [IXtfUserClient::AddUser Method](/ko/reference/tools/xtf/xtfuser/classes/IXtfUserClient/methods/adduser-ixtfuserclient-xtfuser-xbox-windows-m.md)
- [플레이어 암호화 서비스](/ko/services/playfab/identity/player-identity/encryption/player-encryption-services.md)
- [MSIXVC2 암호화 모델](/ko/build/core-features/common/packaging/packaging-encryption-msixvc2.md)
- [IXtfUserClient::SigninUser](/ko/reference/tools/xtf/xtfuser/classes/IXtfUserClient/methods/signinuser-ixtfuserclient-xtfuser-xbox-windows-m.md)
- [IXtfUserClient::SigninUserId](/ko/reference/tools/xtf/xtfuser/classes/IXtfUserClient/methods/signinuserid-ixtfuserclient-xtfuser-xbox-windows-m.md)
