Ethereum で使用する秘密鍵をプログラムから安全に扱うための aws-kms-provider の紹介
この記事は Ethereum Advent Calendar 2021のカレンダー | Advent Calendar 2021 - Qiita の1日目の記事です。
この記事では、秘密鍵の直接管理をせずに web3.js や ethers でトランザクションの署名を可能にするの https://github.com/odanado/aws-kms-provider 紹介をします。
AWS Key Management Service とは
AWS Key Management Service は暗号化鍵や秘密鍵を管理する AWS のマネージドサービスです。他のクラウドサービスにはない公開鍵暗号方式に使用される非対称鍵をサポートしているのが特徴的です。
非対称鍵の秘密鍵は厳重に管理されていて、AWS を利用する開発者ももちろん、 AWS の中の人もアクセスできません[1]。また秘密鍵を使用した履歴は AWS の他のサービスと同様に AWS CloudTrail に記録されているため、不正利用の検出や監査に役立ちます。
サポートしている非対称鍵の種類には RSA と楕円曲線暗号があります。この楕円曲線暗号のうち secp256k1 と呼ばれる仕様は Ethereum で使用されている署名アルゴリズムと同じです。
aws-kms-provider の概要
aws-kms-provider は AWS KMS で提供されている secp256r1 の署名機能を web3.js や ethers から利用できるインタフェースを実装した JavaScript/TypeScript 向けのパッケージです。
トランザクションへの署名を行うプログラムを実装するときに、このパッケージを活用することができます。
トランザクションへの署名を行う秘密鍵を管理する方法を考えたとき、環境変数経由で秘密鍵をプログラム渡すナイーブな方法がまず思いつくかと思います。この方法には次の問題点があります。
- 秘密鍵を直接使えばプログラム以外の意図しない文脈で署名を行える
- 秘密鍵を誰が使用したという履歴はどこにも残らない
- 秘密鍵を直接持ち出せば退職者であっても署名を行える
これらの問題を aws-kms-provider は解決します。
秘密鍵を直接使えばプログラム以外の意図しない文脈で署名を行える
これは KMS の秘密鍵の使用権限を IAM Policy で適切に管理すれば、意図しない文脈で署名が行われることを回避できます。
秘密鍵を誰が使用したという履歴はどこにも残らない
前述の通り、KMS の秘密鍵の使用履歴は、「誰が使用したか」という情報を含めて AWS CloudTrail に記録されます。
秘密鍵を直接持ち出せば退職者であっても署名を行える
KMS にある秘密鍵に直接アクセスできる人はおらず、秘密鍵を使用する権限も AWS へのアクセス権限を無効化すれば制御できます。
このように AWS KMS で管理された秘密鍵を使うことで、生の秘密鍵を直接使用する場合の問題点が解決します。
コードサンプル
web3.js
web3.js で利用できる provider を aws-kms-provider というパッケージで提供しています。
import Web3 from "web3";
import { KmsProvider } from "aws-kms-provider";
const keyId = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxx";
const endpoint = "https://ropsten.infura.io/v3/xxxxxxxxxxxx";
async function main() {
const provider = new KmsProvider(endpoint, { keyIds: [keyId] });
const web3 = new Web3(provider);
// 他の provider を使用している場合と同じように web3 インスタンスを利用可能
const accounts = await web3.eth.getAccounts();
console.log("accounts", accounts);
// 送金する
const receipt = await web3.eth.sendTransaction({
from: accounts[0],
to,
value: web3.utils.toWei("0.00001", "ether"),
});
console.log(receipt);
}
main().catch((e) => console.error(e));
ethers
ethers 向けに aws-kms-ethers-signer というパッケージを提供しています。これは ethers の signer の実装です。
import * as ethers from "ethers";
import { KmsEthersSigner } from "aws-kms-ethers-signer";
async function main() {
const rpcUrl = "http://localhost:8501";
const keyId = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxx";
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const signer = new KmsEthersSigner({ keyId }).connect(provider);
console.log(await signer.getAddress());
}
main().catch((e) => console.error(e));
終わりに
Ethereum で使用する秘密鍵をプログラムから安全に扱うための aws-kms-provider の紹介をしました。秘密鍵の管理に悩んでいる場合にはぜひ検討してください。
リポジトリにスターをもらえると開発の励みになります。
-
AWS KMS のハードウェアセキュリティモジュールに格納されるため、AWS 従業員を含む誰も平文のキーマテリアルにアクセスすることは出来ません
https://aws.amazon.com/jp/blogs/news/digital-signing-asymmetric-keys-aws-kms/ ↩︎
Discussion
大変参考になりました!!