[アップデート] KMS で非対称キーペア(公開鍵暗号方式)が利用できるようになったのでデジタル署名を試してみました!

ちょっと日が経ってしまいましたが、先日のアップデートで KMS で非対称キーがサポートされるようになりました!

非対称キーって何?と思われる方もおられるかもしれませんが、「公開鍵暗号方式」と言ったほうが馴染みがあるでしょうか。公開鍵暗号方式については、以下の記事が参考になります。

非エンジニアに捧げる公開鍵暗号方式

何が嬉しいのか

これまで KMS がサポートしているのは対称キー(共通鍵暗号)のみでした。非対称キー(公開鍵暗号)を使いたい場合は CloudHSM を利用する必要がありましたが、1 HSM インスタンスあたり $1.81/時間 の課金が発生します。さらに可用性のため最低で 2 HSM インスタンスが必要となるので、だいたい1ヶ月(720時間換算)で $2,600 くらいするので、おいそれと使えるものではありませんでした。

CloudHSM はシングルテナントであったり、求められる暗号化アルゴリズムなど、それなりの要件に対応するためであって、単に「非対称キー使いたい」という場合はユーザー側で実装されているかと思います。

これが KMS として非対称キーが利用できるようになりましたので、デジタル署名の実装などが AWS マネージドな環境で提供できるようになりますね。かつ、対称キー同様に CMK あたり $1/月 + 微々たるリクエスト料金で利用できるのですから嬉しいに違いありません。

対応リージョン

執筆時点では以下の 5 リージョンでのみ利用可能です。

  • 米国東部(バージニア北部)
  • 米国西部(オレゴン)
  • アジア太平洋(シドニー)
  • アジア太平洋(東京)
  • EU(アイルランド)

対称キーと非対称キーの比較

キータイプ

非対称キーでも RSA キーペアと ECC(楕円曲線) キーペアでは利用可能範囲に違いがあるので注意ですね。

CMKタイプ 暗号化および復号 署名および検証
対称 CMK OK NG
非対称 CMK(RSA キーペア) OK OK
非対称 CMK(ECC キーペア NG OK

キー仕様と暗号化アルゴリズム

ECC キーペアの場合、暗号通貨などで一般的に利用される ECC_SECG_P256K1 (secp256k1) が使用できます。ただし、KMS 基盤はマルチテナントである点はお忘れなく。

キー 仕様 暗号化アルゴリズム 署名アルゴリズム
対称 CMK SYMMETRIC_DEFAULT SYMMETRIC_DEFAULT -
非対称 CMK(RSA) RSA_2048
RSA_3072
RSA_4096
RSAES_OAEP_SHA_1
RSAES_OAEP_SHA_256
RSASSA_PKCS1_V1_5_SHA_256
RSASSA_PKCS1_V1_5_SHA_384
RSASSA_PKCS1_V1_5_SHA_512
RSASSA_PSS_SHA_256
RSASSA_PSS_SHA_384
RSASSA_PSS_SHA_512
非対称 CMK(ECC) ECC_NIST_P256
ECC_NIST_P384
ECC_NIST_P521
ECC_SECG_P256K1
- ECDSA_SHA_256
ECDSA_SHA_384
ECDSA_SHA_512
ECDSA_SHA_256

デジタル署名の検証

それでは KMS 非対称キーを使ってデジタル署名を検証してみましょう。

デジタル署名のおさらい

以下は CloudHSM の Blackbelt 資料を拝借しました。

左から順に流れをみていくと

  1. 送信者は送りたいメッセージに対するダイジェスト値(ハッシュ値)を取得して、その値を秘密鍵で署名します
  2. 送信者はメッセージに署名をつけて、受信者に送ります
  3. 受信者は受け取ったメッセージのダイジェスト値を取得します
  4. また、メッセージにつけられている署名から公開鍵でダイジェスト値が取得できます
  5. 受信者側でメッセージから取得したダイジェスト値と、署名から取得したダイジェス値を突き合わせて同一であれば改ざんされていないことが判断できます(検証)。あわせて、正しい秘密鍵で署名されていることの確認にもなりますので、秘密鍵の所有者からのメッセージであることも判断できます

というのがデジタル署名です。この過程で仕様する秘密鍵と公開鍵および、署名と検証を KMS で利用できるようになったということですね。

検証環境

今回の検証環境は以下のとおりです。

項目
aws-cli 1.16.292
リージョン 東京リージョン
キーのタイプ 非対称
キーの使用 署名および検証
キーの仕様 ECC_SECG_P256K1(secp256k1)
署名を作成する AWS アカウント 6XXXXXXXXXXX
署名を検証する AWS アカウント 1XXXXXXXXXXX

非対称キーの作成

それでは非対称キーを作成してみましょう。KMS 管理画面から「カスタマー管理型のキー」-「キーの作成」を開きます。キーのタイプに「非対称」が追加されているので、これを選択します。「キーの使用」は「署名および検証」を選択し、「キーの仕様」については今回は暗号通貨などでお馴染みの「ECC_SECG_P256K1(secp256k1)」としました。

「キーの使用」は先の表のとおり、ECC(楕円曲線)キーペアを利用したい場合は「署名および検証」を選ぶ必要があります。RSA キーペアの場合は、「暗号化および復号」「署名および検証」のいずれかを選択します。

キーポリシーの設定

作成したキーに対して署名 kms:Sign の権限と、検証 kms:Verify の権限をそれぞれのアカウントの IAM ロールに付与します。

(署名する側)

{
    "Sid": "Allow use of the key for digital signing",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::6xxxxxxxxxxx:role/cm-marumo.atsushi"
    },
    "Action": "kms:Sign",
    "Resource": "*"
}

