001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.util;
028    
029    
030    
031    import java.io.ByteArrayOutputStream;
032    import java.io.FileInputStream;
033    import java.io.InputStream;
034    import java.io.IOException;
035    import java.io.File;
036    import java.io.OutputStream;
037    import java.security.KeyStore;
038    import java.security.KeyStoreException;
039    import java.security.cert.Certificate;
040    import java.util.ArrayList;
041    import java.util.Enumeration;
042    
043    import org.opends.server.types.OperatingSystem;
044    
045    
046    /**
047     * This class provides an interface for generating self-signed certificates and
048     * certificate signing requests, and for importing, exporting, and deleting
049     * certificates from a key store.  It supports JKS, PKCS11, and PKCS12 key store
050     * types.
051     * <BR><BR>
052     * Note that for some operations, particularly those that require updating the
053     * contents of a key store (including generating certificates and/or certificate
054     * signing  requests, importing certificates, or removing certificates), this
055     * class relies on the keytool utility provided with Sun's implementation of the
056     * Java runtime  environment.  It will perform the associated operations by
057     * invoking the appropriate command.  It is possible that the keytool command
058     * will not exist in all Java runtime environments, especially those not created
059     * by Sun.  In those cases, it will not be possible to invoke operations that
060     * require altering the contents of the key store.  Therefore, it is strongly
061     * recommended that any code that may want to make use of this facility should
062     * first call {@code mayUseCertificateManager} and if it returns {@code false}
063     * the caller should gracefully degrade and suggest that the user perform the
064     * operation manually.
065     */
066    @org.opends.server.types.PublicAPI(
067         stability=org.opends.server.types.StabilityLevel.VOLATILE,
068         mayInstantiate=true,
069         mayExtend=false,
070         mayInvoke=true)
071    public final class CertificateManager
072    {
073      /**
074       * The path to the keytool command, which will be required to perform
075       * operations that modify the contents of a key store.
076       */
077      public static final String KEYTOOL_COMMAND;
078    
079    
080    
081      /**
082       * The key store type value that should be used for the "JKS" key store.
083       */
084      public static final String KEY_STORE_TYPE_JKS = "JKS";
085    
086    
087    
088      /**
089       * The key store type value that should be used for the "PKCS11" key store.
090       */
091      public static final String KEY_STORE_TYPE_PKCS11 = "PKCS11";
092    
093    
094    
095      /**
096       * The key store type value that should be used for the "PKCS12" key store.
097       */
098      public static final String KEY_STORE_TYPE_PKCS12 = "PKCS12";
099    
100    
101    
102      /**
103       * The key store path value that must be used in conjunction with the PKCS11
104       * key store type.
105       */
106      public static final String KEY_STORE_PATH_PKCS11 = "NONE";
107    
108    
109    
110      // The parsed key store backing this certificate manager.
111      private KeyStore keyStore;
112    
113      // The password that should be used to interact with the key store.
114      private String keyStorePIN;
115    
116      // The path to the key store that we should be using.
117      private String keyStorePath;
118    
119      // The name of the key store type we are using.
120      private String keyStoreType;
121    
122    
123    
124      static
125      {
126        String keytoolCommand = null;
127    
128        try
129        {
130          String cmd = System.getProperty("java.home") + File.separator + "bin" +
131                       File.separator + "keytool";
132          File cmdFile = new File(cmd);
133          if (cmdFile.exists())
134          {
135            keytoolCommand = cmdFile.getAbsolutePath();
136          }
137          else
138          {
139            cmd = cmd + ".exe";
140            cmdFile = new File(cmd);
141            if (cmdFile.exists())
142            {
143              keytoolCommand = cmdFile.getAbsolutePath();
144            }
145            else
146            {
147              keytoolCommand = null;
148            }
149          }
150        }
151        catch (Exception e)
152        {
153          keytoolCommand = null;
154        }
155    
156        KEYTOOL_COMMAND = SetupUtils.getScriptPath(keytoolCommand);
157      }
158    
159    
160    
161      /**
162       * Indicates whether it is possible to use this certificate manager code to
163       * perform operations which may alter the contents of a key store.
164       *
165       * @return  {@code true} if it appears that the keytool utility is available
166       *          and may be used to execute commands that may alter the contents of
167       *          a key store, or {@code false} if not.
168       */
169      public static boolean mayUseCertificateManager()
170      {
171        return (KEYTOOL_COMMAND != null);
172      }
173    
174    
175    
176      /**
177       * Creates a new certificate manager instance with the provided information.
178       *
179       * @param  keyStorePath  The path to the key store file, or "NONE" if the key
180       *                       store type is "PKCS11".  For the other key store
181       *                       types, the file does not need to exist if a new
182       *                       self-signed certificate or certificate signing
183       *                       request is to be generated, although the directory
184       *                       containing the file must exist.  The key store file
185       *                       must exist if import or export operations are to be
186       *                       performed.
187       * @param  keyStoreType  The key store type to use.  It should be one of
188       *                       {@code KEY_STORE_TYPE_JKS},
189       *                       {@code KEY_STORE_TYPE_PKCS11}, or
190       *                       {@code KEY_STORE_TYPE_PKCS12}.
191       * @param  keyStorePIN   The PIN required to access the key store.  It must
192       *                       not be {@code null}.
193       *
194       * @throws  IllegalArgumentException  If any of the provided arguments is
195       *                                    invalid.
196       *
197       * @throws  NullPointerException  If any of the provided arguments is
198       *                                {@code null}.
199       *
200       * @throws  UnsupportedOperationException  If it is not possible to use the
201       *                                         certificate manager on the
202       *                                         underlying platform.
203       */
204      public CertificateManager(String keyStorePath, String keyStoreType,
205                                String keyStorePIN)
206             throws IllegalArgumentException, NullPointerException,
207                    UnsupportedOperationException
208      {
209        if ((keyStorePath == null) || (keyStorePath.length() == 0))
210        {
211          throw new NullPointerException("keyStorePath");
212        }
213        else if ((keyStoreType == null) || (keyStoreType.length() == 0))
214        {
215          throw new NullPointerException("keyStoreType");
216        }
217        else if ((keyStorePIN == null) || (keyStorePIN.length() == 0))
218        {
219          throw new NullPointerException("keyStorePIN");
220        }
221    
222    
223        if (keyStoreType.equals(KEY_STORE_TYPE_PKCS11))
224        {
225          if (! keyStorePath.equals(KEY_STORE_PATH_PKCS11))
226          {
227            // FIXME -- Make this an internationalizeable string.
228            throw new IllegalArgumentException("Invalid key store path for " +
229                                               "PKCS11 keystore -- it must be " +
230                                               KEY_STORE_PATH_PKCS11);
231          }
232        }
233        else if (keyStoreType.equals(KEY_STORE_TYPE_JKS) ||
234                 keyStoreType.equals(KEY_STORE_TYPE_PKCS12))
235        {
236          File keyStoreFile = new File(keyStorePath);
237          if (keyStoreFile.exists())
238          {
239            if (! keyStoreFile.isFile())
240            {
241              // FIXME -- Make this an internationalizeable string.
242              throw new IllegalArgumentException("Key store path " + keyStorePath +
243                                                 " exists but is not a file.");
244            }
245          }
246          else
247          {
248            File keyStoreDirectory = keyStoreFile.getParentFile();
249            if ((keyStoreDirectory == null) || (! keyStoreDirectory.exists()) ||
250                (! keyStoreDirectory.isDirectory()))
251            {
252              // FIXME -- Make this an internationalizeable string.
253              throw new IllegalArgumentException("Parent directory for key " +
254                             "store path " + keyStorePath + " does not exist or " +
255                             "is not a directory.");
256            }
257          }
258        }
259        else
260        {
261          // FIXME -- Make this an internationalizeable string.
262          throw new IllegalArgumentException("Invalid key store type -- it must " +
263                      "be one of " + KEY_STORE_TYPE_JKS + ", " +
264                      KEY_STORE_TYPE_PKCS11 + ", or " + KEY_STORE_TYPE_PKCS12);
265        }
266    
267    
268        this.keyStorePath = keyStorePath;
269        this.keyStoreType = keyStoreType;
270        this.keyStorePIN  = keyStorePIN;
271    
272        keyStore = null;
273      }
274    
275    
276    
277      /**
278       * Indicates whether the provided alias is in use in the key store.
279       *
280       * @param  alias  The alias for which to make the determination.  It must not
281       *                be {@code null} or empty.
282       *
283       * @return  {@code true} if the key store exist and already contains a
284       *          certificate with the given alias, or {@code false} if not.
285       *
286       * @throws  KeyStoreException  If a problem occurs while attempting to
287       *                             interact with the key store.
288       *
289       * @throws  NullPointerException  If the provided alias is {@code null} or a
290       *                                zero-length string.
291       */
292      public boolean aliasInUse(String alias)
293             throws KeyStoreException, NullPointerException
294      {
295        if ((alias == null) || (alias.length() == 0))
296        {
297          throw new NullPointerException("alias");
298        }
299    
300    
301        KeyStore keyStore = getKeyStore();
302        if (keyStore == null)
303        {
304          return false;
305        }
306    
307        return keyStore.containsAlias(alias);
308      }
309    
310    
311    
312      /**
313       * Retrieves the aliases of the certificates in the specified key store.
314       *
315       * @return  The aliases of the certificates in the specified key store, or
316       *          {@code null} if the key store does not exist.
317       *
318       * @throws  KeyStoreException  If a problem occurs while attempting to
319       *                             interact with the key store.
320       */
321      public String[] getCertificateAliases()
322             throws KeyStoreException
323      {
324        KeyStore keyStore = getKeyStore();
325        if (keyStore == null)
326        {
327          return null;
328        }
329    
330        Enumeration<String> aliasEnumeration = keyStore.aliases();
331        if (aliasEnumeration == null)
332        {
333          return new String[0];
334        }
335    
336        ArrayList<String> aliasList = new ArrayList<String>();
337        while (aliasEnumeration.hasMoreElements())
338        {
339          aliasList.add(aliasEnumeration.nextElement());
340        }
341    
342    
343        String[] aliases = new String[aliasList.size()];
344        return aliasList.toArray(aliases);
345      }
346    
347    
348    
349      /**
350       * Retrieves the certificate with the specified alias from the key store.
351       *
352       * @param  alias  The alias of the certificate to retrieve.  It must not be
353       *                {@code null} or empty.
354       *
355       * @return  The requested certificate, or {@code null} if the specified
356       *          certificate does not exist.
357       *
358       * @throws  KeyStoreException  If a problem occurs while interacting with the
359       *                             key store, or the key store does not exist.
360       *
361       * @throws  NullPointerException  If the provided alias is {@code null} or a
362       *                                zero-length string.
363       */
364      public Certificate getCertificate(String alias)
365             throws KeyStoreException, NullPointerException
366      {
367        if ((alias == null) || (alias.length() == 0))
368        {
369          throw new NullPointerException("alias");
370        }
371    
372        KeyStore keyStore = getKeyStore();
373        if (keyStore == null)
374        {
375          // FIXME -- Make this an internationalizeable string.
376          throw new KeyStoreException("The key store does not exist.");
377        }
378    
379        return keyStore.getCertificate(alias);
380      }
381    
382    
383    
384      /**
385       * Generates a self-signed certificate using the provided information.
386       *
387       * @param  alias      The nickname to use for the certificate in the key
388       *                    store.  For the server certificate, it should generally
389       *                    be "server-cert".  It must not be {@code null} or empty.
390       * @param  subjectDN  The subject DN to use for the certificate.  It must not
391       *                    be {@code null} or empty.
392       * @param  validity   The length of time in days that the certificate should
393       *                    be valid, starting from the time the certificate is
394       *                    generated.  It must be a positive integer value.
395       *
396       * @throws  IllegalArgumentException  If the validity is not positive.
397       *
398       * @throws  KeyStoreException  If a problem occurs while actually attempting
399       *                             to generate the certificate in the key store.
400       *
401       * @throws  NullPointerException  If either the alias or subject DN is null or
402       *                                a zero-length string.
403       *
404       * @throws  UnsupportedOperationException  If it is not possible to use the
405       *                                         keytool utility to alter the
406       *                                         contents of the key store.
407       */
408      public void generateSelfSignedCertificate(String alias, String subjectDN,
409                                                int validity)
410             throws KeyStoreException, IllegalArgumentException,
411                    NullPointerException, UnsupportedOperationException
412      {
413        if ((alias == null) || (alias.length() == 0))
414        {
415          throw new NullPointerException("alias");
416        }
417        else if ((subjectDN == null) || (subjectDN.length() == 0))
418        {
419          throw new NullPointerException("subjectDN");
420        }
421        else if (validity <= 0)
422        {
423          // FIXME -- Make this an internationalizeable string.
424          throw new IllegalArgumentException("The validity must be positive.");
425        }
426    
427        if (KEYTOOL_COMMAND == null)
428        {
429          // FIXME -- Make this an internationalizeable string.
430          throw new UnsupportedOperationException("The certificate manager may " +
431                         "not be used to alter the contents of key stores on " +
432                         "this system.");
433        }
434    
435        if (aliasInUse(alias))
436        {
437          // FIXME -- Make this an internationalizeable string.
438          throw new IllegalArgumentException("A certificate with alias " + alias +
439                                             " already exists in the key store.");
440        }
441    
442    
443        // Clear the reference to the key store, since it will be altered by
444        // invoking the KeyTool command.
445        keyStore = null;
446    
447        // First, we need to run with the "-genkey" command to create the private
448        // key.
449        String[] commandElements =
450        {
451          KEYTOOL_COMMAND,
452          getGenKeyCommand(),
453          "-alias", alias,
454          "-dname", subjectDN,
455          "-keyalg", "rsa",
456          "-keystore", keyStorePath,
457          "-storetype", keyStoreType
458        };
459        runKeyTool(commandElements, keyStorePIN, keyStorePIN, true);
460    
461        // Next, we need to run with the "-selfcert" command to self-sign the
462        // certificate.
463        commandElements = new String[]
464        {
465          KEYTOOL_COMMAND,
466          "-selfcert",
467          "-alias", alias,
468          "-validity", String.valueOf(validity),
469          "-keystore", keyStorePath,
470          "-storetype", keyStoreType
471        };
472        runKeyTool(commandElements, keyStorePIN, keyStorePIN, true);
473      }
474    
475    
476    
477      /**
478       * Generates a certificate signing request (CSR) using the provided
479       * information.
480       *
481       * @param  alias      The nickname to use for the certificate in the key
482       *                    store.  For the server certificate, it should generally
483       *                    be "server-cert".  It must not be {@code null} or empty.
484       * @param  subjectDN  The subject DN to use for the certificate.  It must not
485       *                    be {@code null} or empty.
486       *
487       * @return  The file containing the generated certificate signing request.
488       *
489       * @throws  KeyStoreException  If a problem occurs while actually attempting
490       *                             to generate the private key in the key store or
491       *                             generate the certificate signing request based
492       *                             on that key.
493       *
494       * @throws  IOException  If a problem occurs while attempting to create the
495       *                       file to which the certificate signing request will be
496       *                       written.
497       *
498       * @throws  NullPointerException  If either the alias or subject DN is null or
499       *                                a zero-length string.
500       *
501       * @throws  UnsupportedOperationException  If it is not possible to use the
502       *                                         keytool utility to alter the
503       *                                         contents of the key store.
504       */
505      public File generateCertificateSigningRequest(String alias, String subjectDN)
506             throws KeyStoreException, IOException, NullPointerException,
507                    UnsupportedOperationException
508      {
509        if ((alias == null) || (alias.length() == 0))
510        {
511          throw new NullPointerException("alias");
512        }
513        else if ((subjectDN == null) || (subjectDN.length() == 0))
514        {
515          throw new NullPointerException("subjectDN");
516        }
517    
518        if (KEYTOOL_COMMAND == null)
519        {
520          // FIXME -- Make this an internationalizeable string.
521          throw new UnsupportedOperationException("The certificate manager may " +
522                         "not be used to alter the contents of key stores on " +
523                         "this system.");
524        }
525    
526        if (aliasInUse(alias))
527        {
528          // FIXME -- Make this an internationalizeable string.
529          throw new IllegalArgumentException("A certificate with alias " + alias +
530                                             " already exists in the key store.");
531        }
532    
533    
534        // Clear the reference to the key store, since it will be altered by
535        // invoking the KeyTool command.
536        keyStore = null;
537    
538    
539        // First, we need to run with the "-genkey" command to create the private
540        // key.
541        String[] commandElements =
542        {
543          KEYTOOL_COMMAND,
544          getGenKeyCommand(),
545          "-alias", alias,
546          "-dname", subjectDN,
547          "-keyalg", "rsa",
548          "-keystore", keyStorePath,
549          "-storetype", keyStoreType
550        };
551        runKeyTool(commandElements, keyStorePIN, keyStorePIN, true);
552    
553        // Next, we need to run with the "-certreq" command to generate the
554        // certificate signing request.
555        File csrFile = File.createTempFile("CertificateManager-", ".csr");
556        csrFile.deleteOnExit();
557        commandElements = new String[]
558        {
559          KEYTOOL_COMMAND,
560          "-certreq",
561          "-alias", alias,
562          "-file", csrFile.getAbsolutePath(),
563          "-keystore", keyStorePath,
564          "-storetype", keyStoreType
565        };
566        runKeyTool(commandElements, keyStorePIN, keyStorePIN, true);
567    
568        return csrFile;
569      }
570    
571    
572    
573      /**
574       * Adds the provided certificate to the key store.  This may be used to
575       * associate an externally-signed certificate with an existing private key
576       * with the given alias.
577       *
578       * @param  alias            The alias to use for the certificate.  It must not
579       *                          be {@code null} or empty.
580       * @param  certificateFile  The file containing the encoded certificate.  It
581       *                          must not be {@code null}, and the file must exist.
582       *
583       * @throws  IllegalArgumentException  If the provided certificate file does
584       *                                    not exist.
585       *
586       * @throws  KeyStoreException  If a problem occurs while interacting with the
587       *                             key store.
588       *
589       * @throws  NullPointerException  If the provided alias is {@code null} or a
590       *                                zero-length string, or the certificate file
591       *                                is {@code null}.
592       *
593       * @throws  UnsupportedOperationException  If it is not possible to use the
594       *                                         keytool utility to alter the
595       *                                         contents of the key store.
596       */
597      public void addCertificate(String alias, File certificateFile)
598             throws IllegalArgumentException, KeyStoreException,
599                    NullPointerException, UnsupportedOperationException
600      {
601        if ((alias == null) || (alias.length() == 0))
602        {
603          throw new NullPointerException("alias");
604        }
605    
606        if (certificateFile == null)
607        {
608          throw new NullPointerException("certificateFile");
609        }
610        else if ((! certificateFile.exists()) ||
611                 (! certificateFile.isFile()))
612        {
613          // FIXME -- Make this an internationalizeable string.
614          throw new IllegalArgumentException("Certificate file " +
615                                             certificateFile.getAbsolutePath() +
616                                             " does not exist or is not a file.");
617        }
618    
619        if (KEYTOOL_COMMAND == null)
620        {
621          // FIXME -- Make this an internationalizeable string.
622          throw new UnsupportedOperationException("The certificate manager may " +
623                         "not be used to alter the contents of key stores on " +
624                         "this system.");
625        }
626    
627    
628        // Clear the reference to the key store, since it will be altered by
629        // invoking the KeyTool command.
630        keyStore = null;
631    
632    
633        String[] commandElements =
634        {
635          KEYTOOL_COMMAND,
636          "-import",
637          "-noprompt",
638          "-alias", alias,
639          "-file", certificateFile.getAbsolutePath(),
640          "-keystore", keyStorePath,
641          "-storetype", keyStoreType
642        };
643        runKeyTool(commandElements, keyStorePIN, keyStorePIN, true);
644      }
645    
646    
647      /**
648       * Removes the specified certificate from the key store.
649       *
650       * @param  alias  The alias to use for the certificate to remove.  It must not
651       *                be {@code null} or an empty string, and it must exist in
652       *                the key store.
653       *
654       * @throws  IllegalArgumentException  If the specified certificate does not
655       *                                    exist in the key store.
656       *
657       * @throws  KeyStoreException  If a problem occurs while interacting with the
658       *                             key store.
659       *
660       * @throws  NullPointerException  If the provided alias is {@code null} or a
661       *                                zero-length string, or the certificate file
662       *                                is {@code null}.
663       *
664       * @throws  UnsupportedOperationException  If it is not possible to use the
665       *                                         keytool utility to alter the
666       *                                         contents of the key store.
667       */
668      public void removeCertificate(String alias)
669             throws IllegalArgumentException, KeyStoreException,
670                    NullPointerException, UnsupportedOperationException
671      {
672        if ((alias == null) || (alias.length() == 0))
673        {
674          throw new NullPointerException("alias");
675        }
676    
677        if (KEYTOOL_COMMAND == null)
678        {
679          // FIXME -- Make this an internationalizeable string.
680          throw new UnsupportedOperationException("The certificate manager may " +
681                         "not be used to alter the contents of key stores on " +
682                         "this system.");
683        }
684    
685        if (! aliasInUse(alias))
686        {
687          // FIXME -- Make this an internationalizeable string.
688          throw new IllegalArgumentException("There is no certificate with alias " +
689                                             alias + " in the key store.");
690        }
691    
692    
693        // Clear the reference to the key store, since it will be altered by
694        // invoking the KeyTool command.
695        keyStore = null;
696    
697    
698        String[] commandElements =
699        {
700          KEYTOOL_COMMAND,
701          "-delete",
702          "-alias", alias,
703          "-keystore", keyStorePath,
704          "-storetype", keyStoreType
705        };
706        runKeyTool(commandElements, keyStorePIN, keyStorePIN, true);
707      }
708    
709    
710    
711      /**
712       * Attempts to run the keytool utility with the provided arguments.
713       *
714       * @param  commandElements   The command and arguments to execute.  The first
715       *                           element of the array must be the command, and the
716       *                           remaining elements must be the arguments.
717       * @param  keyStorePassword  The password of the key store.
718       * @param  storePassword     The password of the certificate.
719       * @param  outputAcceptable  Indicates whether it is acceptable for the
720       *                           command to generate output, as long as the exit
721       *                           code is zero.  Some commands (like "keytool
722       *                           -import") may generate output even on successful
723       *                           completion.  If the command generates output and
724       *                           this is {@code false}, then an exception will
725       *                           be thrown.
726       *
727       * @throws  KeyStoreException  If a problem occurs while attempting to invoke
728       *                             the keytool utility, if it does not exit with
729       *                             the expected exit code, or if any unexpected
730       *                             output is generated while running the tool.
731       */
732      private void runKeyTool(String[] commandElements, String keyStorePassword,
733          String storePassword, boolean outputAcceptable)
734              throws KeyStoreException
735      {
736        String lineSeparator = System.getProperty("line.separator");
737        if (lineSeparator == null)
738        {
739          lineSeparator = "\n";
740        }
741        boolean keyStoreDefined;
742        File keyStoreFile = new File(keyStorePath);
743        keyStoreDefined = (keyStoreFile.exists() && (keyStoreFile.length() > 0)) ||
744          KEY_STORE_TYPE_PKCS11.equals(keyStoreType);
745    
746        boolean isNewKeyStorePassword = !keyStoreDefined &&
747          (getGenKeyCommand().equalsIgnoreCase(commandElements[1]) ||
748          "-import".equalsIgnoreCase(commandElements[1]));
749    
750        boolean isNewStorePassword =
751          getGenKeyCommand().equalsIgnoreCase(commandElements[1]);
752    
753        boolean askForStorePassword =
754          !"-import".equalsIgnoreCase(commandElements[1]);
755    
756        try
757        {
758          ProcessBuilder processBuilder = new ProcessBuilder(commandElements);
759          processBuilder.redirectErrorStream(true);
760    
761          ByteArrayOutputStream output = new ByteArrayOutputStream();
762          byte[] buffer = new byte[1024];
763          Process process = processBuilder.start();
764          InputStream inputStream = process.getInputStream();
765          OutputStream out = process.getOutputStream();
766          if (!isJDK15() &&
767              (SetupUtils.getOperatingSystem() == OperatingSystem.AIX))
768          {
769            // This is required when using JDK 1.6 on AIX to be able to write
770            // on the OutputStream.
771            try
772            {
773              Thread.sleep(1500);
774            } catch (Throwable t) {}
775          }
776          out.write(keyStorePassword.getBytes()) ;
777          out.write(lineSeparator.getBytes()) ;
778          out.flush() ;
779          // With Java6 and above, keytool asks for the password twice.
780          if (!isJDK15() && isNewKeyStorePassword)
781          {
782            if (SetupUtils.getOperatingSystem() == OperatingSystem.AIX)
783            {
784              // This is required when using JDK 1.6 on AIX to be able to write
785              // on the OutputStream.
786              try
787              {
788                Thread.sleep(1500);
789              } catch (Throwable t) {}
790            }
791            out.write(keyStorePassword.getBytes()) ;
792            out.write(lineSeparator.getBytes()) ;
793            out.flush() ;
794          }
795    
796          if (askForStorePassword)
797          {
798            out.write(storePassword.getBytes()) ;
799            out.write(lineSeparator.getBytes()) ;
800            out.flush() ;
801    
802            // With Java6 and above, keytool asks for the password twice (if we
803            // are not running AIX).
804            if (!isJDK15() && isNewStorePassword &&
805                (SetupUtils.getOperatingSystem() != OperatingSystem.AIX))
806            {
807              out.write(storePassword.getBytes()) ;
808              out.write(lineSeparator.getBytes()) ;
809              out.flush() ;
810            }
811          }
812          // Close the output stream since it can generate a deadlock on IBM JVM
813          // (issue 2795).
814          out.close();
815          while (true)
816          {
817            int bytesRead = inputStream.read(buffer);
818            if (bytesRead < 0)
819            {
820              break;
821            }
822            else if (bytesRead > 0)
823            {
824              output.write(buffer, 0, bytesRead);
825            }
826          }
827          process.waitFor();
828          int exitValue = process.exitValue();
829          byte[] outputBytes = output.toByteArray();
830          if (exitValue != 0)
831          {
832            // FIXME -- Make this an internationalizeable string.
833            StringBuilder message = new StringBuilder();
834            message.append("Unexpected exit code of ");
835            message.append(exitValue);
836            message.append(" returned from the keytool utility.");
837    
838            if ((outputBytes != null) && (outputBytes.length > 0))
839            {
840              message.append("  The generated output was:  '");
841              message.append(new String(outputBytes));
842              message.append("'.");
843            }
844    
845            throw new KeyStoreException(message.toString());
846          }
847          else if ((! outputAcceptable) && (outputBytes != null) &&
848                   (outputBytes.length > 0))
849          {
850            // FIXME -- Make this an internationalizeable string.
851            StringBuilder message = new StringBuilder();
852            message.append("Unexpected output generated by the keytool " +
853                           "utility:  '");
854            message.append(new String(outputBytes));
855            message.append("'.");
856    
857            throw new KeyStoreException(message.toString());
858          }
859        }
860        catch (KeyStoreException kse)
861        {
862          throw kse;
863        }
864        catch (Exception e)
865        {
866          // FIXME -- Make this an internationalizeable string.
867          throw new KeyStoreException("Could not invoke the KeyTool.run method:  " +
868                                      e, e);
869        }
870      }
871    
872    
873    
874      /**
875       * Retrieves a handle to the key store.
876       *
877       * @return  The handle to the key store, or {@code null} if the key store
878       *          doesn't exist.
879       *
880       * @throws  KeyStoreException  If a problem occurs while trying to open the
881       *                             key store.
882       */
883      private KeyStore getKeyStore()
884              throws KeyStoreException
885      {
886        if (keyStore != null)
887        {
888          return keyStore;
889        }
890    
891        // For JKS and PKCS12 key stores, we should make sure the file exists, and
892        // we'll need an input stream that we can use to read it.  For PKCS11 key
893        // stores there won't be a file and the input stream should be null.
894        FileInputStream keyStoreInputStream = null;
895        if (keyStoreType.equals(KEY_STORE_TYPE_JKS) ||
896            keyStoreType.equals(KEY_STORE_TYPE_PKCS12))
897        {
898          File keyStoreFile = new File(keyStorePath);
899          if (! keyStoreFile.exists())
900          {
901            return null;
902          }
903    
904          try
905          {
906            keyStoreInputStream = new FileInputStream(keyStoreFile);
907          }
908          catch (Exception e)
909          {
910            throw new KeyStoreException(String.valueOf(e), e);
911          }
912        }
913    
914    
915        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
916        try
917        {
918          keyStore.load(keyStoreInputStream, keyStorePIN.toCharArray());
919          return this.keyStore = keyStore;
920        }
921        catch (Exception e)
922        {
923          throw new KeyStoreException(String.valueOf(e), e);
924        }
925        finally
926        {
927          if (keyStoreInputStream != null)
928          {
929            try
930            {
931              keyStoreInputStream.close();
932            }
933            catch (Throwable t)
934            {
935            }
936          }
937        }
938      }
939    
940      /**
941       * Returns whether we are running JDK 1.5 or not.
942       * @return <CODE>true</CODE> if we are running JDK 1.5 and <CODE>false</CODE>
943       * otherwise.
944       */
945      private boolean isJDK15()
946      {
947        boolean isJDK15 = false;
948        try
949        {
950          String javaRelease = System.getProperty ("java.version");
951          isJDK15 = javaRelease.startsWith("1.5");
952        }
953        catch (Throwable t)
954        {
955          System.err.println("Cannot get the java version: " + t);
956        }
957        return isJDK15;
958      }
959    
960      private String getGenKeyCommand()
961      {
962        String genKeyCommand;
963        if (!isJDK15())
964        {
965          genKeyCommand = "-genkeypair";
966        }
967        else
968        {
969          genKeyCommand = "-genkey";
970        }
971        return genKeyCommand;
972      }
973    }
974    
975