/*
 * (C) Copyright IBM Corp. 1999-2001  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package enc;

import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import com.ibm.dom.util.DOMUtil;
import com.ibm.xml.dsig.IDResolver;
import com.ibm.xml.dsig.XSignatureException;
import com.ibm.xml.enc.AlgorithmFactoryExtn;
import com.ibm.xml.enc.EncryptedKeyRetriever;
import com.ibm.xml.enc.EncryptionContext;
import com.ibm.xml.enc.EncryptionEngine;
import com.ibm.xml.enc.DecryptionContext;
import com.ibm.xml.enc.KeyInfoResolver;
import com.ibm.xml.enc.KeyInfoResolvingException;
import com.ibm.xml.enc.StructureException;
import com.ibm.xml.enc.type.EncryptedData;
import com.ibm.xml.enc.util.AdHocIdResolver;
import com.ibm.xml.enc.util.KeyStoreKeyInfoResolver;
import com.ibm.xml.enc.util.SameDocumentEncryptedKeyRetriever;

import org.apache.xerces.parsers.DOMParser;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import de.tu_dresden.diplom.richter_mirko_mat2628335.Configuration;

/**
 * Class out of XSS4J that shows how to encrypt XML-content. main() and main2() were added later.
 * @author Takeshi IMAMURA
 * @version $Id: DOMCipher.java,v 1.3 2002/12/26 10:49:21 imamu Exp $
 */
public class DOMCipher {
   //
   // Methods
   //

   public static void main(String[] args){
      Configuration conf = new Configuration(Configuration.APPLICATION_BASE);
            DOMCipher.main2(new String[]{"-e",
                                         "*",
                                         conf.getExtAppHome() + File.separator + "ibm_xml_security_suite\\xss4j\\data\\enc\\keyinfo1.xml",
                                         conf.getExtAppHome() + File.separator + "ibm_xml_security_suite\\xss4j\\data\\enc\\bookorder.xml",
                                         "//*[name()='cardinfo']",
                                         conf.getExtAppHome() + File.separator + "ibm_xml_security_suite\\xss4j\\data\\enc\\template1.xml"
            });
   }

   public static void main2(String[] args) {
      if (args.length < 1) {
         printUsage();
         System.exit(1);
      }

      String arg = args[0];
      if (arg.equals("-e") && args.length > 3) {
         try {
            AlgorithmFactoryExtn af = new AlgorithmFactoryExtn();
            if (!"*".equals(args[1])) {
               af.setProvider(args[1]);
            }

            Keyinfo ki = getKeyinfo(args[2]);

            Document d = getDocument(args[3]);

            KeyInfoResolver kir = getKeyInfoResolver(ki, af, null, null);
            kir.setOperationMode(KeyInfoResolver.ENCRYPT_MODE);

            EncryptionContext ec = new EncryptionContext();
            ec.setAlgorithmFactory(af);
            ec.setKeyInfoResolver(kir);

            for (int i = 4; i < args.length;) {
               try {
                  NodeList nl = XPathAPI.selectNodeList(d, args[i++]);
                  Element e = getDocument(args[i++]).getDocumentElement();
                  encryptElements(nl, ec, e);

               } catch (Exception e) {
                  printStackTrace(e);
               }
            }

            printDocument(d);

         } catch (Exception e) {
            printStackTrace(e);
            System.exit(1);
         }
      } else if (arg.equals("-d") && args.length > 3) {
         try {
            AlgorithmFactoryExtn af = new AlgorithmFactoryExtn();
            if (!"*".equals(args[1])) {
               af.setProvider(args[1]);
            }

            Keyinfo ki = getKeyinfo(args[2]);

            Document d = getDocument(args[3]);

            IDResolver ir = new AdHocIdResolver();

            EncryptedKeyRetriever ekr = new SameDocumentEncryptedKeyRetriever(d);

            KeyInfoResolver kir = getKeyInfoResolver(ki, af, ir, ekr);
            kir.setOperationMode(KeyInfoResolver.DECRYPT_MODE);

            DecryptionContext dc = new DecryptionContext();
            dc.setAlgorithmFactory(af);
            dc.setKeyInfoResolver(kir);
            dc.setIdResolver(ir);
            dc.setEncryptedKeyRetriever(ekr);

            for (int i = 4; i < args.length; i++) {
               try {
                  NodeList nl = XPathAPI.selectNodeList(d, args[i]);
                  decryptEncryptedData(nl, dc);

               } catch (Exception e) {
                  printStackTrace(e);
               }
            }

            printDocument(d);

         } catch (Exception e) {
            printStackTrace(e);
            System.exit(1);
         }
      } else if (arg.equals("-h")) {
         printUsage();
         System.exit(0);

      } else {
         printUsage();
         System.exit(1);
      }
   }

