package mixconfig.tools.dataretention.smartcard;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.X509Certificate;
import java.util.Collection;

import org.junit.Test;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.cmp.PKIStatusInfo;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.tsp.MessageImprint;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.tsp.*;
import org.bouncycastle.x509.X509Store;

public class SimpleTimeServerTest {

	protected byte[] getCertificateFromServer() throws IOException, TSPException, NoSuchAlgorithmException, NoSuchProviderException, CMSException, CertStoreException {
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		
		TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator();
		timeStampRequestGenerator.setCertReq(true);
		
		TimeStampRequest request = timeStampRequestGenerator.generate(TSPAlgorithms.SHA1, new byte[20]);

		byte[] requestData = request.getEncoded();

		// Create connection
		URL url = new URL("http://tsa.swisssign.net/");
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestMethod("POST");
		connection.setRequestProperty("Content-Type", "application/timestamp-query");

		connection.setRequestProperty("Content-Length", "" + Integer.toString(requestData.length));

		connection.setUseCaches(false);
		connection.setDoInput(true);
		connection.setDoOutput(true);

		// Send request
		DataOutputStream requestDataStream = new DataOutputStream(connection.getOutputStream());
		requestDataStream.write(requestData);
		requestDataStream.flush();
		requestDataStream.close();

		// Get Response
		InputStream responseDataStream = connection.getInputStream();
		TimeStampResponse resp = new TimeStampResponse (responseDataStream);
        
        resp.validate (request);
        TimeStampToken  tsToken = resp.getTimeStampToken();
        SignerId signer_id = tsToken.getSID();

        BigInteger cert_serial_number = signer_id.getSerialNumber();

        System.out.println ("Signer ID serial "+signer_id.getSerialNumber());
        System.out.println ("Signer ID issuer "+signer_id.getIssuerAsString());
        

        CertStore cs = tsToken.getCertificatesAndCRLs ("Collection", "BC");

        
        Collection certs = cs.getCertificates (null);
        
        X509Certificate certificate = null;
        for (Object tmp : certs) {
            X509Certificate cert = (X509Certificate)tmp;
            if (cert_serial_number != null) {
                if (cert.getSerialNumber().equals (cert_serial_number)) {
                    System.out.println ("using certificate with serial: "+cert.getSerialNumber());
                    certificate = cert;
                }
            } else {
                if (certificate == null) {
                    certificate = cert;
                }
            }
            
            System.out.println ("Certificate subject dn "+cert.getSubjectDN());
            System.out.println ("Certificate serial "+cert.getSerialNumber());
        }
        System.out.println ("USED Certificate subject dn "+certificate.getSubjectDN());
        System.out.println ("USED Certificate serial "+certificate.getSerialNumber());

        System.out.println(Helpers.bytesToHexDigits(certificate.getPublicKey().getEncoded()));
        
        return certificate.getPublicKey().getEncoded();

	}
	@Test
	public void sendToTimeServer() {
		try {
			byte[] certificate = getCertificateFromServer();
			TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator();
			// reqGen.setCertReq(true);

			// Dummy request
			/*
			 * The messageImprint field SHOULD contain the hash of the datum to
			 * be time-stamped. The hash is represented as an OCTET STRING. Its
			 * length MUST match the length of the hash value for that algorithm
			 * (e.g., 20 bytes for SHA-1 or 16 bytes for MD5).
			 * 
			 * The nonce, if included, allows the client to verify the
			 * timeliness of the response when no local clock is available. The
			 * nonce is a large random number with a high probability that the
			 * client generates it only once (e.g., a 64 bit integer). In such a
			 * case the same nonce value MUST be included in the response,
			 * otherwise the response shall be rejected.
			 */
			byte[] byteArray = new byte[] { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09,
					(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte)0x00, (byte)0x00, (byte)0x00 }; // 20 elements
			TimeStampRequest request = timeStampRequestGenerator.generate(TSPAlgorithms.SHA1, byteArray);

			byte[] requestData = request.getEncoded();

			// Create connection
			URL url = new URL("http://tsa.swisssign.net/");
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
			connection.setRequestProperty("Content-Type", "application/timestamp-query");

			connection.setRequestProperty("Content-Length", "" + Integer.toString(requestData.length));

			connection.setUseCaches(false);
			connection.setDoInput(true);
			connection.setDoOutput(true);

			// Send request
			DataOutputStream requestDataStream = new DataOutputStream(connection.getOutputStream());
			requestDataStream.write(requestData);
			requestDataStream.flush();
			requestDataStream.close();

			// Get Response
			InputStream responseDataStream = connection.getInputStream();

			ASN1InputStream inputStream = new ASN1InputStream(responseDataStream);
			DERObject o = inputStream.readObject();
			if (o instanceof ASN1Sequence)
				System.out.println("ASN1Sequence");

			System.out.println("Kompletter String");
			System.out.println(Helpers.bytesToHexDigits(o.getDEREncoded()));
			ASN1Sequence asn1Sequence = (ASN1Sequence) o;

			System.out.println("Outer Object");
			System.out.println("outer - 0 - PKI Status Info");
			System.out.println(PKIStatusInfo.getInstance(asn1Sequence.getObjectAt(0)));
			// System.out.println("outer - 1");
			// System.out.println(asn1Sequence.getObjectAt(1));

			System.out.println("outer - 1 - TimeStampToken");
			ContentInfo timeStampToken = ContentInfo.getInstance(asn1Sequence.getObjectAt(1));
			System.out.println(timeStampToken);
			// new TimeStampToken(timeStampToken);

			// CONTENTS THEMSELVES
			// package org.bouncycastle.cms.CMSSignedData;
			SignedData signedData = SignedData.getInstance(timeStampToken.getContent());
			System.out.println("Payload");
			System.out.println(Helpers.bytesToHexDigits(((ASN1OctetString) signedData.getEncapContentInfo().getContent()).getOctets()));
			
			
			//System.out.println("SignerInfos");
			//System.out.println(Helpers.bytesToHex(signedData.getSignerInfos().getDEREncoded()));
			
			System.out.println("Signer infos RAW");
			System.out.println(signedData.getSignerInfos().getObjectAt(0));
			
			System.out.println("Signer Infos processed - signing bytes");
            SignerInfo info = SignerInfo.getInstance(signedData.getSignerInfos().getObjectAt(0));
            
            // SIGNATURE!!!
            byte[] signature = info.getEncryptedDigest().getOctets();
            System.out.println(Helpers.bytesToHexDigits(signature));
            
            //System.out.println(info.getDigestEncryptionAlgorithm()); // RSA - OID 1.2.840.113549.1.1.1
            //System.out.println(info.getDigestAlgorithm()); // SHA1 Hash Algorithm - OID 1.3.14.3.2.26
            //System.out.println(info.getDigestAlgorithm().getObjectId().getId());
            
            DERObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType();

			//  signerInfos.add(new SignerInformation(info, contentType, signedContent, null));
			
			
			// signer.getSID() -> Signer ID
			
			// signedData.
			// this.signedContent = new CMSProcessableByteArray(
			// ((ASN1OctetString)(signedData.getEncapContentInfo()
			// .getContent())).getOctets());

			/*
			 * ASN1Sequence inner = (ASN1Sequence)tmp;
			 * System.out.println("inner - 0");
			 * System.out.println(inner.getObjectAt(0));
			 * 
			 * System.out.println("inner - 1");
			 * System.out.println(inner.getObjectAt(1)); // TimeStampToken?
			 */

			// MessageImprint.getInstance(inner.getObjectAt(1));
			/*
			 * if (inner.getObjectAt(1) instanceof ASN1Sequence) {
			 * System.out.println("INNER ASN1 SEQ"); }
			 */

			// MessageImprint.getInstance(asn1Sequence.getObjectAt(1));
			/*
			 * System.out.println("2");
			 * System.out.println(asn1Sequence.getObjectAt(2));
			 * System.out.println("3");
			 * System.out.println(asn1Sequence.getObjectAt(3));
			 * System.out.println("4");
			 * System.out.println(asn1Sequence.getObjectAt(4));
			 */
			// DERInteger version =
			// DERInteger.getInstance(asn1Sequence.getObjectAt(0));
			// System.out.println("Version " + version.getValue());

			// o[93]; // irgendwo dort sind die nullen!! - warscheinlich 20
			// bytes lang

			/*
			 * TimeStampResponse timeStampResponse = new
			 * TimeStampResponse(responseDataStream);
			 * responseDataStream.close();
			 * 
			 * timeStampResponse.validate(request); System.out.println("Time: "
			 * +
			 * timeStampResponse.getTimeStampToken().getTimeStampInfo().getGenTime
			 * ().toGMTString()); System.out.println("Serial: " +
			 * timeStampResponse
			 * .getTimeStampToken().getTimeStampInfo().getSerialNumber());
			 * System.out.println("Signer ID: " +
			 * timeStampResponse.getTimeStampToken().getSID());
			 * System.out.println("Accuracy: " +
			 * timeStampResponse.getTimeStampToken
			 * ().getTimeStampInfo().getGenTimeAccuracy().toString());
			 */
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
