Belajar SOAP - WS-SecurityPolicy & Pemrograman (WSS4J)
Series/Belajar SOAP/Episode 15
Episode 15 of 23

Belajar SOAP - WS-SecurityPolicy & Pemrograman (WSS4J)

Menerapkan keamanan WS-Security secara nyata: menyusun WS-SecurityPolicy yang terikat ke WSDL dan mengimplementasikannya dengan Apache WSS4J 4.0.1 dan interceptor CXF untuk signing, encryption, dan UsernameToken password digest beserta alur sertifikat X.509.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Setelah di episode 14 kita memahami konsep WS-Security, sekarang saatnya mengimplementasikannya. Memakai WS-Security secara manual (menyusun header Security dengan tangan) itu rawan dan tidak konsisten — praktik profesional adalah mendeklarasikan WS-SecurityPolicy dan membiarkan framework menerapkannya.

Mengapa penting? Policy adalah kontrak keamanan yang machine-readable: ia memberi tahu tool client apa yang harus dilakukan (token apa, elemen apa yang di-sign, apa yang di-encrypt) tanpa menebak-nebak. Apache WSS4J — pustaka WS-Security untuk Java — dan interceptor CXF adalah cara standar industri menerapkannya. Di episode ini kita membangun keduanya dari nol.

Menyusun WS-SecurityPolicy

WS-SecurityPolicy memperluas WS-Policy (episode 10) dengan assertion keamanan: token apa yang dipakai, apa yang dilindungi, dan bagaimana proteksinya. Contoh policy untuk service yang menuntut UsernameToken + sign body + encrypt body:

Policy keamanan penuh
<wsp:Policy wsu:Id="BankSecurityPolicy"
  xmlns:wsp="http://www.w3.org/ns/ws-policy"
  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
  xmlns:sp13="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/1.3">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:TransportBinding>
        <sp:TransportToken>
          <sp:HttpsToken/>
        </sp:TransportToken>
      </sp:TransportBinding>
 
      <sp:SignedEncryptedSupportingTokens>
        <sp:Policy>
          <sp:UsernameToken sp:IncludeToken=
            "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
            <wsp:Policy>
              <sp:WssUsernameToken11/>
            </wsp:Policy>
          </sp:UsernameToken>
        </sp:Policy>
      </sp:SignedEncryptedSupportingTokens>
 
      <sp:SignedParts>
        <sp:Body/>
      </sp:SignedParts>
 
      <sp:EncryptedParts>
        <sp:Body/>
      </sp:EncryptedParts>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

Maknanya: transport wajib HTTPS; client mengirim UsernameToken (WSS 1.1); body harus di-sign dan di-encrypt. Client yang menghasilkan request dari policy ini otomatis melakukan semuanya.

Note

Mulailah dari policy yang paling sederhana yang memenuhi kebutuhan, lalu perkuat bertahap: (1) HTTPS saja, (2) + UsernameToken, (3) + sign body, (4) + encrypt body. Policy yang terlalu kompleks dari awal membuat debugging keamanan terasa menyakitkan.

Implementasi dengan WSS4J dan CXF

Apache WSS4J 4.0.1 adalah pustaka WS-Security untuk Java — dan ia terintegrasi erat dengan CXF lewat interceptor: WSS4JOutInterceptor di sisi pengirim dan WSS4JInInterceptor di sisi penerima.

Setup Maven

Dependensi CXF + WSS4J
<dependencies>
  <dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-ws-security</artifactId>
    <version>4.2.2</version>
  </dependency>
</dependencies>

WSS4J datang sebagai bagian dari modul cxf-rt-ws-security (versi WSS4J 4.0.1 dibundel di dalamnya).

Sisi Client: Mengirim Request Aman

Client dikonfigurasi dengan actions: timestamp, UsernameToken, sign, dan encrypt:

Konfigurasi client di Spring XML
<bean id="clientWss4j" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
  <property name="properties">
    <map>
      <entry key="action" value="Timestamp UsernameToken Signature Encrypt"/>
      <entry key="user" value="integrasi-mitra"/>
      <entry key="passwordType" value="PasswordDigest"/>
      <entry key="passwordCallbackClass"
             value="id.devnull.soap.ClientPasswordCallback"/>
      <entry key="signatureUser" value="myclientkey"/>
      <entry key="signaturePropFile" value="client-sign.properties"/>
      <entry key="encryptionUser" value="myservicekey"/>
      <entry key="encryptionPropFile" value="client-enc.properties"/>
      <entry key="signatureKeyIdentifier" value="DirectReference"/>
      <entry key="encryptionKeyIdentifier" value="X509KeyIdentifier"/>
    </map>
  </property>
</bean>

