LoginSignup
35
30

More than 3 years have passed since last update.

[swift]キーチェーンを使って値をセキュアに保持する

Posted at

はじめに

ユーザーが入力した値をセキュアに端末に保持する必要がありました。
UserDefaultは確かに永続的に値を保持することはできるけどセキュアではない・・。
そこで辿り着いたのがキーチェーンです。
ライブラリを使用してキーチェーンを利用する記事は何件か見たのですが、ライブラリ未使用のソース例があまりなかったので今回記したいと思います。

キーチェーンって何?というそもそもの話については、以下の記事が非常にわかりやすいためご一読ください。
iosのキーチェーンについて
[iOS] Keychain Services とは

特に2番目のやつは、めちゃめちゃ参考になりました。

ソース

サービス名「key」をキーとして、グループ・ID・パスワードを保存・参照・削除するという内容になります。
パスワードだけでOK!とかキーはいらない!という場合はよしなに修正すればOKだと思います。

KeyChain
import Foundation

class KeyChain {
    // 保存
    class func saveKeyChain()->Bool {
        // グループ
        let grp = "1st"
        // ID
        let id = "id"
        // パスワード
        let str = "password"
        let data = str.data(using: .utf8)

        guard let _data = data else {
            return false
        }

        // APIを実行する際の引数設定
        // これをSecItemCopyMatching の第一引数に渡すと結果を受け取ることができる
        let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword, // パスワードクラス
            kSecAttrGeneric as String: grp,                // 自由項目(グループ)
            kSecAttrAccount as String: id,                 // アカウント(ログインID
            kSecValueData as String: _data,                // パスワード本体
            kSecAttrService as String: "key"]            // サービス名

        // 検索用
        let search: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                     kSecAttrService as String: "key",                           //サービス名
            kSecReturnAttributes as String: kCFBooleanTrue as Any,
            kSecMatchLimit as String: kSecMatchLimitOne]as [String : Any] // 1件表示する

        print(dic)

        var itemAddStatus: OSStatus?
        // 保存データが存在するかの確認
        let matchingStatus = SecItemCopyMatching(search as CFDictionary, nil)
        if matchingStatus == errSecItemNotFound {
            // 保存
            itemAddStatus = SecItemAdd(dic as CFDictionary, nil)
        } else if matchingStatus == errSecSuccess {
            // 更新(削除 新規作成する)
            // 削除
            deleteKeyChain()
            // 新規
            itemAddStatus = SecItemAdd(dic as CFDictionary, nil)
        } else {
            return false
        }
        // 保存・更新ステータス確認
        if itemAddStatus == errSecSuccess {
            print("正常終了")
        } else {
            return false
        }
        return true
    }

    // 取得
    class func getKeyChain(key: String) -> (code1:String?, code2:String?, password: String?) {
        var code1: String!
        var code2 : String!
        var password : String!

        let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                  kSecAttrService as String: key,  //サービス名
            kSecReturnAttributes as String: kCFBooleanTrue as Any,
            kSecMatchLimit as String: kSecMatchLimitOne]as [String : Any]

        var result = noErr
        var queryResult: AnyObject?

        _ = withUnsafeMutablePointer(to: &queryResult){
            result = SecItemCopyMatching(dic as CFDictionary, UnsafeMutablePointer($0))
        }

        if result == noErr {
            // パスワード以外のキーを取得する
            var valueQuery = queryResult as! [String: AnyObject]
            valueQuery[kSecClass as String] = kSecClassGenericPassword
            valueQuery[kSecReturnData as String] = kCFBooleanTrue

            print(valueQuery)

            // 値を取り出す
            if let gena = valueQuery["gena"], let acct = valueQuery["acct"] {
                code1 = gena as? String
                code2 = acct as? String
                print(code1 ?? "",code2 ?? "")
            }else{
                // 取り出せなかったら抜ける
                return (nil,nil,nil)
            }

            // パスワードを取得する
            var valueQueryResult: AnyObject?

            _ = withUnsafeMutablePointer(to: &valueQueryResult){
                result = SecItemCopyMatching(valueQuery as CFDictionary, UnsafeMutablePointer($0))
            }

            if result == noErr {
                if let passwordData = valueQueryResult as? Data {
                    password = String(data: passwordData, encoding: .utf8)
                    print(password ?? "")
                }else{
                    // 取り出せなかったら抜ける
                    return (nil,nil,nil)
                }
            }
        }
        return (code1, code2, password)
    }

    // 削除
    class func deleteKeyChain() {
        // 削除するqueryを設定
        let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                  kSecAttrService as String: "key"]

        if SecItemDelete(dic as CFDictionary) == errSecSuccess {
            print("削除成功")
        } else {
            print("削除失敗")
        }
    }
}

キーチェーンで使われているキーやクラスがどうも使いこなせなくて、実装に躓きかけたのですが
コードのひな型ができればこっちのもんですよね。
ライブラリを導入していればこんなにつらつらと書かなくても数行で済むんですかね?
こういう書き方あるよー!もっとこうしたほうがいいよー!があれば教えてください。

35
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
30