動機
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 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に記述されていなくて(まあそうか)、工夫が必要だった。
お読み頂きありがとうございました。