(検証する側)

{
    "Sid": "Allow use of the key for verification",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::1xxxxxxxxxxx:role/cm-marumo.atsushi-verify"
    },
    "Action": "kms:Verify",
    "Resource": "*"
}

署名する

署名する適当なファイルを準備します。

$ echo 'We are Classmethod!' > message

メッセージに対して署名するには kms sign を利用します。--key-id に先程作成したキーを指定しますが、「キー ID」「キー ARN」「エイリアス名」「エイリアス ARN」のいずれかで指定します。--signing-algorithm にはキーに対応する署名アルゴリズムを指定します。

$ aws kms sign --key-id alias/asymmetric-p256k1 --signing-algorithm ECDSA_SHA_256 --message fileb://message

署名が成功すると以下のようなレスポンスを受け取ります。

{
    "KeyId": "arn:aws:kms:ap-northeast-1:6XXXXXXXXXXX:key/32c4bf9f-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "SigningAlgorithm": "ECDSA_SHA_256",
    "Signature": "MEUCIBfP3GHcojUn9wui+KI6K9nFSA3cmToEL2aO8b+FqEEcAiEAnP+tUa2FlTZTEx2OPm+LTd1mhqc3YpI03JE8B5VDJYE="
}

message を渡すと KMS 側でハッシュダイジェストを生成し署名してくれるのですが、message が 4096 byte 以内である必要があります。4096 byte を超えると、いかのようなエラーになります。

An error occurred (ValidationException) when calling the Sign operation: 1 validation error detected: Value at 'message' failed to satisfy constraint: Member must have length less than or equal to 4096

4096 byte を超える場合はユーザー側でハッシュダイジェストを生成し、--message-typeDIGEST を指定して、message でダイジェストを渡して署名する形式になります。

検証する

次に署名を検証します。今回は別の AWS アカウントで KMS キーを呼び出して検証します。キーポリシーで kms:Verify を許可した IAM ロールであることを確認します。

$ aws sts get-caller-identity
{
    "Account": "1XXXXXXXXXXX",
    "UserId": "AROARQJ6NXXXXXXXXXXXX:i-0df5e68a545905b8a",
    "Arn": "arn:aws:sts::1XXXXXXXXXXX:assumed-role/cm-marumo.atsushi-verify/i-0df5e68a545905b8a"
}

kms sign のレスポンスに含まれる Signature を使って検証するのですが、--signatureblob で渡す必要があるためバイナリに戻します。

$ echo MEUCIBfP3GHcojUn9wui+KI6K9nFSA3cmToEL2aO8b+FqEEcAiEAnP+tUa2FlTZTEx2OPm+LTd1mhqc3YpI03JE8B5VDJYE= | base64 -d > signature

検証は kms verify を利用します。--signature に先程バイナリに戻した署名ファイルを指定します。別アカウントからの呼び出しになるので --key-id は ARN で指定しています。

