Unity入門

【Unity】JsonUtilityでデータを保存・読み込み・暗号化(Base64,AES)

この記事について

この記事では JsonUtility を使ってデータを保存・読み込みする方法について解説します。
クラスを JSON 形式に変換して保存できるため、セーブデータやアイテム管理などに便利です。
さらに、データの存在確認、パスワードチェック、暗号化(Base64 / AES) についても紹介します。

この記事でわかること

  • JsonUtility とは何か
  • ToJson / FromJson でデータを保存・読み込みできる
  • データの存在確認(File.Exists(filePath))
  • JSON文字列の暗号化(Base64 / AES)

JsonUtilityとは?

JsonUtilityは Unityが提供するJSONシリアライズ機能 です。
クラスや構造体を JSON 文字列に変換(保存)、または JSON 文字列をクラスに変換(読み込み)できます。
※勘違いしやすいですがJsonUtilityがデータの保存・読み込みをするのではなく、「保存するための構造体」みたいなものです。

特徴

  • 軽量で高速(Unity 内部に最適化された仕組み)
  • public フィールドのみ対象(プロパティや private は保存されない)
  • [Serializable] 属性が必要

JsonUtilityの準備

まずはヒエラルキーで右クリックをして空のGameObjectを1つ用意します。
・「右クリック」→「Create Empty」

次にスクリプトを作成して先ほどのCreate Emptyにアタッチしてください。

JsonUtilityの保存・存在確認・読み込み

クラスを定義する

JsonUtilityを使用する場合はデータの構造を「クラス+[System.Serializable]」で書いておく必要があります。

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}

JsonUtility.ToJson()でデータを保存する

データの保存にはPlayerPrefsやクラウド保存もできますが今回はFileを使用します。
また、保存前にJSONに変換するために「JsonUtility.ToJson(クラス名,prettyPrint)」で変換します。
※prettyPrintはtrueで一行にまとめる、falseで改行をしてくれます。

using System.IO;
using UnityEngine;

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}


public class Data_JsonUtility : MonoBehaviour
{
    private string filePath;

    void Start()
    {
        filePath = Application.persistentDataPath + "/playerData.json";

        PlayerData data = new PlayerData();
        data.Name = "No Name";
        data.Level = 100;
        data.Score = 12.3f;
        data.password = "MySecret";

        // JSONに変換
        string json = JsonUtility.ToJson(data, true);

        // ファイルに保存
        File.WriteAllText(filePath, json);

        Debug.Log("保存完了: " + filePath);
    }
}

実行してみると、構造通りにデータが保存されていることがわかりますね。
※Application.persistentDataPathを使うと、スマホでもPCでもアプリごとに専用の保存場所を取得できます。

JsonUtility.FromJson()でデータを読み込む

データの読み込みにもPlayerPrefsやクラウド保存もできますが今回はFileを使用します。
また、読み込み時にデータの存在確認「File.Exists(filePath)」とJSONから変換するために「JsonUtility.FromJson()<クラス名>」で変換します。

using System.IO;
using UnityEngine;

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}


public class Data_JsonUtility : MonoBehaviour
{
    private string filePath;
    private string Dummy_filePath; //ダミーパス

    void Start()
    {
        filePath = Application.persistentDataPath + "/playerData.json";
        Dummy_filePath = Application.persistentDataPath + "/player.json";

        //データが存在しない場合
        if (!File.Exists(Dummy_filePath))
        {
            Debug.Log("データが存在しません");
        }
        //データが存在する場合
        if (File.Exists(filePath))
        {
            string json = File.ReadAllText(filePath);
            PlayerData loadedData = JsonUtility.FromJson<PlayerData>(json);

            Debug.Log("名前: " + loadedData.Name);
            Debug.Log("レベル: " + loadedData.Level);
            Debug.Log("スコア: " + loadedData.Score);
            Debug.Log("パスワード: " + loadedData.password);
        }
    }
}

実行してみると、Jsonで保存していたデータが読み込みできましたね。今回は実装しませんでしたが、Jsonの中にパスワードを入れているので、簡易的なパスワードチェックは実装できますね。