   private static void printUsage() {
      System.err.println("Usage: java enc.DOMCipher <option> [<arg>...]");
      System.err.println("where <option> is:");
      System.err.println("   -e <provider> <keyinfo> <target> [<xpath> <template>...]");
      System.err.println("        encrypt elements");
      System.err.println("   -d <provider> <keyinfo> <target> [<xpath>...]");
      System.err.println("        decrypt EncryptedData elements");
      System.err.println("   -h   print this message");
   }

   static Keyinfo getKeyinfo(String name) throws IOException, SAXException {
      Element e = getDocument(name).getDocumentElement();

      return new Keyinfo(e);
   }

   static Document getDocument(String name) throws IOException, SAXException {
      System.out.println("LOADING DOCUMENT '" + name + "'");
      DOMParser dp = new DOMParser();
      dp.setErrorHandler(new ErrorHandler() {
         //
         // Methods of interface: ErrorHandler
         //

         public void warning(SAXParseException exception) throws SAXException {
            printStackTrace(exception);
         }

         public void error(SAXParseException exception) throws SAXException {
            printStackTrace(exception);
         }

         public void fatalError(SAXParseException exception) throws SAXException {
            throw exception;
         }
      });
      dp.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
      dp.parse(name);

      return dp.getDocument();
   }

   static KeyInfoResolver getKeyInfoResolver(Keyinfo keyinfo, AlgorithmFactoryExtn factory,
                                             IDResolver idRes,
                                             EncryptedKeyRetriever retriever)
        throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException,
        NoSuchProviderException {
      return getKeyInfoResolver(keyinfo, factory, idRes, retriever, getKeyStore(keyinfo));
   }

   static KeyInfoResolver getKeyInfoResolver(Keyinfo keyinfo, AlgorithmFactoryExtn factory,
                                             IDResolver idRes,
                                             EncryptedKeyRetriever retriever,
                                             KeyStore store) {
      KeyStoreKeyInfoResolver kiRes = new KeyStoreKeyInfoResolver(store);
      kiRes.setAlgorithmFactory(factory);
      kiRes.setIdResolver(idRes);
      kiRes.setEncryptedKeyRetriever(retriever);
      for (Iterator i = keyinfo.fAlias2Keypass.keySet().iterator(); i.hasNext();) {
         String s = (String) i.next();
         kiRes.putAliasAndPassword(s, (char[]) keyinfo.fAlias2Keypass.get(s));
      }

      return kiRes;
   }

   static KeyStore getKeyStore(Keyinfo keyinfo)
        throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException,
        NoSuchProviderException {
      String s = keyinfo.fStoretype;
      if (s == null) {
         s = KeyStore.getDefaultType();
      }
      KeyStore store = null;
      if (keyinfo.fProvider != null) {
         store = KeyStore.getInstance(s, keyinfo.fProvider);

      } else {
         store = KeyStore.getInstance(s);
      }

      s = keyinfo.fStorename;
      if (s == null) {
         s = System.getProperty("user.home") + File.separator + ".keystore";
      }
      File f = new File(s);
      InputStream is = null;
      if (f.exists()) {
         is = new FileInputStream(f);
      }
      store.load(is, keyinfo.fStorepass);

      return store;
   }

   private static void encryptElements(NodeList nodes, EncryptionContext context,
                                       Element template) {
      for (int i = 0, l = nodes.getLength(); i < l; i++) {
         Node n = nodes.item(i);
         if (n.getNodeType() == Node.ELEMENT_NODE) {
            try {
               encryptElement((Element) n, context, template);

            } catch (Exception e) {
               printStackTrace(e);
            }
         }
      }
   }

