//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.pki;

import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Set;
import java.util.Iterator;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.naming.*;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchResult;
import javax.naming.directory.SearchControls;
import com.dstc.security.provider.OID;
import com.dstc.security.asn1.Asn1;
import com.dstc.security.x509.X500Name;
import com.dstc.security.x509.RDN;
import com.dstc.security.x509.X509CertImpl;
import com.dstc.security.util.Config;
import com.dstc.security.x509.ExtensionFactory;
import com.dstc.security.x509.extns.SubjectAltName;

/**
 * An LDAP Certficate repository tool
 *
 * @version 0.98, 98/11/01
 * @author Ming Yung
 */
public class RepositoryTool extends Repository
{
  public static final String CA_CONF = "jcsi.ca.conf";
  public static final String CA_PROPERTIES = "ca.properties";

  public static void main(String args[])
  {
    RepositoryTool rep = null;
    BufferedReader in = null;

    try
    {

      Properties env = Config.getProperties(CA_CONF, CA_PROPERTIES);

      rep = new RepositoryTool(env);
      rep.search();

      InputStreamReader isr = new InputStreamReader(System.in);
      in = new BufferedReader(isr);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }

    while (true)
    {
      try
      {
        System.out.print("\n(P)ublish cert, (R)etrieve cert, or (Q)uit? ");
        String code = in.readLine();
        if (code.equalsIgnoreCase("Q"))
        {
          break;
        }
        else if (code.equalsIgnoreCase("R"))
        {
          System.out.print("\nDistinguished Name cert is published under? ");
          X509Certificate certRet = rep.getUserCertificate(in.readLine());
    
          System.out.println("\nThe following cert was retrieved:\n");
          System.out.println(certRet.toString());

          System.out.print("\nFile to save cert or (Q)uit? ");

          String resp = in.readLine();
          if (!resp.equalsIgnoreCase("Q"))
          {
            FileOutputStream fos = new FileOutputStream(resp);
            fos.write(certRet.getEncoded());
            fos.close();
          }
        }
        else if (code.equalsIgnoreCase("P"))
        { 
          boolean isCA = false;
          System.out.print("\nPublish a (C)A cert or a (U)ser cert? ");
          if (in.readLine().equalsIgnoreCase("C"))
          {
            isCA = true;
          }
    
          System.out.print("\nCertificate file name? ");
          FileInputStream fis = new FileInputStream(in.readLine());
          byte[] enc = new byte[fis.available()];
          fis.read(enc);
          ByteArrayInputStream bais = new ByteArrayInputStream(enc);

          CertificateFactory certFact = CertificateFactory.getInstance("X509");
          X509Certificate cert 
            =(X509Certificate)certFact.generateCertificate(bais);

          System.out.println("\nAbout to publish the following cert:\n");
          System.out.println("Issuer: " + cert.getIssuerDN().getName());
          System.out.println("Subject: " + cert.getSubjectDN().getName());
          
          System.out.print("\nDistinguished Name to publish cert under? ");
    
          Attributes otherAtts = rep.getOtherAttributes(cert);

          if (isCA)
          {
            otherAtts.put(new BasicAttribute("objectclass", 
                                             "certificationAuthority"));
            rep.publishCACertificate(in.readLine(), cert, otherAtts);
          }
          else
          {
            otherAtts.put(new BasicAttribute("objectclass", "person"));
            rep.publishUserCertificate(in.readLine(), cert, otherAtts);
          }
        }
      }
      catch (NamingException e)
      {
        System.out.println(e.toString());
      }
      catch (Exception e)
      {
        System.out.println(e.toString());
        e.printStackTrace();
      }
    }
  }

  public RepositoryTool(Properties props)
  {
    super(props);
  }

  public void search() throws NamingException
  {
    try 
    {
      // specify search constraints to search subtree 
      SearchControls constraints = new SearchControls();
      constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);

      // search for all entries 
      NamingEnumeration results
          = ctx.search(BASE, "(objectclass=*)", constraints);

      // for each entry print out name 
      while (results != null && results.hasMore()) 
      {
        SearchResult si = (SearchResult)results.next();
        si.setRelative(false);
        System.out.println("name: " + si.getName());
      }
    } 
    catch (NamingException e)
    {
      throw e;
    }
    catch (Exception e) 
    {
      e.printStackTrace();
    }  
  }

  public Attributes getOtherAttributes(X509Certificate cert)
  {
    try
    {
      Attributes otherAtts = new BasicAttributes(true);

      Hashtable atts = getRDNs(new X500Name(cert.getSubjectDN().getName()));

      String val;
      if ((val = (String)atts.get("CN")) != null)
      {
        otherAtts.put("cn", val);
      }
      if ((val = (String)atts.get("O")) != null)
      {
        otherAtts.put("o", val);
      }
      if ((val = (String)atts.get("OU")) != null)
      {
        otherAtts.put("ou", val);
      }

      SubjectAltName name
        = (SubjectAltName)ExtensionFactory.getExtension(
            OID.subjectAltName, cert);

      if (name != null)
      {
        if ((val = name.getName()) != null)
        {
          otherAtts.put("mail", val);
        }
      }
      else if ((val = (String)atts.get("EmailAddress")) != null)
      {
        otherAtts.put("mail", val);
      }

      return otherAtts; 
    }
    catch (Exception e)
    {
      e.printStackTrace();
      return null;
    }
  }

  private static Hashtable getRDNs(X500Name dn)
  {
    Hashtable retval = new Hashtable();

    Vector rdns = dn.getRDNs();
    for (int i=0; i<rdns.size(); i++)
    {
      String rdnName = ((RDN)rdns.elementAt(i)).getName();
      StringTokenizer tknz = new StringTokenizer(rdnName, "=", false);
      retval.put(tknz.nextToken().trim(), tknz.nextToken().trim());
    }
    return retval;
  }
}
