← Semua pembelajaran / Blockchain Nol → RWA
Fase 5 · Deployment & Arsitektur AWS

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 — persis secp256k1 yang dipakai Ethereum (Fase 0) — sebagai salah satu jenis asymmetric key.
  • Private key dibuat dan disimpan di dalam KMS; API Sign mengembalikan 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 menghitung v (recovery id) yang tidak dikembalikan KMS sama sekali.
  • IAM policy harus dibatasi seketat mungkin: service yang butuh signing hanya diberi izin kms:Sign untuk 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.