JSON文字列の暗号化

※暗号化の知識はないので実装方法のみ今回は記載しています。

Base64(簡易)で暗号化をする

Base64でデータの保存

直接読まれたくない場合はBase64で隠すだけでも効果があります。

using System;
using System.IO;
using UnityEditor.Overlays;
using UnityEngine;

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}

public class Data_JsonUtility : MonoBehaviour
{
    private string filePath;

    void Start()
    {
        filePath = Application.persistentDataPath + "/playerData.json";

        PlayerData data = new PlayerData();
        data.Name = "No Name";
        data.Level = 100;
        data.Score = 12.3f;
        data.password = "MySecret";

        Save(data);
    }

    // 保存
    public void Save(PlayerData data)
    {
        string json = JsonUtility.ToJson(data);
        string enc = Base64Helper.Encrypt(json);
        File.WriteAllText(filePath, enc);
        Debug.Log("保存完了");
    }
}

//Base64に暗号化
public static class Base64Helper
{
    public static string Encrypt(string plainText)
    {
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(plainText);
        return Convert.ToBase64String(bytes);
    }
}

実行してみると、暗号化されていて難読化し何を書いているのかさっぱりですね。

Base64でデータの読み込み

次はこの暗号化されたファイルで読み込みを行っていきます。

using System;
using System.IO;
using UnityEditor.Overlays;
using UnityEngine;

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}

public class Data_JsonUtility : MonoBehaviour
{
    private string filePath;

    void Start()
    {
        filePath = Application.persistentDataPath + "/playerData.json";

        PlayerData data = new PlayerData();

        data = Load();
        Debug.Log("名前: " + data.Name);
        Debug.Log("レベル: " + data.Level);
        Debug.Log("スコア: " + data.Score);
        Debug.Log("パスワード: " + data.password);
    }

    // 読み込み
    public PlayerData Load()
    {
        if (!File.Exists(filePath))
        {
            Debug.LogWarning("セーブデータがありません");
            return null;
        }

        string enc = File.ReadAllText(filePath);
        string json = Base64Helper.Decrypt(enc);
        return JsonUtility.FromJson<PlayerData>(json);
    }
}

//Base64の暗号化を解読
public static class Base64Helper
{
    public static string Decrypt(string encodedText)
    {
        byte[] bytes = Convert.FromBase64String(encodedText);
        return System.Text.Encoding.UTF8.GetString(bytes);
    }
}

実行してみると暗号化はそのままで読み込めました。

AES(本格的)で暗号化する

AESでデータの保存

セーブデータを改ざんされにくくしたいなら AES暗号化 を使う方法もあります。
※暗号化は必ずしも解読されないというものではありません。(絶対に侵入されない暗号化は投稿者見つけられておりません。)

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEditor.Overlays;
using UnityEngine;
using UnityEngine.InputSystem;

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}

public class Data_JsonUtility : MonoBehaviour
{
    private string filePath;

    void Start()
    {
        filePath = Application.persistentDataPath + "/playerData.json";

        PlayerData data = new PlayerData();
        data.Name = "No Name";
        data.Level = 100;
        data.Score = 12.3f;
        data.password = "MySecret";

        Save(data);
    }

    public void Save(PlayerData data)
    {
        string json = JsonUtility.ToJson(data);
        string enc = AESHelper.Encrypt(json);
        File.WriteAllText(filePath, enc);
        Debug.Log("AES保存完了");
    }

    public PlayerData Load()
    {
        if (!File.Exists(filePath))
        {
            Debug.LogWarning("AESセーブデータがありません");
            return null;
        }

        string enc = File.ReadAllText(filePath);
        string json = AESHelper.Decrypt(enc);
        return JsonUtility.FromJson<PlayerData>(json);
    }
}

//AESで暗号化
public static class AESHelper
{
    private static readonly string Key = "12345678901234567890123456789012"; // 32文字(256bit)
    private static readonly string IV = "1234567890123456"; // 16文字(128bit)

