【Swift】CryptoKitフレームワークの使い方!データの暗号化・複合化

この記事からわかること

  • SwiftCryptoKitフレームワーク使い方
  • データ暗号化複合化する方法
  • セキュリティ対策
  • AES.GCM.SealedBox型とは?
  • SymmetricKey永続化する方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

暗号化や複合化、ハッシュ化の種類や仕組み自体についてはCryptoSwiftの記事でまとめてありますのでそちらを参考にしてください。CryptoSwiftCryptoKitと同じような機能を提供するライブラリです。

CryptoKitフレームワークとは?

公式リファレンス:CryptoKitフレームワーク

CryptoKitはAppleが提供する暗号化および暗号関連の操作を実行するためのフレームワークです。

暗号化する

CryptoKit「共通鍵暗号方式」のAES-GCM方式で暗号化し、Base64エンコードされた文字列として返す処理を実装してみます。

import UIKit
import CryptoKit

class CryptoManager {
    // 共通の暗号化キー
    private let encryptionKey = SymmetricKey(size: .bits256)
    
    func encryption(word: String) -> String {
        do {
            // UTF-8エンコーディングでバイト配列に変換
            let data = Data(word.utf8)
            
            // AES-GCM暗号化アルゴリズムを使用して暗号化
            let sealedBox: AES.GCM.SealedBox = try AES.GCM.seal(data, using: encryptionKey)
            
            // 暗号化されたデータをBase64エンコード
            let base64EncodedData = sealedBox.combined?.base64EncodedData() ?? Data()
            
            // Base64エンコードされたデータをUTF-8文字列に変換
            let base64Str = String(data: base64EncodedData, encoding: .utf8) ?? "変換失敗"

            return base64Str
        } catch {
            return "変換失敗"
        }
    }
}

「鍵(SymmetricKey)」の生成

公式リファレンス:SymmetricKey

暗号化・復号化を行うためには必要な鍵はSymmetricKey型で用意します。SymmetricKey共通鍵暗号方式で使用される鍵を表すSwiftの構造体です。

引数sizeに指定するのは鍵のビットサイズです。鍵のサイズは大きいほどセキュリティが高まりますが、処理が重くなるので注意が必要です。

// 共通の暗号化キー
private let encryptionKey = SymmetricKey(size: .bits256)

ここで生成する鍵は生成するごとに別の値になってしまうのでアプリ内で使用する場合は生成した鍵をローカルに保存して永続化しておく必要があります。永続化しておかないと暗号化したものを2度と複合化できなくなってしまうので注意してください。

暗号化:AES.GCM.sealメソッド

公式リファレンス:AES.GCM.sealメソッド

実際に暗号化を行なっているのはAES.GCM.sealメソッドです。sealメソッドは、与えられたデータと暗号化キーを使用してデータを暗号化し、結果としてAES.GCM.SealedBox構造体を返します。

// AES-GCM暗号化アルゴリズムを使用して暗号化
let sealedBox: AES.GCM.SealedBox = try AES.GCM.seal(data, using: encryptionKey)

公式リファレンス:AES.GCM.SealedBox

AES.GCM.SealedBox型は暗号化されたデータとそれに関連する認証タグを含むデータオブジェクトです。combinedプロパティから暗号化されたデータとその認証タグを含む単一のバイト配列を取得できます。

暗号化された値を文字列に変換する

バイト配列を取得できたら扱いやすいように文字列に変換していきます。そのためにはData型に変換した後、Base64でエンコードして、String型まで変換していきます。

// 暗号化されたデータをBase64エンコード
let base64EncodedData = sealedBox.combined?.base64EncodedData() ?? Data()

// Base64エンコードされたData型をUTF-8文字列に変換
let base64Str = String(data: base64Data, encoding: String.Encoding.utf8) ?? "変換失敗"

公式リファレンス:base64EncodedDataメソッド

Base64とは?

Base64とはバイナリデータをテキスト形式に変換するためのエンコーディング手法の1つです。A-Za-z0-9+/の64種類で文字を表します。
(※正確には=がパディング(余った部分を詰める用)として使用されるので65種類)

例えばHello World先ほどの共通鍵と初期化ベクトルで暗号化してBase64の文字列に変換すると以下の様になります。

G3+OszvIum5CR14z+6vv80mpLvx5CaUGOBAtmCW2FH5YW7zrmdDtfa8=

複合化する

CryptoKit「共通鍵暗号方式」のAES-GCM方式で暗号化されたデータを複合化するには暗号化で実装した流れとは逆のことを行っていきます。また使用する鍵とは同じものを使用してください。

