AWS
Swift
kms

AWS KMSをSwiftで使う

AWS KMSを使って暗号化、復号化を行います。
データキーによる暗号化も出来るのですが、今回はマスターキーを使った暗号化をやります。

環境

XCode 9.4.1
Swift 4.1

手順

CognitoでPool idを作成

CognitoはKMSというよりもAWS SDKそのものを使うために必要です。

スクリーンショット 2018-10-10 10.41.37.png

「IDプールの管理」をクリックします。

スクリーンショット 2018-10-10 10.41.55.png

「新しいIDプールの作成」をクリック

スクリーンショット 2018-10-10 10.42.33.png

IDプール名を入力し、「認証されていないIDに対しアクセスを有効にする」にチェックを入れます。
「プールの作成」ボタンを押すとIDプールが作成されます。

スクリーンショット 2018-10-10 11.04.08.png

ここのプールIDが後々必要になります。

KMSでマスターキーを作成

IAMを開きます。

スクリーンショット 2018-10-10 10.45.21.png

マスターキーを作る前にCognitoのロールに対し権限を追加します。

スクリーンショット 2018-10-10 10.46.39.png

Cognitoの2つのロールに対しAWSKeyManagementServicePowerUserを付加しました。

スクリーンショット 2018-10-10 10.47.01.png

いよいよマスターキー作成です。
KMSは単独のサービスではなく、IAMの中の「暗号化キー」にあります。

ここで一つ注意があります。
上記画面はリージョンが米国東部になっていますので、必ず東京に変更してから操作を始めてください。

スクリーンショット 2018-10-10 10.47.51.png

手順に従い進めます。

スクリーンショット 2018-10-10 10.48.13.png

この辺りは適当に

スクリーンショット 2018-10-10 10.49.03.png

マスターキーの管理ユーザーを指定します。

スクリーンショット 2018-10-10 10.49.22.png

キーの使用許可ユーザーを指定します。
私はここに、管理ユーザーと先程のCognitoの2ロールを指定しました。

スクリーンショット 2018-10-10 10.49.38.png

プレビューして完了です。

スクリーンショット 2018-10-10 11.14.41.png

このARNを後で使います。

Cocoapodでインストール

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'awskmsTest' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for awskmsTest
  pod 'AWSKMS'

end

Cognitoによる初期化

AppDelegate.swift
import AWSCore
import AWSKMS

let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.APNortheast1,identityPoolId:"YOUR_POOL_ID")
let configuration = AWSServiceConfiguration(region:.APNortheast1, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration

暗号化、復号化

hoge.swift
let str = "本日は晴天なり"
let client = AWSKMS.default()

let encrypt = AWSKMSEncryptRequest()
let data_ = str.data(using: .utf8)!

var encryptData:Data?

encrypt?.plaintext = data_
encrypt?.keyId = "YOUR KEY ID"
client.encrypt(encrypt!).continueWith { (task: AWSTask<AWSKMSEncryptResponse>) -> Any? in
    let response = task.result
    print(task.error ?? "(nil)")
    print("Encrypted data")
    print(response?.ciphertextBlob?.base64EncodedString() ?? "(nil)")

    encryptData = response?.ciphertextBlob
    return nil
} .waitUntilFinished()

let decrypt = AWSKMSDecryptRequest()
decrypt?.ciphertextBlob = encryptData!

client.decrypt(decrypt!).continueWith { (task: AWSTask<AWSKMSDecryptResponse>) -> Any? in
    let response = task.result
    print(task.error ?? "(nil)")
    print("Decrypted data")
    print(String(data: (response?.plaintext)!, encoding: .utf8) ?? "(nil)")
    return nil
} .waitUntilFinished()

パケットキャプチャで確認

Charles使って中身を確認してみます。

スクリーンショット 2018-10-09 14.32.54.png

これはencryptのjsonなんですが、Plaintextの「本日は晴天なり」もそのまま送っているのではないようです。
byte列を16進化しただけであればF以降の文字は出ませんし。

node.jsのソースを見てbase64エンコードと気付きました。失礼しました。

エラー、トラブル等

An error occurred (NotFoundException) when calling the Encrypt operation: Invalid arn

リージョンを全て合わせておかないとエラーになるようです。
今回は全て東京で合わせましたが、最初は米国東部で作ってしまったため発見までに時間がかかりました。

コードからマスターキーの生成が出来ない

ロールに対し権限を付与しているにも関わらず、権限が無いとのことで作成できませんでした。
この辺りは追加で調査が必要です。

おまけ:Lambdaに暗号化データを送って復号化する方法

こちらは参考記事がありました。
https://qiita.com/na0AaooQ/items/cfd9f2babd2ce604049b
https://qiita.com/Yuki_BB3/items/5a95bcef2e6d6156e749

参考

https://github.com/aws/aws-sdk-ios/issues/681
http://techtipshoge.blogspot.com/2017/08/kms.html
https://teratail.com/questions/97100