/*
 * Copyright (c) eHealth
 */
using System;
using System.Collections.Generic;

using Net.Sf.Pkcs11;
using Net.Sf.Pkcs11.Objects;
using Net.Sf.Pkcs11.Wrapper;


namespace be.fgov.ehealth.technicalconnector.jca.proxy
{
	/// <summary>
	/// Description of BeIDDistributedSigner
	/// </summary>
	public class BeIDDistributedSigner :  be.fgov.ehealth.technicalconnector.distributedkeys.DistributedSignerProxy
	{
		private Module m;
		private String mFileName= "beidpkcs11.dll";
		
		
		private static byte[] SHA1_DIGEST_INFO_PREFIX = new byte[]{
			0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14
		};

		private static byte[] SHA256_DIGEST_INFO_PREFIX = new byte[]{
			0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
		};

		private static byte[] SHA512_DIGEST_INFO_PREFIX = new byte[]{
			0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
		};
		
		
		public byte[] sign(byte[] digestValue, string digestAlgo, string alias){
			byte[] encryptedData = null;
			try
			{
				m = Module.GetInstance(mFileName);
				Slot slot = m.GetSlotList(true)[0];
				Session session = slot.Token.OpenSession(true);
				ObjectClassAttribute classAttribute = new ObjectClassAttribute(CKO.PRIVATE_KEY);
				
				ByteArrayAttribute keyLabelAttribute = new ByteArrayAttribute(CKA.LABEL);
				keyLabelAttribute.Value = System.Text.Encoding.UTF8.GetBytes(UppercaseFirst(alias));

				session.FindObjectsInit(new P11Attribute[] {
				                        	classAttribute,
				                        	keyLabelAttribute
				                        }
				                       );
				P11Object[] privatekeys = session.FindObjects(1) as P11Object[];
				session.FindObjectsFinal();
				
				if (privatekeys.Length >= 1)
				{
					session.SignInit(new Mechanism(CKM.RSA_PKCS), (Net.Sf.Pkcs11.Objects.PrivateKey)privatekeys[0]);
					java.io.ByteArrayOutputStream digestInfo = new java.io.ByteArrayOutputStream();
					if("SHA-1".Equals(digestAlgo)){
						digestInfo.write(SHA1_DIGEST_INFO_PREFIX);
					}else if ("SHA-256".Equals(digestAlgo)) {
						digestInfo.write(SHA256_DIGEST_INFO_PREFIX);
					}else if ("SHA-512".Equals(digestAlgo)) {
						digestInfo.write(SHA512_DIGEST_INFO_PREFIX);
					}else{
						throw new ArgumentException("Unsupported Digest Algorithm: " + digestAlgo);
					}
					digestInfo.write(digestValue);
					encryptedData = session.Sign(digestInfo.toByteArray());
				}
				return encryptedData;
			}
			finally
			{
				m.Dispose();
			}
		}
		
		public java.util.Set getAliases(){
			java.util.Set result = new java.util.HashSet();
			result.add("Authentication");
			result.add("Signature");
			return result;
		}
		
		public java.util.List getCertificateChain(string alias){
			java.util.List result = new java.util.ArrayList();
			result.add(GetCertificate(UppercaseFirst(alias)));
			result.add(GetCertificate("CA"));
			result.add(GetCertificate("Root"));
			return result;
		}

		public string getAlgorithm(string alias)
		{
			return GetCertificate(UppercaseFirst(alias)).getPublicKey().getAlgorithm();
		}

		private java.security.cert.Certificate GetCertificate(String alias)
		{
			java.security.cert.Certificate value = null;
			java.security.cert.CertificateFactory certFactory = java.security.cert.CertificateFactory.getInstance("X.509");
			
			try
			{
				m = Module.GetInstance(mFileName);
				Slot slot = m.GetSlotList(true)[0];
				Session session = slot.Token.OpenSession(true);
		
				ByteArrayAttribute fileLabel = new ByteArrayAttribute(CKA.LABEL);
				ObjectClassAttribute certificateAttribute = new ObjectClassAttribute(CKO.CERTIFICATE);
				fileLabel.Value = System.Text.Encoding.UTF8.GetBytes(UppercaseFirst(alias));
				session.FindObjectsInit(new P11Attribute[] {
				                        	certificateAttribute,
				                        	fileLabel
				                        });
				P11Object[] foundObjects = session.FindObjects(1);
				if (foundObjects.Length != 0)
				{
					X509PublicKeyCertificate cert = foundObjects[0] as X509PublicKeyCertificate;
					value =certFactory.generateCertificate(new java.io.ByteArrayInputStream(cert.Value.Value));
				}
				session.FindObjectsFinal();
			}
			
			finally
			{
				m.Dispose();
			}
			return value;

		}
		
		static string UppercaseFirst(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return string.Empty;
			}
			return char.ToUpper(s[0]) + s.Substring(1);
		}
		
		
		
		
	}
}