Tutorial Pasukan Lucee MY

MyInvois: tandatangan digital dan sijil LHDN dari Lucee

Panduan tandatangan digital MyInvois dari Lucee: sijil X.509 CA berlesen, TIN dan BRN dalam DN, tandatangan RSA-SHA256 melalui Java interop, dan ralat DS322.

MyInvois: tandatangan digital dan sijil LHDN dari Lucee
0
Kandungan

MyInvois: tandatangan digital dan sijil LHDN dari Lucee

Sijil digital ialah komponen yang menjadikan e-Invois anda sah di sisi LHDN, dan fungsinya ialah menandatangani dokumen, bukan mendapatkan token OAuth. Artikel ini menerangkan sijil yang anda perlukan, kandungan yang LHDN semak, dan cara menandatangani dokumen terus daripada Lucee melalui Java interop. Laluan JSON diutamakan kerana ia tidak memerlukan canonicalization XML, iaitu bahagian paling sukar untuk ditiru dalam CFML.

Kenapa e-Invois perlukan tandatangan digital

Tandatangan digital membuktikan dua perkara: dokumen tidak diubah selepas ditandatangani, dan dokumen itu benar-benar dihantar oleh organisasi yang disahkan. LHDN mengesahkan tandatangan dengan memadankan TIN dan nombor pendaftaran perniagaan di dalam sijil dengan nilai yang sama di dalam dokumen. Ini bermakna sijil yang betul bukan sahaja perlu sah secara kriptografi, tetapi juga perlu mengandungi identiti organisasi yang tepat. Tanpa tandatangan yang sah, dokumen akan ditolak oleh validator LHDN walaupun struktur XML atau JSON itu sendiri betul.

Penting untuk membezakan dua konsep yang sering dikelirukan. Token OAuth diperoleh dengan client_id dan client_secret melalui aliran client_credentials, seperti yang dijelaskan dalam panduan integrasi MyInvois dengan Lucee. Sijil digital pula menandatangani dokumen itu sendiri. Dua perkara ini berasingan dan tidak boleh ditukar ganti.

Dapatkan sijil digital

Sijil mesti merupakan sijil X.509 yang sah dan dikeluarkan oleh Certification Authority (CA) berlesen di Malaysia. Senarai CA berlesen boleh didapati di laman MCMC. Untuk penghantaran API, anda memerlukan sijil organisasi, bukan sijil individu. Sijil datang dalam dua jenis: soft, yang dipasang pada mesin tempatan, dan roaming, yang dipasang pada pelayan.

Satu amaran yang jujur: format fail sijil, seperti .p12 atau .pfx, dan kata laluannya tidak didokumenkan secara awam dalam SDK rasmi. Dokumentasi rasmi hanya menyatakan sijil digital yang sah daripada CA berlesen Malaysia. Sumber komuniti menyebut sijil .p12 atau .pfx dengan passphrase, tetapi anda perlu sahkan butiran ini dengan CA anda dan portal LHDN kerana butiran sijil tidak didokumenkan secara awam. Sijil juga mempunyai tarikh luput dan perlu diperbaharui dengan CA sebelum tamat. Perantara boleh menggunakan satu sijil untuk semua pelanggan, manakala subsidiari boleh membeli sijil sendiri atau menggunakan model perantara. Simpan passphrase sijil dengan cara yang sama seperti Client Secret, seperti dalam panduan rahsia dan pemboleh ubah persekitaran.

Kandungan wajib dalam sijil

LHDN memadankan sijil dengan organisasi melalui dua medan utama dalam Distinguished Name: TIN dalam organizationIdentifier dan BRN dalam serialNumber. Jadual berikut menyenaraikan medan DN yang wajib dan pilihan.

MedanOIDKandungan
CN2.5.4.3Nama organisasi
C2.5.4.6MY
O2.5.4.10Nama organisasi
organizationIdentifier2.5.4.97TIN
serialNumber2.5.4.5BRN
E1.2.840.113549.1.9.1E-mel (pilihan)
OU2.5.4.11Pilihan

Selain itu, Key Usage mesti mengandungi Non-Repudiation, dan Enhanced Key Usage mesti mengandungi Document Signing. Pastikan CA anda menjana sijil mengikut profil ini, kerana sijil tanpa medan ini akan gagal pengesahan walaupun tandatangan itu sendiri sah secara matematik.

Algoritma rasmi tandatangan

Dokumen MyInvois menggunakan standard UBL 2.1 dalam bentuk XML atau JSON, dan tandatangan mengikut standard XAdES enveloped. Algoritma yang digunakan ialah SHA-256 untuk hashing dan RSA untuk tandatangan, dengan kaedah tandatangan rsa-sha256. Untuk laluan XML, canonicalization xml-c14n11 diperlukan.

Beberapa peraturan struktur yang penting: hanya satu elemen signature dibenarkan, dan kandungan yang ditandatangani ialah seluruh dokumen kecuali UBLExtensions dan Signature. Tandatangan juga tidak memerlukan Timestamp Authority luaran, kerana keperluan ini telah dibuang, jadi SigningTime diisi sendiri oleh penandatangan dalam format UTC.

Struktur tandatangan XAdES yang LHDN jangka merangkumi ds:SignedInfo, ds:SignatureValue, ds:KeyInfo yang membawa X509Certificate, dan ds:Object untuk QualifyingProperties. Di dalam SignedProperties, SigningTime diisi dalam UTC, CertDigest ialah hash SHA-256 sijil, dan IssuerSerial menyimpan X509IssuerName serta X509SerialNumber.

Muatkan fail PKCS12 dalam Lucee