Alurnya: client membuat timestamp, menambah UsernameToken (digest), menandatangani body dengan kunci privat myclientkey, lalu mengenkripsi body dengan kunci publik myservicekey.

Password Callback

WSS4J tidak pernah menyimpan password — ia memintanya lewat callback:

JavaClientPasswordCallback.java
package id.devnull.soap;
 
import java.io.IOException;
import javax.security.auth.callback.*;
import org.apache.wss4j.common.ext.WSPasswordCallback;
 
public class ClientPasswordCallback implements CallbackHandler {
    @Override
    public void handle(Callback[] callbacks) throws IOException {
        for (Callback cb : callbacks) {
            WSPasswordCallback pc = (WSPasswordCallback) cb;
            if ("integrasi-mitra".equals(pc.getIdentifier())) {
                pc.setPassword(System.getenv("MITRA_PASSWORD"));
            }
        }
    }
}

Perhatikan: password diambil dari environment variable, bukan hardcode — pola yang akan kita pertegas di episode 16.

Sisi Server: Memverifikasi Request

Server memakai WSS4JInInterceptor untuk memvalidasi token, signature, dan dekripsi:

Konfigurasi server
<bean id="serverWss4j" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
  <property name="properties">
    <map>
      <entry key="action" value="Timestamp UsernameToken Signature Encrypt"/>
      <entry key="passwordCallbackClass"
             value="id.devnull.soap.ServerPasswordCallback"/>
      <entry key="signaturePropFile" value="server-sign.properties"/>
      <entry key="decryptionPropFile" value="server-dec.properties"/>
    </map>
  </property>
</bean>

Server perlu (a) memverifikasi password digest terhadap store mitra, (b) memverifikasi signature dengan sertifikat publik client, dan (c) mendekripsi body dengan kunci privat service. Jika salah satu gagal, CXF mengembalikan SOAP Fault — pesan tidak pernah sampai ke kode bisnis.

Alur X.509 Sederhana

Untuk signing/encryption, siapkan pasangan keystore. Alur minimal untuk lab:

Buat keystore untuk lab
# Keystore service (kunci privat + sertifikat service)
keytool -genkeypair -alias servicekey -keyalg RSA -keysize 2048 \
  -keystore service.jks -storepass changeit
 
# Keystore client (kunci privat + sertifikat client)
keytool -genkeypair -alias clientkey -keyalg RSA -keysize 2048 \
  -keystore client.jks -storepass changeit
 
# Ekspor dan tukar sertifikat
keytool -exportcert -alias servicekey -keystore service.jks \
  -rfc -file service.crt
keytool -exportcert -alias clientkey -keystore client.jks \
  -rfc -file client.crt

Warning

Keystore yang dibuat keytool hanya untuk lab. Di produksi, kunci harus diterbitkan oleh CA yang dipercaya kedua pihak, dikelola dengan kontrol akses (hanya service yang bisa membaca kunci privat), dan dirotasi berkala — kunci yang sama dipakai selamanya adalah kewajiban yang menunggu kebocoran.

Common Pitfalls

  • Callback salah identifier — WSS4J memanggil callback beberapa kali (untuk password, signature, dst); pastikan callback menangani identifier yang tepat.
  • Timezone timestamp — timestamp yang beda jam antara client dan server bisa ditolak sebagai expired; sinkronkan jam (NTP) dan beri toleransi kebijakan.
  • EncryptedParts vs SignedParts — kalau policy butuh keduanya tapi config hanya sign, request tetap lolos atau gagal tergantung validasi; pastikan action konsisten di client dan server.
  • Kunci tidak saling kenal — signature client gagal diverifikasi karena server belum punya sertifikat publik client di truststore-nya.

Penutup

Inti yang harus dibawa pulang:

  • WS-SecurityPolicy adalah kontrak keamanan machine-readable: token, bagian yang di-sign, dan yang di-encrypt.
  • WSS4J diterapkan lewat WSS4JOutInterceptor (client) dan WSS4JInInterceptor (server) di CXF.
  • Password diambil lewat callback dan disimpan di secret store, bukan hardcode.
  • Signing pakai kunci privat client; encryption pakai kunci publik service.
  • Keystore lab hanya untuk uji — produksi butuh CA terpercaya, akses terkontrol, dan rotasi berkala.

Di episode 16 selanjutnya kita akan membahas authentication & authorization — perbandingan HTTP Basic/Digest, UsernameToken, mutual TLS, dan integrasi SSO dengan SAML/WS-Trust, plus RBAC di level operasi dan pengelolaan credentials dengan vault. Sampai jumpa di episode 16!

Belajar SOAP - WS-SecurityPolicy & Pemrograman (WSS4J) | Belajar SOAP