LPD Seminyak — Crypto Toolkit

AES-256-CBC HMAC-SHA512 RSA-SHA256

Derive AES Keys (Gio_CreateKeyAndIv)

HMAC-SHA512
Menderivasi AES key, IV, dan CS dari clientID + timestamp. Replika tepat dari PHP Gio_CreateKeyAndIv($clientID, $timeStamp). Digunakan pada saat registrasi perangkat baru (bukan DB lookup).
Cara kerja: HMAC-SHA512(key=timestamp, msg=clientID) → 64 bytes → slice ke-HH:MM:SS untuk dapat key/iv/cs. AES Key = 32 bytes, IV = 16 bytes, CS = 8 bytes. X-CLIENT-ID di-encode otomatis menggunakan clientID + timestamp yang sama.
# CLI:
node lpd-crypto.cjs keygen "AQ3A.240912.001.01102025120205" "2026-04-20 11:44:20"

Timestamp Jakarta (WIB UTC+7)

Utility
Menghasilkan timestamp waktu Jakarta sekarang dalam dua format: YYYY-MM-DD HH:MM:SS (untuk iOS API) dan ISO8601+07:00 (untuk SNAP API).
# Format iOS (gmob_request, X-TIMESTAMP field):
2026-04-20 11:44:20
# Format SNAP (ISO8601 dengan offset):
2026-04-20T11:44:20+07:00

Generate X-REFERENCE

Unique ID
Generate nomor referensi unik format SMYHHMMSSxxxxO1012YYYYMMDD. Setiap request transfer membutuhkan X-REFERENCE yang belum pernah digunakan (status 45 = duplikat).
Penting: Setiap X-REFERENCE hanya bisa digunakan SATU KALI per endpoint. Jika server mengembalikan status 45 ("No. referensi duplikat"), generate reference baru.

AES-256-CBC Encrypt (Gio_Encrypt)

AES-256-CBC
Enkripsi plaintext dengan AES-256-CBC. Replika dari PHP openssl_encrypt($plain,'AES-256-CBC',$key,OPENSSL_RAW_DATA,$iv). Gunakan key/iv dari hasil Derive AES Keys.
# CLI:
node lpd-crypto.cjs encrypt "plaintext" "<aesKey_b64>" "<aesIv_b64>"

AES-256-CBC Decrypt (Gio_Decrypt)

AES-256-CBC
Dekripsi ciphertext base64 kembali ke plaintext. Replika dari PHP openssl_decrypt(base64_decode($enc),'AES-256-CBC',$key,OPENSSL_RAW_DATA,$iv).
# CLI:
node lpd-crypto.cjs decrypt "<cipher_b64>" "<aesKey_b64>" "<aesIv_b64>"

Decrypt Full Request Body

Batch Decrypt
Catatan: Body field (from_acc, to_acc, dst) hanya bisa didekripsi jika AES key/IV-nya SAMA dengan yang digunakan saat enkripsi (dari DB atau Gio_CreateKeyAndIv). Data dari curl real membutuhkan AES key dari tabel gmob_nasabah di database.

Decode X-CLIENT-ID (Gio_DecryptDID)

DID
Dekode header X-CLIENT-ID menjadi app|clientID|timestamp. Algoritma custom: ekstrak 3 segmen base64 dari posisi tersembunyi, decode, split oleh "|".
# CLI:
node lpd-crypto.cjs did "<X-CLIENT-ID>"

Encode X-CLIENT-ID (kebalikan DID)

DID Encode
Buat X-CLIENT-ID dari clientID + timestamp. Berguna untuk simulasi request dari perangkat baru.

Decode JWT (Authorization header)

JWT RS256
Decode JWT Authorization header. Format custom LPD: header berisi trans_no + alg:RS256; payload berisi trans_time. Dibuat oleh lamanuna.biz.id Get_Token.
# CLI:
node lpd-crypto.cjs jwt "<token>"

Decode X-SIGNATURE / X-PARTNER-ID

base64 → hex
Decode X-SIGNATURE atau X-PARTNER-ID dari base64 ke hex. Keduanya menggunakan formula yang sama: base64(HMAC-SHA512(token:timestamp, aes_cs)).
# CLI:
node lpd-crypto.cjs sig "<base64_signature>"

Generate X-SIGNATURE & X-PARTNER-ID

HMAC-SHA512
Generate X-SIGNATURE dan X-PARTNER-ID. Formula: base64(HMAC-SHA512(token + ":" + timestamp, aes_cs)). Keduanya menghasilkan nilai yang SAMA jika menggunakan aes_cs yang sama.
# Formula:
base64( HMAC-SHA512( token + ":" + timestamp, aes_cs ) )

iOS Token Signature (iosTokenCtrl)

RSA-SHA256
Generate X-SIGNATURE untuk endpoint POST /api/smart/access/token. Formula: RSA-SHA256(SHA256("Seminyak|" + timestamp)) menggunakan private_key_lpd.pem.
# Formula PHP (iosTokenCtrl.php):
$clientStamp = hash("sha256", "Seminyak|" . $timeStamp);
openssl_sign($clientStamp, $sig, $privateKey);
base64_encode($sig);

SNAP Token Signature (BPD)

RSA-SHA256
Generate X-SIGNATURE untuk endpoint POST /api/v1.0/access-token/b2b. Formula: RSA-SHA256(clientKey + "|" + timestamp) menggunakan private_bpd_003.pem.
# Formula SNAP BPD:
message = clientKey + "|" + timestamp
RSA-SHA256(message, private_bpd_003.pem)
base64_encode(signature)

