// 依赖: JDK 11+ (内置 java.net.http 与 JCA)
import javax.crypto.Cipher;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

public class KamiClient {
    static final String BASE       = "{{BASE}}";
    static final String PUBKEY_URL = "{{PUBKEY}}";
    static final String VERIFY_URL = "{{VERIFY}}";
    static final String CODE       = "YOUR-CARD-KEY";

    static Cipher oaep() throws Exception {
        return Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
    }

    // 每块 190 字节, 以 | 连接
    static String encrypt(String plain, PublicKey pub) throws Exception {
        byte[] data = plain.getBytes(StandardCharsets.UTF_8);
        List<String> parts = new ArrayList<>();
        for (int i = 0; i < data.length; i += 190) {
            byte[] chunk = Arrays.copyOfRange(data, i, Math.min(i + 190, data.length));
            Cipher c = oaep();
            c.init(Cipher.ENCRYPT_MODE, pub);
            parts.add(Base64.getEncoder().encodeToString(c.doFinal(chunk)));
        }
        return String.join("|", parts);
    }

    static String decrypt(String cipher, PrivateKey priv) throws Exception {
        StringBuilder sb = new StringBuilder();
        for (String p : cipher.split("\\|")) {
            Cipher c = oaep();
            c.init(Cipher.DECRYPT_MODE, priv);
            sb.append(new String(c.doFinal(Base64.getMimeDecoder().decode(p)), StandardCharsets.UTF_8));
        }
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        HttpClient http = HttpClient.newHttpClient();

        // 1. 获取服务器公钥
        String pubJson = http.send(
            HttpRequest.newBuilder(URI.create(PUBKEY_URL)).GET().build(),
            HttpResponse.BodyHandlers.ofString()).body();
        String serverPem = pubJson.split("\"public_key\":\"")[1].split("\",\"")[0]
                .replace("\\/", "/").replace("\\n", "\n");
        byte[] der = Base64.getMimeDecoder().decode(
            serverPem.replace("-----BEGIN PUBLIC KEY-----", "")
                     .replace("-----END PUBLIC KEY-----", ""));
        PublicKey serverPub = KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(der));

        // 2. 生成客户端密钥对
        KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
        gen.initialize(2048);
        KeyPair client = gen.generateKeyPair();
        String clientPub = "-----BEGIN PUBLIC KEY-----\n"
                + Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(client.getPublic().getEncoded())
                + "\n-----END PUBLIC KEY-----\n";

        // 3. 组装并加密请求
        String payload = "{\"code\":\"" + CODE + "\",\"timestamp\":" + (System.currentTimeMillis() / 1000)
                + ",\"client_public_key\":\"" + clientPub.replace("\n", "\\n") + "\"}";
        String body = "{\"encrypted\":\"" + encrypt(payload, serverPub) + "\"}";

        // 4. 发送请求
        String resp = http.send(
            HttpRequest.newBuilder(URI.create(VERIFY_URL))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(body)).build(),
            HttpResponse.BodyHandlers.ofString()).body();

        // 5. 解密响应
        if (resp.contains("\"encrypted\"")) {
            String cipher = resp.split("\"encrypted\":\"")[1].split("\"")[0];
            System.out.println(decrypt(cipher, client.getPrivate()));
        } else {
            System.out.println(resp);
        }
    }
}
