/**
   @version 1.00 1999-10-23
   @author Cay Horstmann
*/

import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;

import sun.security.x509.X509CertInfo;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X500Name;
import sun.security.x509.CertificateIssuerName;

/**
   Program podpisujący certyfikat za pomocą klucza prywatnego
   innego certyfikatu znajdującego się w składnicy.
*/
public class CertificateSigner
{  
   public static void main(String[] args)
   {  
      String ksname = null;  // nazwa składnicy
      String alias = null;   // alias klucza prywatnego
      String inname = null;  // nazwa pliku wejściowego
      String outname = null; // nazwa pliku wyjściowego
      for (int i = 0; i < args.length; i += 2)
      {  
         if (args[i].equals("-keystore"))
            ksname = args[i + 1];
         else if (args[i].equals("-alias"))
            alias = args[i + 1];
         else if (args[i].equals("-infile"))
            inname = args[i + 1];
         else if (args[i].equals("-outfile"))
            outname = args[i + 1];
         else usage();
      }

      if (ksname == null || alias == null || inname == null || outname == null) usage();

      try
      {  
         PushbackReader console = new PushbackReader(new InputStreamReader(System.in));

         KeyStore store = KeyStore.getInstance("JKS", "SUN");
         InputStream in = new FileInputStream(ksname);
         System.out.print("Keystore password: ");
         System.out.flush();
         char[] password = readPassword(console);
         store.load(in, password);
         Arrays.fill(password, ' ');
         in.close();

         System.out.print("Key password for " + alias + ": ");
         System.out.flush();
         char[] keyPassword = readPassword(console);
         PrivateKey issuerPrivateKey = (PrivateKey) store.getKey(alias, keyPassword);
         Arrays.fill(keyPassword, ' ');

         if (issuerPrivateKey == null) error("No such private key");

         in = new FileInputStream(inname);

         CertificateFactory factory = CertificateFactory.getInstance("X.509");

         X509Certificate inCert = (X509Certificate) factory.generateCertificate(in);
         in.close();
         byte[] inCertBytes = inCert.getTBSCertificate();

         X509Certificate issuerCert = (X509Certificate) store.getCertificate(alias);
         Principal issuer = issuerCert.getSubjectDN();
         String issuerSigAlg = issuerCert.getSigAlgName();

         FileOutputStream out = new FileOutputStream(outname);

         X509CertInfo info = new X509CertInfo(inCertBytes);
         info.set(X509CertInfo.ISSUER, new CertificateIssuerName((X500Name) issuer));

         X509CertImpl outCert = new X509CertImpl(info);
         outCert.sign(issuerPrivateKey, issuerSigAlg);
         outCert.derEncode(out);

         out.close();
      }
      catch (Exception e)
      {  
         e.printStackTrace();
      }
   }

   /**
      Wczytuje hasło.
      @param in obiekt odczytu używany do wczytania hasła
      @return tablica znaków zawierająca hasło
   */
   public static char[] readPassword(PushbackReader in)
      throws IOException
   {  
      final int MAX_PASSWORD_LENGTH = 100;
      int length = 0;
      char[] buffer = new char[MAX_PASSWORD_LENGTH];

      while (true)
      {  
         int ch = in.read();
         if (ch == '\r' || ch == '\n' || ch == -1
            || length == MAX_PASSWORD_LENGTH)
         {  
            if (ch == '\r') // handle DOS "\r\n" line ends
            {  
               ch = in.read();
               if (ch != '\n' && ch != -1) in.unread(ch);
            }
            char[] password = new char[length];
            System.arraycopy(buffer, 0, password, 0, length);
            Arrays.fill(buffer, ' ');
            return password;
         }
         else
         {  
            buffer[length] = (char) ch;
            length++;
         }
      }
   }

   /**
      Wyświetla komunikat o błędzie 
      i kończy działanie programu.
      @param message komunikat
   */
   public static void error(String message)
   {  
      System.out.println(message);
      System.exit(1);
   }

   /**
      Wyświetla komunikat o sposobie uruchomienia programu
      i kończy jego działanie.
   */
   public static void usage()
   {  
      System.out.println("Uruchamianie: java CertificateSigner"
         + " -keystore keyStore -alias issuerKeyAlias"
         + " -infile inputFile -outfile outputFile");
      System.exit(1);
   }
}