   private static void encryptElement(Element elem, EncryptionContext context,
                                      Element template)
        throws BadPaddingException, IOException, IllegalBlockSizeException,
        InvalidAlgorithmParameterException, InvalidKeyException,
        KeyInfoResolvingException, NoSuchAlgorithmException, NoSuchPaddingException,
        NoSuchProviderException, StructureException {
      context.setData(elem);
      context.setEncryptedType((Element) template.cloneNode(true), null, null, null);
      context.setKey(null);
      context.encrypt();
      context.replace();
   }

   private static void decryptEncryptedData(NodeList nodes, DecryptionContext context) {
      for (int i = 0, l = nodes.getLength(); i < l; i++) {
         Node n = nodes.item(i);
         if (n.getNodeType() == Node.ELEMENT_NODE) {
            Element el = (Element) n;
            if (EncryptedData.isOfType(el)) {
               try {
                  decryptEncryptedData(el, context);

               } catch (Exception ex) {
                  printStackTrace(ex);
               }
            }
         }
      }
   }

   private static void decryptEncryptedData(Element encData, DecryptionContext context)
        throws BadPaddingException, IOException, IllegalBlockSizeException,
        InvalidAlgorithmParameterException, InvalidKeyException,
        KeyInfoResolvingException, NoSuchAlgorithmException, NoSuchPaddingException,
        NoSuchProviderException, ParserConfigurationException, SAXException,
        StructureException, XSignatureException {
      context.setEncryptedType(encData, null, null, null);
      context.setKey(null);
      context.decrypt();
      context.replace();
   }

   static void printDocument(Document doc) throws IOException {
      OutputFormat of = new OutputFormat(doc);
      of.setPreserveSpace(true);
      XMLSerializer xs = new XMLSerializer(System.out, of);
      xs.serialize(doc);
      System.out.println();
   }

   static void printStackTrace(Throwable thr) {
      if (thr instanceof SAXException) {
         Throwable t = ((SAXException) thr).getException();
         if (t != null) {
            thr = t;
         }
      } else if (thr instanceof TransformerException) {
         Throwable t = ((TransformerException) thr).getException();
         if (t != null) {
            thr = t;
         }
      } else if (thr instanceof XSignatureException) {
         Throwable t = ((XSignatureException) thr).getException();
         if (t != null) {
            thr = t;
         }
      }
      thr.printStackTrace();
   }


   //
   // Classes
   //

   static class Keyinfo {
      //
      // Data
      //

      String fStorename;
      String fStoretype;
      char[] fStorepass;
      String fProvider;
      Map fAlias2Keypass;

      private String alias;
      private char[] pass;


      //
      // Constructors
      //

      Keyinfo(Element keyinfo) {
         if (!isOfType(keyinfo)) {
            throw new IllegalArgumentException("Not keyinfo element");
         }

         fAlias2Keypass = new HashMap();
         init(keyinfo);
      }


      //
      // Methods
      //

      static boolean isOfType(Element elem) {
         if(elem == null){
            System.out.println("ELEMENT NULL");
         }else if(!"keyinfo".equals(elem.getTagName())){
            System.out.println("ELEMENT WRONG: '" + elem.getTagName() + "'");
         }
         return (elem != null && "keyinfo".equals(elem.getTagName()));
      }

      private void init(Node node) {
         for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) {
            init(n);
         }

         if (node.getNodeType() == Node.ELEMENT_NODE) {
            String s = node.getNodeName();
            if (s.equals("alias")) {
               alias = DOMUtil.getStringValue(node);

            } else if (s.equals("key")) {
               fAlias2Keypass.put(alias, pass);
               alias = null;
               pass = null;

            } else if (s.equals("keystore")) {
               fStorepass = pass;
               pass = null;

            } else if (s.equals("name")) {
               fStorename = DOMUtil.getStringValue(node);

            } else if (s.equals("password")) {
               pass = DOMUtil.getStringValue(node).toCharArray();

            } else if (s.equals("provider")) {
               fProvider = DOMUtil.getStringValue(node);

            } else if (s.equals("type")) {
               fStoretype = DOMUtil.getStringValue(node);
            }
         }
      }
   }
}