Lucee berjalan di atas JVM, jadi ia boleh memuatkan fail PKCS12 melalui API Java standard. Contoh berikut memuatkan fail dan mendapatkan kunci persendirian:

p12Path = expandPath("./sijil/e-invois.p12");
pass = createObject("java", "java.lang.String").init("kataLaluanAnda");

keyStore = createObject("java", "java.security.KeyStore").getInstance("PKCS12");
inStream = createObject("java", "java.io.FileInputStream").init(p12Path);
try {
    keyStore.load(inStream, pass.toCharArray());
} finally {
    inStream.close();
}

alias = keyStore.aliases().nextElement();
privateKey = keyStore.getKey(alias, pass.toCharArray());

Perhatikan bahawa kita menulis PKCS12 dan bukan PKCS#12, kerana tanda hash di dalam rentetan CFML mematahkan kompilasi. Satu amaran penting: walaupun API Java ini standard, memuatkan fail .p12 yang dikeluarkan CA Malaysia dengan profil DN dan Key Usage khas perlu diuji dengan sijil sebenar di sandbox sebelum anda yakin ia berfungsi sepenuhnya.

Tandatangan RSA-SHA256 dari CFML

Menandatangani RSA daripada CFML memang boleh dilakukan. Kod berikut telah diuji pada Lucee 7.1.0.204 dan mengikut padanan algoritma yang sama dengan sampel rasmi, iaitu padding PKCS1 v1.5:

// DocDigest: hash SHA-256 dokumen yang sudah diminify, dalam base64
hexDigest = hash(jsonDoc, "SHA-256");
docDigest = toBase64(binaryDecode(hexDigest, "hex"));

// Tandatangan: RSA-SHA256 dengan padding PKCS1 v1.5
sig = createObject("java", "java.security.Signature").getInstance("SHA256withRSA");
sig.initSign(privateKey);
docJava = createObject("java", "java.lang.String").init(jsonDoc);
sig.update(docJava.getBytes("UTF-8"));
sigBase64 = toBase64(sig.sign());

Algoritma SHA256withRSA menggunakan padding PKCS1 v1.5, yang padan dengan RSASignaturePadding.Pkcs1 dalam sampel rasmi .NET. Nilai docDigest dimasukkan ke dalam SignedProperties sebagai DigestValue, manakala sigBase64 menjadi SignatureValue.

Aliran penuh menandatangan mengikut lapan langkah rasmi. Cipta dokumen tanpa unsur signature, buang UBLExtensions dan Signature jika ada, kemudian canonicalize atau minify dokumen. Hash dengan SHA-256 untuk mendapat DocDigest, tanda dengan RSA-SHA256 untuk mendapat Sig, dan hash sijil untuk mendapat CertDigest. Isi SignedProperties lalu hash semula untuk mendapat PropsDigest, kemudian masukkan kesemua nilai ini ke dalam dokumen. Langkah ini hanya sebahagian daripada aliran penuh, dan penghantaran dokumen yang sudah ditandatangani dijelaskan dalam panduan cfhttp dan API luar.

Laluan JSON lawan XML

Laluan JSON jauh lebih mudah untuk ditiru dalam CFML berbanding laluan XML. Untuk JSON, anda hanya perlu meminify dokumen, iaitu membuang whitespace dan pemisah baris, kemudian hash dan tandatangan. Tiada canonicalization diperlukan.

Laluan XML pula memerlukan canonicalization xml-c14n11, dan ini ialah bahagian paling sukar untuk ditiru dengan betul dalam CFML. Jika susunan atribut atau namespace tidak tepat, pengesahan akan gagal dengan ralat DS322 digest mismatch, yang bermakna dokumen berubah selepas ditandatangani atau digest tidak sepadan. Lucee membundel Apache XML Security untuk kegunaan dalaman, tetapi sama ada kelas tersebut boleh diakses terus dari kod CFML untuk c14n11 belum disahkan dan perlu diuji di sandbox. Atas sebab ini, utamakan laluan JSON semasa memulakan integrasi, dan rujuk semula panduan integrasi MyInvois dengan Lucee untuk aliran penghantaran penuh.

Ralat validator tandatangan

Apabila tandatangan gagal pengesahan, LHDN memulangkan kod ralat khusus yang membantu anda mencari punca. DS301 bermakna tandatangan tidak dijumpai, DS303 bermakna SigningTime tidak dijumpai, DS310 bermakna serialNumber tidak padan dengan penghantar, DS311 bermakna TIN tidak padan dengan organizationIdentifier, dan DS322 bermakna digest dokumen tidak sepadan. Ralat DS322 biasanya menandakan dokumen diubah selepas ditandatangani, jadi jangan ubah dokumen selepas proses tandatangan selesai.

Soalan Lazim (FAQ)

Adakah sijil digital digunakan untuk mendapatkan token OAuth?

Tidak. Token OAuth diperoleh dengan client_id dan client_secret. Sijil digital menandatangani dokumen, bukan token.

Sijil apa yang saya perlukan untuk penghantaran API?

Sijil organisasi yang dikeluarkan oleh CA berlesen di Malaysia, sama ada jenis soft atau roaming.

Bagaimana LHDN tahu sijil itu milik syarikat saya?

Melalui TIN dalam medan organizationIdentifier dan BRN dalam medan serialNumber sijil, yang mesti padan dengan dokumen.

Bolehkah saya menggunakan JSON dan bukannya XML?

Ya. Kedua-duanya disokong, dan JSON lebih mudah kerana tiada canonicalization XML diperlukan.

Mengapa saya mendapat ralat DS322?

Kerana dokumen berubah selepas ditandatangani atau digest tidak sepadan. Jangan ubah dokumen selepas proses tandatangan.

Sumber

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk