package com.secutix.shop.process.crypto;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.junit.Assert;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Resources;

public class CookieDecoder {
	private PrivateKey clientPrivateKey;
	private Certificate secutixCertificate;

	private ObjectMapper objectMapper = new ObjectMapper();

	private boolean dumpToStdout = true;

	public static void main(String[] args) throws Exception {
		String cookieContent =
				"{\"individualTitle\":\"MR\",\"individualFirstName\":\"Luke\",\"individualLastname\":\"KOENIG\",\"nickname\":\"\",\"structureOfficialName\":\"\",\"lang\":\"fr\",\"currentLang\":\"fr\",\"timestamp\":\"1498651226\",\"expires\":\"2017-06-28T14:30:26.165+02:00\",\"additionalData\":\"{\\\"key\\\":\\\"O2jW7phFlNhc1XXnhPVOaxltGsqC+hhXbu/QMlRZahcNKd6y6wdQFgJ5vL6QQIrFevdsx++g8rr5d8xg5DBVMU5RyQdrjfWr9M0JNkgQNkAPNUj5lssZ67nGQWjR4jm/M2LXxX8cWlM5Im5dwtFNu70yrih75s7n0jqGOz+wCtQ=\\\",\\\"digest\\\":\\\"TL20RUjhPXcne58d8uzT5b3GjfVa7kCerhkIBWzYIsov3DAXbBS90VN7spRHnUb8IpL58rHBCEzDH//5CshxC7dcmqOLsPlw2E9xAcOBqppe8GowtLSSsY2BJ+9XtOPTsQuQ9KWHk9oYOQ5aMLVxMoVSCCZq+0K50ImFgFifIRg=\\\",\\\"data\\\":\\\"qJr1z3f2zM2Hv2zvfC/eBMeBFFrsFjQ0OVtIVkhy6x8yOXo2bZEB+m4OQLAka3GQlXi9g3EnG3vtIpsbivv7gQ==\\\"}\",\"pointOfSalesCode\":\"Int1Java\"}";

		CookieDecoder decoder = new CookieDecoder();

		String[] result = decoder.decode(cookieContent);
		System.out.println("contact number:" + result[0]);
		System.out.println("contact email:" + result[1]);
	}

	@SuppressWarnings("unchecked")
	private String[] decode(String cookieContent) throws Exception {
		init(); // Inits the keys
		final Map<String, String> map = objectMapper.readValue(cookieContent, Map.class);
		String additionalData = map.get("additionalData");
		final Map<String, String> mapAdditionalData = objectMapper.readValue(additionalData, Map.class);
		return testDecodeExampleCookie(mapAdditionalData.get("key"), mapAdditionalData.get("data"),
				mapAdditionalData.get("digest"));

	}

	public void init() throws Exception {
		clientPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(
				Resources.toByteArray(Resources.getResource("stx_contact_cookie/client_private_key_pkcs8.der"))));
		secutixCertificate = CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(
				Resources.toByteArray(Resources.getResource("stx_contact_cookie/secutix_certificate_x509.pem"))));
	}

	public String[] testDecodeExampleCookie(String cookieKey, String cookieData, String cookieDigest) throws Exception {
		try {

			// decrypt the key

			final byte[] keyB = Base64.decodeBase64(cookieKey.getBytes());
			final Cipher keyCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			keyCipher.init(Cipher.DECRYPT_MODE, clientPrivateKey);
			final byte[] clearKey = keyCipher.doFinal(keyB);
			if (dumpToStdout) {
				System.out.println("decrypted key: " + new String(Base64.encodeBase64(clearKey)));
			}

			// decrypt the data
			final String dataS = cookieData;
			final byte[] dataB = Base64.decodeBase64(dataS.getBytes());
			final Cipher dataCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			dataCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(clearKey, "AES"));
			final byte[] clearData = dataCipher.doFinal(dataB);
			if (dumpToStdout) {
				System.out.println("decrypted message: " + new String(clearData));
			}

			String clearDataString = new String(clearData);
			@SuppressWarnings("unchecked")
			final Map<String, String> map = objectMapper.readValue(clearDataString, Map.class);
			String contactNumber = map.get("contactNumber");
			String email = map.get("email");

			// verify message digest (signature)
			final String digestS = cookieDigest;
			final byte[] digestB = Base64.decodeBase64(digestS.getBytes());
			final Cipher digestCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			digestCipher.init(Cipher.DECRYPT_MODE, secutixCertificate);
			final byte[] clearDigest = digestCipher.doFinal(digestB);
			if (dumpToStdout) {
				System.out.println("decrypted digest: " + new String(Base64.encodeBase64(clearDigest)));
			}
			final byte[] computedDigest = MessageDigest.getInstance("SHA-1").digest(clearData);
			if (dumpToStdout) {
				System.out.println("computed digest: " + new String(Base64.encodeBase64(computedDigest)));
			}
			assertThat(clearDigest).isEqualTo(computedDigest);

			return new String[] { contactNumber, email };
		} catch (Exception e) {
			if (dumpToStdout) {
				e.printStackTrace();
			}
			Assert.fail(e.getMessage());
			throw e;
		}

	}
}