func decryption(str: String) -> String {
    do {
        // 文字列をUTF-8エンコーディングしてバイトデータに変換
        guard let byteData = str.data(using: String.Encoding.utf8) else { return "変換失敗" }
        // Base64データをデコードして元のデータに戻す
        guard let data = Data(base64Encoded: byteData) else { return "変換失敗" }
        
        // AES.GCM.SealedBox型を取得する
        let sealedBox = try AES.GCM.SealedBox(combined: data)
        // 暗号化されたデータを複合化
        let decryptedData = try AES.GCM.open(sealedBox, using: encryptionKey)
        // 複合化されたデータをUTF-8エンコーディングで文字列に変換
        let decryptedText = String(data: decryptedData, encoding: .utf8) ?? "変換失敗"
        return decryptedText
    } catch {
        return "変換失敗"
    }
}

鍵を永続化する

SymmetricKey永続化しておかないと暗号化・複合化をできなくなってしまうので永続化された鍵がない場合のみ鍵を新規で生成するような実装にする必要があります。本当はKeyChainなどに保存するのが正しいですがUserDefaultsで簡単に実装する場合は以下のようになります。

private let keyUserDefaultsKey = "encryptionKey"

private lazy var encryptionKey: SymmetricKey = {
    if let savedKeyData = UserDefaults.standard.data(forKey: keyUserDefaultsKey),
        let savedKey = try? SymmetricKey(data: savedKeyData) {
        return savedKey
    } else {
        let newKey = SymmetricKey(size: .bits256)
        let keyData = newKey.withUnsafeBytes { Data($0) }
        UserDefaults.standard.set(keyData, forKey: keyUserDefaultsKey)
        return newKey
    }
}()

実装サンプル

SwiftUIで使用する場合の全体コードを載せておきます。

import SwiftUI
import CryptoKit

class CryptoManager {
    
    private let keyUserDefaultsKey = "encryptionKey" // ユーザーデフォルトに鍵を保存するためのキー
    
    private lazy var encryptionKey: SymmetricKey = {
        // ユーザーデフォルトから保存された鍵を取得する
        if let savedKeyData = UserDefaults.standard.data(forKey: keyUserDefaultsKey),
           let savedKey = try? SymmetricKey(data: savedKeyData) {
            return savedKey
        } else {
            // 初回のみ新しい鍵を生成してユーザーデフォルトに保存する
            let newKey = SymmetricKey(size: .bits256)
            let keyData = newKey.withUnsafeBytes { Data($0) }
            UserDefaults.standard.set(keyData, forKey: keyUserDefaultsKey)
            return newKey
        }
    }()
    
    func encryption(word: String) -> String {
        do {
            let data = Data(word.utf8)
            let sealedBox: AES.GCM.SealedBox = try AES.GCM.seal(data, using: encryptionKey)
            let base64EncodedData = sealedBox.combined?.base64EncodedData() ?? Data()
            let base64Str = String(data: base64EncodedData, encoding: .utf8) ?? "変換失敗"
            return base64Str
        } catch {
            return "変換失敗"
        }
    }
    
    func decryption(str: String) -> String {
        do {
            guard let byteData = str.data(using: String.Encoding.utf8) else { return "変換失敗" }
            guard let data = Data(base64Encoded: byteData) else { return "変換失敗" }
            let sealedBox = try AES.GCM.SealedBox(combined: data)
            let decryptedData = try AES.GCM.open(sealedBox, using: encryptionKey)
            let decryptedText = String(data: decryptedData, encoding: .utf8) ?? "変換失敗"
            return decryptedText
        } catch {
            return "変換失敗"
        }
    }
}


struct ContentView: View {
    private var cryptoManager = CryptoManager()
    
    var body: some View {
        VStack {
            Button(action: {
                let text = "Hello, World!"
                let encryptedData = cryptoManager.encryption(word: text)
                print("Encrypted: \(encryptedData)")
                UserDefaults.standard.set(encryptedData, forKey: "encryptedData")
            }) {
                Text("暗号化して保存する")
            }
            
            Button(action: {
                if let encryptedData = UserDefaults.standard.string(forKey: "encryptedData") {
                    let decryptedText = cryptoManager.decryption(str: encryptedData)
                    print("Decrypted: \(decryptedText)")
                } else {
                    print("保存された暗号化データが見つかりません")
                }
            }) {
                Text("取得して複合化")
            }
        }
    }
}

Androidで暗号化・複合化を実装する

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index