AWS KMS — signing transaksi tanpa expose private key
Fase 4 menjelaskan kenapa private key produksi harus tinggal di KMS. Materi ini menjawab bagaimana caranya — termasuk satu detail teknis yang tidak jelas dari dokumentasi AWS begitu saja: KMS mengembalikan tanda tangan dalam format yang berbeda dari yang dibutuhkan Ethereum.
Intisari
- AWS KMS mendukung kurva
ECC_SECG_P256K1— persissecp256k1yang dipakai Ethereum (Fase 0) — sebagai salah satu jenis asymmetric key. - Private key dibuat dan disimpan di dalam KMS; API
Signmengembalikan tanda tangan atas hash yang kamu kirim, key mentahnya tidak pernah bisa diekspor. - KMS mengembalikan tanda tangan dalam format DER (ASN.1) — Ethereum butuh format
(r, s, v). Kamu harus mengonversinya sendiri, termasuk menghitungv(recovery id) yang tidak dikembalikan KMS sama sekali. - IAM policy harus dibatasi seketat mungkin: service yang butuh signing hanya diberi izin
kms:Signuntuk key ID spesifik, bukan akses KMS secara umum. - Public key (dan address yang diturunkan darinya) bisa diekspor dari KMS kapan saja — hanya private key-nya yang terkunci permanen di dalam modul.
Membuat asymmetric key di KMS
aws kms create-key \
--key-spec ECC_SECG_P256K1 \
--key-usage SIGN_VERIFY \
--description "Signer transaksi GoldToken produksi"
ECC_SECG_P256K1 adalah nama AWS untuk kurva yang sama persis dengan secp256k1 yang
kamu pelajari di Fase 0 — inilah yang membuat KMS bisa dipakai menandatangani transaksi Ethereum sama
sekali. Tidak semua layanan KMS/HSM mendukung kurva ini; ini poin penting saat mengevaluasi alternatif
selain AWS.
Menandatangani hash transaksi
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.awssdk.services.kms.model.SignRequest;
import software.amazon.awssdk.services.kms.model.SigningAlgorithmSpec;
byte[] hashTransaksi = hitungKeccak256(transaksiBelumDitandatangani);
SignRequest req = SignRequest.builder()
.keyId("alias/goldtoken-signer")
.message(SdkBytes.fromByteArray(hashTransaksi))
.messageType(MessageType.DIGEST)
.signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
.build();
byte[] tandaTanganDER = kmsClient.sign(req).signature().asByteArray();
// DER — BUKAN format (r, s, v) yang dibutuhkan Ethereum, lihat bagian berikutnya
Jebakan yang tidak dijelaskan dokumentasi AWS: konversi DER ke (r, s, v)
Ini bagian yang paling sering membuat integrasi KMS-Ethereum terasa lebih sulit dari seharusnya. KMS
mengembalikan tanda tangan dalam encoding standar kriptografi umum (DER/ASN.1), sementara Ethereum
membutuhkan tiga nilai terpisah: r, s, dan v (recovery id, lihat
Fase 0 soal bagaimana node memulihkan address penandatangan).
Langkah konversi:
1. Parse struktur DER → dapatkan r dan s sebagai BigInteger
2. Normalisasi s → kalau s lebih besar dari secp256k1_order/2, ganti s = order - s
(Ethereum mewajibkan "low-s" untuk mencegah signature malleability)
3. Hitung v: coba recovery id 0 dan 1, lakukan ecrecover(hash, r, s, v)
untuk masing-masing, cocokkan mana yang menghasilkan address
yang SAMA dengan public key yang diekspor dari KMS
4. v final = recovery id yang cocok + 27 (atau + chainId*2+35 untuk EIP-155)
Kenapa harus "menebak" v lewat percobaan, bukan dihitung langsung. Secara
matematis, satu pasangan (r, s) bisa berasal dari dua titik berbeda di kurva eliptik — recovery
id-lah yang membedakan mana yang benar. KMS tidak tahu (dan tidak peduli) konteks Ethereum, jadi tidak
mengembalikan info ini. Solusinya: coba kedua kemungkinan, jalankan proses pemulihan address yang sama
seperti yang dilakukan node (dibahas di Fase 0), dan pakai yang hasilnya cocok dengan address signer yang
kamu tahu dari public key KMS.
Mendapatkan address dari public key KMS
GetPublicKeyResponse pub = kmsClient.getPublicKey(
GetPublicKeyRequest.builder().keyId("alias/goldtoken-signer").build()
);
// Public key BOLEH diekspor — hanya private key yang terkunci di KMS
byte[] publicKeyDer = pub.publicKey().asByteArray();
String alamatSigner = turunkanAddressEthereum(publicKeyDer); // keccak256, Fase 0
IAM: siapa boleh meminta signing
{
"Effect": "Allow",
"Action": ["kms:Sign", "kms:GetPublicKey"],
"Resource": "arn:aws:kms:ap-southeast-1:123456789012:key/goldtoken-signer-id",
"Condition": {
"StringEquals": { "aws:PrincipalTag/service": "goldtoken-relayer" }
}
}
Batasi ke kms:Sign dan kms:GetPublicKey saja, ke key ID spesifik.
Jangan berikan akses KMS umum ke role apa pun yang tidak benar-benar butuh menandatangani transaksi.
Setiap principal (user IAM, role ECS task) yang punya izin kms:Sign untuk key ini secara
efektif punya kekuatan yang sama dengan memegang private key-nya — least-privilege di sini bukan
formalitas compliance, tapi batas nyata blast radius kalau satu credential AWS bocor.
Latihan: buat satu asymmetric key ECC_SECG_P256K1 di AWS KMS (tier gratis
mencakup ini untuk volume kecil), ekspor public key-nya, turunkan address Ethereum-nya secara manual
(persis latihan di Fase 0), lalu kirim sedikit ETH testnet ke address itu lewat faucet. Tandatangani satu
transaksi transfer sederhana lewat kms:Sign, konversi ke format (r, s, v), dan
kirim lewat eth_sendRawTransaction — buktikan sendiri transaksinya diterima jaringan.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.