package kz.gamma;

import kz.gamma.crypto.digests.GammaGost3411Digest;
import kz.gamma.jce.provider.GammaTechProvider;
import kz.gamma.tsp.*;

import javax.naming.Context;
import javax.naming.ldap.ExtendedResponse;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.Hashtable;

/**
 * Пример демонстрирующий работу с кртиптографическими функциями:
 * Формирование запроса на метку времени.
 * Загрузка ключей.
 * Загрузка сертификатов.
 */
public class SampleJCECSP_TSP {
    public static void main(String[] args) {
        try {
            // Данный метод добавляет JCE в окружение java.security.
            Security.addProvider(new GammaTechProvider());
            X509Certificate tspCer = loadCertFromFile("C:\\temp\\tspCert.cer");
            String str = "Sample TSP request";
            //Формируем TSP запрос.
            //Для формирования подписаного запроса необходимо загрузить ключи
            //и сертификат из профайла.
            TimeStampRequest req = generateRequest(null, null, str.getBytes());
            // Отправляем запрос на сервер и получаем ответ
            byte[] resp = sendRequest(req.getEncoded(), "ldap://192.168.12.6:62222");
            // Получение метки времени из запроса
            System.out.println("Time - " + getTSPTime(resp));
            if (!checkTSPData(resp, str.getBytes()))
                throw new Exception("Метка времени не подходит к данному тексту");
            // Проверка запроса
            if (!checkTSPResponce(resp, tspCer))
                throw new Exception("Подпись не прошла проверку");

        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    // Метод загружает сертификат из файла

    public static X509Certificate loadCertFromFile(String fileName) {
        X509Certificate cert = null;
        byte[] buf = null;
        try {
            FileInputStream f = new FileInputStream(fileName);
            buf = new byte[f.available()];
            f.read(buf, 0, f.available());
            // Указываем классу CertificateFactory что необходимо использовать JCE GAMMA.
            CertificateFactory cf = CertificateFactory.getInstance("X.509", "GAMMA");
            cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(buf));
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        return cert;
    }
    // Метод формирования запроса на метку времени

    public static TimeStampRequest generateRequest(X509Certificate cert, PrivateKey privKey, byte[] buf) {
        TimeStampRequest req = null;
        try {
            // Создаем объект генерации запроса Time Stamp
            TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(privKey, cert, TSPAlgorithms.GOST3411, "1.2");
            TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
            byte[] hash = new byte[32];
            //Создаем объект формирования Хэш

            GammaGost3411Digest digest = new GammaGost3411Digest();
            digest.update(buf, 0, buf.length);
            digest.doFinal(hash, 0);
            // Формируем Time Stamp запрос
            req = reqGen.generate(TSPAlgorithms.GOST3411, hash, BigInteger.valueOf(100));
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        return req;
    }
    //Метод отсылающий TSP запрос на сервер

    public static byte[] sendRequest(byte[] req, String url) {
        byte[] resp = null;
        LdapContext ctx = null;
        try {
            Hashtable<String, String> env = new Hashtable<String, String>();
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.SECURITY_AUTHENTICATION, "SIMPLE");
            env.put(Context.PROVIDER_URL, url);
            env.put(Context.SECURITY_PRINCIPAL, "");
            env.put(Context.SECURITY_CREDENTIALS, "");
            ctx = new InitialLdapContext(env, null);

            RegisterRequest request1 = new RegisterRequest(req, "1.3.6.1.4.1.6801.11.1.2");
            ExtendedResponse response = ctx.extendedOperation(request1);
            resp = response.getEncodedValue();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return resp;
    }
    //Метод проверяющий TSP запрос

    public static boolean checkTSPResponce(byte[] resp, X509Certificate tspCert) {
        boolean ret = false;
        try {
            TimeStampResponse responce = new TimeStampResponse(resp);
            TimeStampToken tsToken = responce.getTimeStampToken();
            tsToken.validate(tspCert, "GAMMA");
            ret = true;
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        return ret;
    }
    //Метод проверяющий TSP запрос

    public static Date getTSPTime(byte[] resp) {
        Date ret = null;
        try {
            TimeStampResponse responce = new TimeStampResponse(resp);
            TimeStampToken tsToken = responce.getTimeStampToken();
            ret = tsToken.getTimeStampInfo().getGenTime();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        return ret;
    }
    //Метод проверяющий TSP запрос

    public static boolean checkTSPData(byte[] resp, byte[] buf) {
        boolean ret = false;
        try {
            TimeStampResponse responce = new TimeStampResponse(resp);
            TimeStampToken tsToken = responce.getTimeStampToken();
            byte[] digTSP = tsToken.getTimeStampInfo().getMessageImprintDigest();
            byte[] hash = new byte[32];
            //Создаем объект формирования Хэш
            GammaGost3411Digest digest = new GammaGost3411Digest();
            digest.update(buf, 0, buf.length);
            digest.doFinal(hash, 0);
            ret = Arrays.equals(digTSP, hash);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        return ret;
    }
}