/************************************************************************
*
* 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.tokenmanager.codec;


import org.xmlsoap.schemas.ws._2002._12.secext.*;
import de.nece.ccrl.tokenmanager.*;
import de.nece.ccrl.securitycontext.token.types.key.KeyDescriptor;
import de.nece.ccrl.securitycontext.token.types.pki.CertificateDescriptor;
import de.nece.ccrl.securitycontext.token.types.pki.X509CertificateTokenImpl;
import de.nece.ccrl.securitycontext.token.types.contexttoken.SecurityContextTokenImpl;
import de.nece.ccrl.securitycontext.token.types.key.symmetric.SecretKeyTokenImpl;
import eu.gemss.components.security.token.types.contexttoken.SecurityContextToken;
import eu.gemss.components.security.token.types.pki.X509CertificateToken;
import eu.gemss.components.security.token.types.key.symmetric.SecretKeyToken;
import eu.gemss.components.security.token.SecurityToken;
import eu.gemss.components.security.token.TokenDescriptor;
import java.security.spec.KeySpec;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.namespace.QName;
import java.io.ByteArrayInputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
import org.apache.commons.codec.binary.*;

import java.security.cert.*;

public class GEMSSTokenManagerCoDec implements TokenManagerCoDec{

    public GEMSSTokenManagerCoDec() {
	Security.addProvider(new BouncyCastleProvider());
    }

    public Object encode(Object token) throws TokenManagerException {


	if (token instanceof SecurityContextToken) {
	    try{
		SecurityContextTokenType t = new SecurityContextTokenType();
		SecurityToken st = ((SecurityContextToken) token).getIncludedToken();
		BinarySecurityTokenType keys = (BinarySecurityTokenType) this.encode(st);
		t.setKeys(keys);
		return t;
	    }catch(Exception e) {}
	}

	if (token instanceof X509CertificateToken) {
	    try{
		BinarySecurityTokenType t = new BinarySecurityTokenType();
		X509Certificate c = (X509Certificate) ((X509CertificateToken) token).getX509Certificate();
		
		// necessary to avoid character convertions problems in XML because der certificate is in binary
		// and some characters are not supported 
		byte[] binary = c.getEncoded();
		Base64 codec = new Base64();
		
		String v = new String(codec.encode(binary), "UTF-8");
		t.setValue(v);
		TokenDescriptor d = ((X509CertificateToken)token).getTokenDescriptor();
		t.setValueType(new QName(d.getDescription()));
		t.setEncodingType(new QName("Base64Binary"));
		return t;
	    }catch(Exception e) {System.out.println(e.getMessage());}
	}

	if (token instanceof SecretKeyToken){
	    	BinarySecurityTokenType t = new BinarySecurityTokenType();
		String v = null;
		String alg = null;
		String format = null;
		try {
		    // need to extract key and then encod cos encoding of token does 
		    // return serialization of entire token not key material.
		    byte[] binary = (((SecretKeyToken)token).getKey()).getEncoded(); 
		    Base64 codec = new Base64();

		    v = new String(codec.encode(binary), "UTF-8");
		    alg = ((SecretKeyToken)token).getAlgorithm();
		    format = ((SecretKeyToken)token).getEncodingFormat();
		} catch (Exception e) {}
		if (!(format.equals("RAW"))) throw new TokenManagerException("Only RAW keys can be exchanged");
		t.setValue(v);
		TokenDescriptor d = null;
		try{
		    d= ((SecretKeyToken)token).getTokenDescriptor();
		} catch (Exception e){}
		t.setValueType(new QName(d.getDescription())); 
		t.setEncodingType(new QName(alg)); 
		// we assume that secret keys never support encoding but are always RAW
		// this is often the case and is surely the case for java DES keys and SecretKeySpec.
		// therefore we can use algorithm information in the EncodingType instead
		return t;
	}

	throw new TokenManagerException("Unsupported token type in context protocol");
    }


    public Object decode(Object token) throws TokenManagerException{
	

	if (token instanceof SecurityContextTokenType) {
	    SecurityToken st = (SecurityToken) this.decode(((SecurityContextTokenType) token).getKeys());
	    SecurityContextToken t = new SecurityContextTokenImpl(st);
	    return t;
	}

	if (token instanceof BinarySecurityTokenType) {
	    
	    String type = (((BinarySecurityTokenType) token).getValueType()).getLocalPart();
	    String encType = (((BinarySecurityTokenType) token).getEncodingType()).getLocalPart();
	    String v = ((BinarySecurityTokenType) token).getValue();

	    byte[] b = null;
	    try {
		b = v.getBytes("UTF-8");
	    } catch (Exception e) {}	    

	    Base64 codec = new Base64();
	    byte[] bb = Base64.decodeBase64(b);

	    
	    
	    if (type.equals(CertificateDescriptor.X509)) {

		ByteArrayInputStream stream = new ByteArrayInputStream(bb);
		CertificateFactory factory = null;
		try {
		    factory = CertificateFactory.getInstance("X.509");
		} catch (Exception e){}
		X509Certificate c = null;
		try {
		    c = (X509Certificate) factory.generateCertificate(stream);
		} catch (CertificateException e) {System.out.println(e.getMessage());}
		
		X509CertificateToken t = new X509CertificateTokenImpl((X509Certificate)c);
		return t;
	       
	    }
	 
	    if (type.equals(KeyDescriptor.SYM_KEY)) {

		KeySpec spec = null;
		String classname = encType+"KeySpec";
		
		spec = new SecretKeySpec(bb,classname);
		SecretKeyFactory factory = null;
		try{ 
		    factory = SecretKeyFactory.getInstance(encType,"BC");
		} catch (Exception e) {}
		SecretKey k = null;
		try {
		    k = factory.generateSecret(spec);
		} catch (Exception e) {}
		SecretKeyToken t = new SecretKeyTokenImpl(k);
		return t;
	    }
	}

	throw new TokenManagerException("Unsupported token type in context protocol");

    }

}
