iOSアプリでAESでの暗号化のためCryptoSwiftを使用した

# 動機
iOSアプリでテキストをAESで暗号化→暗号化したテキストをサーバーサイドへ送信。
その後、暗号化したテキストをサーバーサイドから取得して、iOSアプリで復号して表示といった流れを実装する必要があった。

## AESとは
ざっくり書くと

+ 暗号化のやり方の一つ
+ 2018年現在のアメリカで標準として採用されているやり方
+ 共通鍵暗号方式
+ 鍵長が128ビット、192ビット、256ビットから選ぶことができる
+ WPA2で標準で採用されているやり方
+ WPAで使われる場合もある

https://wa3.i-3-i.info/word15121.html
https://techbooster.org/android/application/6629/

鍵、初期化ベクトルについては
https://qiita.com/asksaito/items/1793b8d8b3069b0b8d68

# 検証環境
+ Xcode 10.0
+ Swift 4.1
+ [CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift) 0.9.0

# ソース
抜粋しています

```EncryptionUtil.swift
import Foundation
import CryptoSwift

struct EncryptionUtil {
    
    // ----- encryption -----
    // key:鍵
    // iv:初期化ベクトル
    // target:暗号化する文字列
    static func encrypt(key: String, iv: String, target:String) -> String? {

        do {
            let aes = try AES(key: key, iv: iv)
            let cipherArray = try aes.encrypt(Array(target.utf8))

            // [UInt8]をbase64Stringに変換して返す
            let data = NSData(bytes: cipherArray, length: cipherArray.count)
            let base64Data = data.base64EncodedData(options: [])
            let base64String = String(data: base64Data as Data, encoding: String.Encoding.utf8)
            return base64String
        } catch {
            return nil
        }
    }
    
    // ----- decryption -----
    // key:鍵
    // iv:初期化ベクトル
    // base64:復号する文字列、base64エンコードされている
    static func decrypt(key: String, iv: String, base64:String) -> String? {
        
        do {
            let aes = try AES(key: key, iv: iv)

            // 対象をbase64Decodeしてから、[UInt8]に変換する
            // base64 -> Data
            let aData = base64.data(using: String.Encoding.utf8)! as Data
            // Decode base64
            let dData = NSData(base64Encoded: aData, options: [])
            guard let data = dData else {
                return nil
            }
            // dataのバイト数と同じ大きさの配列を作る。
            var aBuffer = Array<UInt8>(repeating: 0, count: data.length)
            // aBufferにバイナリデータを格納。
            data.getBytes(&aBuffer, length: data.length)
            
            let decrypted = try aes.decrypt(aBuffer)
            let string = String(data: Data(bytes: decrypted), encoding: .utf8)
            return string
        } catch {
            return nil
        }
    }

    // ----- Initialization Vector -----
    static func getInitializationVector() -> String {
        let s = randomString(length: 16)
        return s
    }
    
    static func randomString(length: Int) -> String {
        let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        let l = UInt32(letters.length)
        
        var randomString = ""
        
        for _ in 0 ..< length {
            let rand = arc4random_uniform(l)
            var nextChar = letters.character(at: Int(rand))
            randomString += NSString(characters: &nextChar, length: 1) as String
        }
        
        return randomString
    }
    
}
```

# テスト
テストコードで確認

```hogehoge_iosTests.swift
import XCTest
@testable import hogehoge_ios

class hogehoge_iosTests: XCTestCase {
    
    override func setUp() {
        super.setUp()
    }
    
    override func tearDown() {
        super.tearDown()
    }
    
    func testEncryptionUtil() {
        // 鍵(128ビット)
        let key = "keykeykeykeykeyk"
        // 初期化ベクトル
        let iv = EncryptionUtil.getInitializationVector()
        
        let text = "abcdeあいうえおアイウエオ東京都中央区1234567890@+-:"
        let encrypted = EncryptionUtil.encrypt(key:key, iv:iv, target:text)
        XCTAssertNotNil(encrypted)
        XCTAssertFalse(encrypted! == text)

        let decrypted = EncryptionUtil.decrypt(key: key, iv: iv, base64: encrypted!)
        XCTAssertTrue(decrypted! == text)
    }
}
```

# 所感
AESでの暗号化、復号はCryptoSwiftのおかげで簡易であった。
だが暗号化文字列のサーバーサイドとのやり取りのため、[UInt8]とbase64エンコード文字列との相互変換が必要であり、そのあたりは[CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift#aes)に記述されていなくて（まあそうか）、工夫が必要だった。

お読み頂きありがとうございました。