//////////////////////////////////////////////////////////////////////////// 
// 
// 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.ssl;

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Vector;
import java.util.Arrays;
import java.util.Iterator;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.*;

import javax.net.ssl.*;

import com.dstc.security.x509.Base64OutputStream;

/**
 * A class for making trust decisions when presented with a certificate
 * chain by a peer.
 *
 * @author Ming Yung
 */
public class TrustEngine
{
  private static final boolean OFFER = false;
  protected Vector trustedCerts = null;

  public TrustEngine(Vector certs)
  {
    this.trustedCerts = certs;
  }

  public void setTrustedCerts(Vector certs)
  {
    this.trustedCerts = certs;
  }

  public Vector getTrustedCerts()
  {
    return this.trustedCerts;
  }

  public void verifyCertChain(X509Certificate[] chain) 
    throws IOException, CertificateException
  {
    try
    {
      //Verify the cert chain as far as we can go

      X509Certificate thisCert, nextCert;
      thisCert = chain[0];

      for (int i=1; i<chain.length; i++)
      {
        thisCert.checkValidity();
        nextCert = chain[i];
        thisCert.verify(nextCert.getPublicKey());
        thisCert = nextCert;
      }

      Principal issuer = thisCert.getIssuerDN();
      Principal subject = thisCert.getSubjectDN();

      //locate cert or issuer's cert amongst trusted certs

      if (trustedCerts == null)
      {
        throw new SSLKeyException("No trusted CA certs set");
      }

      Iterator iterator = trustedCerts.iterator();
      while (iterator.hasNext())
      {
        X509Certificate cert = (X509Certificate)iterator.next();

        if (issuer.equals(cert.getSubjectDN()))
        {
          thisCert.verify(cert.getPublicKey());
          return;
        }
        else if (subject.equals(cert.getSubjectDN()))
        {
           if (Arrays.equals(thisCert.getEncoded(), cert.getEncoded()))
             return;
        }
      }

      if (OFFER)
        offer(thisCert);

      throw new SSLHandshakeException("Cannot find trusted CA");
    }
    catch (Exception e)
    {
      e.printStackTrace();
      throw new SSLHandshakeException(e.getMessage());
    }
  }

  private void offer(X509Certificate cert) 
    throws IOException, CertificateException
  {
    System.out.print("Your peer root cert is not yet trusted\n");
    System.out.print("Enter filename to save root cert: ");
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader in = new BufferedReader(isr);
    Base64OutputStream os 
      = new Base64OutputStream(new FileOutputStream(in.readLine()),
                               "-----BEGIN CERTIFICATE-----",
                               "-----END CERTIFICATE-----");
    os.write(cert.getEncoded());
    os.flush();
    os.close();
  }
}
