[TIL] Unity - AES, Base64 - day 89

뭉크의 개발·2023년 11월 22일
0

Unity - Camp

목록 보기
57/70
post-thumbnail

🐧 들어가기 앞서

플레이어의 게임 데이터를 저장하기 위해 JSON을 이용할 예정이다.

이때 고려할 점으로 플레이어 데이터를 조작하는 상황을 방지하기 위해 암호화 알고리즘을 어떻게 적용할지 고민해봤다.

AES (Advanced Encryption Standard) 알고리즘을 사용하여 데이터 매니저에서 세이브와 로드 기능을 하고 싶다.

AES?

  • 키 생성: AES 알고리즘은 대칭 키 암호화 방식을 사용한다. 즉, 암호화와 복호화에 같은 키를 사용합니다. 따라서 안전한 키를 생성하고 관리하는 것이 중요하다.

  • 암호화: 저장하기 전에 데이터를 암호화한다. AES 알고리즘은 블록 암호화 방식이며, 일반적으로 128, 192, 256 비트 키를 사용한다.

  • 저장: 암호화된 데이터를 저장소에 저장한다. 이 데이터는 원본 데이터와 달리 외부에서 읽을 수 없다.

  • 복호화: 데이터를 다시 사용하기 위해서는 저장된 암호화된 데이터를 복호화해야 한다. 이 때 사용하는 키는 암호화할 때 사용했던 것과 동일해야 한다.

  • 데이터 사용: 복호화된 데이터를 원하는 방식으로 사용한다.

Base 64?

Base64는 바이너리 데이터를 ASCII 문자열로 인코딩하는 방식 중 하나다.

이 인코딩 방식은 바이너리 데이터를 전자 메일과 같이 순수 텍스트만을 지원하는 시스템을 통해 전송할 필요가 있을 때 유용하게 사용된다.

Base64는 주로 이메일, 웹 페이지의 데이터 URI 스킴 등에서 데이터를 텍스트 형식으로 전송하거나 저장할 때 사용된다.


🐧 오늘 배운 것

우선 구현 순서는 아래와 같다.

  1. 데이터 클래스 정의: 저장할 데이터를 포함하는 클래스를 정의.
  • JSON 직렬화 및 역직렬화: JsonUtility를 사용하여 데이터 객체를 JSON 문자열로 변환하고, JSON 문자열에서 데이터 객체로 변환.

  • AES 암호화 및 복호화: AES 알고리즘을 사용하여 데이터를 암호화하고 복호화.

  • 파일 저장 및 불러오기: 암호화된 데이터를 파일로 저장하고, 파일에서 데이터를 불러온 후 복호화.

  • 세이브 로드 매니저 클래스 구현: 위의 기능들을 포함하는 클래스를 제작.

만약 저장하고 싶은 플레이어 데이터 내용이 아래와 같다고 가정해보자.

[System.Serializable]
public class GameData
{
    public int health;
    public string name;
    public int gold;
    public int clearCount;
}

AES 암/복호화 코드

AES 암호화 및 복호화를 위해 System.Security.Cryptography 네임스페이스의 기능을 사용한다.

키와 IV(Initialization Vector)가 필요한데, 여기서는 단순화를 위해 고정된 키와 IV를 사용하지만, 실제 어플리케이션에서는 더 안전한 방법으로 키 관리를 해야 한다. (ex, 외부 시스템 또는 서버 보관 등등)

추가적으로 보안 향상을 위한 동적 키 생성을 진행하면, 실행마다 또는 정기적으로 키를 변경하여 안전하게 관리할 수 있다.

using System.Security.Cryptography;
using System.Text;

public static class AESManager
{
    private static byte[] key = Encoding.UTF8.GetBytes("여기에16바이트키입력"); // 16바이트 키
    private static byte[] iv = Encoding.UTF8.GetBytes("여기에16바이트IV입력"); // 16바이트 IV

    public static string EncryptString(string plainText)
    {
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = key;
            aesAlg.IV = iv;

            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
            byte[] encrypted = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(plainText), 0, plainText.Length);

            return System.Convert.ToBase64String(encrypted);
        }
    }

    public static string DecryptString(string cipherText)
    {
        byte[] buffer = System.Convert.FromBase64String(cipherText);

        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = key;
            aesAlg.IV = iv;

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
            byte[] decrypted = decryptor.TransformFinalBlock(buffer, 0, buffer.Length);

            return Encoding.UTF8.GetString(decrypted);
        }
    }
}

Save / Load

using System.IO;
using UnityEngine;

public static class SaveLoadManager
{
    private static string filePath = Application.persistentDataPath + "/gamedata.json";

    public static void SaveGameData(GameData data)
    {
        string json = JsonUtility.ToJson(data);
        string encryptedJson = AESManager.EncryptString(json);
        File.WriteAllText(filePath, encryptedJson);
    }

    public static GameData LoadGameData()
    {
        if (File.Exists(filePath))
        {
            string encryptedJson = File.ReadAllText(filePath);
            string json = AESManager.DecryptString(encryptedJson);
            return JsonUtility.FromJson<GameData>(json);
        }
        return new GameData(); // 기본 데이터 반환
    }
}

결과

만약 단일 JSON 파일이라면 아래와 같지만

{"name": "chad", "health": 50, "gold": 40, "clearCount": 10}

AES로 암/복호화를 진행하고 이 데이터를 Base 64로 인코딩 했을 때 아래와 같이 알아볼 수 없는 형태로 나온다.

lh2F0TApr56cE6AwTmRoDpT9lUE/asq8W65YzcS4TqGCCdlq6M2GECN1CXW4kW7SjEmjfgxvG/8upPqrS7nEiw==

0개의 댓글