import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HexFormat;

public class ContohSandiKata {

    private static final int ITERASI = 600_000;

    private static byte[] hash(String sandi, byte[] garam)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        PBEKeySpec spesifikasi = new PBEKeySpec(
                sandi.toCharArray(), garam, ITERASI, 256);
        SecretKeyFactory pabrik =
                SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        return pabrik.generateSecret(spesifikasi).getEncoded();
    }

    private static boolean verifikasi(String sandi, byte[] garam, byte[] hashTersimpan)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] hashBaru = hash(sandi, garam);
        // Bandingkan konstan-waktu agar durasi verifikasi tidak membocorkan
        // berapa byte awal yang sudah cocok.
        return MessageDigest.isEqual(hashBaru, hashTersimpan);
    }

    public static void main(String[] args)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        // Garam contoh 16 byte. Pada sistem nyata garam acak dibuat sekali
        // saat pendaftaran lalu disimpan berdampingan dengan hash.
        byte[] garam = HexFormat.of().parseHex("1f2e3d4c5b6a79880112233445566778");

        byte[] hashTersimpan = hash("rahasiaBudi", garam);

        System.out.println("Garam (hex)  : " + HexFormat.of().formatHex(garam));
        System.out.println("Iterasi      : " + ITERASI);
        System.out.println("Hash (hex)   : " + HexFormat.of().formatHex(hashTersimpan));
        System.out.println("Panjang hash : " + hashTersimpan.length + " byte");
        System.out.println("Sandi cocok  : " + verifikasi("rahasiaBudi", garam, hashTersimpan));
        System.out.println("Sandi salah  : " + verifikasi("rahasiaSiti", garam, hashTersimpan));
    }
}