    public static string Encrypt(string plainText)
    {
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(Key);
            aes.IV = Encoding.UTF8.GetBytes(IV);
            ICryptoTransform encryptor = aes.CreateEncryptor();

            using (MemoryStream ms = new MemoryStream())
            using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            using (StreamWriter sw = new StreamWriter(cs))
            {
                sw.Write(plainText);
                sw.Close();
                return Convert.ToBase64String(ms.ToArray());
            }
        }
    }
}

実行してみると、暗号化されていますね。
AESなら「鍵(Key, IV)」を知らないと復号できないので、単なるBase64より安全です。

AESでデータの読み込み

次はこの暗号化されたファイルで読み込みを行っていきます。

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEditor.Overlays;
using UnityEngine;
using UnityEngine.InputSystem;

[System.Serializable]
public class PlayerData
{
    public string Name;
    public int Level;
    public float Score;
    public string password; // パスワード(後でチェック用)
}

public class Data_JsonUtility : MonoBehaviour
{
    private string filePath;

    void Start()
    {
        filePath = Application.persistentDataPath + "/playerData.json";

        PlayerData data = new PlayerData();

        data = Load();
        Debug.Log("名前: " + data.Name);
        Debug.Log("レベル: " + data.Level);
        Debug.Log("スコア: " + data.Score);
        Debug.Log("パスワード: " + data.password);
    }

    public PlayerData Load()
    {
        if (!File.Exists(filePath))
        {
            Debug.LogWarning("AESセーブデータがありません");
            return null;
        }

        string enc = File.ReadAllText(filePath);
        string json = AESHelper.Decrypt(enc);
        return JsonUtility.FromJson<PlayerData>(json);
    }
}

//AESの暗号化を解読
public static class AESHelper
{
    private static readonly string Key = "12345678901234567890123456789012"; // 32文字(256bit)
    private static readonly string IV = "1234567890123456"; // 16文字(128bit)

    public static string Decrypt(string cipherText)
    {
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(Key);
            aes.IV = Encoding.UTF8.GetBytes(IV);
            ICryptoTransform decryptor = aes.CreateDecryptor();

            byte[] buffer = Convert.FromBase64String(cipherText);

            using (MemoryStream ms = new MemoryStream(buffer))
            using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
            using (StreamReader sr = new StreamReader(cs))
            {
                return sr.ReadToEnd();
            }
        }
    }
}

実行してみると暗号化はそのままで読み込めました。

注意点

  • [Serializable] を忘れると保存されない
  • 保存できるのは public フィールドのみ
  • Base64 はあくまで「難読化」、セキュリティにはならない
  • AES は強力だが「Key, IV」をソースコード内に書くとバレる可能性あり
    → 本格的なセキュリティが必要なら サーバー管理 がベスト

補足

Base64とは?

  • 正式名称:Base64 Encoding
  • 意味:
    • データを 64種類の文字(A–Z, a–z, 0–9, +, /)で表現するエンコード方式
    • もともとバイナリデータ(画像やファイルなど)を文字列として扱えるようにするために考案された方式
  • 用途:
    • メール(MIMEエンコード)
    • JSONやXMLにバイナリを埋め込みたいとき
    • 簡易的な難読化(暗号化ではない)
  • 「Base64」は “基数64の変換方式” という意味です。

AESとは?

  • 正式名称:Advanced Encryption Standard(高度暗号化標準)
  • 意味:
    • 2001年に米国標準技術研究所(NIST)が採用した 共通鍵暗号方式 の標準規格
    • Rijndael(ラインダール)という暗号方式が採用され、現在もっとも広く使われている暗号化規格
  • 用途:
    • HTTPS 通信
    • Wi-Fi (WPA2, WPA3)
    • ファイル暗号化、銀行・政府機関のセキュリティ
  • 「AES」は “高度暗号化規格” という意味で、世界標準の暗号方式です。

まとめ

  • JsonUtility → データを JSON 化して保存・読み込み
  • File.Exists(filePath) → 存在確認できる
  • Base64 → 簡易暗号化(改ざん防止には不十分)
  • AES → 本格的にデータを守りたいときに有効