Hash Code Transfer

SHA-256
Check/Inquiry
Posting
LPD Internal
Formula Check/Inquiry: SHA256("%"+fromAcc+"#"+amount+"@"+dateTime+"^"+refNo+"*"+destBank+"~"+destAcc+"|"+BPD_HASHCODE+"%")
Formula Posting: SHA256("@"+fromAcc+"|"+amount+"~"+dateTime+"*"+refNo+"^"+destBank+"#"+destAcc+"("+destName+")"+BPD_HASHCODE+"@")
Formula LPD Internal: SHA256("{"+nominal+"*"+norekFrom+"^"+norekTo+"%"+nameFrom+"#"+nameTo+"@"+BPD_HASHCODE+"}")
BPD_HASHCODE = p91wrswK (dari .env production). Hash code ini kemudian di-encrypt dengan AES sebelum dikirim di body request.
# CLI:
node lpd-crypto.cjs hashcode <fromAcc> <amount> <dateTime> <refNo> <destBank> <destAcc> <destName>

Build Transfer Bank Request (Lengkap)

Full Builder
Build request lengkap untuk /api/smart/transfer/bank/check atau /post atau /inquiry. Menghasilkan semua header + body terenkripsi + curl command siap pakai.
Check
Inquiry
Posting

Login Nasabah

POST /api/smart/access/login
Login mengirim MD5(user_name) & MD5(user_pass) sebagai body. Header Authorization (JWT), X-SIGNATURE, X-PARTNER-ID, X-CLIENT-ID dibuat otomatis dari AES CS + private key LPD. Setelah berhasil, sesi aktif untuk Cek Saldo & Transfer.
1. Derive Keys
2. Isi Kredensial
3. Login
4. Status 00
Belum ada data Keygen
Jalankan Derive AES Keys terlebih dahulu — X-CLIENT-ID akan otomatis terisi.

Kunci AES & X-CLIENT-ID

Kredensial Nasabah

# Flow otomatis (sesuai POC sukses):
1. MD5(user_name) → user_name hashed (32 hex)
2. MD5(user_pass) → user_pass hashed (32 hex)
3. createJWT(refNo, tsISO, priv_lpd) → Authorization header
4. generateSignature(jwt, ts, aesCs) → X-SIGNATURE & X-PARTNER-ID
5. POST /api/smart/access/login → status:"00" + account_list
# Catatan: Body TIDAK di-AES-encrypt. Hanya header yang perlu AES CS.
Belum login. Silakan Login Nasabah terlebih dahulu untuk mendapatkan token.

Cek Saldo

POST /api/smart/account/balance
Nomor rekening akan dienkripsi AES-256-CBC sebelum dikirim. JWT & signature dibuat ulang otomatis dari private key LPD tiap request.
# Endpoint:
POST /api/smart/account/balance
Body: { no_rek: aesEncrypt(norek) }
Belum login. Silakan Login Nasabah terlebih dahulu.

Transfer Antar Rekening

Inquiry → Posting
1. Inquiry
2. Konfirmasi
3. Posting

Inquiry Transfer

Inquiry untuk mengecek ketersediaan rekening tujuan dan nominal. Semua field dienkripsi sebelum dikirim.
# Flow Transfer:
1. POST /api/smart/transfer/inquiry → cek rekening & saldo
2. Konfirmasi data oleh user
3. POST /api/smart/transfer/posting → eksekusi transfer
# Semua field dienkripsi AES-256-CBC, JWT & signature otomatis

Referensi Cepat

Konstanta dari .env

BPD_HASHCODE
p91wrswK
CLIENT_SECRET
7vCjkNHs
CLIENT_VA
VA-325
WHITELIST_IP
34.50.74.78
BPD_PREFIX
989191
X-CLIENT-KEY (SNAP)
LPD-SEMINYAK-001

Algoritma Enkripsi per Komponen

Body fields
AES-256-CBC key/iv dari DB atau Gio_CreateKeyAndIv
X-SIGNATURE
HMAC-SHA512 base64(HMAC(token:ts, aes_cs))
X-PARTNER-ID
HMAC-SHA512 sama dengan X-SIGNATURE
hash_code (transfer)
SHA-256 string dengan separators khusus
iOS token sig
RSA-SHA256 sign(SHA256("Seminyak|ts"), priv_lpd)
SNAP token sig
RSA-SHA256 sign(clientKey+"|"+ts, priv_bpd)
AES Key derivation
HMAC-SHA512 key=ts, msg=clientID → 64 bytes → slice

CLI Commands

node lpd-crypto.cjs demo
node lpd-crypto.cjs keygen "clientID" "timestamp"
node lpd-crypto.cjs encrypt "plaintext" "key_b64" "iv_b64"
node lpd-crypto.cjs decrypt "cipher_b64" "key_b64" "iv_b64"
node lpd-crypto.cjs did "X-CLIENT-ID"
node lpd-crypto.cjs jwt "JWT_token"
node lpd-crypto.cjs sig "X-SIGNATURE_b64"
node lpd-crypto.cjs hashcode from amount datetime ref bank acc name
node lpd-crypto.cjs ref SMY

File Keys

private_bpd_003.pem
Private key SNAP BPD Bali (untuk X-SIGNATURE SNAP)
private_key_lpd.pem
Private key LPD Seminyak (untuk iOS token sig)
keys/public_key.pem
Public key client (diterima server untuk verifikasi iOS)
public_key_bpd.pem
Public key BPD (untuk verifikasi SNAP token)