/************************************************************************
*
* Copyright (c) 2003-2004, C&C Research Laboratories, NEC Europe Ltd.
*
* Copyright in this library belongs to C&C Research Laboratories,
* Rathausallee 10, 53757 Sankt Augustin, Germany.
*
* This software may not be used, sold, licensed, transferred, copied
* or reproduced in whole or in part in any manner or form or in or
* on any media by any person other than in accordance with the terms
* of the Licence Agreement supplied with the software, or otherwise
* without the prior written consent of the copyright owners.
*
* This software is distributed WITHOUT ANY WARRANTY, without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE, except where stated in the Licence Agreement supplied with
* the software.
*
*  Created By :           Federico Crazzolara
*  Created for Project :  GEMSS (IST-2001-37153)
*
************************************************************************/


package de.nece.ccrl.securitycontext.tokenprovider;

import eu.gemss.components.security.SecurityProviderConfiguration;
import eu.gemss.components.security.token.*;
import eu.gemss.components.security.token.provision.*;
import eu.gemss.components.security.token.store.*;
import eu.gemss.components.security.token.types.key.symmetric.SecretKeyToken;
import eu.gemss.components.security.token.types.pki.CertificateToken;
import eu.gemss.signals.SignalHandler;
import eu.gemss.components.security.token.types.contexttoken.SecurityContextToken;
import de.nece.ccrl.securitycontext.token.types.contexttoken.*;
import de.nece.ccrl.securitycontext.token.types.key.symmetric.SecretKeyTokenImpl;
import de.nece.ccrl.securitycontext.token.types.key.KeyDescriptor;
import de.nece.ccrl.tokenmanager.drivers.apachetestdriver.*;
import de.nece.ccrl.tokenmanager.*;
import de.nece.ccrl.tokenmanager.codec.GEMSSTokenManagerCoDec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import java.security.Security;
import java.util.Random;



public class NECSecurityTokenProvider implements SecurityTokenProvider{

    String keystore = null;
    String password = null;
    TokenDescriptor[] tokentypes;
    
   

    public NECSecurityTokenProvider() {
	TokenDescriptor contexttokentype = new SecurityContextTokenDescriptor("NSL");
	TokenDescriptor symmetrictokentype = new KeyDescriptor(KeyDescriptor.SYM_KEY,KeyDescriptor.DES);
	tokentypes = new TokenDescriptor[2];
	tokentypes[0] = contexttokentype;
	tokentypes[1] = symmetrictokentype;
	Security.addProvider(new BouncyCastleProvider());
	// could be done dynamically with a list of different protocols and managers. *** future work ***
    }
	    

    public void setKeystore(String location, String password) {
	keystore = location;
	this.password = password;		
    }

    
    public void setSignalHandler(SignalHandler channel) {
	// Not needed here because does not affect the keystore
    }

    public void setTokenProviderConfiguration(SecurityProviderConfiguration conf) {
	// Not needed here because does not affect the keystore
    }

    public TokenDescriptor[] getSupportedTokenList() {
	return tokentypes;
    }

    public SecurityToken generateSecurityToken(TokenDescriptor tokenDesc) throws TokenProviderException {
	if (tokenDesc instanceof SecurityContextTokenDescriptor)
	    return generateContextToken((SecurityContextTokenDescriptor)tokenDesc);
	else if ((tokenDesc instanceof KeyDescriptor) && ((tokenDesc.getDescription()).equals(KeyDescriptor.SYM_KEY)))
	    return generateSecretKey((KeyDescriptor)tokenDesc);
	else throw new TokenProviderException("unsupported token type");
    }
	
    protected SecretKeyToken generateSecretKey(KeyDescriptor desc) throws TokenProviderException {
	if (!(desc.getAlgorithm()).equals(KeyDescriptor.DES)) 
	    throw new TokenProviderException("unsupported token type");
	SecretKey key = null;
	try{
	    Random generator = new Random();
	    byte[] b = new byte[8];
	    generator.nextBytes(b);
	    DESKeySpec deskey = new DESKeySpec(b);
	    // convert deskey into key using a factory
	    SecretKeyFactory factory = SecretKeyFactory.getInstance("DES","BC");
	    key = factory.generateSecret(deskey);
	} catch (Exception e) {throw new TokenProviderException("unexpected error when generating the key");}
	return new SecretKeyTokenImpl(key,desc.getOwners());
    }

    protected SecurityToken generateContextToken(SecurityContextTokenDescriptor desc) 
	throws TokenProviderException {

	TokenManagerDriver driver;
	TokenManagerCoDec codec;
	TokenManager tm;	

	String responder = desc.getService();
	String tokenservice = desc.getTokenService();
	String protocol = desc.getProtocol();
	SecurityToken[] supporttokens = desc.getSupportTokens();


	// well formdenss checks for the request
	if (responder == null) throw new TokenProviderException("A responder Service must be provided");
	if (tokenservice == null) throw new TokenProviderException("A Token Service must be provided");


	// get initiator from private key information (certchain associated with it see NECSecurityTokenStoreProvider)
	
	NECTokenStoreProvider prov = new NECTokenStoreProvider(); 
	prov.setKeystore(keystore, password); //keystore parameters must be set
	String initiator = null;
	try{
	    initiator = prov.getInitiator();
	} catch (StoreProviderException e) {throw new TokenProviderException("Private key has no corresponding certificate");}
	

			
	// chose the right token manager according to protocol          **** future work do it better ****
	String tm_classname = "de.nece.ccrl.tokenmanager.manager."+(protocol)+"TokenManager";
	try{
	    Class s = Class.forName(tm_classname);
	    tm = (TokenManager) s.newInstance();
	} catch (Exception e) {throw new TokenProviderException("Undefined token manager class");}


	// chose the right driver according to tokenservice endpoint (IN DESCRIPTOR)    **** future work **** TODO
	driver = new ApacheDriver(tokenservice); //perhaps pass tokenservice endpoint or wsdl to the driver
	codec = new GEMSSTokenManagerCoDec();
	tm.setDriver(driver);
	tm.setCoDec(codec);

	// call the token manager
	SecurityToken token = null;

	try {
	    tm.setSupportTokens(supporttokens);
	    token  = (SecurityToken) tm.execute(initiator, responder);
	} catch (TokenManagerException e) {
	    e.printStackTrace();
	    throw new TokenProviderException(e.getExceptionMsg());}

	// update the descriptor of the token before returning it with information additional
	if (token instanceof SecurityContextToken)
	    return new SecurityContextTokenImpl(((SecurityContextToken)token).getIncludedToken(),
						protocol,responder,tokenservice,
						((SecurityContextToken)token).getExpirationDate());
	if (token instanceof CertificateToken) 
	    return new SecurityContextTokenImpl(token,protocol,responder,tokenservice);
	else throw new TokenProviderException("a context token was requested but not recieved");
    }
    
}