$ aws kms verify --key-id arn:aws:kms:ap-northeast-1:6XXXXXXXXXXX:key/32c4bf9f-XXXX-XXXX-XXXX-XXXXXXXXXXXX --signing-algorithm ECDSA_SHA_256 --message fileb://message --signature fileb://signature

以下のように SignatureValidtrue を返しています。デジタル署名が正しく検証されましたので「ファイルの不改ざん」および「秘密鍵の所有者からのメッセージ」であると判断できます。

{
    "SignatureValid": true,
    "KeyId": "arn:aws:kms:ap-northeast-1:6XXXXXXXXXXX:key/32c4bf9f-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "SigningAlgorithm": "ECDSA_SHA_256"
}

改ざんしてみる

次に message の内容を変更して、検証に失敗することを確認します。

$ echo 'We are Classmethod!?' > message

先程と同じように検証してみると…

$ aws kms verify --key-id arn:aws:kms:ap-northeast-1:6XXXXXXXXXXX:key/32c4bf9f-XXXX-XXXX-XXXX-XXXXXXXXXXXX --signing-algorithm ECDSA_SHA_256 --message fileb://message --signature fileb://signature

An error occurred (KMSInvalidSignatureException) when calling the Verify operation:

$ echo $?
255

署名の検証でエラーになることが確認できました。

ログ

AWS アカウントから KMS キーを利用している場合は、CloudTrail に記録されます。以下、Verify 時のログです。

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "AWSAccount",
        "principalId": "AROARQJ6NQXXXXXXXXXXX:i-0df5e68a545905b8a",
        "accountId": "1XXXXXXXXXXX"
    },
    "eventTime": "2019-11-30T01:28:31Z",
    "eventSource": "kms.amazonaws.com",
    "eventName": "Verify",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "54.238.220.62",
    "userAgent": "aws-cli/1.16.292 Python/2.7.16 Linux/4.14.152-127.182.amzn2.x86_64 botocore/1.13.28",
    "requestParameters": {
        "messageType": "RAW",
        "keyId": "arn:aws:kms:ap-northeast-1:6XXXXXXXXXXX:key/32c4bf9f-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "signingAlgorithm": "ECDSA_SHA_256"
    },
    "responseElements": null,
    "requestID": "0e48ad08-a8b3-409b-92ab-e35ccebd8893",
    "eventID": "1c98c7b6-90e5-429d-a693-dc5d288dacf3",
    "readOnly": true,
    "resources": [
        {
            "accountId": "6XXXXXXXXXXX",
            "type": "AWS::KMS::Key",
            "ARN": "arn:aws:kms:ap-northeast-1:6XXXXXXXXXXX:key/32c4bf9f-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
        }
    ],
    "eventType": "AwsApiCall",
    "recipientAccountId": "6XXXXXXXXXXX",
    "sharedEventID": "1204b506-2578-4f14-89d9-369c4849956b"
}

今回は AWS アカウント内で検証しましたので、KMS 公開鍵ファイルの受け渡しは必要なくキーポリシーでアクセスを許可しましたが、AWS アカウント外から署名を検証する場合は、KMS 公開鍵をダウンロードして利用することも可能です。

注意点

公開鍵ダウンロード利用時の制限

公開鍵をダウンロードして利用される環境では、以下のような制限があります。

  • キーポリシーおよび IAM ポリシーは AWS 外部で実行される操作を制限できません
  • KMS で CMK の無効化、削除などを行っても AWS 外部の公開キーには影響がおよびません
  • 公開キーを利用されても CloudTrail には記録されません

AWS 環境の外部で利用されるわけですから、当然の制限ですね。

インポートおよびキーローテーション

  • 非対称キーのインポートはサポートされていません
  • 非対称キーの自動キーローテーションはサポートされていませんが、手動キーローテーションは可能です

さいごに

これまで KMS では対称キーペアのみでしたが、非対称キーペアのサポートが追加されましたので、KMS を利用したデジタル署名を検証してみました。署名・検証の実装を KMS の API をコールするだけで実装できるのはありがたいですね!

また、IAM 権限で KMS を利用する場合は、キーポリシーを許可するだけで公開鍵ファイルを共有することが出来るのも嬉しいですね。ファイルの受け渡しが必要なくなることで、キーローテーション時の公開鍵の更新運用などが不要になりますので、非常に楽になるんじゃないかと思います!

以上!大阪オフィスの丸毛(@marumo1981)でした!

参考