import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.HexFormat;

public class TigaKeluarga {

    public static void main(String[] args) throws Exception {
        // ============ Keluarga 1: hash (satu arah, tanpa kunci) ============
        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
        byte[] sidik = sha256.digest("Budi".getBytes(StandardCharsets.UTF_8));
        System.out.println("1) Hash SHA-256(\"Budi\")  : " + HexFormat.of().formatHex(sidik));

        // ============ Keluarga 2: enkripsi simetris AES-GCM ============
        // Kunci dan IV tetap HANYA untuk demo deterministik.
        // Di produksi: kunci dari secret manager, IV acak per pesan.
        byte[] kunci = "kunci-demo-aes16".getBytes(StandardCharsets.UTF_8); // 128 bit
        byte[] ivTetap = new byte[12]; // 96 bit, semua nol - jangan ditiru
        String plaintext = "Transfer Rp1.000.000 ke rekening Budi";

        SecretKeySpec spesifikasiKunci = new SecretKeySpec(kunci, "AES");
        GCMParameterSpec parameterGcm = new GCMParameterSpec(128, ivTetap);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, spesifikasiKunci, parameterGcm);
        byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        System.out.println();
        System.out.println("2) AES-GCM ciphertext    : "
                + Base64.getEncoder().encodeToString(ciphertext));

        cipher.init(Cipher.DECRYPT_MODE, spesifikasiKunci, parameterGcm);
        byte[] dipulihkan = cipher.doFinal(ciphertext);
        System.out.println("   Dekripsi berhasil     : "
                + new String(dipulihkan, StandardCharsets.UTF_8));

        // ============ Keluarga 2 lanjutan: integritas GCM ============
        byte[] dipindahtangan = ciphertext.clone();
        dipindahtangan[0] ^= 1; // balik satu bit pada ciphertext
        try {
            cipher.init(Cipher.DECRYPT_MODE, spesifikasiKunci, parameterGcm);
            cipher.doFinal(dipindahtangan);
            System.out.println("3) Dekripsi data ubahan  : berhasil (seharusnya tidak)");
        } catch (Exception e) {
            System.out.println();
            System.out.println("3) Satu bit ciphertext dibalik -> dekripsi DITOLAK");
            System.out.println("   Tag autentikasi GCM tidak cocok: data telah berubah");
        }
    }
}
