001/*
002 * Copyright 2017-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util.ssl.cert;
022
023
024
025import java.io.BufferedInputStream;
026import java.io.BufferedReader;
027import java.io.ByteArrayInputStream;
028import java.io.File;
029import java.io.FileInputStream;
030import java.io.FileOutputStream;
031import java.io.FileReader;
032import java.io.InputStream;
033import java.io.InputStreamReader;
034import java.io.IOException;
035import java.io.OutputStream;
036import java.io.PrintStream;
037import java.net.InetAddress;
038import java.security.Key;
039import java.security.KeyPair;
040import java.security.KeyStore;
041import java.security.PrivateKey;
042import java.security.PublicKey;
043import java.security.UnrecoverableKeyException;
044import java.security.cert.Certificate;
045import java.text.SimpleDateFormat;
046import java.util.ArrayList;
047import java.util.Arrays;
048import java.util.Collections;
049import java.util.Date;
050import java.util.Enumeration;
051import java.util.Iterator;
052import java.util.LinkedHashMap;
053import java.util.LinkedHashSet;
054import java.util.List;
055import java.util.Map;
056import java.util.Set;
057import java.util.concurrent.LinkedBlockingQueue;
058import java.util.concurrent.TimeUnit;
059import java.util.concurrent.atomic.AtomicReference;
060
061import com.unboundid.asn1.ASN1BitString;
062import com.unboundid.asn1.ASN1Element;
063import com.unboundid.ldap.sdk.DN;
064import com.unboundid.ldap.sdk.LDAPException;
065import com.unboundid.ldap.sdk.ResultCode;
066import com.unboundid.ldap.sdk.Version;
067import com.unboundid.util.Base64;
068import com.unboundid.util.ByteStringBuffer;
069import com.unboundid.util.CommandLineTool;
070import com.unboundid.util.Debug;
071import com.unboundid.util.OID;
072import com.unboundid.util.ObjectPair;
073import com.unboundid.util.PasswordReader;
074import com.unboundid.util.StaticUtils;
075import com.unboundid.util.ThreadSafety;
076import com.unboundid.util.ThreadSafetyLevel;
077import com.unboundid.util.Validator;
078import com.unboundid.util.args.ArgumentException;
079import com.unboundid.util.args.ArgumentParser;
080import com.unboundid.util.args.BooleanArgument;
081import com.unboundid.util.args.BooleanValueArgument;
082import com.unboundid.util.args.DNArgument;
083import com.unboundid.util.args.FileArgument;
084import com.unboundid.util.args.IPAddressArgumentValueValidator;
085import com.unboundid.util.args.IntegerArgument;
086import com.unboundid.util.args.OIDArgumentValueValidator;
087import com.unboundid.util.args.StringArgument;
088import com.unboundid.util.args.TimestampArgument;
089import com.unboundid.util.args.SubCommand;
090import com.unboundid.util.ssl.JVMDefaultTrustManager;
091
092import static com.unboundid.util.ssl.cert.CertMessages.*;
093
094
095
096/**
097 * This class provides a tool that can be used to manage X.509 certificates for
098 * use in TLS communication.
099 */
100@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
101public final class ManageCertificates
102       extends CommandLineTool
103{
104  /**
105   * The path to the keystore with the JVM's set of default trusted issuer
106   * certificates.
107   */
108  private static final File JVM_DEFAULT_CACERTS_FILE;
109  static
110  {
111    File caCertsFile;
112    try
113    {
114      caCertsFile = JVMDefaultTrustManager.getInstance().getCACertsFile();
115    }
116    catch (final Exception e)
117    {
118      Debug.debugException(e);
119      caCertsFile = null;
120    }
121
122    JVM_DEFAULT_CACERTS_FILE = caCertsFile;
123  }
124
125
126
127  /**
128   * The name of a system property that can be used to specify the default
129   * keystore type for new keystores.
130   */
131  private static final String PROPERTY_DEFAULT_KEYSTORE_TYPE =
132       ManageCertificates.class.getName() + ".defaultKeystoreType";
133
134
135
136  /**
137   * The default keystore type that will be used for new keystores when the
138   * type is not specified.
139   */
140  private static final String DEFAULT_KEYSTORE_TYPE;
141  static
142  {
143    final String propertyValue =
144         System.getProperty(PROPERTY_DEFAULT_KEYSTORE_TYPE);
145    if ((propertyValue != null) &&
146        (propertyValue.equalsIgnoreCase("PKCS12") ||
147         propertyValue.equalsIgnoreCase("PKCS#12") ||
148         propertyValue.equalsIgnoreCase("PKCS #12") ||
149         propertyValue.equalsIgnoreCase("PKCS 12")))
150    {
151      DEFAULT_KEYSTORE_TYPE = "PKCS12";
152    }
153    else
154    {
155      DEFAULT_KEYSTORE_TYPE = "JKS";
156    }
157  }
158
159
160
161  /**
162   * The column at which to wrap long lines of output.
163   */
164  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
165
166
167
168  // The global argument parser used by this tool.
169  private volatile ArgumentParser globalParser = null;
170
171  // The argument parser for the selected subcommand.
172  private volatile ArgumentParser subCommandParser = null;
173
174  // The input stream to use for standard input.
175  private final InputStream in;
176
177
178
179  /**
180   * Invokes this tool with the default standard output and standard error and
181   * the provided set of arguments.
182   *
183   * @param  args  The command-line arguments provided to this program.
184   */
185  public static void main(final String... args)
186  {
187    final ResultCode resultCode = main(System.in, System.out, System.err, args);
188    if (resultCode != ResultCode.SUCCESS)
189    {
190      System.exit(Math.max(1, Math.min(resultCode.intValue(), 255)));
191    }
192  }
193
194
195
196  /**
197   * Invokes this tool with the provided output and error streams and set of
198   * arguments.
199   *
200   * @param  in    The input stream to use for standard input.  It may be
201   *               {@code null} if no input stream should be available.
202   * @param  out   The output stream to use for standard output.  It may be
203   *               {@code null} if standard output should be suppressed.
204   * @param  err   The output stream to use for standard error.  It may be
205   *               {@code null} if standard error should be suppressed.
206   * @param  args  The command-line arguments provided to this program.
207   *
208   * @return  The result code obtained from tool processing.
209   */
210  public static ResultCode main(final InputStream in, final OutputStream out,
211                                final OutputStream err, final String... args)
212  {
213    final ManageCertificates manageCertificates =
214         new ManageCertificates(in, out, err);
215    return manageCertificates.runTool(args);
216  }
217
218
219
220  /**
221   * Creates a new instance of this tool with the provided output and error
222   * streams.
223   *
224   * @param  in   The input stream to use for standard input.  It may be
225   *              {@code null} if no input stream should be available.
226   * @param  out  The output stream to use for standard output.  It may be
227   *              {@code null} if standard output should be suppressed.
228   * @param  err  The output stream to use for standard error.  It may be
229   *              {@code null} if standard error should be suppressed.
230   */
231  public ManageCertificates(final InputStream in, final OutputStream out,
232                            final OutputStream err)
233  {
234    super(out, err);
235
236    if (in == null)
237    {
238      this.in = new ByteArrayInputStream(StaticUtils.NO_BYTES);
239    }
240    else
241    {
242      this.in = in;
243    }
244  }
245
246
247
248  /**
249   * Retrieves the name of this tool.  It should be the name of the command used
250   * to invoke this tool.
251   *
252   * @return  The name for this tool.
253   */
254  @Override()
255  public String getToolName()
256  {
257    return "manage-certificates";
258  }
259
260
261
262  /**
263   * Retrieves a human-readable description for this tool.
264   *
265   * @return  A human-readable description for this tool.
266   */
267  @Override()
268  public String getToolDescription()
269  {
270    return INFO_MANAGE_CERTS_TOOL_DESC.get();
271  }
272
273
274
275  /**
276   * Retrieves a version string for this tool, if available.
277   *
278   * @return  A version string for this tool, or {@code null} if none is
279   *          available.
280   */
281  @Override()
282  public String getToolVersion()
283  {
284    return Version.NUMERIC_VERSION_STRING;
285  }
286
287
288
289  /**
290   * Indicates whether this tool should provide support for an interactive mode,
291   * in which the tool offers a mode in which the arguments can be provided in
292   * a text-driven menu rather than requiring them to be given on the command
293   * line.  If interactive mode is supported, it may be invoked using the
294   * "--interactive" argument.  Alternately, if interactive mode is supported
295   * and {@link #defaultsToInteractiveMode()} returns {@code true}, then
296   * interactive mode may be invoked by simply launching the tool without any
297   * arguments.
298   *
299   * @return  {@code true} if this tool supports interactive mode, or
300   *          {@code false} if not.
301   */
302  @Override()
303  public boolean supportsInteractiveMode()
304  {
305    return true;
306  }
307
308
309
310  /**
311   * Indicates whether this tool defaults to launching in interactive mode if
312   * the tool is invoked without any command-line arguments.  This will only be
313   * used if {@link #supportsInteractiveMode()} returns {@code true}.
314   *
315   * @return  {@code true} if this tool defaults to using interactive mode if
316   *          launched without any command-line arguments, or {@code false} if
317   *          not.
318   */
319  @Override()
320  public boolean defaultsToInteractiveMode()
321  {
322    return true;
323  }
324
325
326
327  /**
328   * Indicates whether this tool supports the use of a properties file for
329   * specifying default values for arguments that aren't specified on the
330   * command line.
331   *
332   * @return  {@code true} if this tool supports the use of a properties file
333   *          for specifying default values for arguments that aren't specified
334   *          on the command line, or {@code false} if not.
335   */
336  @Override()
337  public boolean supportsPropertiesFile()
338  {
339    return true;
340  }
341
342
343
344  /**
345   * Indicates whether this tool should provide arguments for redirecting output
346   * to a file.  If this method returns {@code true}, then the tool will offer
347   * an "--outputFile" argument that will specify the path to a file to which
348   * all standard output and standard error content will be written, and it will
349   * also offer a "--teeToStandardOut" argument that can only be used if the
350   * "--outputFile" argument is present and will cause all output to be written
351   * to both the specified output file and to standard output.
352   *
353   * @return  {@code true} if this tool should provide arguments for redirecting
354   *          output to a file, or {@code false} if not.
355   */
356  @Override()
357  protected boolean supportsOutputFile()
358  {
359    return false;
360  }
361
362
363
364  /**
365   * Indicates whether to log messages about the launch and completion of this
366   * tool into the invocation log of Ping Identity server products that may
367   * include it.  This method is not needed for tools that are not expected to
368   * be part of the Ping Identity server products suite.  Further, this value
369   * may be overridden by settings in the server's
370   * tool-invocation-logging.properties file.
371   * <BR><BR>
372   * This method should generally return {@code true} for tools that may alter
373   * the server configuration, data, or other state information, and
374   * {@code false} for tools that do not make any changes.
375   *
376   * @return  {@code true} if Ping Identity server products should include
377   *          messages about the launch and completion of this tool in tool
378   *          invocation log files by default, or {@code false} if not.
379   */
380  @Override()
381  protected boolean logToolInvocationByDefault()
382  {
383    return true;
384  }
385
386
387
388  /**
389   * Adds the command-line arguments supported for use with this tool to the
390   * provided argument parser.  The tool may need to retain references to the
391   * arguments (and/or the argument parser, if trailing arguments are allowed)
392   * to it in order to obtain their values for use in later processing.
393   *
394   * @param  parser  The argument parser to which the arguments are to be added.
395   *
396   * @throws  ArgumentException  If a problem occurs while adding any of the
397   *                             tool-specific arguments to the provided
398   *                             argument parser.
399   */
400  @Override()
401  public void addToolArguments(final ArgumentParser parser)
402         throws ArgumentException
403  {
404    globalParser = parser;
405
406
407    // Define the "list-certificates" subcommand and all of its arguments.
408    final ArgumentParser listCertsParser = new ArgumentParser(
409         "list-certificates", INFO_MANAGE_CERTS_SC_LIST_CERTS_DESC.get());
410
411    final FileArgument listCertsKeystore = new FileArgument(null, "keystore",
412         true, 1, null, INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_DESC.get(),
413         true, true,  true, false);
414    listCertsKeystore.addLongIdentifier("keystore-path", true);
415    listCertsKeystore.addLongIdentifier("keystorePath", true);
416    listCertsKeystore.addLongIdentifier("keystore-file", true);
417    listCertsKeystore.addLongIdentifier("keystoreFile", true);
418    listCertsParser.addArgument(listCertsKeystore);
419
420    final StringArgument listCertsKeystorePassword = new StringArgument(null,
421         "keystore-password", false, 1,
422         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
423         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_PW_DESC.get());
424    listCertsKeystorePassword.addLongIdentifier("keystorePassword", true);
425    listCertsKeystorePassword.addLongIdentifier("keystore-passphrase", true);
426    listCertsKeystorePassword.addLongIdentifier("keystorePassphrase", true);
427    listCertsKeystorePassword.addLongIdentifier("keystore-pin", true);
428    listCertsKeystorePassword.addLongIdentifier("keystorePIN", true);
429    listCertsKeystorePassword.addLongIdentifier("storepass", true);
430    listCertsKeystorePassword.setSensitive(true);
431    listCertsParser.addArgument(listCertsKeystorePassword);
432
433    final FileArgument listCertsKeystorePasswordFile = new FileArgument(null,
434         "keystore-password-file", false, 1, null,
435         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_PW_FILE_DESC.get(), true, true,
436         true, false);
437    listCertsKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
438         true);
439    listCertsKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
440         true);
441    listCertsKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
442         true);
443    listCertsKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
444         true);
445    listCertsKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
446    listCertsParser.addArgument(listCertsKeystorePasswordFile);
447
448    final BooleanArgument listCertsPromptForKeystorePassword =
449         new BooleanArgument(null, "prompt-for-keystore-password",
450        INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_PROMPT_FOR_KS_PW_DESC.get());
451    listCertsPromptForKeystorePassword.addLongIdentifier(
452         "promptForKeystorePassword", true);
453    listCertsPromptForKeystorePassword.addLongIdentifier(
454         "prompt-for-keystore-passphrase", true);
455    listCertsPromptForKeystorePassword.addLongIdentifier(
456         "promptForKeystorePassphrase", true);
457    listCertsPromptForKeystorePassword.addLongIdentifier(
458         "prompt-for-keystore-pin", true);
459    listCertsPromptForKeystorePassword.addLongIdentifier(
460         "promptForKeystorePIN", true);
461    listCertsParser.addArgument(listCertsPromptForKeystorePassword);
462
463    final StringArgument listCertsAlias = new StringArgument(null, "alias",
464         false, 0, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
465         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_ALIAS_DESC.get());
466    listCertsAlias.addLongIdentifier("nickname", true);
467    listCertsParser.addArgument(listCertsAlias);
468
469    final BooleanArgument listCertsDisplayPEM = new BooleanArgument(null,
470         "display-pem-certificate", 1,
471         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_DISPLAY_PEM_DESC.get());
472    listCertsDisplayPEM.addLongIdentifier("displayPEMCertificate", true);
473    listCertsDisplayPEM.addLongIdentifier("display-pem", true);
474    listCertsDisplayPEM.addLongIdentifier("displayPEM", true);
475    listCertsDisplayPEM.addLongIdentifier("show-pem-certificate", true);
476    listCertsDisplayPEM.addLongIdentifier("showPEMCertificate", true);
477    listCertsDisplayPEM.addLongIdentifier("show-pem", true);
478    listCertsDisplayPEM.addLongIdentifier("showPEM", true);
479    listCertsDisplayPEM.addLongIdentifier("pem", true);
480    listCertsDisplayPEM.addLongIdentifier("rfc", true);
481    listCertsParser.addArgument(listCertsDisplayPEM);
482
483    final BooleanArgument listCertsVerbose = new BooleanArgument(null,
484         "verbose", 1, INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_VERBOSE_DESC.get());
485    listCertsParser.addArgument(listCertsVerbose);
486
487    final BooleanArgument listCertsDisplayCommand = new BooleanArgument(null,
488         "display-keytool-command", 1,
489         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_DISPLAY_COMMAND_DESC.get());
490    listCertsDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
491    listCertsDisplayCommand.addLongIdentifier("show-keytool-command", true);
492    listCertsDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
493    listCertsParser.addArgument(listCertsDisplayCommand);
494
495    listCertsParser.addExclusiveArgumentSet(listCertsKeystorePassword,
496         listCertsKeystorePasswordFile, listCertsPromptForKeystorePassword);
497
498    final LinkedHashMap<String[],String> listCertsExamples =
499         new LinkedHashMap<>(3);
500    listCertsExamples.put(
501         new String[]
502         {
503           "list-certificates",
504           "--keystore", getPlatformSpecificPath("config", "keystore")
505         },
506         INFO_MANAGE_CERTS_SC_LIST_CERTS_EXAMPLE_1.get(
507              getPlatformSpecificPath("config", "keystore")));
508    listCertsExamples.put(
509         new String[]
510         {
511           "list-certificates",
512           "--keystore", getPlatformSpecificPath("config", "keystore.p12"),
513           "--keystore-password-file",
514                getPlatformSpecificPath("config", "keystore.pin"),
515           "--alias", "server-cert",
516           "--verbose",
517           "--display-pem-certificate",
518           "--display-keytool-command"
519         },
520         INFO_MANAGE_CERTS_SC_LIST_CERTS_EXAMPLE_2.get(
521              getPlatformSpecificPath("config", "keystore.p12"),
522              getPlatformSpecificPath("config", "keystore.pin")));
523    if (JVM_DEFAULT_CACERTS_FILE != null)
524    {
525      listCertsExamples.put(
526           new String[]
527           {
528             "list-certificates",
529             "--keystore", JVM_DEFAULT_CACERTS_FILE.getAbsolutePath()
530           },
531           INFO_MANAGE_CERTS_SC_LIST_CERTS_EXAMPLE_3.get());
532    }
533
534    final SubCommand listCertsSubCommand = new SubCommand("list-certificates",
535         INFO_MANAGE_CERTS_SC_LIST_CERTS_DESC.get(), listCertsParser,
536         listCertsExamples);
537    listCertsSubCommand.addName("listCertificates", true);
538    listCertsSubCommand.addName("list-certs", true);
539    listCertsSubCommand.addName("listCerts", true);
540    listCertsSubCommand.addName("list", false);
541
542    parser.addSubCommand(listCertsSubCommand);
543
544
545    // Define the "export-certificate" subcommand and all of its arguments.
546    final ArgumentParser exportCertParser = new ArgumentParser(
547         "export-certificate", INFO_MANAGE_CERTS_SC_EXPORT_CERT_DESC.get());
548
549    final FileArgument exportCertKeystore = new FileArgument(null, "keystore",
550         true, 1, null, INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_DESC.get(),
551         true, true,  true, false);
552    exportCertKeystore.addLongIdentifier("keystore-path", true);
553    exportCertKeystore.addLongIdentifier("keystorePath", true);
554    exportCertKeystore.addLongIdentifier("keystore-file", true);
555    exportCertKeystore.addLongIdentifier("keystoreFile", true);
556    exportCertParser.addArgument(exportCertKeystore);
557
558    final StringArgument exportCertKeystorePassword = new StringArgument(null,
559         "keystore-password", false, 1,
560         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
561         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_PW_DESC.get());
562    exportCertKeystorePassword.addLongIdentifier("keystorePassword", true);
563    exportCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
564    exportCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
565    exportCertKeystorePassword.addLongIdentifier("keystore-pin", true);
566    exportCertKeystorePassword.addLongIdentifier("keystorePIN", true);
567    exportCertKeystorePassword.addLongIdentifier("storepass", true);
568    exportCertKeystorePassword.setSensitive(true);
569    exportCertParser.addArgument(exportCertKeystorePassword);
570
571    final FileArgument exportCertKeystorePasswordFile = new FileArgument(null,
572         "keystore-password-file", false, 1, null,
573         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
574         true, false);
575    exportCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
576         true);
577    exportCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
578         true);
579    exportCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
580         true);
581    exportCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
582         true);
583    exportCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
584    exportCertParser.addArgument(exportCertKeystorePasswordFile);
585
586    final BooleanArgument exportCertPromptForKeystorePassword =
587         new BooleanArgument(null, "prompt-for-keystore-password",
588        INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
589    exportCertPromptForKeystorePassword.addLongIdentifier(
590         "promptForKeystorePassword", true);
591    exportCertPromptForKeystorePassword.addLongIdentifier(
592         "prompt-for-keystore-passphrase", true);
593    exportCertPromptForKeystorePassword.addLongIdentifier(
594         "promptForKeystorePassphrase", true);
595    exportCertPromptForKeystorePassword.addLongIdentifier(
596         "prompt-for-keystore-pin", true);
597    exportCertPromptForKeystorePassword.addLongIdentifier(
598         "promptForKeystorePIN", true);
599    exportCertParser.addArgument(exportCertPromptForKeystorePassword);
600
601    final StringArgument exportCertAlias = new StringArgument(null, "alias",
602         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
603         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_ALIAS_DESC.get());
604    exportCertAlias.addLongIdentifier("nickname", true);
605    exportCertParser.addArgument(exportCertAlias);
606
607    final BooleanArgument exportCertChain = new BooleanArgument(null,
608         "export-certificate-chain", 1,
609         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_CHAIN_DESC.get());
610    exportCertChain.addLongIdentifier("exportCertificateChain", true);
611    exportCertChain.addLongIdentifier("export-chain", true);
612    exportCertChain.addLongIdentifier("exportChain", true);
613    exportCertChain.addLongIdentifier("certificate-chain", true);
614    exportCertChain.addLongIdentifier("certificateChain", true);
615    exportCertChain.addLongIdentifier("chain", true);
616    exportCertParser.addArgument(exportCertChain);
617
618    final LinkedHashSet<String> exportCertOutputFormatAllowedValues =
619         new LinkedHashSet<>(7);
620    exportCertOutputFormatAllowedValues.add("PEM");
621    exportCertOutputFormatAllowedValues.add("text");
622    exportCertOutputFormatAllowedValues.add("txt");
623    exportCertOutputFormatAllowedValues.add("RFC");
624    exportCertOutputFormatAllowedValues.add("DER");
625    exportCertOutputFormatAllowedValues.add("binary");
626    exportCertOutputFormatAllowedValues.add("bin");
627    final StringArgument exportCertOutputFormat = new StringArgument(null,
628         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
629         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_FORMAT_DESC.get(),
630         exportCertOutputFormatAllowedValues, "PEM");
631    exportCertOutputFormat.addLongIdentifier("outputFormat");
632    exportCertParser.addArgument(exportCertOutputFormat);
633
634    final FileArgument exportCertOutputFile = new FileArgument(null,
635         "output-file", false, 1, null,
636         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_FILE_DESC.get(), false, true,
637         true, false);
638    exportCertOutputFile.addLongIdentifier("outputFile", true);
639    exportCertOutputFile.addLongIdentifier("export-file", true);
640    exportCertOutputFile.addLongIdentifier("exportFile", true);
641    exportCertOutputFile.addLongIdentifier("certificate-file", true);
642    exportCertOutputFile.addLongIdentifier("certificateFile", true);
643    exportCertOutputFile.addLongIdentifier("file", true);
644    exportCertOutputFile.addLongIdentifier("filename", true);
645    exportCertParser.addArgument(exportCertOutputFile);
646
647    final BooleanArgument exportCertSeparateFile = new BooleanArgument(null,
648         "separate-file-per-certificate", 1,
649         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_SEPARATE_FILE_DESC.get());
650    exportCertSeparateFile.addLongIdentifier("separateFilePerCertificate",
651         true);
652    exportCertSeparateFile.addLongIdentifier("separate-files", true);
653    exportCertSeparateFile.addLongIdentifier("separateFiles", true);
654    exportCertParser.addArgument(exportCertSeparateFile);
655
656    final BooleanArgument exportCertDisplayCommand = new BooleanArgument(null,
657         "display-keytool-command", 1,
658         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_DISPLAY_COMMAND_DESC.get());
659    exportCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
660    exportCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
661    exportCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
662    exportCertParser.addArgument(exportCertDisplayCommand);
663
664    exportCertParser.addExclusiveArgumentSet(exportCertKeystorePassword,
665         exportCertKeystorePasswordFile, exportCertPromptForKeystorePassword);
666    exportCertParser.addDependentArgumentSet(exportCertSeparateFile,
667         exportCertChain);
668    exportCertParser.addDependentArgumentSet(exportCertSeparateFile,
669         exportCertOutputFile);
670
671    final LinkedHashMap<String[],String> exportCertExamples =
672         new LinkedHashMap<>(2);
673    exportCertExamples.put(
674         new String[]
675         {
676           "export-certificate",
677           "--keystore", getPlatformSpecificPath("config", "keystore"),
678           "--alias", "server-cert"
679         },
680         INFO_MANAGE_CERTS_SC_EXPORT_CERT_EXAMPLE_1.get());
681    exportCertExamples.put(
682         new String[]
683         {
684           "export-certificate",
685           "--keystore", getPlatformSpecificPath("config", "keystore.p12"),
686           "--keystore-password-file",
687                getPlatformSpecificPath("config", "keystore.pin"),
688           "--alias", "server-cert",
689           "--export-certificate-chain",
690           "--output-format", "DER",
691           "--output-file", "certificate-chain.der",
692           "--display-keytool-command"
693         },
694         INFO_MANAGE_CERTS_SC_EXPORT_CERT_EXAMPLE_2.get());
695
696    final SubCommand exportCertSubCommand = new SubCommand("export-certificate",
697         INFO_MANAGE_CERTS_SC_EXPORT_CERT_DESC.get(), exportCertParser,
698         exportCertExamples);
699    exportCertSubCommand.addName("exportCertificate", true);
700    exportCertSubCommand.addName("export-cert", true);
701    exportCertSubCommand.addName("exportCert", true);
702    exportCertSubCommand.addName("export", false);
703
704    parser.addSubCommand(exportCertSubCommand);
705
706
707    // Define the "export-private-key" subcommand and all of its arguments.
708    final ArgumentParser exportKeyParser = new ArgumentParser(
709         "export-private-key", INFO_MANAGE_CERTS_SC_EXPORT_KEY_DESC.get());
710
711    final FileArgument exportKeyKeystore = new FileArgument(null, "keystore",
712         true, 1, null, INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_DESC.get(),
713         true, true,  true, false);
714    exportKeyKeystore.addLongIdentifier("keystore-path", true);
715    exportKeyKeystore.addLongIdentifier("keystorePath", true);
716    exportKeyKeystore.addLongIdentifier("keystore-file", true);
717    exportKeyKeystore.addLongIdentifier("keystoreFile", true);
718    exportKeyParser.addArgument(exportKeyKeystore);
719
720    final StringArgument exportKeyKeystorePassword = new StringArgument(null,
721         "keystore-password", false, 1,
722         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
723         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_PW_DESC.get());
724    exportKeyKeystorePassword.addLongIdentifier("keystorePassword", true);
725    exportKeyKeystorePassword.addLongIdentifier("keystore-passphrase", true);
726    exportKeyKeystorePassword.addLongIdentifier("keystorePassphrase", true);
727    exportKeyKeystorePassword.addLongIdentifier("keystore-pin", true);
728    exportKeyKeystorePassword.addLongIdentifier("keystorePIN", true);
729    exportKeyKeystorePassword.addLongIdentifier("storepass", true);
730    exportKeyKeystorePassword.setSensitive(true);
731    exportKeyParser.addArgument(exportKeyKeystorePassword);
732
733    final FileArgument exportKeyKeystorePasswordFile = new FileArgument(null,
734         "keystore-password-file", false, 1, null,
735         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_PW_FILE_DESC.get(), true, true,
736         true, false);
737    exportKeyKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
738         true);
739    exportKeyKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
740         true);
741    exportKeyKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
742         true);
743    exportKeyKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
744         true);
745    exportKeyKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
746    exportKeyParser.addArgument(exportKeyKeystorePasswordFile);
747
748    final BooleanArgument exportKeyPromptForKeystorePassword =
749         new BooleanArgument(null, "prompt-for-keystore-password",
750        INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PROMPT_FOR_KS_PW_DESC.get());
751    exportKeyPromptForKeystorePassword.addLongIdentifier(
752         "promptForKeystorePassword", true);
753    exportKeyPromptForKeystorePassword.addLongIdentifier(
754         "prompt-for-keystore-passphrase", true);
755    exportKeyPromptForKeystorePassword.addLongIdentifier(
756         "promptForKeystorePassphrase", true);
757    exportKeyPromptForKeystorePassword.addLongIdentifier(
758         "prompt-for-keystore-pin", true);
759    exportKeyPromptForKeystorePassword.addLongIdentifier(
760         "promptForKeystorePIN", true);
761    exportKeyParser.addArgument(exportKeyPromptForKeystorePassword);
762
763    final StringArgument exportKeyPKPassword = new StringArgument(null,
764         "private-key-password", false, 1,
765         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
766         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PK_PW_DESC.get());
767    exportKeyPKPassword.addLongIdentifier("privateKeyPassword", true);
768    exportKeyPKPassword.addLongIdentifier("private-key-passphrase", true);
769    exportKeyPKPassword.addLongIdentifier("privateKeyPassphrase", true);
770    exportKeyPKPassword.addLongIdentifier("private-key-pin", true);
771    exportKeyPKPassword.addLongIdentifier("privateKeyPIN", true);
772    exportKeyPKPassword.addLongIdentifier("key-password", true);
773    exportKeyPKPassword.addLongIdentifier("keyPassword", true);
774    exportKeyPKPassword.addLongIdentifier("key-passphrase", true);
775    exportKeyPKPassword.addLongIdentifier("keyPassphrase", true);
776    exportKeyPKPassword.addLongIdentifier("key-pin", true);
777    exportKeyPKPassword.addLongIdentifier("keyPIN", true);
778    exportKeyPKPassword.addLongIdentifier("keypass", true);
779    exportKeyPKPassword.setSensitive(true);
780    exportKeyParser.addArgument(exportKeyPKPassword);
781
782    final FileArgument exportKeyPKPasswordFile = new FileArgument(null,
783         "private-key-password-file", false, 1, null,
784         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PK_PW_FILE_DESC.get(), true, true,
785         true, false);
786    exportKeyPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
787    exportKeyPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
788         true);
789    exportKeyPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
790         true);
791    exportKeyPKPasswordFile.addLongIdentifier("private-key-pin-file",
792         true);
793    exportKeyPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
794    exportKeyPKPasswordFile.addLongIdentifier("key-password-file", true);
795    exportKeyPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
796    exportKeyPKPasswordFile.addLongIdentifier("key-passphrase-file",
797         true);
798    exportKeyPKPasswordFile.addLongIdentifier("keyPassphraseFile",
799         true);
800    exportKeyPKPasswordFile.addLongIdentifier("key-pin-file",
801         true);
802    exportKeyPKPasswordFile.addLongIdentifier("keyPINFile", true);
803    exportKeyParser.addArgument(exportKeyPKPasswordFile);
804
805    final BooleanArgument exportKeyPromptForPKPassword =
806         new BooleanArgument(null, "prompt-for-private-key-password",
807        INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PROMPT_FOR_PK_PW_DESC.get());
808    exportKeyPromptForPKPassword.addLongIdentifier(
809         "promptForPrivateKeyPassword", true);
810    exportKeyPromptForPKPassword.addLongIdentifier(
811         "prompt-for-private-key-passphrase", true);
812    exportKeyPromptForPKPassword.addLongIdentifier(
813         "promptForPrivateKeyPassphrase", true);
814    exportKeyPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
815         true);
816    exportKeyPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
817         true);
818    exportKeyPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
819         true);
820    exportKeyPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
821         true);
822    exportKeyPromptForPKPassword.addLongIdentifier(
823         "prompt-for-key-passphrase", true);
824    exportKeyPromptForPKPassword.addLongIdentifier(
825         "promptForKeyPassphrase", true);
826    exportKeyPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
827    exportKeyPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
828    exportKeyParser.addArgument(exportKeyPromptForPKPassword);
829
830    final StringArgument exportKeyAlias = new StringArgument(null, "alias",
831         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
832         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_ALIAS_DESC.get());
833    exportKeyAlias.addLongIdentifier("nickname", true);
834    exportKeyParser.addArgument(exportKeyAlias);
835
836    final LinkedHashSet<String> exportKeyOutputFormatAllowedValues =
837         new LinkedHashSet<>(7);
838    exportKeyOutputFormatAllowedValues.add("PEM");
839    exportKeyOutputFormatAllowedValues.add("text");
840    exportKeyOutputFormatAllowedValues.add("txt");
841    exportKeyOutputFormatAllowedValues.add("RFC");
842    exportKeyOutputFormatAllowedValues.add("DER");
843    exportKeyOutputFormatAllowedValues.add("binary");
844    exportKeyOutputFormatAllowedValues.add("bin");
845    final StringArgument exportKeyOutputFormat = new StringArgument(null,
846         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
847         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_FORMAT_DESC.get(),
848         exportKeyOutputFormatAllowedValues, "PEM");
849    exportKeyOutputFormat.addLongIdentifier("outputFormat");
850    exportKeyParser.addArgument(exportKeyOutputFormat);
851
852    final FileArgument exportKeyOutputFile = new FileArgument(null,
853         "output-file", false, 1, null,
854         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_FILE_DESC.get(), false, true,
855         true, false);
856    exportKeyOutputFile.addLongIdentifier("outputFile", true);
857    exportKeyOutputFile.addLongIdentifier("export-file", true);
858    exportKeyOutputFile.addLongIdentifier("exportFile", true);
859    exportKeyOutputFile.addLongIdentifier("private-key-file", true);
860    exportKeyOutputFile.addLongIdentifier("privateKeyFile", true);
861    exportKeyOutputFile.addLongIdentifier("key-file", true);
862    exportKeyOutputFile.addLongIdentifier("keyFile", true);
863    exportKeyOutputFile.addLongIdentifier("file", true);
864    exportKeyOutputFile.addLongIdentifier("filename", true);
865    exportKeyParser.addArgument(exportKeyOutputFile);
866
867    exportKeyParser.addRequiredArgumentSet(exportKeyKeystorePassword,
868         exportKeyKeystorePasswordFile, exportKeyPromptForKeystorePassword);
869    exportKeyParser.addExclusiveArgumentSet(exportKeyKeystorePassword,
870         exportKeyKeystorePasswordFile, exportKeyPromptForKeystorePassword);
871    exportKeyParser.addExclusiveArgumentSet(exportKeyPKPassword,
872         exportKeyPKPasswordFile, exportKeyPromptForPKPassword);
873
874    final LinkedHashMap<String[],String> exportKeyExamples =
875         new LinkedHashMap<>(2);
876    exportKeyExamples.put(
877         new String[]
878         {
879           "export-private-key",
880           "--keystore", getPlatformSpecificPath("config", "keystore"),
881           "--keystore-password-file",
882                getPlatformSpecificPath("config", "keystore.pin"),
883           "--alias", "server-cert"
884         },
885         INFO_MANAGE_CERTS_SC_EXPORT_KEY_EXAMPLE_1.get());
886    exportKeyExamples.put(
887         new String[]
888         {
889           "export-private-key",
890           "--keystore", getPlatformSpecificPath("config", "keystore.p12"),
891           "--keystore-password-file",
892                getPlatformSpecificPath("config", "keystore.pin"),
893           "--private-key-password-file",
894                getPlatformSpecificPath("config", "server-cert-key.pin"),
895           "--alias", "server-cert",
896           "--output-format", "DER",
897           "--output-file", "server-cert-key.der"
898         },
899         INFO_MANAGE_CERTS_SC_EXPORT_KEY_EXAMPLE_2.get());
900
901    final SubCommand exportKeySubCommand = new SubCommand("export-private-key",
902         INFO_MANAGE_CERTS_SC_EXPORT_CERT_DESC.get(), exportKeyParser,
903         exportKeyExamples);
904    exportKeySubCommand.addName("exportPrivateKey", true);
905    exportKeySubCommand.addName("export-key", true);
906    exportKeySubCommand.addName("exportKey", true);
907
908    parser.addSubCommand(exportKeySubCommand);
909
910
911    // Define the "import-certificate" subcommand and all of its arguments.
912    final ArgumentParser importCertParser = new ArgumentParser(
913         "import-certificate", INFO_MANAGE_CERTS_SC_IMPORT_CERT_DESC.get());
914
915    final FileArgument importCertKeystore = new FileArgument(null, "keystore",
916         true, 1, null, INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_DESC.get(),
917         false, true,  true, false);
918    importCertKeystore.addLongIdentifier("keystore-path", true);
919    importCertKeystore.addLongIdentifier("keystorePath", true);
920    importCertKeystore.addLongIdentifier("keystore-file", true);
921    importCertKeystore.addLongIdentifier("keystoreFile", true);
922    importCertParser.addArgument(importCertKeystore);
923
924    final StringArgument importCertKeystorePassword = new StringArgument(null,
925         "keystore-password", false, 1,
926         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
927         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_PW_DESC.get());
928    importCertKeystorePassword.addLongIdentifier("keystorePassword", true);
929    importCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
930    importCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
931    importCertKeystorePassword.addLongIdentifier("keystore-pin", true);
932    importCertKeystorePassword.addLongIdentifier("keystorePIN", true);
933    importCertKeystorePassword.addLongIdentifier("storepass", true);
934    importCertKeystorePassword.setSensitive(true);
935    importCertParser.addArgument(importCertKeystorePassword);
936
937    final FileArgument importCertKeystorePasswordFile = new FileArgument(null,
938         "keystore-password-file", false, 1, null,
939         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
940         true, false);
941    importCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
942         true);
943    importCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
944         true);
945    importCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
946         true);
947    importCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
948         true);
949    importCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
950    importCertParser.addArgument(importCertKeystorePasswordFile);
951
952    final BooleanArgument importCertPromptForKeystorePassword =
953         new BooleanArgument(null, "prompt-for-keystore-password",
954        INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
955    importCertPromptForKeystorePassword.addLongIdentifier(
956         "promptForKeystorePassword", true);
957    importCertPromptForKeystorePassword.addLongIdentifier(
958         "prompt-for-keystore-passphrase", true);
959    importCertPromptForKeystorePassword.addLongIdentifier(
960         "promptForKeystorePassphrase", true);
961    importCertPromptForKeystorePassword.addLongIdentifier(
962         "prompt-for-keystore-pin", true);
963    importCertPromptForKeystorePassword.addLongIdentifier(
964         "promptForKeystorePIN", true);
965    importCertParser.addArgument(importCertPromptForKeystorePassword);
966
967    final LinkedHashSet<String> importCertKeystoreTypeAllowedValues =
968         new LinkedHashSet<>(2);
969    importCertKeystoreTypeAllowedValues.add("jks");
970    importCertKeystoreTypeAllowedValues.add("pkcs12");
971    importCertKeystoreTypeAllowedValues.add("pkcs 12");
972    importCertKeystoreTypeAllowedValues.add("pkcs#12");
973    importCertKeystoreTypeAllowedValues.add("pkcs #12");
974    final StringArgument importCertKeystoreType = new StringArgument(null,
975         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
976         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_TYPE_DESC.get(),
977         importCertKeystoreTypeAllowedValues);
978    importCertKeystoreType.addLongIdentifier("keystoreType", true);
979    importCertKeystoreType.addLongIdentifier("storetype", true);
980    importCertParser.addArgument(importCertKeystoreType);
981
982    final StringArgument importCertAlias = new StringArgument(null, "alias",
983         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
984         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_ALIAS_DESC.get());
985    importCertAlias.addLongIdentifier("nickname", true);
986    importCertParser.addArgument(importCertAlias);
987
988    final FileArgument importCertCertificateFile = new FileArgument(null,
989         "certificate-file", true, 0, null,
990         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_CERT_FILE_DESC.get(), true, true,
991         true, false);
992    importCertCertificateFile.addLongIdentifier("certificateFile", true);
993    importCertCertificateFile.addLongIdentifier("certificate-chain-file", true);
994    importCertCertificateFile.addLongIdentifier("certificateChainFile", true);
995    importCertCertificateFile.addLongIdentifier("input-file", true);
996    importCertCertificateFile.addLongIdentifier("inputFile", true);
997    importCertCertificateFile.addLongIdentifier("import-file", true);
998    importCertCertificateFile.addLongIdentifier("importFile", true);
999    importCertCertificateFile.addLongIdentifier("file", true);
1000    importCertCertificateFile.addLongIdentifier("filename", true);
1001    importCertParser.addArgument(importCertCertificateFile);
1002
1003    final FileArgument importCertPKFile = new FileArgument(null,
1004         "private-key-file", false, 1, null,
1005         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KEY_FILE_DESC.get(), true, true,
1006         true, false);
1007    importCertPKFile.addLongIdentifier("privateKeyFile", true);
1008    importCertPKFile.addLongIdentifier("key-file", true);
1009    importCertPKFile.addLongIdentifier("keyFile", true);
1010    importCertParser.addArgument(importCertPKFile);
1011
1012    final StringArgument importCertPKPassword = new StringArgument(null,
1013         "private-key-password", false, 1,
1014         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1015         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PK_PW_DESC.get());
1016    importCertPKPassword.addLongIdentifier("privateKeyPassword", true);
1017    importCertPKPassword.addLongIdentifier("private-key-passphrase", true);
1018    importCertPKPassword.addLongIdentifier("privateKeyPassphrase", true);
1019    importCertPKPassword.addLongIdentifier("private-key-pin", true);
1020    importCertPKPassword.addLongIdentifier("privateKeyPIN", true);
1021    importCertPKPassword.addLongIdentifier("key-password", true);
1022    importCertPKPassword.addLongIdentifier("keyPassword", true);
1023    importCertPKPassword.addLongIdentifier("key-passphrase", true);
1024    importCertPKPassword.addLongIdentifier("keyPassphrase", true);
1025    importCertPKPassword.addLongIdentifier("key-pin", true);
1026    importCertPKPassword.addLongIdentifier("keyPIN", true);
1027    importCertPKPassword.addLongIdentifier("keypass", true);
1028    importCertPKPassword.setSensitive(true);
1029    importCertParser.addArgument(importCertPKPassword);
1030
1031    final FileArgument importCertPKPasswordFile = new FileArgument(null,
1032         "private-key-password-file", false, 1, null,
1033         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PK_PW_FILE_DESC.get(), true, true,
1034         true, false);
1035    importCertPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
1036    importCertPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
1037         true);
1038    importCertPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
1039         true);
1040    importCertPKPasswordFile.addLongIdentifier("private-key-pin-file",
1041         true);
1042    importCertPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
1043    importCertPKPasswordFile.addLongIdentifier("key-password-file", true);
1044    importCertPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
1045    importCertPKPasswordFile.addLongIdentifier("key-passphrase-file",
1046         true);
1047    importCertPKPasswordFile.addLongIdentifier("keyPassphraseFile",
1048         true);
1049    importCertPKPasswordFile.addLongIdentifier("key-pin-file",
1050         true);
1051    importCertPKPasswordFile.addLongIdentifier("keyPINFile", true);
1052    importCertParser.addArgument(importCertPKPasswordFile);
1053
1054    final BooleanArgument importCertPromptForPKPassword =
1055         new BooleanArgument(null, "prompt-for-private-key-password",
1056        INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PROMPT_FOR_PK_PW_DESC.get());
1057    importCertPromptForPKPassword.addLongIdentifier(
1058         "promptForPrivateKeyPassword", true);
1059    importCertPromptForPKPassword.addLongIdentifier(
1060         "prompt-for-private-key-passphrase", true);
1061    importCertPromptForPKPassword.addLongIdentifier(
1062         "promptForPrivateKeyPassphrase", true);
1063    importCertPromptForPKPassword.addLongIdentifier(
1064         "prompt-for-private-key-pin", true);
1065    importCertPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
1066         true);
1067    importCertPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
1068         true);
1069    importCertPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
1070         true);
1071    importCertPromptForPKPassword.addLongIdentifier(
1072         "prompt-for-key-passphrase", true);
1073    importCertPromptForPKPassword.addLongIdentifier(
1074         "promptForKeyPassphrase", true);
1075    importCertPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
1076    importCertPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
1077    importCertParser.addArgument(importCertPromptForPKPassword);
1078
1079    final BooleanArgument importCertNoPrompt = new BooleanArgument(null,
1080         "no-prompt", 1,
1081         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_NO_PROMPT_DESC.get());
1082    importCertNoPrompt.addLongIdentifier("noPrompt", true);
1083    importCertParser.addArgument(importCertNoPrompt);
1084
1085    final BooleanArgument importCertDisplayCommand = new BooleanArgument(null,
1086         "display-keytool-command", 1,
1087         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_DISPLAY_COMMAND_DESC.get());
1088    importCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
1089    importCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
1090    importCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
1091    importCertParser.addArgument(importCertDisplayCommand);
1092
1093    importCertParser.addRequiredArgumentSet(importCertKeystorePassword,
1094         importCertKeystorePasswordFile, importCertPromptForKeystorePassword);
1095    importCertParser.addExclusiveArgumentSet(importCertKeystorePassword,
1096         importCertKeystorePasswordFile, importCertPromptForKeystorePassword);
1097    importCertParser.addExclusiveArgumentSet(importCertPKPassword,
1098         importCertPKPasswordFile, importCertPromptForPKPassword);
1099
1100    final LinkedHashMap<String[],String> importCertExamples =
1101         new LinkedHashMap<>(2);
1102    importCertExamples.put(
1103         new String[]
1104         {
1105           "import-certificate",
1106           "--keystore", getPlatformSpecificPath("config", "keystore"),
1107           "--keystore-password-file",
1108                getPlatformSpecificPath("config", "keystore.pin"),
1109           "--alias", "server-cert",
1110           "--certificate-file", "server-cert.crt"
1111         },
1112         INFO_MANAGE_CERTS_SC_IMPORT_CERT_EXAMPLE_1.get("server-cert.crt"));
1113    importCertExamples.put(
1114         new String[]
1115         {
1116           "import-certificate",
1117           "--keystore", getPlatformSpecificPath("config", "keystore"),
1118           "--keystore-password-file",
1119                getPlatformSpecificPath("config", "keystore.pin"),
1120           "--alias", "server-cert",
1121           "--certificate-file", "server-cert.crt",
1122           "--certificate-file", "server-cert-issuer.crt",
1123           "--private-key-file", "server-cert.key",
1124           "--display-keytool-command"
1125         },
1126         INFO_MANAGE_CERTS_SC_IMPORT_CERT_EXAMPLE_2.get());
1127
1128    final SubCommand importCertSubCommand = new SubCommand("import-certificate",
1129         INFO_MANAGE_CERTS_SC_IMPORT_CERT_DESC.get(), importCertParser,
1130         importCertExamples);
1131    importCertSubCommand.addName("importCertificate", true);
1132    importCertSubCommand.addName("import-certificates", true);
1133    importCertSubCommand.addName("importCertificates", true);
1134    importCertSubCommand.addName("import-cert", true);
1135    importCertSubCommand.addName("importCert", true);
1136    importCertSubCommand.addName("import-certs", true);
1137    importCertSubCommand.addName("importCerts", true);
1138    importCertSubCommand.addName("import-certificate-chain", true);
1139    importCertSubCommand.addName("importCertificateChain", true);
1140    importCertSubCommand.addName("import-chain", true);
1141    importCertSubCommand.addName("importChain", true);
1142    importCertSubCommand.addName("import", false);
1143
1144    parser.addSubCommand(importCertSubCommand);
1145
1146
1147    // Define the "delete-certificate" subcommand and all of its arguments.
1148    final ArgumentParser deleteCertParser = new ArgumentParser(
1149         "delete-certificate", INFO_MANAGE_CERTS_SC_DELETE_CERT_DESC.get());
1150
1151    final FileArgument deleteCertKeystore = new FileArgument(null, "keystore",
1152         true, 1, null, INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_DESC.get(),
1153         true, true,  true, false);
1154    deleteCertKeystore.addLongIdentifier("keystore-path", true);
1155    deleteCertKeystore.addLongIdentifier("keystorePath", true);
1156    deleteCertKeystore.addLongIdentifier("keystore-file", true);
1157    deleteCertKeystore.addLongIdentifier("keystoreFile", true);
1158    deleteCertParser.addArgument(deleteCertKeystore);
1159
1160    final StringArgument deleteCertKeystorePassword = new StringArgument(null,
1161         "keystore-password", false, 1,
1162         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1163         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_PW_DESC.get());
1164    deleteCertKeystorePassword.addLongIdentifier("keystorePassword", true);
1165    deleteCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
1166    deleteCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
1167    deleteCertKeystorePassword.addLongIdentifier("keystore-pin", true);
1168    deleteCertKeystorePassword.addLongIdentifier("keystorePIN", true);
1169    deleteCertKeystorePassword.addLongIdentifier("storepass", true);
1170    deleteCertKeystorePassword.setSensitive(true);
1171    deleteCertParser.addArgument(deleteCertKeystorePassword);
1172
1173    final FileArgument deleteCertKeystorePasswordFile = new FileArgument(null,
1174         "keystore-password-file", false, 1, null,
1175         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
1176         true, false);
1177    deleteCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
1178         true);
1179    deleteCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
1180         true);
1181    deleteCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
1182         true);
1183    deleteCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
1184         true);
1185    deleteCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
1186    deleteCertParser.addArgument(deleteCertKeystorePasswordFile);
1187
1188    final BooleanArgument deleteCertPromptForKeystorePassword =
1189         new BooleanArgument(null, "prompt-for-keystore-password",
1190        INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
1191    deleteCertPromptForKeystorePassword.addLongIdentifier(
1192         "promptForKeystorePassword", true);
1193    deleteCertPromptForKeystorePassword.addLongIdentifier(
1194         "prompt-for-keystore-passphrase", true);
1195    deleteCertPromptForKeystorePassword.addLongIdentifier(
1196         "promptForKeystorePassphrase", true);
1197    deleteCertPromptForKeystorePassword.addLongIdentifier(
1198         "prompt-for-keystore-pin", true);
1199    deleteCertPromptForKeystorePassword.addLongIdentifier(
1200         "promptForKeystorePIN", true);
1201    deleteCertParser.addArgument(deleteCertPromptForKeystorePassword);
1202
1203    final StringArgument deleteCertAlias = new StringArgument(null, "alias",
1204         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
1205         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_ALIAS_DESC.get());
1206    deleteCertAlias.addLongIdentifier("nickname", true);
1207    deleteCertParser.addArgument(deleteCertAlias);
1208
1209    final BooleanArgument deleteCertNoPrompt = new BooleanArgument(null,
1210         "no-prompt", 1,
1211         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_NO_PROMPT_DESC.get());
1212    deleteCertNoPrompt.addLongIdentifier("noPrompt", true);
1213    deleteCertParser.addArgument(deleteCertNoPrompt);
1214
1215    final BooleanArgument deleteCertDisplayCommand = new BooleanArgument(null,
1216         "display-keytool-command", 1,
1217         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_DISPLAY_COMMAND_DESC.get());
1218    deleteCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
1219    deleteCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
1220    deleteCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
1221    deleteCertParser.addArgument(deleteCertDisplayCommand);
1222
1223    deleteCertParser.addExclusiveArgumentSet(deleteCertKeystorePassword,
1224         deleteCertKeystorePasswordFile, deleteCertPromptForKeystorePassword);
1225    deleteCertParser.addRequiredArgumentSet(deleteCertKeystorePassword,
1226         deleteCertKeystorePasswordFile, deleteCertPromptForKeystorePassword);
1227
1228    final LinkedHashMap<String[],String> deleteCertExamples =
1229         new LinkedHashMap<>(1);
1230    deleteCertExamples.put(
1231         new String[]
1232         {
1233           "delete-certificate",
1234           "--keystore", getPlatformSpecificPath("config", "keystore"),
1235           "--alias", "server-cert"
1236         },
1237         INFO_MANAGE_CERTS_SC_DELETE_CERT_EXAMPLE_1.get(
1238              getPlatformSpecificPath("config", "keystore")));
1239
1240    final SubCommand deleteCertSubCommand = new SubCommand("delete-certificate",
1241         INFO_MANAGE_CERTS_SC_DELETE_CERT_DESC.get(), deleteCertParser,
1242         deleteCertExamples);
1243    deleteCertSubCommand.addName("deleteCertificate", true);
1244    deleteCertSubCommand.addName("remove-certificate", false);
1245    deleteCertSubCommand.addName("removeCertificate", true);
1246    deleteCertSubCommand.addName("delete", false);
1247    deleteCertSubCommand.addName("remove", false);
1248
1249    parser.addSubCommand(deleteCertSubCommand);
1250
1251
1252    // Define the "generate-self-signed-certificate" subcommand and all of its
1253    // arguments.
1254    final ArgumentParser genCertParser = new ArgumentParser(
1255         "generate-self-signed-certificate",
1256         INFO_MANAGE_CERTS_SC_GEN_CERT_DESC.get());
1257
1258    final FileArgument genCertKeystore = new FileArgument(null, "keystore",
1259         true, 1, null, INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_DESC.get(), false,
1260         true,  true, false);
1261    genCertKeystore.addLongIdentifier("keystore-path", true);
1262    genCertKeystore.addLongIdentifier("keystorePath", true);
1263    genCertKeystore.addLongIdentifier("keystore-file", true);
1264    genCertKeystore.addLongIdentifier("keystoreFile", true);
1265    genCertParser.addArgument(genCertKeystore);
1266
1267    final StringArgument genCertKeystorePassword = new StringArgument(null,
1268         "keystore-password", false, 1,
1269         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1270         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_PW_DESC.get());
1271    genCertKeystorePassword.addLongIdentifier("keystorePassword", true);
1272    genCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
1273    genCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
1274    genCertKeystorePassword.addLongIdentifier("keystore-pin", true);
1275    genCertKeystorePassword.addLongIdentifier("keystorePIN", true);
1276    genCertKeystorePassword.addLongIdentifier("storepass", true);
1277    genCertKeystorePassword.setSensitive(true);
1278    genCertParser.addArgument(genCertKeystorePassword);
1279
1280    final FileArgument genCertKeystorePasswordFile = new FileArgument(null,
1281         "keystore-password-file", false, 1, null,
1282         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
1283         true, false);
1284    genCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
1285         true);
1286    genCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
1287         true);
1288    genCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
1289         true);
1290    genCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
1291         true);
1292    genCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
1293    genCertParser.addArgument(genCertKeystorePasswordFile);
1294
1295    final BooleanArgument genCertPromptForKeystorePassword =
1296         new BooleanArgument(null, "prompt-for-keystore-password",
1297        INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
1298    genCertPromptForKeystorePassword.addLongIdentifier(
1299         "promptForKeystorePassword", true);
1300    genCertPromptForKeystorePassword.addLongIdentifier(
1301         "prompt-for-keystore-passphrase", true);
1302    genCertPromptForKeystorePassword.addLongIdentifier(
1303         "promptForKeystorePassphrase", true);
1304    genCertPromptForKeystorePassword.addLongIdentifier(
1305         "prompt-for-keystore-pin", true);
1306    genCertPromptForKeystorePassword.addLongIdentifier(
1307         "promptForKeystorePIN", true);
1308    genCertParser.addArgument(genCertPromptForKeystorePassword);
1309
1310    final StringArgument genCertPKPassword = new StringArgument(null,
1311         "private-key-password", false, 1,
1312         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1313         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PK_PW_DESC.get());
1314    genCertPKPassword.addLongIdentifier("privateKeyPassword", true);
1315    genCertPKPassword.addLongIdentifier("private-key-passphrase", true);
1316    genCertPKPassword.addLongIdentifier("privateKeyPassphrase", true);
1317    genCertPKPassword.addLongIdentifier("private-key-pin", true);
1318    genCertPKPassword.addLongIdentifier("privateKeyPIN", true);
1319    genCertPKPassword.addLongIdentifier("key-password", true);
1320    genCertPKPassword.addLongIdentifier("keyPassword", true);
1321    genCertPKPassword.addLongIdentifier("key-passphrase", true);
1322    genCertPKPassword.addLongIdentifier("keyPassphrase", true);
1323    genCertPKPassword.addLongIdentifier("key-pin", true);
1324    genCertPKPassword.addLongIdentifier("keyPIN", true);
1325    genCertPKPassword.addLongIdentifier("keypass", true);
1326    genCertPKPassword.setSensitive(true);
1327    genCertParser.addArgument(genCertPKPassword);
1328
1329    final FileArgument genCertPKPasswordFile = new FileArgument(null,
1330         "private-key-password-file", false, 1, null,
1331         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PK_PW_FILE_DESC.get(), true, true,
1332         true, false);
1333    genCertPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
1334    genCertPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
1335         true);
1336    genCertPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
1337         true);
1338    genCertPKPasswordFile.addLongIdentifier("private-key-pin-file",
1339         true);
1340    genCertPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
1341    genCertPKPasswordFile.addLongIdentifier("key-password-file", true);
1342    genCertPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
1343    genCertPKPasswordFile.addLongIdentifier("key-passphrase-file",
1344         true);
1345    genCertPKPasswordFile.addLongIdentifier("keyPassphraseFile",
1346         true);
1347    genCertPKPasswordFile.addLongIdentifier("key-pin-file",
1348         true);
1349    genCertPKPasswordFile.addLongIdentifier("keyPINFile", true);
1350    genCertParser.addArgument(genCertPKPasswordFile);
1351
1352    final BooleanArgument genCertPromptForPKPassword =
1353         new BooleanArgument(null, "prompt-for-private-key-password",
1354        INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PROMPT_FOR_PK_PW_DESC.get());
1355    genCertPromptForPKPassword.addLongIdentifier(
1356         "promptForPrivateKeyPassword", true);
1357    genCertPromptForPKPassword.addLongIdentifier(
1358         "prompt-for-private-key-passphrase", true);
1359    genCertPromptForPKPassword.addLongIdentifier(
1360         "promptForPrivateKeyPassphrase", true);
1361    genCertPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
1362         true);
1363    genCertPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
1364         true);
1365    genCertPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
1366         true);
1367    genCertPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
1368         true);
1369    genCertPromptForPKPassword.addLongIdentifier(
1370         "prompt-for-key-passphrase", true);
1371    genCertPromptForPKPassword.addLongIdentifier(
1372         "promptForKeyPassphrase", true);
1373    genCertPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
1374    genCertPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
1375    genCertParser.addArgument(genCertPromptForPKPassword);
1376
1377    final LinkedHashSet<String> genCertKeystoreTypeAllowedValues =
1378         new LinkedHashSet<>(2);
1379    genCertKeystoreTypeAllowedValues.add("jks");
1380    genCertKeystoreTypeAllowedValues.add("pkcs12");
1381    genCertKeystoreTypeAllowedValues.add("pkcs 12");
1382    genCertKeystoreTypeAllowedValues.add("pkcs#12");
1383    genCertKeystoreTypeAllowedValues.add("pkcs #12");
1384    final StringArgument genCertKeystoreType = new StringArgument(null,
1385         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
1386         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_TYPE_DESC.get(),
1387         genCertKeystoreTypeAllowedValues);
1388    genCertKeystoreType.addLongIdentifier("keystoreType", true);
1389    genCertKeystoreType.addLongIdentifier("storetype", true);
1390    genCertParser.addArgument(genCertKeystoreType);
1391
1392    final StringArgument genCertAlias = new StringArgument(null, "alias",
1393         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
1394         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_ALIAS_DESC.get());
1395    genCertAlias.addLongIdentifier("nickname", true);
1396    genCertParser.addArgument(genCertAlias);
1397
1398    final BooleanArgument genCertReplace = new BooleanArgument(null,
1399         "replace-existing-certificate", 1,
1400         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_REPLACE_DESC.get());
1401    genCertReplace.addLongIdentifier("replaceExistingCertificate", true);
1402    genCertReplace.addLongIdentifier("replace-certificate", true);
1403    genCertReplace.addLongIdentifier("replaceCertificate", true);
1404    genCertReplace.addLongIdentifier("replace-existing", true);
1405    genCertReplace.addLongIdentifier("replaceExisting", true);
1406    genCertReplace.addLongIdentifier("replace", true);
1407    genCertReplace.addLongIdentifier("use-existing-key-pair", true);
1408    genCertReplace.addLongIdentifier("use-existing-keypair", true);
1409    genCertReplace.addLongIdentifier("useExistingKeypair", true);
1410    genCertParser.addArgument(genCertReplace);
1411
1412    final DNArgument genCertSubjectDN = new DNArgument(null, "subject-dn",
1413         false, 1, null,
1414         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SUBJECT_DN_DESC.get());
1415    genCertSubjectDN.addLongIdentifier("subjectDN", true);
1416    genCertSubjectDN.addLongIdentifier("subject", true);
1417    genCertSubjectDN.addLongIdentifier("dname", true);
1418    genCertParser.addArgument(genCertSubjectDN);
1419
1420    final IntegerArgument genCertDaysValid = new IntegerArgument(null,
1421         "days-valid", false, 1, null,
1422         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_DAYS_VALID_DESC.get(), 1,
1423         Integer.MAX_VALUE);
1424    genCertDaysValid.addLongIdentifier("daysValid", true);
1425    genCertDaysValid.addLongIdentifier("validity", true);
1426    genCertParser.addArgument(genCertDaysValid);
1427
1428    final TimestampArgument genCertNotBefore = new TimestampArgument(null,
1429         "validity-start-time", false, 1,
1430         INFO_MANAGE_CERTS_PLACEHOLDER_TIMESTAMP.get(),
1431         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_VALIDITY_START_TIME_DESC.get(
1432              "20180102123456"));
1433    genCertNotBefore.addLongIdentifier("validityStartTime", true);
1434    genCertNotBefore.addLongIdentifier("not-before", true);
1435    genCertNotBefore.addLongIdentifier("notBefore", true);
1436    genCertParser.addArgument(genCertNotBefore);
1437
1438    final StringArgument genCertKeyAlgorithm = new StringArgument(null,
1439         "key-algorithm", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1440         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KEY_ALGORITHM_DESC.get());
1441    genCertKeyAlgorithm.addLongIdentifier("keyAlgorithm", true);
1442    genCertKeyAlgorithm.addLongIdentifier("key-alg", true);
1443    genCertKeyAlgorithm.addLongIdentifier("keyAlg", true);
1444    genCertParser.addArgument(genCertKeyAlgorithm);
1445
1446    final IntegerArgument genCertKeySizeBits = new IntegerArgument(null,
1447         "key-size-bits", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_BITS.get(),
1448         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KEY_ALGORITHM_DESC.get(), 1,
1449         Integer.MAX_VALUE);
1450    genCertKeySizeBits.addLongIdentifier("keySizeBits", true);
1451    genCertKeySizeBits.addLongIdentifier("key-length-bits", true);
1452    genCertKeySizeBits.addLongIdentifier("keyLengthBits", true);
1453    genCertKeySizeBits.addLongIdentifier("key-size", true);
1454    genCertKeySizeBits.addLongIdentifier("keySize", true);
1455    genCertKeySizeBits.addLongIdentifier("key-length", true);
1456    genCertKeySizeBits.addLongIdentifier("keyLength", true);
1457    genCertParser.addArgument(genCertKeySizeBits);
1458
1459    final StringArgument genCertSignatureAlgorithm = new StringArgument(null,
1460         "signature-algorithm", false, 1,
1461         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1462         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SIG_ALG_DESC.get());
1463    genCertSignatureAlgorithm.addLongIdentifier("signatureAlgorithm", true);
1464    genCertSignatureAlgorithm.addLongIdentifier("signature-alg", true);
1465    genCertSignatureAlgorithm.addLongIdentifier("signatureAlg", true);
1466    genCertSignatureAlgorithm.addLongIdentifier("sig-alg", true);
1467    genCertSignatureAlgorithm.addLongIdentifier("sigAlg", true);
1468    genCertParser.addArgument(genCertSignatureAlgorithm);
1469
1470    final BooleanArgument genCertInheritExtensions = new BooleanArgument(null,
1471         "inherit-extensions", 1,
1472         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_INHERIT_EXT_DESC.get());
1473    genCertInheritExtensions.addLongIdentifier("inheritExtensions", true);
1474    genCertParser.addArgument(genCertInheritExtensions);
1475
1476    final StringArgument genCertSubjectAltDNS = new StringArgument(null,
1477         "subject-alternative-name-dns", false, 0,
1478         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1479         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_DNS_DESC.get());
1480    genCertSubjectAltDNS.addLongIdentifier("subjectAlternativeNameDNS", true);
1481    genCertSubjectAltDNS.addLongIdentifier("subject-alt-name-dns", true);
1482    genCertSubjectAltDNS.addLongIdentifier("subjectAltNameDNS", true);
1483    genCertSubjectAltDNS.addLongIdentifier("subject-alternative-dns", true);
1484    genCertSubjectAltDNS.addLongIdentifier("subjectAlternativeDNS", true);
1485    genCertSubjectAltDNS.addLongIdentifier("subject-alt-dns", true);
1486    genCertSubjectAltDNS.addLongIdentifier("subjectAltDNS", true);
1487    genCertSubjectAltDNS.addLongIdentifier("san-dns", true);
1488    genCertSubjectAltDNS.addLongIdentifier("sanDNS", true);
1489    genCertParser.addArgument(genCertSubjectAltDNS);
1490
1491    final StringArgument genCertSubjectAltIP = new StringArgument(null,
1492         "subject-alternative-name-ip-address", false, 0,
1493         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1494         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_IP_DESC.get());
1495    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeNameIPAddress",
1496         true);
1497    genCertSubjectAltIP.addLongIdentifier("subject-alternative-name-ip", true);
1498    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeNameIP", true);
1499    genCertSubjectAltIP.addLongIdentifier("subject-alt-name-ip-address", true);
1500    genCertSubjectAltIP.addLongIdentifier("subjectAltNameIPAddress", true);
1501    genCertSubjectAltIP.addLongIdentifier("subject-alt-name-ip", true);
1502    genCertSubjectAltIP.addLongIdentifier("subjectAltNameIP", true);
1503    genCertSubjectAltIP.addLongIdentifier("subject-alternative-ip-address",
1504         true);
1505    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeIPAddress", true);
1506    genCertSubjectAltIP.addLongIdentifier("subject-alternative-ip", true);
1507    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeIP", true);
1508    genCertSubjectAltIP.addLongIdentifier("subject-alt-ip-address", true);
1509    genCertSubjectAltIP.addLongIdentifier("subjectAltIPAddress", true);
1510    genCertSubjectAltIP.addLongIdentifier("subject-alt-ip", true);
1511    genCertSubjectAltIP.addLongIdentifier("subjectAltIP", true);
1512    genCertSubjectAltIP.addLongIdentifier("san-ip-address", true);
1513    genCertSubjectAltIP.addLongIdentifier("sanIPAddress", true);
1514    genCertSubjectAltIP.addLongIdentifier("san-ip", true);
1515    genCertSubjectAltIP.addLongIdentifier("sanIP", true);
1516    genCertSubjectAltIP.addValueValidator(
1517         new IPAddressArgumentValueValidator(true, true));
1518    genCertParser.addArgument(genCertSubjectAltIP);
1519
1520    final StringArgument genCertSubjectAltEmail = new StringArgument(null,
1521         "subject-alternative-name-email-address", false, 0,
1522         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1523         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_EMAIL_DESC.get());
1524    genCertSubjectAltEmail.addLongIdentifier(
1525         "subjectAlternativeNameEmailAddress", true);
1526    genCertSubjectAltEmail.addLongIdentifier("subject-alternative-name-email",
1527         true);
1528    genCertSubjectAltEmail.addLongIdentifier("subjectAlternativeNameEmail",
1529         true);
1530    genCertSubjectAltEmail.addLongIdentifier("subject-alt-name-email-address",
1531         true);
1532    genCertSubjectAltEmail.addLongIdentifier("subjectAltNameEmailAddress",
1533         true);
1534    genCertSubjectAltEmail.addLongIdentifier("subject-alt-name-email", true);
1535    genCertSubjectAltEmail.addLongIdentifier("subjectAltNameEmail", true);
1536    genCertSubjectAltEmail.addLongIdentifier(
1537         "subject-alternative-email-address", true);
1538    genCertSubjectAltEmail.addLongIdentifier("subjectAlternativeEmailAddress",
1539         true);
1540    genCertSubjectAltEmail.addLongIdentifier("subject-alternative-email", true);
1541    genCertSubjectAltEmail.addLongIdentifier("subjectAlternativeEmail", true);
1542    genCertSubjectAltEmail.addLongIdentifier("subject-alt-email-address", true);
1543    genCertSubjectAltEmail.addLongIdentifier("subjectAltEmailAddress", true);
1544    genCertSubjectAltEmail.addLongIdentifier("subject-alt-email", true);
1545    genCertSubjectAltEmail.addLongIdentifier("subjectAltEmail", true);
1546    genCertSubjectAltEmail.addLongIdentifier("san-email-address", true);
1547    genCertSubjectAltEmail.addLongIdentifier("sanEmailAddress", true);
1548    genCertSubjectAltEmail.addLongIdentifier("san-email", true);
1549    genCertSubjectAltEmail.addLongIdentifier("sanEmail", true);
1550    genCertParser.addArgument(genCertSubjectAltEmail);
1551
1552    final StringArgument genCertSubjectAltURI = new StringArgument(null,
1553         "subject-alternative-name-uri", false, 0,
1554         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
1555         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_URI_DESC.get());
1556    genCertSubjectAltURI.addLongIdentifier("subjectAlternativeNameURI", true);
1557    genCertSubjectAltURI.addLongIdentifier("subject-alt-name-uri", true);
1558    genCertSubjectAltURI.addLongIdentifier("subjectAltNameURI", true);
1559    genCertSubjectAltURI.addLongIdentifier("subject-alternative-uri", true);
1560    genCertSubjectAltURI.addLongIdentifier("subjectAlternativeURI", true);
1561    genCertSubjectAltURI.addLongIdentifier("subject-alt-uri", true);
1562    genCertSubjectAltURI.addLongIdentifier("subjectAltURI", true);
1563    genCertSubjectAltURI.addLongIdentifier("san-uri", true);
1564    genCertSubjectAltURI.addLongIdentifier("sanURI", true);
1565    genCertParser.addArgument(genCertSubjectAltURI);
1566
1567    final StringArgument genCertSubjectAltOID = new StringArgument(null,
1568         "subject-alternative-name-oid", false, 0,
1569         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
1570         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_OID_DESC.get());
1571    genCertSubjectAltOID.addLongIdentifier("subjectAlternativeNameOID", true);
1572    genCertSubjectAltOID.addLongIdentifier("subject-alt-name-oid", true);
1573    genCertSubjectAltOID.addLongIdentifier("subjectAltNameOID", true);
1574    genCertSubjectAltOID.addLongIdentifier("subject-alternative-oid", true);
1575    genCertSubjectAltOID.addLongIdentifier("subjectAlternativeOID", true);
1576    genCertSubjectAltOID.addLongIdentifier("subject-alt-oid", true);
1577    genCertSubjectAltOID.addLongIdentifier("subjectAltOID", true);
1578    genCertSubjectAltOID.addLongIdentifier("san-oid", true);
1579    genCertSubjectAltOID.addLongIdentifier("sanOID", true);
1580    genCertSubjectAltOID.addValueValidator(new OIDArgumentValueValidator(true));
1581    genCertParser.addArgument(genCertSubjectAltOID);
1582
1583    final BooleanValueArgument genCertBasicConstraintsIsCA =
1584         new BooleanValueArgument(null, "basic-constraints-is-ca", false, null,
1585              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_IS_CA_DESC.get());
1586    genCertBasicConstraintsIsCA.addLongIdentifier("basicConstraintsIsCA", true);
1587    genCertBasicConstraintsIsCA.addLongIdentifier("bc-is-ca", true);
1588    genCertBasicConstraintsIsCA.addLongIdentifier("bcIsCA", true);
1589    genCertParser.addArgument(genCertBasicConstraintsIsCA);
1590
1591    final IntegerArgument genCertBasicConstraintsPathLength =
1592         new IntegerArgument(null, "basic-constraints-maximum-path-length",
1593              false, 1, null,
1594              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_PATH_LENGTH_DESC.get(), 1,
1595              Integer.MAX_VALUE);
1596    genCertBasicConstraintsPathLength.addLongIdentifier(
1597         "basicConstraintsMaximumPathLength", true);
1598    genCertBasicConstraintsPathLength.addLongIdentifier(
1599         "basic-constraints-max-path-length", true);
1600    genCertBasicConstraintsPathLength.addLongIdentifier(
1601         "basicConstraintsMaxPathLength", true);
1602    genCertBasicConstraintsPathLength.addLongIdentifier(
1603         "basic-constraints-path-length", true);
1604    genCertBasicConstraintsPathLength.addLongIdentifier(
1605         "basicConstraintsPathLength", true);
1606    genCertBasicConstraintsPathLength.addLongIdentifier(
1607         "bc-maximum-path-length", true);
1608    genCertBasicConstraintsPathLength.addLongIdentifier("bcMaximumPathLength",
1609         true);
1610    genCertBasicConstraintsPathLength.addLongIdentifier("bc-max-path-length",
1611         true);
1612    genCertBasicConstraintsPathLength.addLongIdentifier("bcMaxPathLength",
1613         true);
1614    genCertBasicConstraintsPathLength.addLongIdentifier("bc-path-length", true);
1615    genCertBasicConstraintsPathLength.addLongIdentifier("bcPathLength", true);
1616    genCertParser.addArgument(genCertBasicConstraintsPathLength);
1617
1618    final StringArgument genCertKeyUsage = new StringArgument(null, "key-usage",
1619         false, 0, null, INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KU_DESC.get());
1620    genCertKeyUsage.addLongIdentifier("keyUsage", true);
1621    genCertParser.addArgument(genCertKeyUsage);
1622
1623    final StringArgument genCertExtendedKeyUsage = new StringArgument(null,
1624         "extended-key-usage", false, 0, null,
1625         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_EKU_DESC.get());
1626    genCertExtendedKeyUsage.addLongIdentifier("extendedKeyUsage", true);
1627    genCertParser.addArgument(genCertExtendedKeyUsage);
1628
1629    final StringArgument genCertExtension = new StringArgument(null,
1630         "extension", false, 0, null,
1631         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_EXT_DESC.get());
1632    genCertExtension.addLongIdentifier("ext", true);
1633    genCertParser.addArgument(genCertExtension);
1634
1635    final BooleanArgument genCertDisplayCommand = new BooleanArgument(null,
1636         "display-keytool-command", 1,
1637         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_DISPLAY_COMMAND_DESC.get());
1638    genCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
1639    genCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
1640    genCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
1641    genCertParser.addArgument(genCertDisplayCommand);
1642
1643    genCertParser.addRequiredArgumentSet(genCertKeystorePassword,
1644         genCertKeystorePasswordFile, genCertPromptForKeystorePassword);
1645    genCertParser.addExclusiveArgumentSet(genCertKeystorePassword,
1646         genCertKeystorePasswordFile, genCertPromptForKeystorePassword);
1647    genCertParser.addExclusiveArgumentSet(genCertPKPassword,
1648         genCertPKPasswordFile, genCertPromptForPKPassword);
1649    genCertParser.addExclusiveArgumentSet(genCertReplace, genCertKeyAlgorithm);
1650    genCertParser.addExclusiveArgumentSet(genCertReplace, genCertKeySizeBits);
1651    genCertParser.addExclusiveArgumentSet(genCertReplace,
1652         genCertSignatureAlgorithm);
1653    genCertParser.addDependentArgumentSet(genCertBasicConstraintsPathLength,
1654         genCertBasicConstraintsIsCA);
1655
1656    final LinkedHashMap<String[],String> genCertExamples =
1657         new LinkedHashMap<>(4);
1658    genCertExamples.put(
1659         new String[]
1660         {
1661           "generate-self-signed-certificate",
1662           "--keystore", getPlatformSpecificPath("config", "keystore"),
1663           "--keystore-password-file",
1664                getPlatformSpecificPath("config", "keystore.pin"),
1665           "--alias", "server-cert",
1666           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US"
1667         },
1668         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_1.get());
1669    genCertExamples.put(
1670         new String[]
1671         {
1672           "generate-self-signed-certificate",
1673           "--keystore", getPlatformSpecificPath("config", "keystore"),
1674           "--keystore-password-file",
1675                getPlatformSpecificPath("config", "keystore.pin"),
1676           "--alias", "server-cert",
1677           "--replace-existing-certificate",
1678           "--inherit-extensions"
1679         },
1680         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_2.get());
1681    genCertExamples.put(
1682         new String[]
1683         {
1684           "generate-self-signed-certificate",
1685           "--keystore", getPlatformSpecificPath("config", "keystore"),
1686           "--keystore-password-file",
1687                getPlatformSpecificPath("config", "keystore.pin"),
1688           "--alias", "server-cert",
1689           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US",
1690           "--days-valid", "3650",
1691           "--validity-start-time", "20170101000000",
1692           "--key-algorithm", "RSA",
1693           "--key-size-bits", "4096",
1694           "--signature-algorithm", "SHA256withRSA",
1695           "--subject-alternative-name-dns", "ldap1.example.com",
1696           "--subject-alternative-name-dns", "ldap2.example.com",
1697           "--subject-alternative-name-ip-address", "1.2.3.4",
1698           "--subject-alternative-name-ip-address", "1.2.3.5",
1699           "--extended-key-usage", "server-auth",
1700           "--extended-key-usage", "client-auth",
1701           "--display-keytool-command"
1702         },
1703         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_3.get());
1704    genCertExamples.put(
1705         new String[]
1706         {
1707           "generate-self-signed-certificate",
1708           "--keystore", getPlatformSpecificPath("config", "keystore"),
1709           "--keystore-password-file",
1710                getPlatformSpecificPath("config", "keystore.pin"),
1711           "--alias", "ca-cert",
1712           "--subject-dn",
1713                "CN=Example Certification Authority,O=Example Corp,C=US",
1714           "--days-valid", "7300",
1715           "--validity-start-time", "20170101000000",
1716           "--key-algorithm", "EC",
1717           "--key-size-bits", "256",
1718           "--signature-algorithm", "SHA256withECDSA",
1719           "--basic-constraints-is-ca", "true",
1720           "--key-usage", "key-cert-sign",
1721           "--key-usage", "crl-sign",
1722           "--display-keytool-command"
1723         },
1724         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_4.get());
1725
1726    final SubCommand genCertSubCommand = new SubCommand(
1727         "generate-self-signed-certificate",
1728         INFO_MANAGE_CERTS_SC_GEN_CERT_DESC.get(), genCertParser,
1729         genCertExamples);
1730    genCertSubCommand.addName("generateSelfSignedCertificate", true);
1731    genCertSubCommand.addName("generate-certificate", false);
1732    genCertSubCommand.addName("generateCertificate", true);
1733    genCertSubCommand.addName("self-signed-certificate", true);
1734    genCertSubCommand.addName("selfSignedCertificate", true);
1735    genCertSubCommand.addName("selfcert", true);
1736
1737    parser.addSubCommand(genCertSubCommand);
1738
1739
1740    // Define the "generate-certificate-signing-request" subcommand and all of
1741    // its arguments.
1742    final ArgumentParser genCSRParser = new ArgumentParser(
1743         "generate-certificate-signing-request",
1744         INFO_MANAGE_CERTS_SC_GEN_CSR_DESC.get());
1745
1746    final LinkedHashSet<String> genCSROutputFormatAllowedValues =
1747         new LinkedHashSet<>(7);
1748    genCSROutputFormatAllowedValues.add("PEM");
1749    genCSROutputFormatAllowedValues.add("text");
1750    genCSROutputFormatAllowedValues.add("txt");
1751    genCSROutputFormatAllowedValues.add("RFC");
1752    genCSROutputFormatAllowedValues.add("DER");
1753    genCSROutputFormatAllowedValues.add("binary");
1754    genCSROutputFormatAllowedValues.add("bin");
1755    final StringArgument genCSROutputFormat = new StringArgument(null,
1756         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
1757         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_FORMAT_DESC.get(),
1758         genCSROutputFormatAllowedValues, "PEM");
1759    genCSROutputFormat.addLongIdentifier("outputFormat");
1760    genCSRParser.addArgument(genCSROutputFormat);
1761
1762    final FileArgument genCSROutputFile = new FileArgument(null, "output-file",
1763         false, 1, null,
1764         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_OUTPUT_FILE_DESC.get(), false, true,
1765         true, false);
1766    genCSROutputFile.addLongIdentifier("outputFile", true);
1767    genCSROutputFile.addLongIdentifier("filename", true);
1768    genCSROutputFile.addLongIdentifier("file", true);
1769    genCSRParser.addArgument(genCSROutputFile);
1770
1771    final FileArgument genCSRKeystore = new FileArgument(null, "keystore",
1772         true, 1, null, INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_DESC.get(), false,
1773         true,  true, false);
1774    genCSRKeystore.addLongIdentifier("keystore-path", true);
1775    genCSRKeystore.addLongIdentifier("keystorePath", true);
1776    genCSRKeystore.addLongIdentifier("keystore-file", true);
1777    genCSRKeystore.addLongIdentifier("keystoreFile", true);
1778    genCSRParser.addArgument(genCSRKeystore);
1779
1780    final StringArgument genCSRKeystorePassword = new StringArgument(null,
1781         "keystore-password", false, 1,
1782         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1783         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_PW_DESC.get());
1784    genCSRKeystorePassword.addLongIdentifier("keystorePassword", true);
1785    genCSRKeystorePassword.addLongIdentifier("keystore-passphrase", true);
1786    genCSRKeystorePassword.addLongIdentifier("keystorePassphrase", true);
1787    genCSRKeystorePassword.addLongIdentifier("keystore-pin", true);
1788    genCSRKeystorePassword.addLongIdentifier("keystorePIN", true);
1789    genCSRKeystorePassword.addLongIdentifier("storepass", true);
1790    genCSRKeystorePassword.setSensitive(true);
1791    genCSRParser.addArgument(genCSRKeystorePassword);
1792
1793    final FileArgument genCSRKeystorePasswordFile = new FileArgument(null,
1794         "keystore-password-file", false, 1, null,
1795         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_PW_FILE_DESC.get(), true, true,
1796         true, false);
1797    genCSRKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
1798         true);
1799    genCSRKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
1800         true);
1801    genCSRKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
1802         true);
1803    genCSRKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
1804         true);
1805    genCSRKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
1806    genCSRParser.addArgument(genCSRKeystorePasswordFile);
1807
1808    final BooleanArgument genCSRPromptForKeystorePassword =
1809         new BooleanArgument(null, "prompt-for-keystore-password",
1810        INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PROMPT_FOR_KS_PW_DESC.get());
1811    genCSRPromptForKeystorePassword.addLongIdentifier(
1812         "promptForKeystorePassword", true);
1813    genCSRPromptForKeystorePassword.addLongIdentifier(
1814         "prompt-for-keystore-passphrase", true);
1815    genCSRPromptForKeystorePassword.addLongIdentifier(
1816         "promptForKeystorePassphrase", true);
1817    genCSRPromptForKeystorePassword.addLongIdentifier(
1818         "prompt-for-keystore-pin", true);
1819    genCSRPromptForKeystorePassword.addLongIdentifier(
1820         "promptForKeystorePIN", true);
1821    genCSRParser.addArgument(genCSRPromptForKeystorePassword);
1822
1823    final StringArgument genCSRPKPassword = new StringArgument(null,
1824         "private-key-password", false, 1,
1825         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1826         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PK_PW_DESC.get());
1827    genCSRPKPassword.addLongIdentifier("privateKeyPassword", true);
1828    genCSRPKPassword.addLongIdentifier("private-key-passphrase", true);
1829    genCSRPKPassword.addLongIdentifier("privateKeyPassphrase", true);
1830    genCSRPKPassword.addLongIdentifier("private-key-pin", true);
1831    genCSRPKPassword.addLongIdentifier("privateKeyPIN", true);
1832    genCSRPKPassword.addLongIdentifier("key-password", true);
1833    genCSRPKPassword.addLongIdentifier("keyPassword", true);
1834    genCSRPKPassword.addLongIdentifier("key-passphrase", true);
1835    genCSRPKPassword.addLongIdentifier("keyPassphrase", true);
1836    genCSRPKPassword.addLongIdentifier("key-pin", true);
1837    genCSRPKPassword.addLongIdentifier("keyPIN", true);
1838    genCSRPKPassword.addLongIdentifier("keypass", true);
1839    genCSRPKPassword.setSensitive(true);
1840    genCSRParser.addArgument(genCSRPKPassword);
1841
1842    final FileArgument genCSRPKPasswordFile = new FileArgument(null,
1843         "private-key-password-file", false, 1, null,
1844         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PK_PW_FILE_DESC.get(), true, true,
1845         true, false);
1846    genCSRPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
1847    genCSRPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
1848         true);
1849    genCSRPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
1850         true);
1851    genCSRPKPasswordFile.addLongIdentifier("private-key-pin-file",
1852         true);
1853    genCSRPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
1854    genCSRPKPasswordFile.addLongIdentifier("key-password-file", true);
1855    genCSRPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
1856    genCSRPKPasswordFile.addLongIdentifier("key-passphrase-file",
1857         true);
1858    genCSRPKPasswordFile.addLongIdentifier("keyPassphraseFile",
1859         true);
1860    genCSRPKPasswordFile.addLongIdentifier("key-pin-file",
1861         true);
1862    genCSRPKPasswordFile.addLongIdentifier("keyPINFile", true);
1863    genCSRParser.addArgument(genCSRPKPasswordFile);
1864
1865    final BooleanArgument genCSRPromptForPKPassword =
1866         new BooleanArgument(null, "prompt-for-private-key-password",
1867        INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PROMPT_FOR_PK_PW_DESC.get());
1868    genCSRPromptForPKPassword.addLongIdentifier(
1869         "promptForPrivateKeyPassword", true);
1870    genCSRPromptForPKPassword.addLongIdentifier(
1871         "prompt-for-private-key-passphrase", true);
1872    genCSRPromptForPKPassword.addLongIdentifier(
1873         "promptForPrivateKeyPassphrase", true);
1874    genCSRPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
1875         true);
1876    genCSRPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
1877         true);
1878    genCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
1879         true);
1880    genCSRPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
1881         true);
1882    genCSRPromptForPKPassword.addLongIdentifier(
1883         "prompt-for-key-passphrase", true);
1884    genCSRPromptForPKPassword.addLongIdentifier(
1885         "promptForKeyPassphrase", true);
1886    genCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
1887    genCSRPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
1888    genCSRParser.addArgument(genCSRPromptForPKPassword);
1889
1890    final LinkedHashSet<String> genCSRKeystoreTypeAllowedValues =
1891         new LinkedHashSet<>(2);
1892    genCSRKeystoreTypeAllowedValues.add("jks");
1893    genCSRKeystoreTypeAllowedValues.add("pkcs12");
1894    genCSRKeystoreTypeAllowedValues.add("pkcs 12");
1895    genCSRKeystoreTypeAllowedValues.add("pkcs#12");
1896    genCSRKeystoreTypeAllowedValues.add("pkcs #12");
1897    final StringArgument genCSRKeystoreType = new StringArgument(null,
1898         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
1899         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_TYPE_DESC.get(),
1900         genCSRKeystoreTypeAllowedValues);
1901    genCSRKeystoreType.addLongIdentifier("keystoreType", true);
1902    genCSRKeystoreType.addLongIdentifier("storetype", true);
1903    genCSRParser.addArgument(genCSRKeystoreType);
1904
1905    final StringArgument genCSRAlias = new StringArgument(null, "alias",
1906         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
1907         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_ALIAS_DESC.get());
1908    genCSRAlias.addLongIdentifier("nickname", true);
1909    genCSRParser.addArgument(genCSRAlias);
1910
1911    final BooleanArgument genCSRReplace = new BooleanArgument(null,
1912         "use-existing-key-pair", 1,
1913         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_REPLACE_DESC.get());
1914    genCSRReplace.addLongIdentifier("use-existing-keypair", true);
1915    genCSRReplace.addLongIdentifier("useExistingKeyPair", true);
1916    genCSRReplace.addLongIdentifier("replace-existing-certificate", true);
1917    genCSRReplace.addLongIdentifier("replaceExistingCertificate", true);
1918    genCSRReplace.addLongIdentifier("replace-certificate", true);
1919    genCSRReplace.addLongIdentifier("replaceCertificate", true);
1920    genCSRReplace.addLongIdentifier("replace-existing", true);
1921    genCSRReplace.addLongIdentifier("replaceExisting", true);
1922    genCSRReplace.addLongIdentifier("replace", true);
1923    genCSRParser.addArgument(genCSRReplace);
1924
1925    final DNArgument genCSRSubjectDN = new DNArgument(null, "subject-dn",
1926         false, 1, null,
1927         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SUBJECT_DN_DESC.get());
1928    genCSRSubjectDN.addLongIdentifier("subjectDN", true);
1929    genCSRSubjectDN.addLongIdentifier("subject", true);
1930    genCSRSubjectDN.addLongIdentifier("dname", true);
1931    genCSRParser.addArgument(genCSRSubjectDN);
1932
1933    final StringArgument genCSRKeyAlgorithm = new StringArgument(null,
1934         "key-algorithm", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1935         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KEY_ALGORITHM_DESC.get());
1936    genCSRKeyAlgorithm.addLongIdentifier("keyAlgorithm", true);
1937    genCSRKeyAlgorithm.addLongIdentifier("key-alg", true);
1938    genCSRKeyAlgorithm.addLongIdentifier("keyAlg", true);
1939    genCSRParser.addArgument(genCSRKeyAlgorithm);
1940
1941    final IntegerArgument genCSRKeySizeBits = new IntegerArgument(null,
1942         "key-size-bits", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_BITS.get(),
1943         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KEY_ALGORITHM_DESC.get(), 1,
1944         Integer.MAX_VALUE);
1945    genCSRKeySizeBits.addLongIdentifier("keySizeBits", true);
1946    genCSRKeySizeBits.addLongIdentifier("key-length-bits", true);
1947    genCSRKeySizeBits.addLongIdentifier("keyLengthBits", true);
1948    genCSRKeySizeBits.addLongIdentifier("key-size", true);
1949    genCSRKeySizeBits.addLongIdentifier("keySize", true);
1950    genCSRKeySizeBits.addLongIdentifier("key-length", true);
1951    genCSRKeySizeBits.addLongIdentifier("keyLength", true);
1952    genCSRParser.addArgument(genCSRKeySizeBits);
1953
1954    final StringArgument genCSRSignatureAlgorithm = new StringArgument(null,
1955         "signature-algorithm", false, 1,
1956         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1957         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SIG_ALG_DESC.get());
1958    genCSRSignatureAlgorithm.addLongIdentifier("signatureAlgorithm", true);
1959    genCSRSignatureAlgorithm.addLongIdentifier("signature-alg", true);
1960    genCSRSignatureAlgorithm.addLongIdentifier("signatureAlg", true);
1961    genCSRSignatureAlgorithm.addLongIdentifier("sig-alg", true);
1962    genCSRSignatureAlgorithm.addLongIdentifier("sigAlg", true);
1963    genCSRParser.addArgument(genCSRSignatureAlgorithm);
1964
1965    final BooleanArgument genCSRInheritExtensions = new BooleanArgument(null,
1966         "inherit-extensions", 1,
1967         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_INHERIT_EXT_DESC.get());
1968    genCSRInheritExtensions.addLongIdentifier("inheritExtensions", true);
1969    genCSRParser.addArgument(genCSRInheritExtensions);
1970
1971    final StringArgument genCSRSubjectAltDNS = new StringArgument(null,
1972         "subject-alternative-name-dns", false, 0,
1973         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1974         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_DNS_DESC.get());
1975    genCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeNameDNS", true);
1976    genCSRSubjectAltDNS.addLongIdentifier("subject-alt-name-dns", true);
1977    genCSRSubjectAltDNS.addLongIdentifier("subjectAltNameDNS", true);
1978    genCSRSubjectAltDNS.addLongIdentifier("subject-alternative-dns", true);
1979    genCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeDNS", true);
1980    genCSRSubjectAltDNS.addLongIdentifier("subject-alt-dns", true);
1981    genCSRSubjectAltDNS.addLongIdentifier("subjectAltDNS", true);
1982    genCSRSubjectAltDNS.addLongIdentifier("san-dns", true);
1983    genCSRSubjectAltDNS.addLongIdentifier("sanDNS", true);
1984    genCSRParser.addArgument(genCSRSubjectAltDNS);
1985
1986    final StringArgument genCSRSubjectAltIP = new StringArgument(null,
1987         "subject-alternative-name-ip-address", false, 0,
1988         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1989         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_IP_DESC.get());
1990    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIPAddress",
1991         true);
1992    genCSRSubjectAltIP.addLongIdentifier("subject-alternative-name-ip", true);
1993    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIP", true);
1994    genCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip-address", true);
1995    genCSRSubjectAltIP.addLongIdentifier("subjectAltNameIPAddress", true);
1996    genCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip", true);
1997    genCSRSubjectAltIP.addLongIdentifier("subjectAltNameIP", true);
1998    genCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip-address",
1999         true);
2000    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIPAddress", true);
2001    genCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip", true);
2002    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIP", true);
2003    genCSRSubjectAltIP.addLongIdentifier("subject-alt-ip-address", true);
2004    genCSRSubjectAltIP.addLongIdentifier("subjectAltIPAddress", true);
2005    genCSRSubjectAltIP.addLongIdentifier("subject-alt-ip", true);
2006    genCSRSubjectAltIP.addLongIdentifier("subjectAltIP", true);
2007    genCSRSubjectAltIP.addLongIdentifier("san-ip-address", true);
2008    genCSRSubjectAltIP.addLongIdentifier("sanIPAddress", true);
2009    genCSRSubjectAltIP.addLongIdentifier("san-ip", true);
2010    genCSRSubjectAltIP.addLongIdentifier("sanIP", true);
2011    genCSRSubjectAltIP.addValueValidator(
2012         new IPAddressArgumentValueValidator(true, true));
2013    genCSRParser.addArgument(genCSRSubjectAltIP);
2014
2015    final StringArgument genCSRSubjectAltEmail = new StringArgument(null,
2016         "subject-alternative-name-email-address", false, 0,
2017         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2018         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_EMAIL_DESC.get());
2019    genCSRSubjectAltEmail.addLongIdentifier(
2020         "subjectAlternativeNameEmailAddress", true);
2021    genCSRSubjectAltEmail.addLongIdentifier("subject-alternative-name-email",
2022         true);
2023    genCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeNameEmail",
2024         true);
2025    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email-address",
2026         true);
2027    genCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmailAddress",
2028         true);
2029    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email", true);
2030    genCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmail", true);
2031    genCSRSubjectAltEmail.addLongIdentifier(
2032         "subject-alternative-email-address", true);
2033    genCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmailAddress",
2034         true);
2035    genCSRSubjectAltEmail.addLongIdentifier("subject-alternative-email", true);
2036    genCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmail", true);
2037    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-email-address", true);
2038    genCSRSubjectAltEmail.addLongIdentifier("subjectAltEmailAddress", true);
2039    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-email", true);
2040    genCSRSubjectAltEmail.addLongIdentifier("subjectAltEmail", true);
2041    genCSRSubjectAltEmail.addLongIdentifier("san-email-address", true);
2042    genCSRSubjectAltEmail.addLongIdentifier("sanEmailAddress", true);
2043    genCSRSubjectAltEmail.addLongIdentifier("san-email", true);
2044    genCSRSubjectAltEmail.addLongIdentifier("sanEmail", true);
2045    genCSRParser.addArgument(genCSRSubjectAltEmail);
2046
2047    final StringArgument genCSRSubjectAltURI = new StringArgument(null,
2048         "subject-alternative-name-uri", false, 0,
2049         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
2050         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_URI_DESC.get());
2051    genCSRSubjectAltURI.addLongIdentifier("subjectAlternativeNameURI", true);
2052    genCSRSubjectAltURI.addLongIdentifier("subject-alt-name-uri", true);
2053    genCSRSubjectAltURI.addLongIdentifier("subjectAltNameURI", true);
2054    genCSRSubjectAltURI.addLongIdentifier("subject-alternative-uri", true);
2055    genCSRSubjectAltURI.addLongIdentifier("subjectAlternativeURI", true);
2056    genCSRSubjectAltURI.addLongIdentifier("subject-alt-uri", true);
2057    genCSRSubjectAltURI.addLongIdentifier("subjectAltURI", true);
2058    genCSRSubjectAltURI.addLongIdentifier("san-uri", true);
2059    genCSRSubjectAltURI.addLongIdentifier("sanURI", true);
2060    genCSRParser.addArgument(genCSRSubjectAltURI);
2061
2062    final StringArgument genCSRSubjectAltOID = new StringArgument(null,
2063         "subject-alternative-name-oid", false, 0,
2064         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
2065         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_OID_DESC.get());
2066    genCSRSubjectAltOID.addLongIdentifier("subjectAlternativeNameOID", true);
2067    genCSRSubjectAltOID.addLongIdentifier("subject-alt-name-oid", true);
2068    genCSRSubjectAltOID.addLongIdentifier("subjectAltNameOID", true);
2069    genCSRSubjectAltOID.addLongIdentifier("subject-alternative-oid", true);
2070    genCSRSubjectAltOID.addLongIdentifier("subjectAlternativeOID", true);
2071    genCSRSubjectAltOID.addLongIdentifier("subject-alt-oid", true);
2072    genCSRSubjectAltOID.addLongIdentifier("subjectAltOID", true);
2073    genCSRSubjectAltOID.addLongIdentifier("san-oid", true);
2074    genCSRSubjectAltOID.addLongIdentifier("sanOID", true);
2075    genCSRSubjectAltOID.addValueValidator(new OIDArgumentValueValidator(true));
2076    genCSRParser.addArgument(genCSRSubjectAltOID);
2077
2078    final BooleanValueArgument genCSRBasicConstraintsIsCA =
2079         new BooleanValueArgument(null, "basic-constraints-is-ca", false, null,
2080              INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_BC_IS_CA_DESC.get());
2081    genCSRBasicConstraintsIsCA.addLongIdentifier("basicConstraintsIsCA", true);
2082    genCSRBasicConstraintsIsCA.addLongIdentifier("bc-is-ca", true);
2083    genCSRBasicConstraintsIsCA.addLongIdentifier("bcIsCA", true);
2084    genCSRParser.addArgument(genCSRBasicConstraintsIsCA);
2085
2086    final IntegerArgument genCSRBasicConstraintsPathLength =
2087         new IntegerArgument(null, "basic-constraints-maximum-path-length",
2088              false, 1, null,
2089              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_PATH_LENGTH_DESC.get(), 1,
2090              Integer.MAX_VALUE);
2091    genCSRBasicConstraintsPathLength.addLongIdentifier(
2092         "basicConstraintsMaximumPathLength", true);
2093    genCSRBasicConstraintsPathLength.addLongIdentifier(
2094         "basic-constraints-max-path-length", true);
2095    genCSRBasicConstraintsPathLength.addLongIdentifier(
2096         "basicConstraintsMaxPathLength", true);
2097    genCSRBasicConstraintsPathLength.addLongIdentifier(
2098         "basic-constraints-path-length", true);
2099    genCSRBasicConstraintsPathLength.addLongIdentifier(
2100         "basicConstraintsPathLength", true);
2101    genCSRBasicConstraintsPathLength.addLongIdentifier(
2102         "bc-maximum-path-length", true);
2103    genCSRBasicConstraintsPathLength.addLongIdentifier("bcMaximumPathLength",
2104         true);
2105    genCSRBasicConstraintsPathLength.addLongIdentifier("bc-max-path-length",
2106         true);
2107    genCSRBasicConstraintsPathLength.addLongIdentifier("bcMaxPathLength",
2108         true);
2109    genCSRBasicConstraintsPathLength.addLongIdentifier("bc-path-length", true);
2110    genCSRBasicConstraintsPathLength.addLongIdentifier("bcPathLength", true);
2111    genCSRParser.addArgument(genCSRBasicConstraintsPathLength);
2112
2113    final StringArgument genCSRKeyUsage = new StringArgument(null, "key-usage",
2114         false, 0, null, INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KU_DESC.get());
2115    genCSRKeyUsage.addLongIdentifier("keyUsage", true);
2116    genCSRParser.addArgument(genCSRKeyUsage);
2117
2118    final StringArgument genCSRExtendedKeyUsage = new StringArgument(null,
2119         "extended-key-usage", false, 0, null,
2120         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_EKU_DESC.get());
2121    genCSRExtendedKeyUsage.addLongIdentifier("extendedKeyUsage", true);
2122    genCSRParser.addArgument(genCSRExtendedKeyUsage);
2123
2124    final StringArgument genCSRExtension = new StringArgument(null,
2125         "extension", false, 0, null,
2126         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_EXT_DESC.get());
2127    genCSRExtension.addLongIdentifier("ext", true);
2128    genCSRParser.addArgument(genCSRExtension);
2129
2130    final BooleanArgument genCSRDisplayCommand = new BooleanArgument(null,
2131         "display-keytool-command", 1,
2132         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_DISPLAY_COMMAND_DESC.get());
2133    genCSRDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
2134    genCSRDisplayCommand.addLongIdentifier("show-keytool-command", true);
2135    genCSRDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
2136    genCSRParser.addArgument(genCSRDisplayCommand);
2137
2138    genCSRParser.addRequiredArgumentSet(genCSRKeystorePassword,
2139         genCSRKeystorePasswordFile, genCSRPromptForKeystorePassword);
2140    genCSRParser.addExclusiveArgumentSet(genCSRKeystorePassword,
2141         genCSRKeystorePasswordFile, genCSRPromptForKeystorePassword);
2142    genCSRParser.addExclusiveArgumentSet(genCSRPKPassword,
2143         genCSRPKPasswordFile, genCSRPromptForPKPassword);
2144    genCSRParser.addExclusiveArgumentSet(genCSRReplace, genCSRKeyAlgorithm);
2145    genCSRParser.addExclusiveArgumentSet(genCSRReplace, genCSRKeySizeBits);
2146    genCSRParser.addExclusiveArgumentSet(genCSRReplace,
2147         genCSRSignatureAlgorithm);
2148    genCSRParser.addDependentArgumentSet(genCSRBasicConstraintsPathLength,
2149         genCSRBasicConstraintsIsCA);
2150
2151    final LinkedHashMap<String[],String> genCSRExamples =
2152         new LinkedHashMap<>(3);
2153    genCSRExamples.put(
2154         new String[]
2155         {
2156           "generate-certificate-signing-request",
2157           "--keystore", getPlatformSpecificPath("config", "keystore"),
2158           "--keystore-password-file",
2159                getPlatformSpecificPath("config", "keystore.pin"),
2160           "--alias", "server-cert",
2161           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US"
2162         },
2163         INFO_MANAGE_CERTS_SC_GEN_CSR_EXAMPLE_1.get());
2164    genCSRExamples.put(
2165         new String[]
2166         {
2167           "generate-certificate-signing-request",
2168           "--keystore", getPlatformSpecificPath("config", "keystore"),
2169           "--keystore-password-file",
2170                getPlatformSpecificPath("config", "keystore.pin"),
2171           "--alias", "server-cert",
2172           "--use-existing-key-pair",
2173           "--inherit-extensions",
2174           "--output-file", "server-cert.csr"
2175         },
2176         INFO_MANAGE_CERTS_SC_GEN_CSR_EXAMPLE_2.get());
2177    genCSRExamples.put(
2178         new String[]
2179         {
2180           "generate-certificate-signing-request",
2181           "--keystore", getPlatformSpecificPath("config", "keystore"),
2182           "--keystore-password-file",
2183                getPlatformSpecificPath("config", "keystore.pin"),
2184           "--alias", "server-cert",
2185           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US",
2186           "--key-algorithm", "EC",
2187           "--key-size-bits", "256",
2188           "--signature-algorithm", "SHA256withECDSA",
2189           "--subject-alternative-name-dns", "ldap1.example.com",
2190           "--subject-alternative-name-dns", "ldap2.example.com",
2191           "--subject-alternative-name-ip-address", "1.2.3.4",
2192           "--subject-alternative-name-ip-address", "1.2.3.5",
2193           "--extended-key-usage", "server-auth",
2194           "--extended-key-usage", "client-auth",
2195           "--output-file", "server-cert.csr",
2196           "--display-keytool-command"
2197         },
2198         INFO_MANAGE_CERTS_SC_GEN_CSR_EXAMPLE_3.get());
2199
2200    final SubCommand genCSRSubCommand = new SubCommand(
2201         "generate-certificate-signing-request",
2202         INFO_MANAGE_CERTS_SC_GEN_CSR_DESC.get(), genCSRParser,
2203         genCSRExamples);
2204    genCSRSubCommand.addName("generateCertificateSigningRequest", true);
2205    genCSRSubCommand.addName("generate-certificate-request", false);
2206    genCSRSubCommand.addName("generateCertificateRequest", true);
2207    genCSRSubCommand.addName("generate-csr", true);
2208    genCSRSubCommand.addName("generateCSR", true);
2209    genCSRSubCommand.addName("certificate-signing-request", true);
2210    genCSRSubCommand.addName("certificateSigningRequest", true);
2211    genCSRSubCommand.addName("csr", true);
2212    genCSRSubCommand.addName("certreq", true);
2213
2214    parser.addSubCommand(genCSRSubCommand);
2215
2216
2217    // Define the "sign-certificate-signing-request" subcommand and all of its
2218    // arguments.
2219    final ArgumentParser signCSRParser = new ArgumentParser(
2220         "sign-certificate-signing-request",
2221         INFO_MANAGE_CERTS_SC_SIGN_CSR_DESC.get());
2222
2223    final FileArgument signCSRInputFile = new FileArgument(null,
2224         "request-input-file", true, 1, null,
2225         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_INPUT_FILE_DESC.get(), true, true,
2226         true, false);
2227    signCSRInputFile.addLongIdentifier("requestInputFile", true);
2228    signCSRInputFile.addLongIdentifier("certificate-signing-request", true);
2229    signCSRInputFile.addLongIdentifier("certificateSigningRequest", true);
2230    signCSRInputFile.addLongIdentifier("input-file", false);
2231    signCSRInputFile.addLongIdentifier("inputFile", true);
2232    signCSRInputFile.addLongIdentifier("csr", true);
2233    signCSRParser.addArgument(signCSRInputFile);
2234
2235    final FileArgument signCSROutputFile = new FileArgument(null,
2236         "certificate-output-file", false, 1, null,
2237         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_OUTPUT_FILE_DESC.get(), false, true,
2238         true, false);
2239    signCSROutputFile.addLongIdentifier("certificateOutputFile", true);
2240    signCSROutputFile.addLongIdentifier("output-file", false);
2241    signCSROutputFile.addLongIdentifier("outputFile", true);
2242    signCSROutputFile.addLongIdentifier("certificate-file", true);
2243    signCSROutputFile.addLongIdentifier("certificateFile", true);
2244    signCSRParser.addArgument(signCSROutputFile);
2245
2246    final LinkedHashSet<String> signCSROutputFormatAllowedValues =
2247         new LinkedHashSet<>(7);
2248    signCSROutputFormatAllowedValues.add("PEM");
2249    signCSROutputFormatAllowedValues.add("text");
2250    signCSROutputFormatAllowedValues.add("txt");
2251    signCSROutputFormatAllowedValues.add("RFC");
2252    signCSROutputFormatAllowedValues.add("DER");
2253    signCSROutputFormatAllowedValues.add("binary");
2254    signCSROutputFormatAllowedValues.add("bin");
2255    final StringArgument signCSROutputFormat = new StringArgument(null,
2256         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
2257         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_FORMAT_DESC.get(),
2258         signCSROutputFormatAllowedValues, "PEM");
2259    signCSROutputFormat.addLongIdentifier("outputFormat");
2260    signCSRParser.addArgument(signCSROutputFormat);
2261
2262    final FileArgument signCSRKeystore = new FileArgument(null, "keystore",
2263         true, 1, null, INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_DESC.get(), true,
2264         true,  true, false);
2265    signCSRKeystore.addLongIdentifier("keystore-path", true);
2266    signCSRKeystore.addLongIdentifier("keystorePath", true);
2267    signCSRKeystore.addLongIdentifier("keystore-file", true);
2268    signCSRKeystore.addLongIdentifier("keystoreFile", true);
2269    signCSRParser.addArgument(signCSRKeystore);
2270
2271    final StringArgument signCSRKeystorePassword = new StringArgument(null,
2272         "keystore-password", false, 1,
2273         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2274         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_PW_DESC.get());
2275    signCSRKeystorePassword.addLongIdentifier("keystorePassword", true);
2276    signCSRKeystorePassword.addLongIdentifier("keystore-passphrase", true);
2277    signCSRKeystorePassword.addLongIdentifier("keystorePassphrase", true);
2278    signCSRKeystorePassword.addLongIdentifier("keystore-pin", true);
2279    signCSRKeystorePassword.addLongIdentifier("keystorePIN", true);
2280    signCSRKeystorePassword.addLongIdentifier("storepass", true);
2281    signCSRKeystorePassword.setSensitive(true);
2282    signCSRParser.addArgument(signCSRKeystorePassword);
2283
2284    final FileArgument signCSRKeystorePasswordFile = new FileArgument(null,
2285         "keystore-password-file", false, 1, null,
2286         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_PW_FILE_DESC.get(), true, true,
2287         true, false);
2288    signCSRKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
2289         true);
2290    signCSRKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
2291         true);
2292    signCSRKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
2293         true);
2294    signCSRKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
2295         true);
2296    signCSRKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
2297    signCSRParser.addArgument(signCSRKeystorePasswordFile);
2298
2299    final BooleanArgument signCSRPromptForKeystorePassword =
2300         new BooleanArgument(null, "prompt-for-keystore-password",
2301        INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PROMPT_FOR_KS_PW_DESC.get());
2302    signCSRPromptForKeystorePassword.addLongIdentifier(
2303         "promptForKeystorePassword", true);
2304    signCSRPromptForKeystorePassword.addLongIdentifier(
2305         "prompt-for-keystore-passphrase", true);
2306    signCSRPromptForKeystorePassword.addLongIdentifier(
2307         "promptForKeystorePassphrase", true);
2308    signCSRPromptForKeystorePassword.addLongIdentifier(
2309         "prompt-for-keystore-pin", true);
2310    signCSRPromptForKeystorePassword.addLongIdentifier(
2311         "promptForKeystorePIN", true);
2312    signCSRParser.addArgument(signCSRPromptForKeystorePassword);
2313
2314    final StringArgument signCSRPKPassword = new StringArgument(null,
2315         "private-key-password", false, 1,
2316         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2317         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PK_PW_DESC.get());
2318    signCSRPKPassword.addLongIdentifier("privateKeyPassword", true);
2319    signCSRPKPassword.addLongIdentifier("private-key-passphrase", true);
2320    signCSRPKPassword.addLongIdentifier("privateKeyPassphrase", true);
2321    signCSRPKPassword.addLongIdentifier("private-key-pin", true);
2322    signCSRPKPassword.addLongIdentifier("privateKeyPIN", true);
2323    signCSRPKPassword.addLongIdentifier("key-password", true);
2324    signCSRPKPassword.addLongIdentifier("keyPassword", true);
2325    signCSRPKPassword.addLongIdentifier("key-passphrase", true);
2326    signCSRPKPassword.addLongIdentifier("keyPassphrase", true);
2327    signCSRPKPassword.addLongIdentifier("key-pin", true);
2328    signCSRPKPassword.addLongIdentifier("keyPIN", true);
2329    signCSRPKPassword.addLongIdentifier("keypass", true);
2330    signCSRPKPassword.setSensitive(true);
2331    signCSRParser.addArgument(signCSRPKPassword);
2332
2333    final FileArgument signCSRPKPasswordFile = new FileArgument(null,
2334         "private-key-password-file", false, 1, null,
2335         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PK_PW_FILE_DESC.get(), true, true,
2336         true, false);
2337    signCSRPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
2338    signCSRPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
2339         true);
2340    signCSRPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
2341         true);
2342    signCSRPKPasswordFile.addLongIdentifier("private-key-pin-file",
2343         true);
2344    signCSRPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
2345    signCSRPKPasswordFile.addLongIdentifier("key-password-file", true);
2346    signCSRPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
2347    signCSRPKPasswordFile.addLongIdentifier("key-passphrase-file",
2348         true);
2349    signCSRPKPasswordFile.addLongIdentifier("keyPassphraseFile",
2350         true);
2351    signCSRPKPasswordFile.addLongIdentifier("key-pin-file",
2352         true);
2353    signCSRPKPasswordFile.addLongIdentifier("keyPINFile", true);
2354    signCSRParser.addArgument(signCSRPKPasswordFile);
2355
2356    final BooleanArgument signCSRPromptForPKPassword =
2357         new BooleanArgument(null, "prompt-for-private-key-password",
2358        INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PROMPT_FOR_PK_PW_DESC.get());
2359    signCSRPromptForPKPassword.addLongIdentifier(
2360         "promptForPrivateKeyPassword", true);
2361    signCSRPromptForPKPassword.addLongIdentifier(
2362         "prompt-for-private-key-passphrase", true);
2363    signCSRPromptForPKPassword.addLongIdentifier(
2364         "promptForPrivateKeyPassphrase", true);
2365    signCSRPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
2366         true);
2367    signCSRPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
2368         true);
2369    signCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
2370         true);
2371    signCSRPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
2372         true);
2373    signCSRPromptForPKPassword.addLongIdentifier(
2374         "prompt-for-key-passphrase", true);
2375    signCSRPromptForPKPassword.addLongIdentifier(
2376         "promptForKeyPassphrase", true);
2377    signCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
2378    signCSRPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
2379    signCSRParser.addArgument(signCSRPromptForPKPassword);
2380
2381    final StringArgument signCSRAlias = new StringArgument(null,
2382         "signing-certificate-alias",
2383         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
2384         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_ALIAS_DESC.get());
2385    signCSRAlias.addLongIdentifier("signingCertificateAlias", true);
2386    signCSRAlias.addLongIdentifier("signing-certificate-nickname", true);
2387    signCSRAlias.addLongIdentifier("signingCertificateNickname", true);
2388    signCSRAlias.addLongIdentifier("alias", true);
2389    signCSRAlias.addLongIdentifier("nickname", true);
2390    signCSRParser.addArgument(signCSRAlias);
2391
2392    final DNArgument signCSRSubjectDN = new DNArgument(null, "subject-dn",
2393         false, 1, null,
2394         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SUBJECT_DN_DESC.get());
2395    signCSRSubjectDN.addLongIdentifier("subjectDN", true);
2396    signCSRSubjectDN.addLongIdentifier("subject", true);
2397    signCSRSubjectDN.addLongIdentifier("dname", true);
2398    signCSRParser.addArgument(signCSRSubjectDN);
2399
2400    final IntegerArgument signCSRDaysValid = new IntegerArgument(null,
2401         "days-valid", false, 1, null,
2402         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_DAYS_VALID_DESC.get(), 1,
2403         Integer.MAX_VALUE);
2404    signCSRDaysValid.addLongIdentifier("daysValid", true);
2405    signCSRDaysValid.addLongIdentifier("validity", true);
2406    signCSRParser.addArgument(signCSRDaysValid);
2407
2408    final TimestampArgument signCSRNotBefore = new TimestampArgument(null,
2409         "validity-start-time", false, 1,
2410         INFO_MANAGE_CERTS_PLACEHOLDER_TIMESTAMP.get(),
2411         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_VALIDITY_START_TIME_DESC.get(
2412              "20180102123456"));
2413    signCSRNotBefore.addLongIdentifier("validityStartTime", true);
2414    signCSRNotBefore.addLongIdentifier("not-before", true);
2415    signCSRNotBefore.addLongIdentifier("notBefore", true);
2416    signCSRParser.addArgument(signCSRNotBefore);
2417
2418    final StringArgument signCSRSignatureAlgorithm = new StringArgument(null,
2419         "signature-algorithm", false, 1,
2420         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2421         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SIG_ALG_DESC.get());
2422    signCSRSignatureAlgorithm.addLongIdentifier("signatureAlgorithm", true);
2423    signCSRSignatureAlgorithm.addLongIdentifier("signature-alg", true);
2424    signCSRSignatureAlgorithm.addLongIdentifier("signatureAlg", true);
2425    signCSRSignatureAlgorithm.addLongIdentifier("sig-alg", true);
2426    signCSRSignatureAlgorithm.addLongIdentifier("sigAlg", true);
2427    signCSRParser.addArgument(signCSRSignatureAlgorithm);
2428
2429    final BooleanArgument signCSRIncludeExtensions = new BooleanArgument(null,
2430         "include-requested-extensions", 1,
2431         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_INCLUDE_EXT_DESC.get());
2432    signCSRIncludeExtensions.addLongIdentifier("includeRequestedExtensions",
2433         true);
2434    signCSRParser.addArgument(signCSRIncludeExtensions);
2435
2436    final StringArgument signCSRSubjectAltDNS = new StringArgument(null,
2437         "subject-alternative-name-dns", false, 0,
2438         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2439         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_DNS_DESC.get());
2440    signCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeNameDNS", true);
2441    signCSRSubjectAltDNS.addLongIdentifier("subject-alt-name-dns", true);
2442    signCSRSubjectAltDNS.addLongIdentifier("subjectAltNameDNS", true);
2443    signCSRSubjectAltDNS.addLongIdentifier("subject-alternative-dns", true);
2444    signCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeDNS", true);
2445    signCSRSubjectAltDNS.addLongIdentifier("subject-alt-dns", true);
2446    signCSRSubjectAltDNS.addLongIdentifier("subjectAltDNS", true);
2447    signCSRSubjectAltDNS.addLongIdentifier("san-dns", true);
2448    signCSRSubjectAltDNS.addLongIdentifier("sanDNS", true);
2449    signCSRParser.addArgument(signCSRSubjectAltDNS);
2450
2451    final StringArgument signCSRSubjectAltIP = new StringArgument(null,
2452         "subject-alternative-name-ip-address", false, 0,
2453         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2454         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_IP_DESC.get());
2455    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIPAddress",
2456         true);
2457    signCSRSubjectAltIP.addLongIdentifier("subject-alternative-name-ip", true);
2458    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIP", true);
2459    signCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip-address", true);
2460    signCSRSubjectAltIP.addLongIdentifier("subjectAltNameIPAddress", true);
2461    signCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip", true);
2462    signCSRSubjectAltIP.addLongIdentifier("subjectAltNameIP", true);
2463    signCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip-address",
2464         true);
2465    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIPAddress", true);
2466    signCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip", true);
2467    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIP", true);
2468    signCSRSubjectAltIP.addLongIdentifier("subject-alt-ip-address", true);
2469    signCSRSubjectAltIP.addLongIdentifier("subjectAltIPAddress", true);
2470    signCSRSubjectAltIP.addLongIdentifier("subject-alt-ip", true);
2471    signCSRSubjectAltIP.addLongIdentifier("subjectAltIP", true);
2472    signCSRSubjectAltIP.addLongIdentifier("san-ip-address", true);
2473    signCSRSubjectAltIP.addLongIdentifier("sanIPAddress", true);
2474    signCSRSubjectAltIP.addLongIdentifier("san-ip", true);
2475    signCSRSubjectAltIP.addLongIdentifier("sanIP", true);
2476    signCSRSubjectAltIP.addValueValidator(
2477         new IPAddressArgumentValueValidator(true, true));
2478    signCSRParser.addArgument(signCSRSubjectAltIP);
2479
2480    final StringArgument signCSRSubjectAltEmail = new StringArgument(null,
2481         "subject-alternative-name-email-address", false, 0,
2482         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2483         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_EMAIL_DESC.get());
2484    signCSRSubjectAltEmail.addLongIdentifier(
2485         "subjectAlternativeNameEmailAddress", true);
2486    signCSRSubjectAltEmail.addLongIdentifier("subject-alternative-name-email",
2487         true);
2488    signCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeNameEmail",
2489         true);
2490    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email-address",
2491         true);
2492    signCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmailAddress",
2493         true);
2494    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email", true);
2495    signCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmail", true);
2496    signCSRSubjectAltEmail.addLongIdentifier(
2497         "subject-alternative-email-address", true);
2498    signCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmailAddress",
2499         true);
2500    signCSRSubjectAltEmail.addLongIdentifier("subject-alternative-email", true);
2501    signCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmail", true);
2502    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-email-address", true);
2503    signCSRSubjectAltEmail.addLongIdentifier("subjectAltEmailAddress", true);
2504    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-email", true);
2505    signCSRSubjectAltEmail.addLongIdentifier("subjectAltEmail", true);
2506    signCSRSubjectAltEmail.addLongIdentifier("san-email-address", true);
2507    signCSRSubjectAltEmail.addLongIdentifier("sanEmailAddress", true);
2508    signCSRSubjectAltEmail.addLongIdentifier("san-email", true);
2509    signCSRSubjectAltEmail.addLongIdentifier("sanEmail", true);
2510    signCSRParser.addArgument(signCSRSubjectAltEmail);
2511
2512    final StringArgument signCSRSubjectAltURI = new StringArgument(null,
2513         "subject-alternative-name-uri", false, 0,
2514         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
2515         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_URI_DESC.get());
2516    signCSRSubjectAltURI.addLongIdentifier("subjectAlternativeNameURI", true);
2517    signCSRSubjectAltURI.addLongIdentifier("subject-alt-name-uri", true);
2518    signCSRSubjectAltURI.addLongIdentifier("subjectAltNameURI", true);
2519    signCSRSubjectAltURI.addLongIdentifier("subject-alternative-uri", true);
2520    signCSRSubjectAltURI.addLongIdentifier("subjectAlternativeURI", true);
2521    signCSRSubjectAltURI.addLongIdentifier("subject-alt-uri", true);
2522    signCSRSubjectAltURI.addLongIdentifier("subjectAltURI", true);
2523    signCSRSubjectAltURI.addLongIdentifier("san-uri", true);
2524    signCSRSubjectAltURI.addLongIdentifier("sanURI", true);
2525    signCSRParser.addArgument(signCSRSubjectAltURI);
2526
2527    final StringArgument signCSRSubjectAltOID = new StringArgument(null,
2528         "subject-alternative-name-oid", false, 0,
2529         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
2530         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_OID_DESC.get());
2531    signCSRSubjectAltOID.addLongIdentifier("subjectAlternativeNameOID", true);
2532    signCSRSubjectAltOID.addLongIdentifier("subject-alt-name-oid", true);
2533    signCSRSubjectAltOID.addLongIdentifier("subjectAltNameOID", true);
2534    signCSRSubjectAltOID.addLongIdentifier("subject-alternative-oid", true);
2535    signCSRSubjectAltOID.addLongIdentifier("subjectAlternativeOID", true);
2536    signCSRSubjectAltOID.addLongIdentifier("subject-alt-oid", true);
2537    signCSRSubjectAltOID.addLongIdentifier("subjectAltOID", true);
2538    signCSRSubjectAltOID.addLongIdentifier("san-oid", true);
2539    signCSRSubjectAltOID.addLongIdentifier("sanOID", true);
2540    signCSRSubjectAltOID.addValueValidator(new OIDArgumentValueValidator(true));
2541    signCSRParser.addArgument(signCSRSubjectAltOID);
2542
2543    final StringArgument signCSRIssuerAltDNS = new StringArgument(null,
2544         "issuer-alternative-name-dns", false, 0,
2545         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2546         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_DNS_DESC.get());
2547    signCSRIssuerAltDNS.addLongIdentifier("issuerAlternativeNameDNS", true);
2548    signCSRIssuerAltDNS.addLongIdentifier("issuer-alt-name-dns", true);
2549    signCSRIssuerAltDNS.addLongIdentifier("issuerAltNameDNS", true);
2550    signCSRIssuerAltDNS.addLongIdentifier("issuer-alternative-dns", true);
2551    signCSRIssuerAltDNS.addLongIdentifier("issuerAlternativeDNS", true);
2552    signCSRIssuerAltDNS.addLongIdentifier("issuer-alt-dns", true);
2553    signCSRIssuerAltDNS.addLongIdentifier("issuerAltDNS", true);
2554    signCSRIssuerAltDNS.addLongIdentifier("ian-dns", true);
2555    signCSRIssuerAltDNS.addLongIdentifier("ianDNS", true);
2556    signCSRParser.addArgument(signCSRIssuerAltDNS);
2557
2558    final StringArgument signCSRIssuerAltIP = new StringArgument(null,
2559         "issuer-alternative-name-ip-address", false, 0,
2560         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2561         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_IP_DESC.get());
2562    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeNameIPAddress",
2563         true);
2564    signCSRIssuerAltIP.addLongIdentifier("issuer-alternative-name-ip", true);
2565    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeNameIP", true);
2566    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-name-ip-address", true);
2567    signCSRIssuerAltIP.addLongIdentifier("issuerAltNameIPAddress", true);
2568    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-name-ip", true);
2569    signCSRIssuerAltIP.addLongIdentifier("issuerAltNameIP", true);
2570    signCSRIssuerAltIP.addLongIdentifier("issuer-alternative-ip-address",
2571         true);
2572    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeIPAddress", true);
2573    signCSRIssuerAltIP.addLongIdentifier("issuer-alternative-ip", true);
2574    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeIP", true);
2575    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-ip-address", true);
2576    signCSRIssuerAltIP.addLongIdentifier("issuerAltIPAddress", true);
2577    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-ip", true);
2578    signCSRIssuerAltIP.addLongIdentifier("issuerAltIP", true);
2579    signCSRIssuerAltIP.addLongIdentifier("ian-ip-address", true);
2580    signCSRIssuerAltIP.addLongIdentifier("ianIPAddress", true);
2581    signCSRIssuerAltIP.addLongIdentifier("ian-ip", true);
2582    signCSRIssuerAltIP.addLongIdentifier("ianIP", true);
2583    signCSRIssuerAltIP.addValueValidator(
2584         new IPAddressArgumentValueValidator(true, true));
2585    signCSRParser.addArgument(signCSRIssuerAltIP);
2586
2587    final StringArgument signCSRIssuerAltEmail = new StringArgument(null,
2588         "issuer-alternative-name-email-address", false, 0,
2589         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2590         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_EMAIL_DESC.get());
2591    signCSRIssuerAltEmail.addLongIdentifier(
2592         "issuerAlternativeNameEmailAddress", true);
2593    signCSRIssuerAltEmail.addLongIdentifier("issuer-alternative-name-email",
2594         true);
2595    signCSRIssuerAltEmail.addLongIdentifier("issuerAlternativeNameEmail",
2596         true);
2597    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-name-email-address",
2598         true);
2599    signCSRIssuerAltEmail.addLongIdentifier("issuerAltNameEmailAddress",
2600         true);
2601    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-name-email", true);
2602    signCSRIssuerAltEmail.addLongIdentifier("issuerAltNameEmail", true);
2603    signCSRIssuerAltEmail.addLongIdentifier(
2604         "issuer-alternative-email-address", true);
2605    signCSRIssuerAltEmail.addLongIdentifier("issuerAlternativeEmailAddress",
2606         true);
2607    signCSRIssuerAltEmail.addLongIdentifier("issuer-alternative-email", true);
2608    signCSRIssuerAltEmail.addLongIdentifier("issuerAlternativeEmail", true);
2609    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-email-address", true);
2610    signCSRIssuerAltEmail.addLongIdentifier("issuerAltEmailAddress", true);
2611    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-email", true);
2612    signCSRIssuerAltEmail.addLongIdentifier("issuerAltEmail", true);
2613    signCSRIssuerAltEmail.addLongIdentifier("ian-email-address", true);
2614    signCSRIssuerAltEmail.addLongIdentifier("ianEmailAddress", true);
2615    signCSRIssuerAltEmail.addLongIdentifier("ian-email", true);
2616    signCSRIssuerAltEmail.addLongIdentifier("ianEmail", true);
2617    signCSRParser.addArgument(signCSRIssuerAltEmail);
2618
2619    final StringArgument signCSRIssuerAltURI = new StringArgument(null,
2620         "issuer-alternative-name-uri", false, 0,
2621         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
2622         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_URI_DESC.get());
2623    signCSRIssuerAltURI.addLongIdentifier("issuerAlternativeNameURI", true);
2624    signCSRIssuerAltURI.addLongIdentifier("issuer-alt-name-uri", true);
2625    signCSRIssuerAltURI.addLongIdentifier("issuerAltNameURI", true);
2626    signCSRIssuerAltURI.addLongIdentifier("issuer-alternative-uri", true);
2627    signCSRIssuerAltURI.addLongIdentifier("issuerAlternativeURI", true);
2628    signCSRIssuerAltURI.addLongIdentifier("issuer-alt-uri", true);
2629    signCSRIssuerAltURI.addLongIdentifier("issuerAltURI", true);
2630    signCSRIssuerAltURI.addLongIdentifier("ian-uri", true);
2631    signCSRIssuerAltURI.addLongIdentifier("ianURI", true);
2632    signCSRParser.addArgument(signCSRIssuerAltURI);
2633
2634    final StringArgument signCSRIssuerAltOID = new StringArgument(null,
2635         "issuer-alternative-name-oid", false, 0,
2636         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
2637         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_OID_DESC.get());
2638    signCSRIssuerAltOID.addLongIdentifier("issuerAlternativeNameOID", true);
2639    signCSRIssuerAltOID.addLongIdentifier("issuer-alt-name-oid", true);
2640    signCSRIssuerAltOID.addLongIdentifier("issuerAltNameOID", true);
2641    signCSRIssuerAltOID.addLongIdentifier("issuer-alternative-oid", true);
2642    signCSRIssuerAltOID.addLongIdentifier("issuerAlternativeOID", true);
2643    signCSRIssuerAltOID.addLongIdentifier("issuer-alt-oid", true);
2644    signCSRIssuerAltOID.addLongIdentifier("issuerAltOID", true);
2645    signCSRIssuerAltOID.addLongIdentifier("ian-oid", true);
2646    signCSRIssuerAltOID.addLongIdentifier("ianOID", true);
2647    signCSRIssuerAltOID.addValueValidator(new OIDArgumentValueValidator(true));
2648    signCSRParser.addArgument(signCSRIssuerAltOID);
2649
2650    final BooleanValueArgument signCSRBasicConstraintsIsCA =
2651         new BooleanValueArgument(null, "basic-constraints-is-ca", false, null,
2652              INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_BC_IS_CA_DESC.get());
2653    signCSRBasicConstraintsIsCA.addLongIdentifier("basicConstraintsIsCA", true);
2654    signCSRBasicConstraintsIsCA.addLongIdentifier("bc-is-ca", true);
2655    signCSRBasicConstraintsIsCA.addLongIdentifier("bcIsCA", true);
2656    signCSRParser.addArgument(signCSRBasicConstraintsIsCA);
2657
2658    final IntegerArgument signCSRBasicConstraintsPathLength =
2659         new IntegerArgument(null, "basic-constraints-maximum-path-length",
2660              false, 1, null,
2661              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_PATH_LENGTH_DESC.get(), 1,
2662              Integer.MAX_VALUE);
2663    signCSRBasicConstraintsPathLength.addLongIdentifier(
2664         "basicConstraintsMaximumPathLength", true);
2665    signCSRBasicConstraintsPathLength.addLongIdentifier(
2666         "basic-constraints-max-path-length", true);
2667    signCSRBasicConstraintsPathLength.addLongIdentifier(
2668         "basicConstraintsMaxPathLength", true);
2669    signCSRBasicConstraintsPathLength.addLongIdentifier(
2670         "basic-constraints-path-length", true);
2671    signCSRBasicConstraintsPathLength.addLongIdentifier(
2672         "basicConstraintsPathLength", true);
2673    signCSRBasicConstraintsPathLength.addLongIdentifier(
2674         "bc-maximum-path-length", true);
2675    signCSRBasicConstraintsPathLength.addLongIdentifier("bcMaximumPathLength",
2676         true);
2677    signCSRBasicConstraintsPathLength.addLongIdentifier("bc-max-path-length",
2678         true);
2679    signCSRBasicConstraintsPathLength.addLongIdentifier("bcMaxPathLength",
2680         true);
2681    signCSRBasicConstraintsPathLength.addLongIdentifier("bc-path-length", true);
2682    signCSRBasicConstraintsPathLength.addLongIdentifier("bcPathLength", true);
2683    signCSRParser.addArgument(signCSRBasicConstraintsPathLength);
2684
2685    final StringArgument signCSRKeyUsage = new StringArgument(null, "key-usage",
2686         false, 0, null, INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KU_DESC.get());
2687    signCSRKeyUsage.addLongIdentifier("keyUsage", true);
2688    signCSRParser.addArgument(signCSRKeyUsage);
2689
2690    final StringArgument signCSRExtendedKeyUsage = new StringArgument(null,
2691         "extended-key-usage", false, 0, null,
2692         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_EKU_DESC.get());
2693    signCSRExtendedKeyUsage.addLongIdentifier("extendedKeyUsage", true);
2694    signCSRParser.addArgument(signCSRExtendedKeyUsage);
2695
2696    final StringArgument signCSRExtension = new StringArgument(null,
2697         "extension", false, 0, null,
2698         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_EXT_DESC.get());
2699    signCSRExtension.addLongIdentifier("ext", true);
2700    signCSRParser.addArgument(signCSRExtension);
2701
2702    final BooleanArgument signCSRNoPrompt = new BooleanArgument(null,
2703         "no-prompt", 1,
2704         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_NO_PROMPT_DESC.get());
2705    signCSRNoPrompt.addLongIdentifier("noPrompt", true);
2706    signCSRParser.addArgument(signCSRNoPrompt);
2707
2708    final BooleanArgument signCSRDisplayCommand = new BooleanArgument(null,
2709         "display-keytool-command", 1,
2710         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_DISPLAY_COMMAND_DESC.get());
2711    signCSRDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
2712    signCSRDisplayCommand.addLongIdentifier("show-keytool-command", true);
2713    signCSRDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
2714    signCSRParser.addArgument(signCSRDisplayCommand);
2715
2716    signCSRParser.addRequiredArgumentSet(signCSRKeystorePassword,
2717         signCSRKeystorePasswordFile, signCSRPromptForKeystorePassword);
2718    signCSRParser.addExclusiveArgumentSet(signCSRKeystorePassword,
2719         signCSRKeystorePasswordFile, signCSRPromptForKeystorePassword);
2720    signCSRParser.addExclusiveArgumentSet(signCSRPKPassword,
2721         signCSRPKPasswordFile, signCSRPromptForPKPassword);
2722    signCSRParser.addDependentArgumentSet(signCSRBasicConstraintsPathLength,
2723         signCSRBasicConstraintsIsCA);
2724
2725    final LinkedHashMap<String[],String> signCSRExamples =
2726         new LinkedHashMap<>(2);
2727    signCSRExamples.put(
2728         new String[]
2729         {
2730           "sign-certificate-signing-request",
2731           "--request-input-file", "server-cert.csr",
2732           "--keystore", getPlatformSpecificPath("config", "keystore"),
2733           "--keystore-password-file",
2734                getPlatformSpecificPath("config", "keystore.pin"),
2735           "--signing-certificate-alias", "ca-cert",
2736           "--include-requested-extensions"
2737         },
2738         INFO_MANAGE_CERTS_SC_SIGN_CSR_EXAMPLE_1.get(
2739              getPlatformSpecificPath("config", "keystore")));
2740    signCSRExamples.put(
2741         new String[]
2742         {
2743           "sign-certificate-signing-request",
2744           "--request-input-file", "server-cert.csr",
2745           "--certificate-output-file", "server-cert.der",
2746           "--output-format", "DER",
2747           "--keystore", getPlatformSpecificPath("config", "keystore"),
2748           "--keystore-password-file",
2749                getPlatformSpecificPath("config", "keystore.pin"),
2750           "--signing-certificate-alias", "ca-cert",
2751           "--days-valid", "730",
2752           "--validity-start-time", "20170101000000",
2753           "--include-requested-extensions",
2754           "--issuer-alternative-name-email-address", "ca@example.com",
2755         },
2756         INFO_MANAGE_CERTS_SC_SIGN_CSR_EXAMPLE_2.get(
2757              getPlatformSpecificPath("config", "keystore")));
2758
2759    final SubCommand signCSRSubCommand = new SubCommand(
2760         "sign-certificate-signing-request",
2761         INFO_MANAGE_CERTS_SC_SIGN_CSR_DESC.get(), signCSRParser,
2762         signCSRExamples);
2763    signCSRSubCommand.addName("signCertificateSigningRequest", true);
2764    signCSRSubCommand.addName("sign-certificate-request", false);
2765    signCSRSubCommand.addName("signCertificateRequest", true);
2766    signCSRSubCommand.addName("sign-certificate", false);
2767    signCSRSubCommand.addName("signCertificate", true);
2768    signCSRSubCommand.addName("sign-csr", true);
2769    signCSRSubCommand.addName("signCSR", true);
2770    signCSRSubCommand.addName("sign", false);
2771    signCSRSubCommand.addName("gencert", true);
2772
2773    parser.addSubCommand(signCSRSubCommand);
2774
2775
2776    // Define the "change-certificate-alias" subcommand and all of its
2777    // arguments.
2778    final ArgumentParser changeAliasParser = new ArgumentParser(
2779         "change-certificate-alias",
2780         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_DESC.get());
2781
2782    final FileArgument changeAliasKeystore = new FileArgument(null, "keystore",
2783         true, 1, null, INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_DESC.get(),
2784         true, true,  true, false);
2785    changeAliasKeystore.addLongIdentifier("keystore-path", true);
2786    changeAliasKeystore.addLongIdentifier("keystorePath", true);
2787    changeAliasKeystore.addLongIdentifier("keystore-file", true);
2788    changeAliasKeystore.addLongIdentifier("keystoreFile", true);
2789    changeAliasParser.addArgument(changeAliasKeystore);
2790
2791    final StringArgument changeAliasKeystorePassword = new StringArgument(null,
2792         "keystore-password", false, 1,
2793         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2794         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_PW_DESC.get());
2795    changeAliasKeystorePassword.addLongIdentifier("keystorePassword", true);
2796    changeAliasKeystorePassword.addLongIdentifier("keystore-passphrase", true);
2797    changeAliasKeystorePassword.addLongIdentifier("keystorePassphrase", true);
2798    changeAliasKeystorePassword.addLongIdentifier("keystore-pin", true);
2799    changeAliasKeystorePassword.addLongIdentifier("keystorePIN", true);
2800    changeAliasKeystorePassword.addLongIdentifier("storepass", true);
2801    changeAliasKeystorePassword.setSensitive(true);
2802    changeAliasParser.addArgument(changeAliasKeystorePassword);
2803
2804    final FileArgument changeAliasKeystorePasswordFile = new FileArgument(null,
2805         "keystore-password-file", false, 1, null,
2806         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_PW_FILE_DESC.get(), true,
2807         true, true, false);
2808    changeAliasKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
2809         true);
2810    changeAliasKeystorePasswordFile.addLongIdentifier(
2811         "keystore-passphrase-file", true);
2812    changeAliasKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
2813         true);
2814    changeAliasKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
2815         true);
2816    changeAliasKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
2817    changeAliasParser.addArgument(changeAliasKeystorePasswordFile);
2818
2819    final BooleanArgument changeAliasPromptForKeystorePassword =
2820         new BooleanArgument(null, "prompt-for-keystore-password",
2821        INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PROMPT_FOR_KS_PW_DESC.get());
2822    changeAliasPromptForKeystorePassword.addLongIdentifier(
2823         "promptForKeystorePassword", true);
2824    changeAliasPromptForKeystorePassword.addLongIdentifier(
2825         "prompt-for-keystore-passphrase", true);
2826    changeAliasPromptForKeystorePassword.addLongIdentifier(
2827         "promptForKeystorePassphrase", true);
2828    changeAliasPromptForKeystorePassword.addLongIdentifier(
2829         "prompt-for-keystore-pin", true);
2830    changeAliasPromptForKeystorePassword.addLongIdentifier(
2831         "promptForKeystorePIN", true);
2832    changeAliasParser.addArgument(changeAliasPromptForKeystorePassword);
2833
2834    final StringArgument changeAliasPKPassword = new StringArgument(null,
2835         "private-key-password", false, 1,
2836         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2837         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PK_PW_DESC.get());
2838    changeAliasPKPassword.addLongIdentifier("privateKeyPassword", true);
2839    changeAliasPKPassword.addLongIdentifier("private-key-passphrase", true);
2840    changeAliasPKPassword.addLongIdentifier("privateKeyPassphrase", true);
2841    changeAliasPKPassword.addLongIdentifier("private-key-pin", true);
2842    changeAliasPKPassword.addLongIdentifier("privateKeyPIN", true);
2843    changeAliasPKPassword.addLongIdentifier("key-password", true);
2844    changeAliasPKPassword.addLongIdentifier("keyPassword", true);
2845    changeAliasPKPassword.addLongIdentifier("key-passphrase", true);
2846    changeAliasPKPassword.addLongIdentifier("keyPassphrase", true);
2847    changeAliasPKPassword.addLongIdentifier("key-pin", true);
2848    changeAliasPKPassword.addLongIdentifier("keyPIN", true);
2849    changeAliasPKPassword.addLongIdentifier("keypass", true);
2850    changeAliasPKPassword.setSensitive(true);
2851    changeAliasParser.addArgument(changeAliasPKPassword);
2852
2853    final FileArgument changeAliasPKPasswordFile = new FileArgument(null,
2854         "private-key-password-file", false, 1, null,
2855         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PK_PW_FILE_DESC.get(), true,
2856         true, true, false);
2857    changeAliasPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
2858    changeAliasPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
2859         true);
2860    changeAliasPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
2861         true);
2862    changeAliasPKPasswordFile.addLongIdentifier("private-key-pin-file",
2863         true);
2864    changeAliasPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
2865    changeAliasPKPasswordFile.addLongIdentifier("key-password-file", true);
2866    changeAliasPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
2867    changeAliasPKPasswordFile.addLongIdentifier("key-passphrase-file",
2868         true);
2869    changeAliasPKPasswordFile.addLongIdentifier("keyPassphraseFile",
2870         true);
2871    changeAliasPKPasswordFile.addLongIdentifier("key-pin-file",
2872         true);
2873    changeAliasPKPasswordFile.addLongIdentifier("keyPINFile", true);
2874    changeAliasParser.addArgument(changeAliasPKPasswordFile);
2875
2876    final BooleanArgument changeAliasPromptForPKPassword =
2877         new BooleanArgument(null, "prompt-for-private-key-password",
2878        INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PROMPT_FOR_PK_PW_DESC.get());
2879    changeAliasPromptForPKPassword.addLongIdentifier(
2880         "promptForPrivateKeyPassword", true);
2881    changeAliasPromptForPKPassword.addLongIdentifier(
2882         "prompt-for-private-key-passphrase", true);
2883    changeAliasPromptForPKPassword.addLongIdentifier(
2884         "promptForPrivateKeyPassphrase", true);
2885    changeAliasPromptForPKPassword.addLongIdentifier(
2886         "prompt-for-private-key-pin", true);
2887    changeAliasPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
2888         true);
2889    changeAliasPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
2890         true);
2891    changeAliasPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
2892         true);
2893    changeAliasPromptForPKPassword.addLongIdentifier(
2894         "prompt-for-key-passphrase", true);
2895    changeAliasPromptForPKPassword.addLongIdentifier(
2896         "promptForKeyPassphrase", true);
2897    changeAliasPromptForPKPassword.addLongIdentifier("prompt-for-key-pin",
2898         true);
2899    changeAliasPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
2900    changeAliasParser.addArgument(changeAliasPromptForPKPassword);
2901
2902    final StringArgument changeAliasCurrentAlias = new StringArgument(null,
2903         "current-alias", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
2904         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_CURRENT_ALIAS_DESC.get());
2905    changeAliasCurrentAlias.addLongIdentifier("currentAlias", true);
2906    changeAliasCurrentAlias.addLongIdentifier("old-alias", true);
2907    changeAliasCurrentAlias.addLongIdentifier("oldAlias", true);
2908    changeAliasCurrentAlias.addLongIdentifier("source-alias", true);
2909    changeAliasCurrentAlias.addLongIdentifier("sourceAlias", true);
2910    changeAliasCurrentAlias.addLongIdentifier("alias", true);
2911    changeAliasCurrentAlias.addLongIdentifier("current-nickname", true);
2912    changeAliasCurrentAlias.addLongIdentifier("currentNickname", true);
2913    changeAliasCurrentAlias.addLongIdentifier("old-nickname", true);
2914    changeAliasCurrentAlias.addLongIdentifier("oldNickname", true);
2915    changeAliasCurrentAlias.addLongIdentifier("source-nickname", true);
2916    changeAliasCurrentAlias.addLongIdentifier("sourceNickname", true);
2917    changeAliasCurrentAlias.addLongIdentifier("nickname", true);
2918    changeAliasCurrentAlias.addLongIdentifier("from", false);
2919    changeAliasParser.addArgument(changeAliasCurrentAlias);
2920
2921    final StringArgument changeAliasNewAlias = new StringArgument(null,
2922         "new-alias", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
2923         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_NEW_ALIAS_DESC.get());
2924    changeAliasNewAlias.addLongIdentifier("newAlias", true);
2925    changeAliasNewAlias.addLongIdentifier("destination-alias", true);
2926    changeAliasNewAlias.addLongIdentifier("destinationAlias", true);
2927    changeAliasNewAlias.addLongIdentifier("new-nickname", true);
2928    changeAliasNewAlias.addLongIdentifier("newNickname", true);
2929    changeAliasNewAlias.addLongIdentifier("destination-nickname", true);
2930    changeAliasNewAlias.addLongIdentifier("destinationNickname", true);
2931    changeAliasNewAlias.addLongIdentifier("to", false);
2932    changeAliasParser.addArgument(changeAliasNewAlias);
2933
2934    final BooleanArgument changeAliasDisplayCommand = new BooleanArgument(null,
2935         "display-keytool-command", 1,
2936         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_DISPLAY_COMMAND_DESC.get());
2937    changeAliasDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
2938    changeAliasDisplayCommand.addLongIdentifier("show-keytool-command", true);
2939    changeAliasDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
2940    changeAliasParser.addArgument(changeAliasDisplayCommand);
2941
2942    changeAliasParser.addRequiredArgumentSet(changeAliasKeystorePassword,
2943         changeAliasKeystorePasswordFile, changeAliasPromptForKeystorePassword);
2944    changeAliasParser.addExclusiveArgumentSet(changeAliasKeystorePassword,
2945         changeAliasKeystorePasswordFile, changeAliasPromptForKeystorePassword);
2946    changeAliasParser.addExclusiveArgumentSet(changeAliasPKPassword,
2947         changeAliasPKPasswordFile, changeAliasPromptForPKPassword);
2948
2949    final LinkedHashMap<String[],String> changeAliasExamples =
2950         new LinkedHashMap<>(1);
2951    changeAliasExamples.put(
2952         new String[]
2953         {
2954           "change-certificate-alias",
2955           "--keystore", getPlatformSpecificPath("config", "keystore"),
2956           "--keystore-password-file",
2957                getPlatformSpecificPath("config", "keystore.pin"),
2958           "--current-alias", "server-cert",
2959           "--new-alias", "server-certificate",
2960           "--display-keytool-command"
2961         },
2962         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_EXAMPLE_1.get());
2963
2964    final SubCommand changeAliasSubCommand = new SubCommand(
2965         "change-certificate-alias",
2966         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_DESC.get(), changeAliasParser,
2967         changeAliasExamples);
2968    changeAliasSubCommand.addName("changeCertificateAlias", true);
2969    changeAliasSubCommand.addName("change-alias", false);
2970    changeAliasSubCommand.addName("changeAlias", true);
2971    changeAliasSubCommand.addName("rename-certificate", true);
2972    changeAliasSubCommand.addName("renameCertificate", true);
2973    changeAliasSubCommand.addName("rename", false);
2974
2975    parser.addSubCommand(changeAliasSubCommand);
2976
2977
2978    // Define the "change-keystore-password" subcommand and all of its
2979    // arguments.
2980    final ArgumentParser changeKSPWParser = new ArgumentParser(
2981         "change-keystore-password",
2982         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_DESC.get());
2983
2984    final FileArgument changeKSPWKeystore = new FileArgument(null, "keystore",
2985         true, 1, null, INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_KS_DESC.get(),
2986         true, true,  true, false);
2987    changeKSPWKeystore.addLongIdentifier("keystore-path", true);
2988    changeKSPWKeystore.addLongIdentifier("keystorePath", true);
2989    changeKSPWKeystore.addLongIdentifier("keystore-file", true);
2990    changeKSPWKeystore.addLongIdentifier("keystoreFile", true);
2991    changeKSPWParser.addArgument(changeKSPWKeystore);
2992
2993    final StringArgument changeKSPWCurrentPassword = new StringArgument(null,
2994         "current-keystore-password", false, 1,
2995         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2996         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_CURRENT_PW_DESC.get());
2997    changeKSPWCurrentPassword.addLongIdentifier("currentKeystorePassword",
2998         true);
2999    changeKSPWCurrentPassword.addLongIdentifier("current-keystore-passphrase",
3000         true);
3001    changeKSPWCurrentPassword.addLongIdentifier("currentKeystorePassphrase",
3002         true);
3003    changeKSPWCurrentPassword.addLongIdentifier("current-keystore-pin", true);
3004    changeKSPWCurrentPassword.addLongIdentifier("currentKeystorePIN", true);
3005    changeKSPWCurrentPassword.addLongIdentifier("storepass", true);
3006    changeKSPWCurrentPassword.setSensitive(true);
3007    changeKSPWParser.addArgument(changeKSPWCurrentPassword);
3008
3009    final FileArgument changeKSPWCurrentPasswordFile = new FileArgument(null,
3010         "current-keystore-password-file", false, 1, null,
3011         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_CURRENT_PW_FILE_DESC.get(), true,
3012         true, true, false);
3013    changeKSPWCurrentPasswordFile.addLongIdentifier(
3014         "currentKeystorePasswordFile", true);
3015    changeKSPWCurrentPasswordFile.addLongIdentifier(
3016         "current-keystore-passphrase-file", true);
3017    changeKSPWCurrentPasswordFile.addLongIdentifier(
3018         "currentKeystorePassphraseFile", true);
3019    changeKSPWCurrentPasswordFile.addLongIdentifier("current-keystore-pin-file",
3020         true);
3021    changeKSPWCurrentPasswordFile.addLongIdentifier("currentKeystorePINFile",
3022         true);
3023    changeKSPWParser.addArgument(changeKSPWCurrentPasswordFile);
3024
3025    final BooleanArgument changeKSPWPromptForCurrentPassword =
3026         new BooleanArgument(null, "prompt-for-current-keystore-password",
3027        INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_PROMPT_FOR_CURRENT_PW_DESC.get());
3028    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3029         "promptForCurrentKeystorePassword", true);
3030    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3031         "prompt-for-current-keystore-passphrase", true);
3032    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3033         "promptForCurrentKeystorePassphrase", true);
3034    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3035         "prompt-for-current-keystore-pin", true);
3036    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3037         "promptForCurrentKeystorePIN", true);
3038    changeKSPWParser.addArgument(changeKSPWPromptForCurrentPassword);
3039
3040    final StringArgument changeKSPWNewPassword = new StringArgument(null,
3041         "new-keystore-password", false, 1,
3042         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3043         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_NEW_PW_DESC.get());
3044    changeKSPWNewPassword.addLongIdentifier("newKeystorePassword",
3045         true);
3046    changeKSPWNewPassword.addLongIdentifier("new-keystore-passphrase",
3047         true);
3048    changeKSPWNewPassword.addLongIdentifier("newKeystorePassphrase",
3049         true);
3050    changeKSPWNewPassword.addLongIdentifier("new-keystore-pin", true);
3051    changeKSPWNewPassword.addLongIdentifier("newKeystorePIN", true);
3052    changeKSPWNewPassword.addLongIdentifier("new", true);
3053    changeKSPWNewPassword.setSensitive(true);
3054    changeKSPWParser.addArgument(changeKSPWNewPassword);
3055
3056    final FileArgument changeKSPWNewPasswordFile = new FileArgument(null,
3057         "new-keystore-password-file", false, 1, null,
3058         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_NEW_PW_FILE_DESC.get(), true,
3059         true, true, false);
3060    changeKSPWNewPasswordFile.addLongIdentifier("newKeystorePasswordFile",
3061         true);
3062    changeKSPWNewPasswordFile.addLongIdentifier("new-keystore-passphrase-file",
3063         true);
3064    changeKSPWNewPasswordFile.addLongIdentifier("newKeystorePassphraseFile",
3065         true);
3066    changeKSPWNewPasswordFile.addLongIdentifier("new-keystore-pin-file", true);
3067    changeKSPWNewPasswordFile.addLongIdentifier("newKeystorePINFile", true);
3068    changeKSPWParser.addArgument(changeKSPWNewPasswordFile);
3069
3070    final BooleanArgument changeKSPWPromptForNewPassword =
3071         new BooleanArgument(null, "prompt-for-new-keystore-password",
3072        INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_PROMPT_FOR_NEW_PW_DESC.get());
3073    changeKSPWPromptForNewPassword.addLongIdentifier(
3074         "promptForNewKeystorePassword", true);
3075    changeKSPWPromptForNewPassword.addLongIdentifier(
3076         "prompt-for-new-keystore-passphrase", true);
3077    changeKSPWPromptForNewPassword.addLongIdentifier(
3078         "promptForNewKeystorePassphrase", true);
3079    changeKSPWPromptForNewPassword.addLongIdentifier(
3080         "prompt-for-new-keystore-pin", true);
3081    changeKSPWPromptForNewPassword.addLongIdentifier(
3082         "promptForNewKeystorePIN", true);
3083    changeKSPWParser.addArgument(changeKSPWPromptForNewPassword);
3084
3085    final BooleanArgument changeKSPWDisplayCommand = new BooleanArgument(null,
3086         "display-keytool-command", 1,
3087         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_DISPLAY_COMMAND_DESC.get());
3088    changeKSPWDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3089    changeKSPWDisplayCommand.addLongIdentifier("show-keytool-command", true);
3090    changeKSPWDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3091    changeKSPWParser.addArgument(changeKSPWDisplayCommand);
3092
3093    changeKSPWParser.addRequiredArgumentSet(changeKSPWCurrentPassword,
3094         changeKSPWCurrentPasswordFile, changeKSPWPromptForCurrentPassword);
3095    changeKSPWParser.addExclusiveArgumentSet(changeKSPWCurrentPassword,
3096         changeKSPWCurrentPasswordFile, changeKSPWPromptForCurrentPassword);
3097    changeKSPWParser.addRequiredArgumentSet(changeKSPWNewPassword,
3098         changeKSPWNewPasswordFile, changeKSPWPromptForNewPassword);
3099    changeKSPWParser.addExclusiveArgumentSet(changeKSPWNewPassword,
3100         changeKSPWNewPasswordFile, changeKSPWPromptForNewPassword);
3101
3102    final LinkedHashMap<String[],String> changeKSPWExamples =
3103         new LinkedHashMap<>(1);
3104    changeKSPWExamples.put(
3105         new String[]
3106         {
3107           "change-keystore-password",
3108           "--keystore", getPlatformSpecificPath("config", "keystore"),
3109           "--current-keystore-password-file",
3110                getPlatformSpecificPath("config", "current.pin"),
3111           "--new-keystore-password-file",
3112                getPlatformSpecificPath("config", "new.pin"),
3113           "--display-keytool-command"
3114         },
3115         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_EXAMPLE_1.get(
3116              getPlatformSpecificPath("config", "keystore"),
3117              getPlatformSpecificPath("config", "current.pin"),
3118              getPlatformSpecificPath("config", "new.pin")));
3119
3120    final SubCommand changeKSPWSubCommand = new SubCommand(
3121         "change-keystore-password",
3122         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_DESC.get(), changeKSPWParser,
3123         changeKSPWExamples);
3124    changeKSPWSubCommand.addName("changeKeystorePassword", true);
3125    changeKSPWSubCommand.addName("change-keystore-passphrase", true);
3126    changeKSPWSubCommand.addName("changeKeystorePassphrase", true);
3127    changeKSPWSubCommand.addName("change-keystore-pin", true);
3128    changeKSPWSubCommand.addName("changeKeystorePIN", true);
3129    changeKSPWSubCommand.addName("storepasswd", true);
3130
3131    parser.addSubCommand(changeKSPWSubCommand);
3132
3133
3134    // Define the "change-private-key-password" subcommand and all of its
3135    // arguments.
3136    final ArgumentParser changePKPWParser = new ArgumentParser(
3137         "change-private-key-password",
3138         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_DESC.get());
3139
3140    final FileArgument changePKPWKeystore = new FileArgument(null, "keystore",
3141         true, 1, null, INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_DESC.get(),
3142         true, true,  true, false);
3143    changePKPWKeystore.addLongIdentifier("keystore-path", true);
3144    changePKPWKeystore.addLongIdentifier("keystorePath", true);
3145    changePKPWKeystore.addLongIdentifier("keystore-file", true);
3146    changePKPWKeystore.addLongIdentifier("keystoreFile", true);
3147    changePKPWParser.addArgument(changePKPWKeystore);
3148
3149    final StringArgument changePKPWKeystorePassword = new StringArgument(null,
3150         "keystore-password", false, 1,
3151         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3152         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_PW_DESC.get());
3153    changePKPWKeystorePassword.addLongIdentifier("keystorePassword", true);
3154    changePKPWKeystorePassword.addLongIdentifier("keystore-passphrase", true);
3155    changePKPWKeystorePassword.addLongIdentifier("keystorePassphrase", true);
3156    changePKPWKeystorePassword.addLongIdentifier("keystore-pin", true);
3157    changePKPWKeystorePassword.addLongIdentifier("keystorePIN", true);
3158    changePKPWKeystorePassword.addLongIdentifier("storepass", true);
3159    changePKPWKeystorePassword.setSensitive(true);
3160    changePKPWParser.addArgument(changePKPWKeystorePassword);
3161
3162    final FileArgument changePKPWKeystorePasswordFile = new FileArgument(null,
3163         "keystore-password-file", false, 1, null,
3164         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_PW_FILE_DESC.get(), true,
3165         true, true, false);
3166    changePKPWKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
3167         true);
3168    changePKPWKeystorePasswordFile.addLongIdentifier(
3169         "keystore-passphrase-file", true);
3170    changePKPWKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
3171         true);
3172    changePKPWKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
3173         true);
3174    changePKPWKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
3175    changePKPWParser.addArgument(changePKPWKeystorePasswordFile);
3176
3177    final BooleanArgument changePKPWPromptForKeystorePassword =
3178         new BooleanArgument(null, "prompt-for-keystore-password",
3179        INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_PROMPT_FOR_KS_PW_DESC.get());
3180    changePKPWPromptForKeystorePassword.addLongIdentifier(
3181         "promptForKeystorePassword", true);
3182    changePKPWPromptForKeystorePassword.addLongIdentifier(
3183         "prompt-for-keystore-passphrase", true);
3184    changePKPWPromptForKeystorePassword.addLongIdentifier(
3185         "promptForKeystorePassphrase", true);
3186    changePKPWPromptForKeystorePassword.addLongIdentifier(
3187         "prompt-for-keystore-pin", true);
3188    changePKPWPromptForKeystorePassword.addLongIdentifier(
3189         "promptForKeystorePIN", true);
3190    changePKPWParser.addArgument(changePKPWPromptForKeystorePassword);
3191
3192    final StringArgument changePKPWAlias = new StringArgument(null, "alias",
3193         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3194         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_ALIAS_DESC.get());
3195    changePKPWAlias.addLongIdentifier("nickname", true);
3196    changePKPWParser.addArgument(changePKPWAlias);
3197
3198    final StringArgument changePKPWCurrentPassword = new StringArgument(null,
3199         "current-private-key-password", false, 1,
3200         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3201         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_CURRENT_PW_DESC.get());
3202    changePKPWCurrentPassword.addLongIdentifier("currentPrivateKeyPassword",
3203         true);
3204    changePKPWCurrentPassword.addLongIdentifier(
3205         "current-private-key-passphrase", true);
3206    changePKPWCurrentPassword.addLongIdentifier("currentPrivateKeyPassphrase",
3207         true);
3208    changePKPWCurrentPassword.addLongIdentifier("current-private-key-pin",
3209         true);
3210    changePKPWCurrentPassword.addLongIdentifier("currentPrivateKeyPIN", true);
3211    changePKPWCurrentPassword.addLongIdentifier("keypass", true);
3212    changePKPWCurrentPassword.setSensitive(true);
3213    changePKPWParser.addArgument(changePKPWCurrentPassword);
3214
3215    final FileArgument changePKPWCurrentPasswordFile = new FileArgument(null,
3216         "current-private-key-password-file", false, 1, null,
3217         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_CURRENT_PW_FILE_DESC.get(), true,
3218         true, true, false);
3219    changePKPWCurrentPasswordFile.addLongIdentifier(
3220         "currentPrivateKeyPasswordFile", true);
3221    changePKPWCurrentPasswordFile.addLongIdentifier(
3222         "current-private-key-passphrase-file", true);
3223    changePKPWCurrentPasswordFile.addLongIdentifier(
3224         "currentPrivateKeyPassphraseFile", true);
3225    changePKPWCurrentPasswordFile.addLongIdentifier(
3226         "current-private-key-pin-file", true);
3227    changePKPWCurrentPasswordFile.addLongIdentifier("currentPrivateKeyPINFile",
3228         true);
3229    changePKPWParser.addArgument(changePKPWCurrentPasswordFile);
3230
3231    final BooleanArgument changePKPWPromptForCurrentPassword =
3232         new BooleanArgument(null, "prompt-for-current-private-key-password",
3233        INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_PROMPT_FOR_CURRENT_PW_DESC.get());
3234    changePKPWPromptForCurrentPassword.addLongIdentifier(
3235         "promptForCurrentPrivateKeyPassword", true);
3236    changePKPWPromptForCurrentPassword.addLongIdentifier(
3237         "prompt-for-current-private-key-passphrase", true);
3238    changePKPWPromptForCurrentPassword.addLongIdentifier(
3239         "promptForCurrentPrivateKeyPassphrase", true);
3240    changePKPWPromptForCurrentPassword.addLongIdentifier(
3241         "prompt-for-current-private-key-pin", true);
3242    changePKPWPromptForCurrentPassword.addLongIdentifier(
3243         "promptForCurrentPrivateKeyPIN", true);
3244    changePKPWParser.addArgument(changePKPWPromptForCurrentPassword);
3245
3246    final StringArgument changePKPWNewPassword = new StringArgument(null,
3247         "new-private-key-password", false, 1,
3248         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3249         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_NEW_PW_DESC.get());
3250    changePKPWNewPassword.addLongIdentifier("newPrivateKeyPassword",
3251         true);
3252    changePKPWNewPassword.addLongIdentifier("new-private-key-passphrase", true);
3253    changePKPWNewPassword.addLongIdentifier("newPrivateKeyPassphrase", true);
3254    changePKPWNewPassword.addLongIdentifier("new-private-key-pin", true);
3255    changePKPWNewPassword.addLongIdentifier("newPrivateKeyPIN", true);
3256    changePKPWNewPassword.addLongIdentifier("new", true);
3257    changePKPWNewPassword.setSensitive(true);
3258    changePKPWParser.addArgument(changePKPWNewPassword);
3259
3260    final FileArgument changePKPWNewPasswordFile = new FileArgument(null,
3261         "new-private-key-password-file", false, 1, null,
3262         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_NEW_PW_FILE_DESC.get(), true,
3263         true, true, false);
3264    changePKPWNewPasswordFile.addLongIdentifier("newPrivateKeyPasswordFile",
3265         true);
3266    changePKPWNewPasswordFile.addLongIdentifier(
3267         "new-private-key-passphrase-file", true);
3268    changePKPWNewPasswordFile.addLongIdentifier("newPrivateKeyPassphraseFile",
3269         true);
3270    changePKPWNewPasswordFile.addLongIdentifier("new-private-key-pin-file",
3271         true);
3272    changePKPWNewPasswordFile.addLongIdentifier("newPrivateKeyPINFile", true);
3273    changePKPWParser.addArgument(changePKPWNewPasswordFile);
3274
3275    final BooleanArgument changePKPWPromptForNewPassword =
3276         new BooleanArgument(null, "prompt-for-new-private-key-password",
3277        INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_PROMPT_FOR_NEW_PW_DESC.get());
3278    changePKPWPromptForNewPassword.addLongIdentifier(
3279         "promptForNewPrivateKeyPassword", true);
3280    changePKPWPromptForNewPassword.addLongIdentifier(
3281         "prompt-for-new-private-key-passphrase", true);
3282    changePKPWPromptForNewPassword.addLongIdentifier(
3283         "promptForNewPrivateKeyPassphrase", true);
3284    changePKPWPromptForNewPassword.addLongIdentifier(
3285         "prompt-for-new-private-key-pin", true);
3286    changePKPWPromptForNewPassword.addLongIdentifier(
3287         "promptForNewPrivateKeyPIN", true);
3288    changePKPWParser.addArgument(changePKPWPromptForNewPassword);
3289
3290    final BooleanArgument changePKPWDisplayCommand = new BooleanArgument(null,
3291         "display-keytool-command", 1,
3292         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_DISPLAY_COMMAND_DESC.get());
3293    changePKPWDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3294    changePKPWDisplayCommand.addLongIdentifier("show-keytool-command", true);
3295    changePKPWDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3296    changePKPWParser.addArgument(changePKPWDisplayCommand);
3297
3298    changePKPWParser.addRequiredArgumentSet(changePKPWKeystorePassword,
3299         changePKPWKeystorePasswordFile, changePKPWPromptForKeystorePassword);
3300    changePKPWParser.addExclusiveArgumentSet(changePKPWKeystorePassword,
3301         changePKPWKeystorePasswordFile, changePKPWPromptForKeystorePassword);
3302    changePKPWParser.addRequiredArgumentSet(changePKPWCurrentPassword,
3303         changePKPWCurrentPasswordFile, changePKPWPromptForCurrentPassword);
3304    changePKPWParser.addExclusiveArgumentSet(changePKPWCurrentPassword,
3305         changePKPWCurrentPasswordFile, changePKPWPromptForCurrentPassword);
3306    changePKPWParser.addRequiredArgumentSet(changePKPWNewPassword,
3307         changePKPWNewPasswordFile, changePKPWPromptForNewPassword);
3308    changePKPWParser.addExclusiveArgumentSet(changePKPWNewPassword,
3309         changePKPWNewPasswordFile, changePKPWPromptForNewPassword);
3310
3311    final LinkedHashMap<String[],String> changePKPWExamples =
3312         new LinkedHashMap<>(1);
3313    changePKPWExamples.put(
3314         new String[]
3315         {
3316           "change-private-key-password",
3317           "--keystore", getPlatformSpecificPath("config", "keystore"),
3318           "--keystore-password-file",
3319                getPlatformSpecificPath("config", "keystore.pin"),
3320           "--alias", "server-cert",
3321           "--current-private-key-password-file",
3322                getPlatformSpecificPath("config", "current.pin"),
3323           "--new-private-key-password-file",
3324                getPlatformSpecificPath("config", "new.pin"),
3325           "--display-keytool-command"
3326         },
3327         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_EXAMPLE_1.get(
3328              getPlatformSpecificPath("config", "keystore"),
3329              getPlatformSpecificPath("config", "current.pin"),
3330              getPlatformSpecificPath("config", "new.pin")));
3331
3332    final SubCommand changePKPWSubCommand = new SubCommand(
3333         "change-private-key-password",
3334         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_DESC.get(), changePKPWParser,
3335         changePKPWExamples);
3336    changePKPWSubCommand.addName("changePrivateKeyPassword", true);
3337    changePKPWSubCommand.addName("change-private-key-passphrase", true);
3338    changePKPWSubCommand.addName("changePrivateKeyPassphrase", true);
3339    changePKPWSubCommand.addName("change-private-key-pin", true);
3340    changePKPWSubCommand.addName("changePrivateKeyPIN", true);
3341    changePKPWSubCommand.addName("change-key-password", false);
3342    changePKPWSubCommand.addName("changeKeyPassword", true);
3343    changePKPWSubCommand.addName("change-key-passphrase", true);
3344    changePKPWSubCommand.addName("changeKeyPassphrase", true);
3345    changePKPWSubCommand.addName("change-key-pin", true);
3346    changePKPWSubCommand.addName("changeKeyPIN", true);
3347    changePKPWSubCommand.addName("keypasswd", true);
3348
3349    parser.addSubCommand(changePKPWSubCommand);
3350
3351
3352    // Define the "trust-server-certificate" subcommand and all of its
3353    // arguments.
3354    final ArgumentParser trustServerParser = new ArgumentParser(
3355         "trust-server-certificate",
3356         INFO_MANAGE_CERTS_SC_TRUST_SERVER_DESC.get());
3357
3358    final StringArgument trustServerHostname = new StringArgument('h',
3359         "hostname", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_HOST.get(),
3360         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_HOSTNAME_DESC.get());
3361    trustServerHostname.addLongIdentifier("server-address", true);
3362    trustServerHostname.addLongIdentifier("serverAddress", true);
3363    trustServerHostname.addLongIdentifier("address", true);
3364    trustServerParser.addArgument(trustServerHostname);
3365
3366    final IntegerArgument trustServerPort = new IntegerArgument('p',
3367         "port", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_PORT.get(),
3368         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_PORT_DESC.get(), 1, 65535);
3369    trustServerPort.addLongIdentifier("server-port", true);
3370    trustServerPort.addLongIdentifier("serverPort", true);
3371    trustServerParser.addArgument(trustServerPort);
3372
3373    final BooleanArgument trustServerUseStartTLS = new BooleanArgument('q',
3374         "use-ldap-start-tls", 1,
3375         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_USE_START_TLS_DESC.get());
3376    trustServerUseStartTLS.addLongIdentifier("use-ldap-starttls", true);
3377    trustServerUseStartTLS.addLongIdentifier("useLDAPStartTLS", true);
3378    trustServerUseStartTLS.addLongIdentifier("use-start-tls", true);
3379    trustServerUseStartTLS.addLongIdentifier("use-starttls", true);
3380    trustServerUseStartTLS.addLongIdentifier("useStartTLS", true);
3381    trustServerParser.addArgument(trustServerUseStartTLS);
3382
3383    final FileArgument trustServerKeystore = new FileArgument(null, "keystore",
3384         true, 1, null, INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_DESC.get(),
3385         false, true,  true, false);
3386    trustServerKeystore.addLongIdentifier("keystore-path", true);
3387    trustServerKeystore.addLongIdentifier("keystorePath", true);
3388    trustServerKeystore.addLongIdentifier("keystore-file", true);
3389    trustServerKeystore.addLongIdentifier("keystoreFile", true);
3390    trustServerParser.addArgument(trustServerKeystore);
3391
3392    final StringArgument trustServerKeystorePassword = new StringArgument(null,
3393         "keystore-password", false, 1,
3394         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3395         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_PW_DESC.get());
3396    trustServerKeystorePassword.addLongIdentifier("keystorePassword", true);
3397    trustServerKeystorePassword.addLongIdentifier("keystore-passphrase", true);
3398    trustServerKeystorePassword.addLongIdentifier("keystorePassphrase", true);
3399    trustServerKeystorePassword.addLongIdentifier("keystore-pin", true);
3400    trustServerKeystorePassword.addLongIdentifier("keystorePIN", true);
3401    trustServerKeystorePassword.addLongIdentifier("storepass", true);
3402    trustServerKeystorePassword.setSensitive(true);
3403    trustServerParser.addArgument(trustServerKeystorePassword);
3404
3405    final FileArgument trustServerKeystorePasswordFile = new FileArgument(null,
3406         "keystore-password-file", false, 1, null,
3407         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_PW_FILE_DESC.get(), true,
3408         true, true, false);
3409    trustServerKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
3410         true);
3411    trustServerKeystorePasswordFile.addLongIdentifier(
3412         "keystore-passphrase-file", true);
3413    trustServerKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
3414         true);
3415    trustServerKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
3416         true);
3417    trustServerKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
3418    trustServerParser.addArgument(trustServerKeystorePasswordFile);
3419
3420    final BooleanArgument trustServerPromptForKeystorePassword =
3421         new BooleanArgument(null, "prompt-for-keystore-password",
3422        INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_PROMPT_FOR_KS_PW_DESC.get());
3423    trustServerPromptForKeystorePassword.addLongIdentifier(
3424         "promptForKeystorePassword", true);
3425    trustServerPromptForKeystorePassword.addLongIdentifier(
3426         "prompt-for-keystore-passphrase", true);
3427    trustServerPromptForKeystorePassword.addLongIdentifier(
3428         "promptForKeystorePassphrase", true);
3429    trustServerPromptForKeystorePassword.addLongIdentifier(
3430         "prompt-for-keystore-pin", true);
3431    trustServerPromptForKeystorePassword.addLongIdentifier(
3432         "promptForKeystorePIN", true);
3433    trustServerParser.addArgument(trustServerPromptForKeystorePassword);
3434
3435    final LinkedHashSet<String> trustServerKeystoreTypeAllowedValues =
3436         new LinkedHashSet<>(2);
3437    trustServerKeystoreTypeAllowedValues.add("jks");
3438    trustServerKeystoreTypeAllowedValues.add("pkcs12");
3439    trustServerKeystoreTypeAllowedValues.add("pkcs 12");
3440    trustServerKeystoreTypeAllowedValues.add("pkcs#12");
3441    trustServerKeystoreTypeAllowedValues.add("pkcs #12");
3442    final StringArgument trustServerKeystoreType = new StringArgument(null,
3443         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
3444         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_TYPE_DESC.get(),
3445         trustServerKeystoreTypeAllowedValues);
3446    trustServerKeystoreType.addLongIdentifier("keystoreType", true);
3447    trustServerKeystoreType.addLongIdentifier("storetype", true);
3448    trustServerParser.addArgument(trustServerKeystoreType);
3449
3450    final StringArgument trustServerAlias = new StringArgument(null,
3451         "alias", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3452         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_ALIAS_DESC.get());
3453    trustServerAlias.addLongIdentifier("nickname", true);
3454    trustServerParser.addArgument(trustServerAlias);
3455
3456    final BooleanArgument trustServerIssuersOnly = new BooleanArgument(null,
3457         "issuers-only", 1,
3458         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_ISSUERS_ONLY_DESC.get());
3459    trustServerIssuersOnly.addLongIdentifier("issuersOnly", true);
3460    trustServerIssuersOnly.addLongIdentifier("issuer-certificates-only", true);
3461    trustServerIssuersOnly.addLongIdentifier("issuerCertificatesOnly", true);
3462    trustServerIssuersOnly.addLongIdentifier("only-issuers", true);
3463    trustServerIssuersOnly.addLongIdentifier("onlyIssuers", true);
3464    trustServerIssuersOnly.addLongIdentifier("only-issuer-certificates", true);
3465    trustServerIssuersOnly.addLongIdentifier("onlyIssuerCertificates", true);
3466    trustServerParser.addArgument(trustServerIssuersOnly);
3467
3468    final BooleanArgument trustServerVerbose = new BooleanArgument(null,
3469         "verbose", 1,
3470         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_VERBOSE_DESC.get());
3471    trustServerParser.addArgument(trustServerVerbose);
3472
3473    final BooleanArgument trustServerNoPrompt = new BooleanArgument(null,
3474         "no-prompt", 1,
3475         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_NO_PROMPT_DESC.get());
3476    trustServerNoPrompt.addLongIdentifier("noPrompt", true);
3477    trustServerParser.addArgument(trustServerNoPrompt);
3478
3479    trustServerParser.addRequiredArgumentSet(trustServerKeystorePassword,
3480         trustServerKeystorePasswordFile, trustServerPromptForKeystorePassword);
3481    trustServerParser.addExclusiveArgumentSet(trustServerKeystorePassword,
3482         trustServerKeystorePasswordFile, trustServerPromptForKeystorePassword);
3483
3484    final LinkedHashMap<String[],String> trustServerExamples =
3485         new LinkedHashMap<>(2);
3486    trustServerExamples.put(
3487         new String[]
3488         {
3489           "trust-server-certificate",
3490           "--hostname", "ds.example.com",
3491           "--port", "636",
3492           "--keystore", getPlatformSpecificPath("config", "keystore"),
3493           "--keystore-password-file",
3494                getPlatformSpecificPath("config", "keystore.pin"),
3495           "--verbose"
3496         },
3497         INFO_MANAGE_CERTS_SC_TRUST_SERVER_EXAMPLE_1.get(
3498              getPlatformSpecificPath("config", "keystore")));
3499    trustServerExamples.put(
3500         new String[]
3501         {
3502           "trust-server-certificate",
3503           "--hostname", "ds.example.com",
3504           "--port", "389",
3505           "--use-ldap-start-tls",
3506           "--keystore", getPlatformSpecificPath("config", "keystore"),
3507           "--keystore-password-file",
3508                getPlatformSpecificPath("config", "keystore.pin"),
3509           "--issuers-only",
3510           "--alias", "ds-start-tls-cert",
3511           "--no-prompt"
3512         },
3513         INFO_MANAGE_CERTS_SC_TRUST_SERVER_EXAMPLE_2.get(
3514              getPlatformSpecificPath("config", "keystore")));
3515
3516    final SubCommand trustServerSubCommand = new SubCommand(
3517         "trust-server-certificate",
3518         INFO_MANAGE_CERTS_SC_TRUST_SERVER_DESC.get(), trustServerParser,
3519         trustServerExamples);
3520    trustServerSubCommand.addName("trustServerCertificate", true);
3521    trustServerSubCommand.addName("trust-server", false);
3522    trustServerSubCommand.addName("trustServer", true);
3523
3524    parser.addSubCommand(trustServerSubCommand);
3525
3526
3527    // Define the "check-certificate-usability" subcommand and all of its
3528    // arguments.
3529    final ArgumentParser checkUsabilityParser = new ArgumentParser(
3530         "check-certificate-usability",
3531         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_DESC.get());
3532
3533    final FileArgument checkUsabilityKeystore = new FileArgument(null,
3534         "keystore", true, 1, null,
3535         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_DESC.get(),
3536         true, true,  true, false);
3537    checkUsabilityKeystore.addLongIdentifier("keystore-path", true);
3538    checkUsabilityKeystore.addLongIdentifier("keystorePath", true);
3539    checkUsabilityKeystore.addLongIdentifier("keystore-file", true);
3540    checkUsabilityKeystore.addLongIdentifier("keystoreFile", true);
3541    checkUsabilityParser.addArgument(checkUsabilityKeystore);
3542
3543    final StringArgument checkUsabilityKeystorePassword = new StringArgument(
3544         null, "keystore-password", false, 1,
3545         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3546         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_PW_DESC.get());
3547    checkUsabilityKeystorePassword.addLongIdentifier("keystorePassword", true);
3548    checkUsabilityKeystorePassword.addLongIdentifier("keystore-passphrase",
3549         true);
3550    checkUsabilityKeystorePassword.addLongIdentifier("keystorePassphrase",
3551         true);
3552    checkUsabilityKeystorePassword.addLongIdentifier("keystore-pin", true);
3553    checkUsabilityKeystorePassword.addLongIdentifier("keystorePIN", true);
3554    checkUsabilityKeystorePassword.addLongIdentifier("storepass", true);
3555    checkUsabilityKeystorePassword.setSensitive(true);
3556    checkUsabilityParser.addArgument(checkUsabilityKeystorePassword);
3557
3558    final FileArgument checkUsabilityKeystorePasswordFile = new FileArgument(
3559         null, "keystore-password-file", false, 1, null,
3560         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_PW_FILE_DESC.get(), true,
3561         true, true, false);
3562    checkUsabilityKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
3563         true);
3564    checkUsabilityKeystorePasswordFile.addLongIdentifier(
3565         "keystore-passphrase-file", true);
3566    checkUsabilityKeystorePasswordFile.addLongIdentifier(
3567         "keystorePassphraseFile", true);
3568    checkUsabilityKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
3569         true);
3570    checkUsabilityKeystorePasswordFile.addLongIdentifier("keystorePINFile",
3571         true);
3572    checkUsabilityParser.addArgument(checkUsabilityKeystorePasswordFile);
3573
3574    final BooleanArgument checkUsabilityPromptForKeystorePassword =
3575         new BooleanArgument(null, "prompt-for-keystore-password",
3576        INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_PROMPT_FOR_KS_PW_DESC.get());
3577    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
3578         "promptForKeystorePassword", true);
3579    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
3580         "prompt-for-keystore-passphrase", true);
3581    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
3582         "promptForKeystorePassphrase", true);
3583    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
3584         "prompt-for-keystore-pin", true);
3585    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
3586         "promptForKeystorePIN", true);
3587    checkUsabilityParser.addArgument(checkUsabilityPromptForKeystorePassword);
3588
3589    final StringArgument checkUsabilityAlias = new StringArgument(null, "alias",
3590         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3591         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_ALIAS_DESC.get());
3592    checkUsabilityAlias.addLongIdentifier("nickname", true);
3593    checkUsabilityParser.addArgument(checkUsabilityAlias);
3594
3595    checkUsabilityParser.addRequiredArgumentSet(checkUsabilityKeystorePassword,
3596         checkUsabilityKeystorePasswordFile,
3597         checkUsabilityPromptForKeystorePassword);
3598    checkUsabilityParser.addExclusiveArgumentSet(checkUsabilityKeystorePassword,
3599         checkUsabilityKeystorePasswordFile,
3600         checkUsabilityPromptForKeystorePassword);
3601
3602    final LinkedHashMap<String[],String> checkUsabilityExamples =
3603         new LinkedHashMap<>(2);
3604    checkUsabilityExamples.put(
3605         new String[]
3606         {
3607           "check-certificate-usability",
3608           "--keystore", getPlatformSpecificPath("config", "keystore"),
3609           "--keystore-password-file",
3610                getPlatformSpecificPath("config", "keystore.pin"),
3611           "--alias", "server-cert"
3612         },
3613         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_EXAMPLE_1.get(
3614              getPlatformSpecificPath("config", "keystore")));
3615
3616    final SubCommand checkUsabilitySubCommand = new SubCommand(
3617         "check-certificate-usability",
3618         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_DESC.get(), checkUsabilityParser,
3619         checkUsabilityExamples);
3620    checkUsabilitySubCommand.addName("checkCertificateUsability", true);
3621    checkUsabilitySubCommand.addName("check-usability", true);
3622    checkUsabilitySubCommand.addName("checkUsability", true);
3623
3624    parser.addSubCommand(checkUsabilitySubCommand);
3625
3626
3627    // Define the "display-certificate-file" subcommand and all of its
3628    // arguments.
3629    final ArgumentParser displayCertParser = new ArgumentParser(
3630         "display-certificate-file",
3631         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_DESC.get());
3632
3633    final FileArgument displayCertFile = new FileArgument(null,
3634         "certificate-file", true, 1, null,
3635         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_ARG_FILE_DESC.get(), true, true,
3636         true, false);
3637    displayCertFile.addLongIdentifier("certificateFile", true);
3638    displayCertFile.addLongIdentifier("input-file", true);
3639    displayCertFile.addLongIdentifier("inputFile", true);
3640    displayCertFile.addLongIdentifier("file", true);
3641    displayCertFile.addLongIdentifier("filename", true);
3642    displayCertParser.addArgument(displayCertFile);
3643
3644    final BooleanArgument displayCertVerbose = new BooleanArgument(null,
3645         "verbose", 1,
3646         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_ARG_VERBOSE_DESC.get());
3647    displayCertParser.addArgument(displayCertVerbose);
3648
3649    final BooleanArgument displayCertDisplayCommand = new BooleanArgument(null,
3650         "display-keytool-command", 1,
3651         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_ARG_DISPLAY_COMMAND_DESC.get());
3652    displayCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3653    displayCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
3654    displayCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3655    displayCertParser.addArgument(displayCertDisplayCommand);
3656
3657    final LinkedHashMap<String[],String> displayCertExamples =
3658         new LinkedHashMap<>(2);
3659    displayCertExamples.put(
3660         new String[]
3661         {
3662           "display-certificate-file",
3663           "--certificate-file", "certificate.pem",
3664         },
3665         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_EXAMPLE_1.get("certificate.pem"));
3666    displayCertExamples.put(
3667         new String[]
3668         {
3669           "display-certificate-file",
3670           "--certificate-file", "certificate.pem",
3671           "--verbose",
3672           "--display-keytool-command"
3673         },
3674         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_EXAMPLE_2.get("certificate.pem"));
3675
3676    final SubCommand displayCertSubCommand = new SubCommand(
3677         "display-certificate-file",
3678         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_DESC.get(), displayCertParser,
3679         displayCertExamples);
3680    displayCertSubCommand.addName("displayCertificateFile", true);
3681    displayCertSubCommand.addName("display-certificate", false);
3682    displayCertSubCommand.addName("displayCertificate", true);
3683    displayCertSubCommand.addName("display-certificates", true);
3684    displayCertSubCommand.addName("displayCertificates", true);
3685    displayCertSubCommand.addName("show-certificate", true);
3686    displayCertSubCommand.addName("showCertificate", true);
3687    displayCertSubCommand.addName("show-certificate-file", true);
3688    displayCertSubCommand.addName("showCertificateFile", true);
3689    displayCertSubCommand.addName("show-certificates", true);
3690    displayCertSubCommand.addName("showCertificates", true);
3691    displayCertSubCommand.addName("print-certificate-file", false);
3692    displayCertSubCommand.addName("printCertificateFile", true);
3693    displayCertSubCommand.addName("print-certificate", false);
3694    displayCertSubCommand.addName("printCertificate", true);
3695    displayCertSubCommand.addName("print-certificates", true);
3696    displayCertSubCommand.addName("printCertificates", true);
3697    displayCertSubCommand.addName("printcert", true);
3698
3699    parser.addSubCommand(displayCertSubCommand);
3700
3701
3702    // Define the "display-certificate-signing-request-file" subcommand and all
3703    // of its arguments.
3704    final ArgumentParser displayCSRParser = new ArgumentParser(
3705         "display-certificate-signing-request-file",
3706         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_DESC.get());
3707
3708    final FileArgument displayCSRFile = new FileArgument(null,
3709         "certificate-signing-request-file", true, 1, null,
3710         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_ARG_FILE_DESC.get(), true, true,
3711         true, false);
3712    displayCSRFile.addLongIdentifier("certificateSigningRequestFile", true);
3713    displayCSRFile.addLongIdentifier("request-file", false);
3714    displayCSRFile.addLongIdentifier("requestFile", true);
3715    displayCSRFile.addLongIdentifier("input-file", true);
3716    displayCSRFile.addLongIdentifier("inputFile", true);
3717    displayCSRFile.addLongIdentifier("file", true);
3718    displayCSRFile.addLongIdentifier("filename", true);
3719    displayCSRParser.addArgument(displayCSRFile);
3720
3721    final BooleanArgument displayCSRVerbose = new BooleanArgument(null,
3722         "verbose", 1,
3723         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_ARG_VERBOSE_DESC.get());
3724    displayCSRParser.addArgument(displayCSRVerbose);
3725
3726    final BooleanArgument displayCSRDisplayCommand = new BooleanArgument(null,
3727         "display-keytool-command", 1,
3728         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_ARG_DISPLAY_COMMAND_DESC.get());
3729    displayCSRDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3730    displayCSRDisplayCommand.addLongIdentifier("show-keytool-command", true);
3731    displayCSRDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3732    displayCSRParser.addArgument(displayCSRDisplayCommand);
3733
3734    final LinkedHashMap<String[],String> displayCSRExamples =
3735         new LinkedHashMap<>(1);
3736    displayCSRExamples.put(
3737         new String[]
3738         {
3739           "display-certificate-signing-request-file",
3740           "--certificate-signing-request-file", "server-cert.csr",
3741           "--display-keytool-command"
3742         },
3743         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_EXAMPLE_1.get("server-cert.csr"));
3744
3745    final SubCommand displayCSRSubCommand = new SubCommand(
3746         "display-certificate-signing-request-file",
3747         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_DESC.get(), displayCSRParser,
3748         displayCSRExamples);
3749    displayCSRSubCommand.addName("displayCertificateSigningRequestFile", true);
3750    displayCSRSubCommand.addName("display-certificate-signing-request", true);
3751    displayCSRSubCommand.addName("displayCertificateSigningRequest", true);
3752    displayCSRSubCommand.addName("display-certificate-request-file", true);
3753    displayCSRSubCommand.addName("displayCertificateRequestFile", true);
3754    displayCSRSubCommand.addName("display-certificate-request", false);
3755    displayCSRSubCommand.addName("displayCertificateRequest", true);
3756    displayCSRSubCommand.addName("display-csr-file", true);
3757    displayCSRSubCommand.addName("displayCSRFile", true);
3758    displayCSRSubCommand.addName("display-csr", true);
3759    displayCSRSubCommand.addName("displayCSR", true);
3760    displayCSRSubCommand.addName("show-certificate-signing-request-file", true);
3761    displayCSRSubCommand.addName("showCertificateSigningRequestFile", true);
3762    displayCSRSubCommand.addName("show-certificate-signing-request", true);
3763    displayCSRSubCommand.addName("showCertificateSigningRequest", true);
3764    displayCSRSubCommand.addName("show-certificate-request-file", true);
3765    displayCSRSubCommand.addName("showCertificateRequestFile", true);
3766    displayCSRSubCommand.addName("show-certificate-request", true);
3767    displayCSRSubCommand.addName("showCertificateRequest", true);
3768    displayCSRSubCommand.addName("show-csr-file", true);
3769    displayCSRSubCommand.addName("showCSRFile", true);
3770    displayCSRSubCommand.addName("show-csr", true);
3771    displayCSRSubCommand.addName("showCSR", true);
3772    displayCSRSubCommand.addName("print-certificate-signing-request-file",
3773         false);
3774    displayCSRSubCommand.addName("printCertificateSigningRequestFile", true);
3775    displayCSRSubCommand.addName("print-certificate-signing-request", true);
3776    displayCSRSubCommand.addName("printCertificateSigningRequest", true);
3777    displayCSRSubCommand.addName("print-certificate-request-file", true);
3778    displayCSRSubCommand.addName("printCertificateRequestFile", true);
3779    displayCSRSubCommand.addName("print-certificate-request", false);
3780    displayCSRSubCommand.addName("printCertificateRequest", true);
3781    displayCSRSubCommand.addName("print-csr-file", true);
3782    displayCSRSubCommand.addName("printCSRFile", true);
3783    displayCSRSubCommand.addName("print-csr", true);
3784    displayCSRSubCommand.addName("printCSR", true);
3785    displayCSRSubCommand.addName("printcertreq", true);
3786
3787    parser.addSubCommand(displayCSRSubCommand);
3788  }
3789
3790
3791
3792  /**
3793   * Constructs a platform-specific relative path from the provided elements.
3794   *
3795   * @param  pathElements  The elements of the path to construct.  It must not
3796   *                       be {@code null} or empty.
3797   *
3798   * @return  The constructed path.
3799   */
3800  private static String getPlatformSpecificPath(final String... pathElements)
3801  {
3802    final StringBuilder buffer = new StringBuilder();
3803    for (int i=0; i < pathElements.length; i++)
3804    {
3805      if (i > 0)
3806      {
3807        buffer.append(File.separatorChar);
3808      }
3809
3810      buffer.append(pathElements[i]);
3811    }
3812
3813    return buffer.toString();
3814  }
3815
3816
3817
3818  /**
3819   * Performs the core set of processing for this tool.
3820   *
3821   * @return  A result code that indicates whether the processing completed
3822   *          successfully.
3823   */
3824  @Override()
3825  public ResultCode doToolProcessing()
3826  {
3827    final SubCommand selectedSubCommand = globalParser.getSelectedSubCommand();
3828    if (selectedSubCommand == null)
3829    {
3830      // This should never happen.
3831      wrapErr(0, WRAP_COLUMN, ERR_MANAGE_CERTS_NO_SUBCOMMAND.get());
3832      return ResultCode.PARAM_ERROR;
3833    }
3834
3835    subCommandParser = selectedSubCommand.getArgumentParser();
3836
3837    if (selectedSubCommand.hasName("list-certificates"))
3838    {
3839      return doListCertificates();
3840    }
3841    else if (selectedSubCommand.hasName("export-certificate"))
3842    {
3843      return doExportCertificate();
3844    }
3845    else if (selectedSubCommand.hasName("export-private-key"))
3846    {
3847      return doExportPrivateKey();
3848    }
3849    else if (selectedSubCommand.hasName("import-certificate"))
3850    {
3851      return doImportCertificate();
3852    }
3853    else if (selectedSubCommand.hasName("delete-certificate"))
3854    {
3855      return doDeleteCertificate();
3856    }
3857    else if (selectedSubCommand.hasName("generate-self-signed-certificate"))
3858    {
3859      return doGenerateOrSignCertificateOrCSR();
3860    }
3861    else if (selectedSubCommand.hasName("generate-certificate-signing-request"))
3862    {
3863      return doGenerateOrSignCertificateOrCSR();
3864    }
3865    else if (selectedSubCommand.hasName("sign-certificate-signing-request"))
3866    {
3867      return doGenerateOrSignCertificateOrCSR();
3868    }
3869    else if (selectedSubCommand.hasName("change-certificate-alias"))
3870    {
3871      return doChangeCertificateAlias();
3872    }
3873    else if (selectedSubCommand.hasName("change-keystore-password"))
3874    {
3875      return doChangeKeystorePassword();
3876    }
3877    else if (selectedSubCommand.hasName("change-private-key-password"))
3878    {
3879      return doChangePrivateKeyPassword();
3880    }
3881    else if (selectedSubCommand.hasName("trust-server-certificate"))
3882    {
3883      return doTrustServerCertificate();
3884    }
3885    else if (selectedSubCommand.hasName("check-certificate-usability"))
3886    {
3887      return doCheckCertificateUsability();
3888    }
3889    else if (selectedSubCommand.hasName("display-certificate-file"))
3890    {
3891      return doDisplayCertificateFile();
3892    }
3893    else if (selectedSubCommand.hasName(
3894         "display-certificate-signing-request-file"))
3895    {
3896      return doDisplayCertificateSigningRequestFile();
3897    }
3898    else
3899    {
3900      // This should never happen.
3901      wrapErr(0, WRAP_COLUMN,
3902           ERR_MANAGE_CERTS_UNKNOWN_SUBCOMMAND.get(
3903                selectedSubCommand.getPrimaryName()));
3904      return ResultCode.PARAM_ERROR;
3905    }
3906  }
3907
3908
3909
3910  /**
3911   * Performs the necessary processing for the list-certificates subcommand.
3912   *
3913   * @return  A result code that indicates whether the processing completed
3914   *          successfully.
3915   */
3916  private ResultCode doListCertificates()
3917  {
3918    // Get the values of a number of configured arguments.
3919    final BooleanArgument displayPEMArgument =
3920         subCommandParser.getBooleanArgument("display-pem-certificate");
3921    final boolean displayPEM =
3922         ((displayPEMArgument != null) && displayPEMArgument.isPresent());
3923
3924    final BooleanArgument verboseArgument =
3925         subCommandParser.getBooleanArgument("verbose");
3926    final boolean verbose =
3927         ((verboseArgument != null) && verboseArgument.isPresent());
3928
3929    final Map<String,String> missingAliases;
3930    final Set<String> aliases;
3931    final StringArgument aliasArgument =
3932         subCommandParser.getStringArgument("alias");
3933    if ((aliasArgument == null) || (! aliasArgument.isPresent()))
3934    {
3935      aliases = Collections.emptySet();
3936      missingAliases = Collections.emptyMap();
3937    }
3938    else
3939    {
3940      final List<String> values = aliasArgument.getValues();
3941      aliases = new LinkedHashSet<>(values.size());
3942      missingAliases = new LinkedHashMap<>(values.size());
3943      for (final String alias : values)
3944      {
3945        final String lowerAlias = StaticUtils.toLowerCase(alias);
3946        aliases.add(StaticUtils.toLowerCase(lowerAlias));
3947        missingAliases.put(lowerAlias, alias);
3948      }
3949    }
3950
3951    final String keystoreType;
3952    final File keystorePath = getKeystorePath();
3953    try
3954    {
3955      keystoreType = inferKeystoreType(keystorePath);
3956    }
3957    catch (final LDAPException le)
3958    {
3959      Debug.debugException(le);
3960      wrapErr(0, WRAP_COLUMN, le.getMessage());
3961      return le.getResultCode();
3962    }
3963
3964    final char[] keystorePassword;
3965    try
3966    {
3967      keystorePassword = getKeystorePassword(keystorePath);
3968    }
3969    catch (final LDAPException le)
3970    {
3971      Debug.debugException(le);
3972      wrapErr(0, WRAP_COLUMN, le.getMessage());
3973      return le.getResultCode();
3974    }
3975
3976    final BooleanArgument displayKeytoolCommandArgument =
3977         subCommandParser.getBooleanArgument("display-keytool-command");
3978    if ((displayKeytoolCommandArgument != null) &&
3979        displayKeytoolCommandArgument.isPresent())
3980    {
3981      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
3982      keytoolArgs.add("-list");
3983
3984      keytoolArgs.add("-keystore");
3985      keytoolArgs.add(keystorePath.getAbsolutePath());
3986      keytoolArgs.add("-storetype");
3987      keytoolArgs.add(keystoreType);
3988
3989      if (keystorePassword != null)
3990      {
3991        keytoolArgs.add("-storepass");
3992        keytoolArgs.add("*****REDACTED*****");
3993      }
3994
3995      for (final String alias : missingAliases.values())
3996      {
3997        keytoolArgs.add("-alias");
3998        keytoolArgs.add(alias);
3999      }
4000
4001      if (displayPEM)
4002      {
4003        keytoolArgs.add("-rfc");
4004      }
4005
4006      if (verbose)
4007      {
4008        keytoolArgs.add("-v");
4009      }
4010
4011      displayKeytoolCommand(keytoolArgs);
4012    }
4013
4014
4015    // Get the keystore.
4016    final KeyStore keystore;
4017    try
4018    {
4019      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
4020    }
4021    catch (final LDAPException le)
4022    {
4023      Debug.debugException(le);
4024      wrapErr(0, WRAP_COLUMN, le.getMessage());
4025      return le.getResultCode();
4026    }
4027
4028
4029    // Iterate through the keystore and display the appropriate certificates.
4030    final Enumeration<String> aliasEnumeration;
4031    try
4032    {
4033      aliasEnumeration = keystore.aliases();
4034    }
4035    catch (final Exception e)
4036    {
4037      Debug.debugException(e);
4038      err();
4039      wrapErr(0, WRAP_COLUMN,
4040           ERR_MANAGE_CERTS_LIST_CERTS_CANNOT_GET_ALIASES.get(
4041                keystorePath.getAbsolutePath()));
4042      e.printStackTrace(getErr());
4043      return ResultCode.LOCAL_ERROR;
4044    }
4045
4046    int listedCount = 0;
4047    ResultCode resultCode = ResultCode.SUCCESS;
4048    while (aliasEnumeration.hasMoreElements())
4049    {
4050      final String alias = aliasEnumeration.nextElement();
4051      final String lowerAlias = StaticUtils.toLowerCase(alias);
4052      if ((!aliases.isEmpty()) && (missingAliases.remove(lowerAlias) == null))
4053      {
4054        // We don't care about this alias.
4055        continue;
4056      }
4057
4058      final X509Certificate[] certificateChain;
4059      try
4060      {
4061        // NOTE:  Keystore entries that have private keys may have a certificate
4062        // chain associated with them (the end certificate plus all of the
4063        // issuer certificates).  In that case all of those certificates in the
4064        // chain will be stored under the same alias, and the only way we can
4065        // access them is to call the getCertificateChain method.  However, if
4066        // the keystore only has a certificate for the alias but no private key,
4067        // then the entry will not have a chain, and the call to
4068        // getCertificateChain will return null for that alias.  We want to be
4069        // able to handle both of these cases, so we will first try
4070        // getCertificateChain to see if we can get a complete chain, but if
4071        // that returns null, then use getCertificate to see if we can get a
4072        // single certificate.  That call to getCertificate can also return null
4073        // because the entry with this alias might be some other type of entry,
4074        // like a secret key entry.
4075        Certificate[] chain = keystore.getCertificateChain(alias);
4076        if ((chain == null) || (chain.length == 0))
4077        {
4078          final Certificate cert = keystore.getCertificate(alias);
4079          if (cert == null)
4080          {
4081            continue;
4082          }
4083          else
4084          {
4085            chain = new Certificate[] { cert };
4086          }
4087        }
4088
4089        certificateChain = new X509Certificate[chain.length];
4090        for (int i = 0; i < chain.length; i++)
4091        {
4092          certificateChain[i] = new X509Certificate(chain[i].getEncoded());
4093        }
4094      }
4095      catch (final Exception e)
4096      {
4097        Debug.debugException(e);
4098        err();
4099        wrapErr(0, WRAP_COLUMN,
4100             ERR_MANAGE_CERTS_LIST_CERTS_ERROR_GETTING_CERT.get(alias,
4101                  StaticUtils.getExceptionMessage(e)));
4102        resultCode = ResultCode.LOCAL_ERROR;
4103        continue;
4104      }
4105
4106      listedCount++;
4107      for (int i = 0; i < certificateChain.length; i++)
4108      {
4109        out();
4110        if (certificateChain.length == 1)
4111        {
4112          out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_ALIAS_WITHOUT_CHAIN.get(
4113               alias));
4114        }
4115        else
4116        {
4117          out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_ALIAS_WITH_CHAIN.get(alias,
4118               (i + 1), certificateChain.length));
4119        }
4120
4121        printCertificate(certificateChain[i], "", verbose);
4122
4123        if (i == 0)
4124        {
4125          if (hasKeyAlias(keystore, alias))
4126          {
4127            out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_HAS_PK_YES.get());
4128          }
4129          else
4130          {
4131            out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_HAS_PK_NO.get());
4132          }
4133        }
4134
4135        CertException signatureVerificationException = null;
4136        if (certificateChain[i].isSelfSigned())
4137        {
4138          try
4139          {
4140            certificateChain[i].verifySignature(null);
4141          }
4142          catch (final CertException ce)
4143          {
4144            Debug.debugException(ce);
4145            signatureVerificationException = ce;
4146          }
4147        }
4148        else
4149        {
4150          X509Certificate issuerCertificate = null;
4151          try
4152          {
4153            final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
4154                 new AtomicReference<>();
4155            final AtomicReference<DN> missingIssuerRef =
4156                 new AtomicReference<>();
4157            issuerCertificate = getIssuerCertificate(certificateChain[i],
4158                 keystore, jvmDefaultTrustStoreRef, missingIssuerRef);
4159          }
4160          catch (final Exception e)
4161          {
4162            Debug.debugException(e);
4163          }
4164
4165          if (issuerCertificate == null)
4166          {
4167            signatureVerificationException = new CertException(
4168                 ERR_MANAGE_CERTS_LIST_CERTS_VERIFY_SIGNATURE_NO_ISSUER.get(
4169                      certificateChain[i].getIssuerDN()));
4170          }
4171          else
4172          {
4173            try
4174            {
4175              certificateChain[i].verifySignature(issuerCertificate);
4176            }
4177            catch (final CertException ce)
4178            {
4179              Debug.debugException(ce);
4180              signatureVerificationException = ce;
4181            }
4182          }
4183        }
4184
4185        if (signatureVerificationException == null)
4186        {
4187          wrapOut(0, WRAP_COLUMN,
4188               INFO_MANAGE_CERTS_LIST_CERTS_SIGNATURE_VALID.get());
4189        }
4190        else
4191        {
4192          wrapErr(0, WRAP_COLUMN,
4193               signatureVerificationException.getMessage());
4194        }
4195
4196        if (displayPEM)
4197        {
4198          out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_PEM.get());
4199          writePEMCertificate(getOut(),
4200               certificateChain[i].getX509CertificateBytes());
4201        }
4202      }
4203    }
4204
4205    if (! missingAliases.isEmpty())
4206    {
4207      err();
4208      for (final String missingAlias : missingAliases.values())
4209      {
4210        wrapErr(0, WRAP_COLUMN,
4211             WARN_MANAGE_CERTS_LIST_CERTS_ALIAS_NOT_IN_KS.get(missingAlias,
4212                  keystorePath.getAbsolutePath()));
4213        resultCode = ResultCode.PARAM_ERROR;
4214      }
4215    }
4216    else if (listedCount == 0)
4217    {
4218      out();
4219      if (keystorePassword == null)
4220      {
4221        wrapOut(0, WRAP_COLUMN,
4222             INFO_MANAGE_CERTS_LIST_CERTS_NO_CERTS_OR_KEYS_WITHOUT_PW.get());
4223      }
4224      else
4225      {
4226        wrapOut(0, WRAP_COLUMN,
4227             INFO_MANAGE_CERTS_LIST_CERTS_NO_CERTS_OR_KEYS_WITH_PW.get());
4228      }
4229    }
4230
4231    return resultCode;
4232  }
4233
4234
4235
4236  /**
4237   * Performs the necessary processing for the export-certificate subcommand.
4238   *
4239   * @return  A result code that indicates whether the processing completed
4240   *          successfully.
4241   */
4242  private ResultCode doExportCertificate()
4243  {
4244    // Get the values of a number of configured arguments.
4245    final StringArgument aliasArgument =
4246         subCommandParser.getStringArgument("alias");
4247    final String alias = aliasArgument.getValue();
4248
4249    final BooleanArgument exportChainArgument =
4250         subCommandParser.getBooleanArgument("export-certificate-chain");
4251    final boolean exportChain =
4252         ((exportChainArgument != null) && exportChainArgument.isPresent());
4253
4254    final BooleanArgument separateFilePerCertificateArgument =
4255         subCommandParser.getBooleanArgument("separate-file-per-certificate");
4256    final boolean separateFilePerCertificate =
4257         ((separateFilePerCertificateArgument != null) &&
4258          separateFilePerCertificateArgument.isPresent());
4259
4260    boolean exportPEM = true;
4261    final StringArgument outputFormatArgument =
4262         subCommandParser.getStringArgument("output-format");
4263    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
4264    {
4265      final String format = outputFormatArgument.getValue().toLowerCase();
4266      if (format.equals("der") || format.equals("binary") ||
4267          format.equals("bin"))
4268      {
4269        exportPEM = false;
4270      }
4271    }
4272
4273    File outputFile = null;
4274    final FileArgument outputFileArgument =
4275         subCommandParser.getFileArgument("output-file");
4276    if ((outputFileArgument != null) && outputFileArgument.isPresent())
4277    {
4278      outputFile = outputFileArgument.getValue();
4279    }
4280
4281    if ((outputFile == null) && (! exportPEM))
4282    {
4283      wrapErr(0, WRAP_COLUMN,
4284           ERR_MANAGE_CERTS_EXPORT_CERT_NO_FILE_WITH_DER.get());
4285      return ResultCode.PARAM_ERROR;
4286    }
4287
4288    final String keystoreType;
4289    final File keystorePath = getKeystorePath();
4290    try
4291    {
4292      keystoreType = inferKeystoreType(keystorePath);
4293    }
4294    catch (final LDAPException le)
4295    {
4296      Debug.debugException(le);
4297      wrapErr(0, WRAP_COLUMN, le.getMessage());
4298      return le.getResultCode();
4299    }
4300
4301    final char[] keystorePassword;
4302    try
4303    {
4304      keystorePassword = getKeystorePassword(keystorePath);
4305    }
4306    catch (final LDAPException le)
4307    {
4308      Debug.debugException(le);
4309      wrapErr(0, WRAP_COLUMN, le.getMessage());
4310      return le.getResultCode();
4311    }
4312
4313    final BooleanArgument displayKeytoolCommandArgument =
4314         subCommandParser.getBooleanArgument("display-keytool-command");
4315    if ((displayKeytoolCommandArgument != null) &&
4316        displayKeytoolCommandArgument.isPresent())
4317    {
4318      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
4319      keytoolArgs.add("-list");
4320
4321      keytoolArgs.add("-keystore");
4322      keytoolArgs.add(keystorePath.getAbsolutePath());
4323      keytoolArgs.add("-storetype");
4324      keytoolArgs.add(keystoreType);
4325
4326      if (keystorePassword != null)
4327      {
4328        keytoolArgs.add("-storepass");
4329        keytoolArgs.add("*****REDACTED*****");
4330      }
4331
4332      keytoolArgs.add("-alias");
4333      keytoolArgs.add(alias);
4334
4335      if (exportPEM)
4336      {
4337        keytoolArgs.add("-rfc");
4338      }
4339
4340      if (outputFile != null)
4341      {
4342        keytoolArgs.add("-file");
4343        keytoolArgs.add(outputFile.getAbsolutePath());
4344      }
4345
4346      displayKeytoolCommand(keytoolArgs);
4347    }
4348
4349
4350    // Get the keystore.
4351    final KeyStore keystore;
4352    try
4353    {
4354      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
4355    }
4356    catch (final LDAPException le)
4357    {
4358      Debug.debugException(le);
4359      wrapErr(0, WRAP_COLUMN, le.getMessage());
4360      return le.getResultCode();
4361    }
4362
4363
4364    // Get the certificates to export.  If the --export-certificate-chain
4365    // argument was provided, this can be multiple certificates.  Otherwise, it
4366    // there will only be one.
4367    DN missingIssuerDN = null;
4368    final X509Certificate[] certificatesToExport;
4369    if (exportChain)
4370    {
4371      try
4372      {
4373        final AtomicReference<DN> missingIssuerRef = new AtomicReference<>();
4374        certificatesToExport =
4375             getCertificateChain(alias, keystore, missingIssuerRef);
4376        missingIssuerDN = missingIssuerRef.get();
4377      }
4378      catch (final LDAPException le)
4379      {
4380        Debug.debugException(le);
4381        wrapErr(0, WRAP_COLUMN, le.getMessage());
4382        return le.getResultCode();
4383      }
4384    }
4385    else
4386    {
4387      try
4388      {
4389        final Certificate cert = keystore.getCertificate(alias);
4390        if (cert == null)
4391        {
4392          certificatesToExport = new X509Certificate[0];
4393        }
4394        else
4395        {
4396          certificatesToExport = new X509Certificate[]
4397          {
4398            new X509Certificate(cert.getEncoded())
4399          };
4400        }
4401      }
4402      catch (final Exception e)
4403      {
4404        Debug.debugException(e);
4405        wrapErr(0, WRAP_COLUMN,
4406             ERR_MANAGE_CERTS_EXPORT_CERT_ERROR_GETTING_CERT.get(alias,
4407                  keystorePath.getAbsolutePath()));
4408        e.printStackTrace(getErr());
4409        return ResultCode.LOCAL_ERROR;
4410      }
4411    }
4412
4413    if (certificatesToExport.length == 0)
4414    {
4415      wrapErr(0, WRAP_COLUMN,
4416           ERR_MANAGE_CERTS_EXPORT_CERT_NO_CERT_WITH_ALIAS.get(alias,
4417                keystorePath.getAbsolutePath()));
4418      return ResultCode.PARAM_ERROR;
4419    }
4420
4421
4422    // Get a PrintStream to use for the output.
4423    int fileCounter = 1;
4424    String filename = null;
4425    PrintStream printStream;
4426    if (outputFile == null)
4427    {
4428      printStream = getOut();
4429    }
4430    else
4431    {
4432      try
4433      {
4434        if ((certificatesToExport.length > 1) && separateFilePerCertificate)
4435        {
4436          filename = outputFile.getAbsolutePath() + '.' + fileCounter;
4437        }
4438        else
4439        {
4440          filename = outputFile.getAbsolutePath();
4441        }
4442        printStream = new PrintStream(filename);
4443      }
4444      catch (final Exception e)
4445      {
4446        Debug.debugException(e);
4447        wrapErr(0, WRAP_COLUMN,
4448             ERR_MANAGE_CERTS_EXPORT_CERT_ERROR_OPENING_OUTPUT.get(
4449                  outputFile.getAbsolutePath()));
4450        e.printStackTrace(getErr());
4451        return ResultCode.LOCAL_ERROR;
4452      }
4453    }
4454
4455    try
4456    {
4457      for (final X509Certificate certificate : certificatesToExport)
4458      {
4459        try
4460        {
4461          if (separateFilePerCertificate && (certificatesToExport.length > 1))
4462          {
4463            if (fileCounter > 1)
4464            {
4465              printStream.close();
4466              filename = outputFile.getAbsolutePath() + '.' + fileCounter;
4467              printStream = new PrintStream(filename);
4468            }
4469
4470            fileCounter++;
4471          }
4472
4473          if (exportPEM)
4474          {
4475            writePEMCertificate(printStream,
4476                 certificate.getX509CertificateBytes());
4477          }
4478          else
4479          {
4480            printStream.write(certificate.getX509CertificateBytes());
4481          }
4482        }
4483        catch (final Exception e)
4484        {
4485          Debug.debugException(e);
4486          wrapErr(0, WRAP_COLUMN,
4487               ERR_MANAGE_CERTS_EXPORT_CERT_ERROR_WRITING_CERT.get(alias,
4488                    certificate.getSubjectDN()));
4489          e.printStackTrace(getErr());
4490          return ResultCode.LOCAL_ERROR;
4491        }
4492
4493        if (outputFile != null)
4494        {
4495          out();
4496          wrapOut(0, WRAP_COLUMN,
4497               INFO_MANAGE_CERTS_EXPORT_CERT_EXPORT_SUCCESSFUL.get(filename));
4498          printCertificate(certificate, "", false);
4499        }
4500      }
4501    }
4502    finally
4503    {
4504      printStream.flush();
4505      if (outputFile != null)
4506      {
4507        printStream.close();
4508      }
4509    }
4510
4511    if (missingIssuerDN != null)
4512    {
4513      err();
4514      wrapErr(0, WRAP_COLUMN,
4515           WARN_MANAGE_CERTS_EXPORT_CERT_MISSING_CERT_IN_CHAIN.get(
4516                missingIssuerDN, keystorePath.getAbsolutePath()));
4517      return ResultCode.NO_SUCH_OBJECT;
4518    }
4519
4520    return ResultCode.SUCCESS;
4521  }
4522
4523
4524
4525  /**
4526   * Performs the necessary processing for the export-private-key subcommand.
4527   *
4528   * @return  A result code that indicates whether the processing completed
4529   *          successfully.
4530   */
4531  private ResultCode doExportPrivateKey()
4532  {
4533    // Get the values of a number of configured arguments.
4534    final StringArgument aliasArgument =
4535         subCommandParser.getStringArgument("alias");
4536    final String alias = aliasArgument.getValue();
4537
4538    boolean exportPEM = true;
4539    final StringArgument outputFormatArgument =
4540         subCommandParser.getStringArgument("output-format");
4541    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
4542    {
4543      final String format = outputFormatArgument.getValue().toLowerCase();
4544      if (format.equals("der") || format.equals("binary") ||
4545          format.equals("bin"))
4546      {
4547        exportPEM = false;
4548      }
4549    }
4550
4551    File outputFile = null;
4552    final FileArgument outputFileArgument =
4553         subCommandParser.getFileArgument("output-file");
4554    if ((outputFileArgument != null) && outputFileArgument.isPresent())
4555    {
4556      outputFile = outputFileArgument.getValue();
4557    }
4558
4559    if ((outputFile == null) && (! exportPEM))
4560    {
4561      wrapErr(0, WRAP_COLUMN,
4562           ERR_MANAGE_CERTS_EXPORT_KEY_NO_FILE_WITH_DER.get());
4563      return ResultCode.PARAM_ERROR;
4564    }
4565
4566    final String keystoreType;
4567    final File keystorePath = getKeystorePath();
4568    try
4569    {
4570      keystoreType = inferKeystoreType(keystorePath);
4571    }
4572    catch (final LDAPException le)
4573    {
4574      Debug.debugException(le);
4575      wrapErr(0, WRAP_COLUMN, le.getMessage());
4576      return le.getResultCode();
4577    }
4578
4579    final char[] keystorePassword;
4580    try
4581    {
4582      keystorePassword = getKeystorePassword(keystorePath);
4583    }
4584    catch (final LDAPException le)
4585    {
4586      Debug.debugException(le);
4587      wrapErr(0, WRAP_COLUMN, le.getMessage());
4588      return le.getResultCode();
4589    }
4590
4591
4592    // Get the keystore.
4593    final KeyStore keystore;
4594    try
4595    {
4596      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
4597    }
4598    catch (final LDAPException le)
4599    {
4600      Debug.debugException(le);
4601      wrapErr(0, WRAP_COLUMN, le.getMessage());
4602      return le.getResultCode();
4603    }
4604
4605
4606    // See if we need to use a private key password that is different from the
4607    // keystore password.
4608    final char[] privateKeyPassword;
4609    try
4610    {
4611      privateKeyPassword =
4612           getPrivateKeyPassword(keystore, alias, keystorePassword);
4613    }
4614    catch (final LDAPException le)
4615    {
4616      Debug.debugException(le);
4617      wrapErr(0, WRAP_COLUMN, le.getMessage());
4618      return le.getResultCode();
4619    }
4620
4621
4622    // Get the private key to export.
4623    final PrivateKey privateKey;
4624    try
4625    {
4626      final Key key = keystore.getKey(alias, privateKeyPassword);
4627      if (key == null)
4628      {
4629        wrapErr(0, WRAP_COLUMN,
4630             ERR_MANAGE_CERTS_EXPORT_KEY_NO_KEY_WITH_ALIAS.get(alias,
4631                  keystorePath.getAbsolutePath()));
4632        return ResultCode.PARAM_ERROR;
4633      }
4634
4635      privateKey = (PrivateKey) key;
4636    }
4637    catch (final UnrecoverableKeyException e)
4638    {
4639      Debug.debugException(e);
4640      wrapErr(0, WRAP_COLUMN,
4641           ERR_MANAGE_CERTS_EXPORT_KEY_WRONG_KEY_PW.get(alias,
4642                keystorePath.getAbsolutePath()));
4643      return ResultCode.PARAM_ERROR;
4644    }
4645    catch (final Exception e)
4646    {
4647      Debug.debugException(e);
4648      wrapErr(0, WRAP_COLUMN,
4649           ERR_MANAGE_CERTS_EXPORT_KEY_ERROR_GETTING_KEY.get(alias,
4650                keystorePath.getAbsolutePath()));
4651      e.printStackTrace(getErr());
4652      return ResultCode.LOCAL_ERROR;
4653    }
4654
4655
4656    // Get a PrintStream to use for the output.
4657    final PrintStream printStream;
4658    if (outputFile == null)
4659    {
4660      printStream = getOut();
4661    }
4662    else
4663    {
4664      try
4665      {
4666        printStream = new PrintStream(outputFile);
4667      }
4668      catch (final Exception e)
4669      {
4670        Debug.debugException(e);
4671        wrapErr(0, WRAP_COLUMN,
4672             ERR_MANAGE_CERTS_EXPORT_KEY_ERROR_OPENING_OUTPUT.get(
4673                  outputFile.getAbsolutePath()));
4674        e.printStackTrace(getErr());
4675        return ResultCode.LOCAL_ERROR;
4676      }
4677    }
4678
4679    try
4680    {
4681      try
4682      {
4683        if (exportPEM)
4684        {
4685          writePEMPrivateKey(printStream, privateKey.getEncoded());
4686        }
4687        else
4688        {
4689          printStream.write(privateKey.getEncoded());
4690        }
4691      }
4692      catch (final Exception e)
4693      {
4694        Debug.debugException(e);
4695        wrapErr(0, WRAP_COLUMN,
4696             ERR_MANAGE_CERTS_EXPORT_KEY_ERROR_WRITING_KEY.get(alias));
4697        e.printStackTrace(getErr());
4698        return ResultCode.LOCAL_ERROR;
4699      }
4700
4701      if (outputFile != null)
4702      {
4703        out();
4704        wrapOut(0, WRAP_COLUMN,
4705             INFO_MANAGE_CERTS_EXPORT_KEY_EXPORT_SUCCESSFUL.get());
4706      }
4707    }
4708    finally
4709    {
4710      printStream.flush();
4711      if (outputFile != null)
4712      {
4713        printStream.close();
4714      }
4715    }
4716
4717    return ResultCode.SUCCESS;
4718  }
4719
4720
4721
4722  /**
4723   * Performs the necessary processing for the import-certificate subcommand.
4724   *
4725   * @return  A result code that indicates whether the processing completed
4726   *          successfully.
4727   */
4728  private ResultCode doImportCertificate()
4729  {
4730    // Get the values of a number of configured arguments.
4731    final StringArgument aliasArgument =
4732         subCommandParser.getStringArgument("alias");
4733    final String alias = aliasArgument.getValue();
4734
4735    final FileArgument certificateFileArgument =
4736         subCommandParser.getFileArgument("certificate-file");
4737    final List<File> certFiles = certificateFileArgument.getValues();
4738
4739    final File privateKeyFile;
4740    final FileArgument privateKeyFileArgument =
4741         subCommandParser.getFileArgument("private-key-file");
4742    if ((privateKeyFileArgument != null) && privateKeyFileArgument.isPresent())
4743    {
4744      privateKeyFile = privateKeyFileArgument.getValue();
4745    }
4746    else
4747    {
4748      privateKeyFile = null;
4749    }
4750
4751    final BooleanArgument noPromptArgument =
4752         subCommandParser.getBooleanArgument("no-prompt");
4753    final boolean noPrompt =
4754         ((noPromptArgument != null) && noPromptArgument.isPresent());
4755
4756    final String keystoreType;
4757    final File keystorePath = getKeystorePath();
4758    final boolean isNewKeystore = (! keystorePath.exists());
4759    try
4760    {
4761      keystoreType = inferKeystoreType(keystorePath);
4762    }
4763    catch (final LDAPException le)
4764    {
4765      Debug.debugException(le);
4766      wrapErr(0, WRAP_COLUMN, le.getMessage());
4767      return le.getResultCode();
4768    }
4769
4770
4771    final char[] keystorePassword;
4772    try
4773    {
4774      keystorePassword = getKeystorePassword(keystorePath);
4775    }
4776    catch (final LDAPException le)
4777    {
4778      Debug.debugException(le);
4779      wrapErr(0, WRAP_COLUMN, le.getMessage());
4780      return le.getResultCode();
4781    }
4782
4783
4784    // Read the contents of the certificate files.
4785    final ArrayList<X509Certificate> certList = new ArrayList<>(5);
4786    for (final File certFile : certFiles)
4787    {
4788      try
4789      {
4790        final List<X509Certificate> certs = readCertificatesFromFile(certFile);
4791        if (certs.isEmpty())
4792        {
4793          wrapErr(0, WRAP_COLUMN,
4794               ERR_MANAGE_CERTS_IMPORT_CERT_NO_CERTS_IN_FILE.get(
4795                    certFile.getAbsolutePath()));
4796          return ResultCode.PARAM_ERROR;
4797        }
4798
4799        certList.addAll(certs);
4800      }
4801      catch (final LDAPException le)
4802      {
4803        Debug.debugException(le);
4804        wrapErr(0, WRAP_COLUMN, le.getMessage());
4805        return le.getResultCode();
4806      }
4807    }
4808
4809
4810    // If a private key file was specified, then read the private key.
4811    final PKCS8PrivateKey privateKey;
4812    if (privateKeyFile == null)
4813    {
4814      privateKey = null;
4815    }
4816    else
4817    {
4818      try
4819      {
4820        privateKey = readPrivateKeyFromFile(privateKeyFile);
4821      }
4822      catch (final LDAPException le)
4823      {
4824        Debug.debugException(le);
4825        wrapErr(0, WRAP_COLUMN, le.getMessage());
4826        return le.getResultCode();
4827      }
4828    }
4829
4830
4831    // Get the keystore.
4832    final KeyStore keystore;
4833    try
4834    {
4835      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
4836    }
4837    catch (final LDAPException le)
4838    {
4839      Debug.debugException(le);
4840      wrapErr(0, WRAP_COLUMN, le.getMessage());
4841      return le.getResultCode();
4842    }
4843
4844
4845    // If there is a private key, then see if we need to use a private key
4846    // password that is different from the keystore password.
4847    final char[] privateKeyPassword;
4848    try
4849    {
4850      privateKeyPassword =
4851           getPrivateKeyPassword(keystore, alias, keystorePassword);
4852    }
4853    catch (final LDAPException le)
4854    {
4855      Debug.debugException(le);
4856      wrapErr(0, WRAP_COLUMN, le.getMessage());
4857      return le.getResultCode();
4858    }
4859
4860
4861    // If we should display an equivalent keytool command, then do that now.
4862    final BooleanArgument displayKeytoolCommandArgument =
4863         subCommandParser.getBooleanArgument("display-keytool-command");
4864    if ((displayKeytoolCommandArgument != null) &&
4865        displayKeytoolCommandArgument.isPresent())
4866    {
4867      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
4868      keytoolArgs.add("-import");
4869
4870      keytoolArgs.add("-keystore");
4871      keytoolArgs.add(keystorePath.getAbsolutePath());
4872      keytoolArgs.add("-storetype");
4873      keytoolArgs.add(keystoreType);
4874      keytoolArgs.add("-storepass");
4875      keytoolArgs.add("*****REDACTED*****");
4876      keytoolArgs.add("-keypass");
4877      keytoolArgs.add("*****REDACTED*****");
4878      keytoolArgs.add("-alias");
4879      keytoolArgs.add(alias);
4880      keytoolArgs.add("-file");
4881      keytoolArgs.add(certFiles.get(0).getAbsolutePath());
4882      keytoolArgs.add("-trustcacerts");
4883
4884      displayKeytoolCommand(keytoolArgs);
4885    }
4886
4887
4888    // Look at all the certificates to be imported.  Make sure that every
4889    // subsequent certificate in the chain is the issuer for the previous.
4890    final Iterator<X509Certificate> certIterator = certList.iterator();
4891    X509Certificate subjectCert = certIterator.next();
4892    while (true)
4893    {
4894      if (subjectCert.isSelfSigned())
4895      {
4896        if (certIterator.hasNext())
4897        {
4898          wrapErr(0, WRAP_COLUMN,
4899               ERR_MANAGE_CERTS_IMPORT_CERT_SELF_SIGNED_NOT_LAST.get(
4900                    subjectCert.getSubjectDN()));
4901          return ResultCode.PARAM_ERROR;
4902        }
4903      }
4904
4905      if (! certIterator.hasNext())
4906      {
4907        break;
4908      }
4909
4910      final X509Certificate issuerCert = certIterator.next();
4911      final StringBuilder notIssuerReason = new StringBuilder();
4912      if (! issuerCert.isIssuerFor(subjectCert, notIssuerReason))
4913      {
4914        // In some cases, the process of signing a certificate can put two
4915        // certificates in the output file (both the signed certificate and its
4916        // issuer.  If the same certificate is in the chain twice, then we'll
4917        // silently ignore it.
4918        if (Arrays.equals(issuerCert.getX509CertificateBytes(),
4919                 subjectCert.getX509CertificateBytes()))
4920        {
4921          certIterator.remove();
4922        }
4923        else
4924        {
4925          wrapErr(0, WRAP_COLUMN,
4926               ERR_MANAGE_CERTS_IMPORT_CERT_NEXT_NOT_ISSUER_OF_PREV.get(
4927                    notIssuerReason.toString()));
4928          return ResultCode.PARAM_ERROR;
4929        }
4930      }
4931
4932      subjectCert = issuerCert;
4933    }
4934
4935
4936    // If the last certificate in the chain is not self-signed, then make sure
4937    // that we can complete the chain using other certificates in the keystore
4938    // or in the JVM's set of default trusted issuers.  If we can't complete
4939    // the chain, then that's an error, although we'll go ahead and proceed
4940    // anyway with the import if we're not also importing a private key.
4941    final ArrayList<X509Certificate> chain;
4942    if (certList.get(certList.size() - 1).isSelfSigned())
4943    {
4944      chain = certList;
4945    }
4946    else
4947    {
4948      chain = new ArrayList<>(certList.size() + 5);
4949      chain.addAll(certList);
4950
4951      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
4952           new AtomicReference<>();
4953      final AtomicReference<DN> missingIssuerRef = new AtomicReference<>();
4954
4955      X509Certificate c = certList.get(certList.size() - 1);
4956      while (! c.isSelfSigned())
4957      {
4958        final X509Certificate issuer;
4959        try
4960        {
4961          issuer = getIssuerCertificate(c, keystore, jvmDefaultTrustStoreRef,
4962               missingIssuerRef);
4963        }
4964        catch (final Exception e)
4965        {
4966          Debug.debugException(e);
4967          wrapErr(0, WRAP_COLUMN,
4968               ERR_MANAGE_CERTS_IMPORT_CERT_CANNOT_GET_ISSUER.get(
4969                    c.getIssuerDN()));
4970          e.printStackTrace(getErr());
4971          return ResultCode.LOCAL_ERROR;
4972        }
4973
4974        if (issuer == null)
4975        {
4976          final byte[] authorityKeyIdentifier = getAuthorityKeyIdentifier(c);
4977
4978          // We couldn't find the issuer certificate.  If we're importing a
4979          // private key, or if the keystore already has a key entry with the
4980          // same alias that we're going to use, then this is definitely an
4981          // error because we can only write a key entry if we have a complete
4982          // certificate chain.
4983          //
4984          // If we weren't explicitly provided with a private key, then it's
4985          // still an undesirable thing to import a certificate without having
4986          // the complete set of issuers, but we'll go ahead and let it slide
4987          // with just a warning.
4988          if ((privateKey != null) || hasKeyAlias(keystore, alias))
4989          {
4990            if (authorityKeyIdentifier == null)
4991            {
4992              err();
4993              wrapErr(0, WRAP_COLUMN,
4994                   ERR_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_NO_AKI.get(
4995                        c.getIssuerDN()));
4996            }
4997            else
4998            {
4999              err();
5000              wrapErr(0, WRAP_COLUMN,
5001                   ERR_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_WITH_AKI.get(
5002                        c.getIssuerDN(),
5003                        toColonDelimitedHex(authorityKeyIdentifier)));
5004            }
5005
5006            return ResultCode.PARAM_ERROR;
5007          }
5008          else
5009          {
5010            if (authorityKeyIdentifier == null)
5011            {
5012              err();
5013              wrapErr(0, WRAP_COLUMN,
5014                   WARN_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_NO_AKI.get(
5015                        c.getIssuerDN()));
5016            }
5017            else
5018            {
5019              err();
5020              wrapErr(0, WRAP_COLUMN,
5021                   WARN_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_WITH_AKI.get(
5022                        c.getIssuerDN(),
5023                        toColonDelimitedHex(authorityKeyIdentifier)));
5024            }
5025
5026            break;
5027          }
5028        }
5029        else
5030        {
5031          chain.add(issuer);
5032          c = issuer;
5033        }
5034      }
5035    }
5036
5037
5038    // If we're going to import a private key with a certificate chain, then
5039    // perform the necessary validation and do the import.
5040    if (privateKey != null)
5041    {
5042      // Make sure that the keystore doesn't already have a key or certificate
5043      // with the specified alias.
5044      if (hasKeyAlias(keystore, alias))
5045      {
5046        wrapErr(0, WRAP_COLUMN,
5047             ERR_MANAGE_CERTS_IMPORT_CERT_WITH_PK_KEY_ALIAS_CONFLICT.get(
5048                  alias));
5049        return ResultCode.PARAM_ERROR;
5050      }
5051      else if (hasCertificateAlias(keystore, alias))
5052      {
5053        wrapErr(0, WRAP_COLUMN,
5054             ERR_MANAGE_CERTS_IMPORT_CERT_WITH_PK_CERT_ALIAS_CONFLICT.get(
5055                  alias));
5056        return ResultCode.PARAM_ERROR;
5057      }
5058
5059
5060      // Make sure that the private key has a key algorithm of either RSA or EC,
5061      // and convert it into a Java PrivateKey object.
5062      final PrivateKey javaPrivateKey;
5063      try
5064      {
5065        javaPrivateKey = privateKey.toPrivateKey();
5066      }
5067      catch (final Exception e)
5068      {
5069        Debug.debugException(e);
5070        wrapErr(0, WRAP_COLUMN,
5071             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_KEY.get(
5072                  privateKeyFile.getAbsolutePath()));
5073        e.printStackTrace(getErr());
5074        return ResultCode.LOCAL_ERROR;
5075      }
5076
5077
5078      // Convert the certificate chain into a Java Certificate[].
5079      final Certificate[] javaCertificateChain = new Certificate[chain.size()];
5080      for (int i=0; i < javaCertificateChain.length; i++)
5081      {
5082        final X509Certificate c = chain.get(i);
5083        try
5084        {
5085          javaCertificateChain[i] = c.toCertificate();
5086        }
5087        catch (final Exception e)
5088        {
5089          Debug.debugException(e);
5090          wrapErr(0, WRAP_COLUMN,
5091               ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_CERT.get(
5092                    c.getSubjectDN()));
5093          e.printStackTrace(getErr());
5094          return ResultCode.LOCAL_ERROR;
5095        }
5096      }
5097
5098
5099      // Prompt the user to confirm the import, if appropriate.
5100      if (! noPrompt)
5101      {
5102        out();
5103        wrapOut(0, WRAP_COLUMN,
5104             INFO_MANAGE_CERTS_IMPORT_CERT_CONFIRM_IMPORT_CHAIN_NEW_KEY.get(
5105                  alias));
5106
5107        for (final X509Certificate c : chain)
5108        {
5109          out();
5110          printCertificate(c, "", false);
5111        }
5112
5113        out();
5114
5115        try
5116        {
5117          if (! promptForYesNo(
5118               INFO_MANAGE_CERTS_IMPORT_CERT_PROMPT_IMPORT_CHAIN.get()))
5119          {
5120            wrapErr(0, WRAP_COLUMN,
5121                 ERR_MANAGE_CERTS_IMPORT_CERT_CANCELED.get());
5122            return ResultCode.USER_CANCELED;
5123          }
5124        }
5125        catch (final LDAPException le)
5126        {
5127          Debug.debugException(le);
5128          err();
5129          wrapErr(0, WRAP_COLUMN, le.getMessage());
5130          return le.getResultCode();
5131        }
5132      }
5133
5134
5135      // Set the private key entry in the keystore.
5136      try
5137      {
5138        keystore.setKeyEntry(alias, javaPrivateKey, privateKeyPassword,
5139             javaCertificateChain);
5140      }
5141      catch (final Exception e)
5142      {
5143        Debug.debugException(e);
5144        wrapErr(0, WRAP_COLUMN,
5145             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_UPDATING_KS_WITH_CHAIN.get(
5146                  alias));
5147        e.printStackTrace(getErr());
5148        return ResultCode.LOCAL_ERROR;
5149      }
5150
5151
5152      // Write the updated keystore to disk.
5153      try
5154      {
5155        writeKeystore(keystore, keystorePath, keystorePassword);
5156      }
5157      catch (final LDAPException le)
5158      {
5159        Debug.debugException(le);
5160        wrapErr(0, WRAP_COLUMN, le.getMessage());
5161        return le.getResultCode();
5162      }
5163
5164      if (isNewKeystore)
5165      {
5166        out();
5167        wrapOut(0, WRAP_COLUMN,
5168             INFO_MANAGE_CERTS_IMPORT_CERT_CREATED_KEYSTORE.get(
5169                  getUserFriendlyKeystoreType(keystoreType)));
5170      }
5171
5172      out();
5173      wrapOut(0, WRAP_COLUMN,
5174           INFO_MANAGE_CERTS_IMPORT_CERT_IMPORTED_CHAIN_WITH_PK.get());
5175      return ResultCode.SUCCESS;
5176    }
5177
5178
5179    // If we've gotten here, then we were given one or more certificates but no
5180    // private key.  See if the keystore already has a certificate entry with
5181    // the specified alias.  If so, then that's always an error.
5182    if (hasCertificateAlias(keystore, alias))
5183    {
5184      wrapErr(0, WRAP_COLUMN,
5185           ERR_MANAGE_CERTS_IMPORT_CERT_WITH_CONFLICTING_CERT_ALIAS.get(alias));
5186      return ResultCode.PARAM_ERROR;
5187    }
5188
5189
5190    // See if the keystore already has a key entry with the specified alias.
5191    // If so, then it may or may not be an error.  This can happen if we
5192    // generated a certificate signing request from an existing key pair, and
5193    // now want to import the signed certificate.  If that is the case, then we
5194    // will replace the existing key entry with a new one that contains the full
5195    // new certificate chain and the existing private key, but only if the
5196    // new certificate uses the same public key as the certificate at the head
5197    // of the existing chain in that alias.
5198    if (hasKeyAlias(keystore, alias))
5199    {
5200      // Make sure that the existing key pair uses the same public key as the
5201      // new certificate we are importing.
5202      final PrivateKey existingPrivateKey;
5203      final Certificate[] existingChain;
5204      final X509Certificate existingEndCertificate;
5205      try
5206      {
5207        existingPrivateKey =
5208             (PrivateKey) keystore.getKey(alias, privateKeyPassword);
5209        existingChain = keystore.getCertificateChain(alias);
5210        existingEndCertificate =
5211             new X509Certificate(existingChain[0].getEncoded());
5212      }
5213      catch (final Exception e)
5214      {
5215        Debug.debugException(e);
5216        wrapErr(0, WRAP_COLUMN,
5217             ERR_MANAGE_CERTS_IMPORT_CERT_INTO_KEY_ALIAS_CANNOT_GET_KEY.get(
5218                  alias));
5219        e.printStackTrace(getErr());
5220        return ResultCode.LOCAL_ERROR;
5221      }
5222
5223      final boolean[] existingPublicKeyBits =
5224           existingEndCertificate.getEncodedPublicKey().getBits();
5225      final boolean[] newPublicKeyBits =
5226           chain.get(0).getEncodedPublicKey().getBits();
5227      if (! Arrays.equals(existingPublicKeyBits, newPublicKeyBits))
5228      {
5229        wrapErr(0, WRAP_COLUMN,
5230             ERR_MANAGE_CERTS_IMPORT_CERT_INTO_KEY_ALIAS_KEY_MISMATCH.get(
5231                  alias));
5232        return ResultCode.PARAM_ERROR;
5233      }
5234
5235
5236      // Prepare the new certificate chain to store in the alias.
5237      final Certificate[] newChain = new Certificate[chain.size()];
5238      for (int i=0; i < chain.size(); i++)
5239      {
5240        final X509Certificate c = chain.get(i);
5241        try
5242        {
5243          newChain[i] = c.toCertificate();
5244        }
5245        catch (final Exception e)
5246        {
5247          Debug.debugException(e);
5248          wrapErr(0, WRAP_COLUMN,
5249               ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_CERT.get(
5250                    c.getSubjectDN()));
5251          e.printStackTrace(getErr());
5252          return ResultCode.LOCAL_ERROR;
5253        }
5254      }
5255
5256
5257      // Prompt the user to confirm the import, if appropriate.
5258      if (! noPrompt)
5259      {
5260        out();
5261        wrapOut(0, WRAP_COLUMN,
5262             INFO_MANAGE_CERTS_IMPORT_CERT_CONFIRM_IMPORT_CHAIN_EXISTING_KEY.
5263                  get(alias));
5264
5265        for (final X509Certificate c : chain)
5266        {
5267          out();
5268          printCertificate(c, "", false);
5269        }
5270
5271        out();
5272
5273        try
5274        {
5275          if (! promptForYesNo(
5276               INFO_MANAGE_CERTS_IMPORT_CERT_PROMPT_IMPORT_CHAIN.get()))
5277          {
5278            wrapErr(0, WRAP_COLUMN,
5279                 ERR_MANAGE_CERTS_IMPORT_CERT_CANCELED.get());
5280            return ResultCode.USER_CANCELED;
5281          }
5282        }
5283        catch (final LDAPException le)
5284        {
5285          Debug.debugException(le);
5286          err();
5287          wrapErr(0, WRAP_COLUMN, le.getMessage());
5288          return le.getResultCode();
5289        }
5290      }
5291
5292
5293      // Set the private key entry in the keystore.
5294      try
5295      {
5296        keystore.setKeyEntry(alias, existingPrivateKey, privateKeyPassword,
5297             newChain);
5298      }
5299      catch (final Exception e)
5300      {
5301        Debug.debugException(e);
5302        wrapErr(0, WRAP_COLUMN,
5303             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_UPDATING_KS_WITH_CHAIN.get(
5304                  alias));
5305        e.printStackTrace(getErr());
5306        return ResultCode.LOCAL_ERROR;
5307      }
5308
5309
5310      // Write the updated keystore to disk.
5311      try
5312      {
5313        writeKeystore(keystore, keystorePath, keystorePassword);
5314      }
5315      catch (final LDAPException le)
5316      {
5317        Debug.debugException(le);
5318        wrapErr(0, WRAP_COLUMN, le.getMessage());
5319        return le.getResultCode();
5320      }
5321
5322      out();
5323
5324      if (isNewKeystore)
5325      {
5326        wrapOut(0, WRAP_COLUMN,
5327             INFO_MANAGE_CERTS_IMPORT_CERT_CREATED_KEYSTORE.get(
5328                  getUserFriendlyKeystoreType(keystoreType)));
5329      }
5330
5331      wrapOut(0, WRAP_COLUMN,
5332           INFO_MANAGE_CERTS_IMPORT_CERT_IMPORTED_CHAIN_WITHOUT_PK.get());
5333      return ResultCode.SUCCESS;
5334    }
5335
5336
5337    // If we've gotten here, then we know that we're just going to add
5338    // certificate entries to the keystore.  Iterate through the certificates
5339    // and add them to the keystore under the appropriate aliases, first making
5340    // sure that the alias isn't already in use.
5341    final LinkedHashMap<String,X509Certificate> certMap =
5342         new LinkedHashMap<>(certList.size());
5343    for (int i=0; i < certList.size(); i++)
5344    {
5345      final X509Certificate x509Certificate = certList.get(i);
5346      final Certificate javaCertificate;
5347      try
5348      {
5349        javaCertificate = x509Certificate.toCertificate();
5350      }
5351      catch (final Exception e)
5352      {
5353        Debug.debugException(e);
5354        wrapErr(0, WRAP_COLUMN,
5355             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_CERT.get(
5356                  x509Certificate.getSubjectDN()));
5357        e.printStackTrace(getErr());
5358        return ResultCode.LOCAL_ERROR;
5359      }
5360
5361      final String certAlias;
5362      if (i == 0)
5363      {
5364        certAlias = alias;
5365      }
5366      else if (certList.size() > 2)
5367      {
5368        certAlias = alias + "-issuer-" + i;
5369      }
5370      else
5371      {
5372        certAlias = alias + "-issuer";
5373      }
5374
5375      certMap.put(certAlias, x509Certificate);
5376
5377      if (hasKeyAlias(keystore, certAlias) ||
5378          hasCertificateAlias(keystore, certAlias))
5379      {
5380        wrapErr(0, WRAP_COLUMN,
5381             ERR_MANAGE_CERTS_IMPORT_CERT_WITH_CONFLICTING_ISSUER_ALIAS.get(
5382                  x509Certificate.getSubjectDN(), certAlias));
5383        return ResultCode.PARAM_ERROR;
5384      }
5385
5386      try
5387      {
5388        keystore.setCertificateEntry(certAlias, javaCertificate);
5389      }
5390      catch (final Exception e)
5391      {
5392        Debug.debugException(e);
5393        wrapErr(0, WRAP_COLUMN,
5394             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_UPDATING_KS_WITH_CERT.get(
5395                  x509Certificate.getSubjectDN(), alias));
5396        e.printStackTrace(getErr());
5397        return ResultCode.LOCAL_ERROR;
5398      }
5399    }
5400
5401
5402    // Prompt about whether to perform the import, if appropriate.
5403    if (! noPrompt)
5404    {
5405      out();
5406      wrapOut(0, WRAP_COLUMN,
5407           INFO_MANAGE_CERTS_IMPORT_CERT_CONFIRM_IMPORT_CHAIN_NO_KEY.
5408                get(alias));
5409
5410      for (final Map.Entry<String,X509Certificate> e : certMap.entrySet())
5411      {
5412        out();
5413        wrapOut(0, WRAP_COLUMN,
5414             INFO_MANAGE_CERTS_IMPORT_CERT_LABEL_ALIAS.get(e.getKey()));
5415        printCertificate(e.getValue(), "", false);
5416      }
5417
5418      out();
5419
5420      try
5421      {
5422        if (! promptForYesNo(
5423             INFO_MANAGE_CERTS_IMPORT_CERT_PROMPT_IMPORT_CHAIN.get()))
5424        {
5425          wrapErr(0, WRAP_COLUMN,
5426               ERR_MANAGE_CERTS_IMPORT_CERT_CANCELED.get());
5427          return ResultCode.USER_CANCELED;
5428        }
5429      }
5430      catch (final LDAPException le)
5431      {
5432        Debug.debugException(le);
5433        err();
5434        wrapErr(0, WRAP_COLUMN, le.getMessage());
5435        return le.getResultCode();
5436      }
5437    }
5438
5439
5440    // Write the updated keystore to disk.
5441    try
5442    {
5443      writeKeystore(keystore, keystorePath, keystorePassword);
5444    }
5445    catch (final LDAPException le)
5446    {
5447      Debug.debugException(le);
5448      wrapErr(0, WRAP_COLUMN, le.getMessage());
5449      return le.getResultCode();
5450    }
5451
5452    out();
5453
5454    if (isNewKeystore)
5455    {
5456      wrapOut(0, WRAP_COLUMN,
5457           INFO_MANAGE_CERTS_IMPORT_CERT_CREATED_KEYSTORE.get(
5458                getUserFriendlyKeystoreType(keystoreType)));
5459    }
5460
5461    wrapOut(0, WRAP_COLUMN,
5462         INFO_MANAGE_CERTS_IMPORT_CERT_IMPORTED_CHAIN_WITHOUT_PK.get());
5463    return ResultCode.SUCCESS;
5464  }
5465
5466
5467
5468  /**
5469   * Performs the necessary processing for the delete-certificate subcommand.
5470   *
5471   * @return  A result code that indicates whether the processing completed
5472   *          successfully.
5473   */
5474  private ResultCode doDeleteCertificate()
5475  {
5476    // Get the values of a number of configured arguments.
5477    final StringArgument aliasArgument =
5478         subCommandParser.getStringArgument("alias");
5479    final String alias = aliasArgument.getValue();
5480
5481    final BooleanArgument noPromptArgument =
5482         subCommandParser.getBooleanArgument("no-prompt");
5483    final boolean noPrompt =
5484         ((noPromptArgument != null) && noPromptArgument.isPresent());
5485
5486    final String keystoreType;
5487    final File keystorePath = getKeystorePath();
5488    try
5489    {
5490      keystoreType = inferKeystoreType(keystorePath);
5491    }
5492    catch (final LDAPException le)
5493    {
5494      Debug.debugException(le);
5495      wrapErr(0, WRAP_COLUMN, le.getMessage());
5496      return le.getResultCode();
5497    }
5498
5499    final char[] keystorePassword;
5500    try
5501    {
5502      keystorePassword = getKeystorePassword(keystorePath);
5503    }
5504    catch (final LDAPException le)
5505    {
5506      Debug.debugException(le);
5507      wrapErr(0, WRAP_COLUMN, le.getMessage());
5508      return le.getResultCode();
5509    }
5510
5511    final BooleanArgument displayKeytoolCommandArgument =
5512         subCommandParser.getBooleanArgument("display-keytool-command");
5513    if ((displayKeytoolCommandArgument != null) &&
5514         displayKeytoolCommandArgument.isPresent())
5515    {
5516      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
5517      keytoolArgs.add("-delete");
5518
5519      keytoolArgs.add("-keystore");
5520      keytoolArgs.add(keystorePath.getAbsolutePath());
5521      keytoolArgs.add("-storetype");
5522      keytoolArgs.add(keystoreType);
5523      keytoolArgs.add("-storepass");
5524      keytoolArgs.add("*****REDACTED*****");
5525      keytoolArgs.add("-alias");
5526      keytoolArgs.add(alias);
5527
5528      displayKeytoolCommand(keytoolArgs);
5529    }
5530
5531
5532    // Get the keystore.
5533    final KeyStore keystore;
5534    try
5535    {
5536      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
5537    }
5538    catch (final LDAPException le)
5539    {
5540      Debug.debugException(le);
5541      wrapErr(0, WRAP_COLUMN, le.getMessage());
5542      return le.getResultCode();
5543    }
5544
5545
5546    // Get the entry for the specified alias.
5547    final boolean hasPrivateKey;
5548    final ArrayList<X509Certificate> certList = new ArrayList<>(5);
5549    if (hasCertificateAlias(keystore, alias))
5550    {
5551      try
5552      {
5553        hasPrivateKey = false;
5554        certList.add(
5555             new X509Certificate(keystore.getCertificate(alias).getEncoded()));
5556      }
5557      catch (final Exception e)
5558      {
5559        Debug.debugException(e);
5560        wrapErr(0, WRAP_COLUMN,
5561             ERR_MANAGE_CERTS_DELETE_CERT_ERROR_GETTING_CERT.get(alias));
5562        e.printStackTrace(getErr());
5563        return ResultCode.LOCAL_ERROR;
5564      }
5565    }
5566    else if (hasKeyAlias(keystore, alias))
5567    {
5568      try
5569      {
5570        hasPrivateKey = true;
5571        for (final Certificate c : keystore.getCertificateChain(alias))
5572        {
5573          certList.add(new X509Certificate(c.getEncoded()));
5574        }
5575      }
5576      catch (final Exception e)
5577      {
5578        Debug.debugException(e);
5579        wrapErr(0, WRAP_COLUMN,
5580             ERR_MANAGE_CERTS_DELETE_CERT_ERROR_GETTING_CHAIN.get(alias));
5581        e.printStackTrace(getErr());
5582        return ResultCode.LOCAL_ERROR;
5583      }
5584    }
5585    else
5586    {
5587      wrapErr(0, WRAP_COLUMN,
5588           ERR_MANAGE_CERTS_DELETE_CERT_ERROR_ALIAS_NOT_CERT_OR_KEY.get(alias));
5589      return ResultCode.PARAM_ERROR;
5590    }
5591
5592
5593    // Prompt about whether to perform the delete, if appropriate.
5594    if (! noPrompt)
5595    {
5596      out();
5597      if (! hasPrivateKey)
5598      {
5599        wrapOut(0, WRAP_COLUMN,
5600             INFO_MANAGE_CERTS_DELETE_CERT_CONFIRM_DELETE_CERT.get());
5601      }
5602      else
5603      {
5604        wrapOut(0, WRAP_COLUMN,
5605             INFO_MANAGE_CERTS_DELETE_CERT_CONFIRM_DELETE_CHAIN.get());
5606      }
5607
5608      for (final X509Certificate c : certList)
5609      {
5610        out();
5611        printCertificate(c, "", false);
5612      }
5613
5614      out();
5615
5616      try
5617      {
5618        if (! promptForYesNo(
5619             INFO_MANAGE_CERTS_DELETE_CERT_PROMPT_DELETE.get()))
5620        {
5621          wrapErr(0, WRAP_COLUMN,
5622               ERR_MANAGE_CERTS_DELETE_CERT_CANCELED.get());
5623          return ResultCode.USER_CANCELED;
5624        }
5625      }
5626      catch (final LDAPException le)
5627      {
5628        Debug.debugException(le);
5629        err();
5630        wrapErr(0, WRAP_COLUMN, le.getMessage());
5631        return le.getResultCode();
5632      }
5633    }
5634
5635
5636    // Delete the entry from the keystore.
5637    try
5638    {
5639      keystore.deleteEntry(alias);
5640    }
5641    catch (final Exception e)
5642    {
5643      Debug.debugException(e);
5644      wrapErr(0, WRAP_COLUMN,
5645           ERR_MANAGE_CERTS_DELETE_CERT_DELETE_ERROR.get(alias));
5646      e.printStackTrace(getErr());
5647      return ResultCode.LOCAL_ERROR;
5648    }
5649
5650
5651    // Write the updated keystore to disk.
5652    try
5653    {
5654      writeKeystore(keystore, keystorePath, keystorePassword);
5655    }
5656    catch (final LDAPException le)
5657    {
5658      Debug.debugException(le);
5659      wrapErr(0, WRAP_COLUMN, le.getMessage());
5660      return le.getResultCode();
5661    }
5662
5663    if (certList.size() == 1)
5664    {
5665      out();
5666      wrapOut(0, WRAP_COLUMN,
5667           INFO_MANAGE_CERTS_DELETE_CERT_DELETED_CERT.get());
5668    }
5669    else
5670    {
5671      out();
5672      wrapOut(0, WRAP_COLUMN,
5673           INFO_MANAGE_CERTS_DELETE_CERT_DELETED_CHAIN.get());
5674    }
5675
5676    return ResultCode.SUCCESS;
5677  }
5678
5679
5680
5681  /**
5682   * Performs the necessary processing for the generate-self-signed-certificate,
5683   * generate-certificate-signing-request, and sign-certificate-signing-request
5684   * subcommands.
5685   *
5686   * @return  A result code that indicates whether the processing completed
5687   *          successfully.
5688   */
5689  private ResultCode doGenerateOrSignCertificateOrCSR()
5690  {
5691    // Figure out which subcommand we're processing.
5692    final boolean isGenerateCertificate;
5693    final boolean isGenerateCSR;
5694    final boolean isSignCSR;
5695    final SubCommand selectedSubCommand = globalParser.getSelectedSubCommand();
5696    if (selectedSubCommand.hasName("generate-self-signed-certificate"))
5697    {
5698      isGenerateCertificate = true;
5699      isGenerateCSR = false;
5700      isSignCSR = false;
5701    }
5702    else if (selectedSubCommand.hasName("generate-certificate-signing-request"))
5703    {
5704      isGenerateCertificate = false;
5705      isGenerateCSR = true;
5706      isSignCSR = false;
5707    }
5708    else
5709    {
5710      Validator.ensureTrue(
5711           selectedSubCommand.hasName("sign-certificate-signing-request"));
5712      isGenerateCertificate = false;
5713      isGenerateCSR = false;
5714      isSignCSR = true;
5715    }
5716
5717
5718    // Get the values of a number of configured arguments.
5719    final StringArgument aliasArgument =
5720         subCommandParser.getStringArgument("alias");
5721    final String alias = aliasArgument.getValue();
5722
5723    final File keystorePath = getKeystorePath();
5724    final boolean isNewKeystore = (! keystorePath.exists());
5725
5726    DN subjectDN = null;
5727    final DNArgument subjectDNArgument =
5728         subCommandParser.getDNArgument("subject-dn");
5729    if ((subjectDNArgument != null) && subjectDNArgument.isPresent())
5730    {
5731      subjectDN = subjectDNArgument.getValue();
5732    }
5733
5734    File inputFile = null;
5735    final FileArgument inputFileArgument =
5736         subCommandParser.getFileArgument("input-file");
5737    if ((inputFileArgument != null) && inputFileArgument.isPresent())
5738    {
5739      inputFile = inputFileArgument.getValue();
5740    }
5741
5742    File outputFile = null;
5743    final FileArgument outputFileArgument =
5744         subCommandParser.getFileArgument("output-file");
5745    if ((outputFileArgument != null) && outputFileArgument.isPresent())
5746    {
5747      outputFile = outputFileArgument.getValue();
5748    }
5749
5750    boolean outputPEM = true;
5751    final StringArgument outputFormatArgument =
5752         subCommandParser.getStringArgument("output-format");
5753    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
5754    {
5755      final String format = outputFormatArgument.getValue().toLowerCase();
5756      if (format.equals("der") || format.equals("binary") ||
5757          format.equals("bin"))
5758      {
5759        outputPEM = false;
5760      }
5761    }
5762
5763    if ((! outputPEM) && (outputFile == null))
5764    {
5765      wrapErr(0, WRAP_COLUMN,
5766           ERR_MANAGE_CERTS_GEN_CERT_NO_FILE_WITH_DER.get());
5767      return ResultCode.PARAM_ERROR;
5768    }
5769
5770    final BooleanArgument replaceExistingCertificateArgument =
5771         subCommandParser.getBooleanArgument("replace-existing-certificate");
5772    final boolean replaceExistingCertificate =
5773         ((replaceExistingCertificateArgument != null) &&
5774              replaceExistingCertificateArgument.isPresent());
5775    if (replaceExistingCertificate && (! keystorePath.exists()))
5776    {
5777      wrapErr(0, WRAP_COLUMN,
5778           ERR_MANAGE_CERTS_GEN_CERT_REPLACE_WITHOUT_KS.get());
5779      return ResultCode.PARAM_ERROR;
5780    }
5781
5782    final BooleanArgument inheritExtensionsArgument =
5783         subCommandParser.getBooleanArgument("inherit-extensions");
5784    final boolean inheritExtensions =
5785         ((inheritExtensionsArgument != null) &&
5786              inheritExtensionsArgument.isPresent());
5787
5788    final BooleanArgument includeRequestedExtensionsArgument =
5789         subCommandParser.getBooleanArgument("include-requested-extensions");
5790    final boolean includeRequestedExtensions =
5791         ((includeRequestedExtensionsArgument != null) &&
5792              includeRequestedExtensionsArgument.isPresent());
5793
5794    final BooleanArgument noPromptArgument =
5795         subCommandParser.getBooleanArgument("no-prompt");
5796    final boolean noPrompt =
5797         ((noPromptArgument != null) && noPromptArgument.isPresent());
5798
5799    final BooleanArgument displayKeytoolCommandArgument =
5800         subCommandParser.getBooleanArgument("display-keytool-command");
5801    final boolean displayKeytoolCommand =
5802         ((displayKeytoolCommandArgument != null) &&
5803          displayKeytoolCommandArgument.isPresent());
5804
5805    int daysValid = 365;
5806    final IntegerArgument daysValidArgument =
5807         subCommandParser.getIntegerArgument("days-valid");
5808    if ((daysValidArgument != null) && daysValidArgument.isPresent())
5809    {
5810      daysValid = daysValidArgument.getValue();
5811    }
5812
5813    Date validityStartTime = null;
5814    final TimestampArgument validityStartTimeArgument =
5815         subCommandParser.getTimestampArgument("validity-start-time");
5816    if ((validityStartTimeArgument != null) &&
5817         validityStartTimeArgument.isPresent())
5818    {
5819      validityStartTime = validityStartTimeArgument.getValue();
5820    }
5821
5822    PublicKeyAlgorithmIdentifier keyAlgorithmIdentifier = null;
5823    String keyAlgorithmName = null;
5824    final StringArgument keyAlgorithmArgument =
5825         subCommandParser.getStringArgument("key-algorithm");
5826    if ((keyAlgorithmArgument != null) && keyAlgorithmArgument.isPresent())
5827    {
5828      final String name = keyAlgorithmArgument.getValue();
5829      keyAlgorithmIdentifier = PublicKeyAlgorithmIdentifier.forName(name);
5830      if (keyAlgorithmIdentifier == null)
5831      {
5832        wrapErr(0, WRAP_COLUMN,
5833             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_KEY_ALG.get(name));
5834        return ResultCode.PARAM_ERROR;
5835      }
5836      else
5837      {
5838        keyAlgorithmName = keyAlgorithmIdentifier.getName();
5839      }
5840    }
5841
5842    Integer keySizeBits = null;
5843    final IntegerArgument keySizeBitsArgument =
5844         subCommandParser.getIntegerArgument("key-size-bits");
5845    if ((keySizeBitsArgument != null) && keySizeBitsArgument.isPresent())
5846    {
5847      keySizeBits = keySizeBitsArgument.getValue();
5848    }
5849
5850    if ((keyAlgorithmIdentifier != null) &&
5851        (keyAlgorithmIdentifier != PublicKeyAlgorithmIdentifier.RSA) &&
5852        (keySizeBits == null))
5853    {
5854      wrapErr(0, WRAP_COLUMN,
5855           ERR_MANAGE_CERTS_GEN_CERT_NO_KEY_SIZE_FOR_NON_RSA_KEY.get());
5856      return ResultCode.PARAM_ERROR;
5857    }
5858
5859    String signatureAlgorithmName = null;
5860    SignatureAlgorithmIdentifier signatureAlgorithmIdentifier = null;
5861    final StringArgument signatureAlgorithmArgument =
5862         subCommandParser.getStringArgument("signature-algorithm");
5863    if ((signatureAlgorithmArgument != null) &&
5864        signatureAlgorithmArgument.isPresent())
5865    {
5866      final String name = signatureAlgorithmArgument.getValue();
5867      signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forName(name);
5868      if (signatureAlgorithmIdentifier == null)
5869      {
5870        wrapErr(0, WRAP_COLUMN,
5871             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG.get(name));
5872        return ResultCode.PARAM_ERROR;
5873      }
5874      else
5875      {
5876        signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
5877      }
5878    }
5879
5880    if ((keyAlgorithmIdentifier != null) &&
5881        (keyAlgorithmIdentifier != PublicKeyAlgorithmIdentifier.RSA) &&
5882        (signatureAlgorithmIdentifier == null))
5883    {
5884      wrapErr(0, WRAP_COLUMN,
5885           ERR_MANAGE_CERTS_GEN_CERT_NO_SIG_ALG_FOR_NON_RSA_KEY.get());
5886      return ResultCode.PARAM_ERROR;
5887    }
5888
5889
5890    // Build a subject alternative name extension, if appropriate.
5891    final ArrayList<X509CertificateExtension> extensionList =
5892         new ArrayList<>(10);
5893    final GeneralNamesBuilder sanBuilder = new GeneralNamesBuilder();
5894    final LinkedHashSet<String> sanValues = new LinkedHashSet<>(10);
5895    final StringArgument sanDNSArgument =
5896         subCommandParser.getStringArgument("subject-alternative-name-dns");
5897    if ((sanDNSArgument != null) && sanDNSArgument.isPresent())
5898    {
5899      for (final String value : sanDNSArgument.getValues())
5900      {
5901        sanBuilder.addDNSName(value);
5902        sanValues.add("DNS:" + value);
5903      }
5904    }
5905
5906    final StringArgument sanIPArgument = subCommandParser.getStringArgument(
5907         "subject-alternative-name-ip-address");
5908    if ((sanIPArgument != null) && sanIPArgument.isPresent())
5909    {
5910      for (final String value : sanIPArgument.getValues())
5911      {
5912        try
5913        {
5914          sanBuilder.addIPAddress(InetAddress.getByName(value));
5915          sanValues.add("IP:" + value);
5916        }
5917        catch (final Exception e)
5918        {
5919          // This should never happen.
5920          Debug.debugException(e);
5921          throw new RuntimeException(e);
5922        }
5923      }
5924    }
5925
5926    final StringArgument sanEmailArgument = subCommandParser.getStringArgument(
5927         "subject-alternative-name-email-address");
5928    if ((sanEmailArgument != null) && sanEmailArgument.isPresent())
5929    {
5930      for (final String value : sanEmailArgument.getValues())
5931      {
5932        sanBuilder.addRFC822Name(value);
5933        sanValues.add("EMAIL:" + value);
5934      }
5935    }
5936
5937    final StringArgument sanURIArgument =
5938         subCommandParser.getStringArgument("subject-alternative-name-uri");
5939    if ((sanURIArgument != null) && sanURIArgument.isPresent())
5940    {
5941      for (final String value : sanURIArgument.getValues())
5942      {
5943        sanBuilder.addUniformResourceIdentifier(value);
5944        sanValues.add("URI:" + value);
5945      }
5946    }
5947
5948    final StringArgument sanOIDArgument =
5949         subCommandParser.getStringArgument("subject-alternative-name-oid");
5950    if ((sanOIDArgument != null) && sanOIDArgument.isPresent())
5951    {
5952      for (final String value : sanOIDArgument.getValues())
5953      {
5954        sanBuilder.addRegisteredID(new OID(value));
5955        sanValues.add("OID:" + value);
5956      }
5957    }
5958
5959    if (! sanValues.isEmpty())
5960    {
5961      try
5962      {
5963        extensionList.add(
5964             new SubjectAlternativeNameExtension(false, sanBuilder.build()));
5965      }
5966      catch (final Exception e)
5967      {
5968        // This should never happen.
5969        Debug.debugException(e);
5970        throw new RuntimeException(e);
5971      }
5972    }
5973
5974    // Build a set of issuer alternative name extension values.
5975    final GeneralNamesBuilder ianBuilder = new GeneralNamesBuilder();
5976    final LinkedHashSet<String> ianValues = new LinkedHashSet<>(10);
5977    final StringArgument ianDNSArgument =
5978         subCommandParser.getStringArgument("issuer-alternative-name-dns");
5979    if ((ianDNSArgument != null) && ianDNSArgument.isPresent())
5980    {
5981      for (final String value : ianDNSArgument.getValues())
5982      {
5983        ianBuilder.addDNSName(value);
5984        ianValues.add("DNS:" + value);
5985      }
5986    }
5987
5988    final StringArgument ianIPArgument = subCommandParser.getStringArgument(
5989         "issuer-alternative-name-ip-address");
5990    if ((ianIPArgument != null) && ianIPArgument.isPresent())
5991    {
5992      for (final String value : ianIPArgument.getValues())
5993      {
5994        try
5995        {
5996          ianBuilder.addIPAddress(InetAddress.getByName(value));
5997          ianValues.add("IP:" + value);
5998        }
5999        catch (final Exception e)
6000        {
6001          // This should never happen.
6002          Debug.debugException(e);
6003          throw new RuntimeException(e);
6004        }
6005      }
6006    }
6007
6008    final StringArgument ianEmailArgument = subCommandParser.getStringArgument(
6009         "issuer-alternative-name-email-address");
6010    if ((ianEmailArgument != null) && ianEmailArgument.isPresent())
6011    {
6012      for (final String value : ianEmailArgument.getValues())
6013      {
6014        ianBuilder.addRFC822Name(value);
6015        ianValues.add("EMAIL:" + value);
6016      }
6017    }
6018
6019    final StringArgument ianURIArgument =
6020         subCommandParser.getStringArgument("issuer-alternative-name-uri");
6021    if ((ianURIArgument != null) && ianURIArgument.isPresent())
6022    {
6023      for (final String value : ianURIArgument.getValues())
6024      {
6025        ianBuilder.addUniformResourceIdentifier(value);
6026        ianValues.add("URI:" + value);
6027      }
6028    }
6029
6030    final StringArgument ianOIDArgument =
6031         subCommandParser.getStringArgument("issuer-alternative-name-oid");
6032    if ((ianOIDArgument != null) && ianOIDArgument.isPresent())
6033    {
6034      for (final String value : ianOIDArgument.getValues())
6035      {
6036        ianBuilder.addRegisteredID(new OID(value));
6037        ianValues.add("OID:" + value);
6038      }
6039    }
6040
6041    if (! ianValues.isEmpty())
6042    {
6043      try
6044      {
6045        extensionList.add(
6046             new IssuerAlternativeNameExtension(false, ianBuilder.build()));
6047      }
6048      catch (final Exception e)
6049      {
6050        // This should never happen.
6051        Debug.debugException(e);
6052        throw new RuntimeException(e);
6053      }
6054    }
6055
6056
6057    // Build a basic constraints extension, if appropriate.
6058    BasicConstraintsExtension basicConstraints = null;
6059    final BooleanValueArgument basicConstraintsIsCAArgument =
6060         subCommandParser.getBooleanValueArgument("basic-constraints-is-ca");
6061    if ((basicConstraintsIsCAArgument != null) &&
6062         basicConstraintsIsCAArgument.isPresent())
6063    {
6064      final boolean isCA = basicConstraintsIsCAArgument.getValue();
6065
6066      Integer pathLength = null;
6067      final IntegerArgument pathLengthArgument =
6068           subCommandParser.getIntegerArgument(
6069                "basic-constraints-maximum-path-length");
6070      if ((pathLengthArgument != null) && pathLengthArgument.isPresent())
6071      {
6072        if (isCA)
6073        {
6074          pathLength = pathLengthArgument.getValue();
6075        }
6076        else
6077        {
6078          wrapErr(0, WRAP_COLUMN,
6079               ERR_MANAGE_CERTS_GEN_CERT_BC_PATH_LENGTH_WITHOUT_CA.get());
6080          return ResultCode.PARAM_ERROR;
6081        }
6082      }
6083
6084      basicConstraints = new BasicConstraintsExtension(false, isCA, pathLength);
6085      extensionList.add(basicConstraints);
6086    }
6087
6088
6089    // Build a key usage extension, if appropriate.
6090    KeyUsageExtension keyUsage = null;
6091    final StringArgument keyUsageArgument =
6092         subCommandParser.getStringArgument("key-usage");
6093    if ((keyUsageArgument != null) && keyUsageArgument.isPresent())
6094    {
6095      boolean digitalSignature = false;
6096      boolean nonRepudiation = false;
6097      boolean keyEncipherment = false;
6098      boolean dataEncipherment = false;
6099      boolean keyAgreement = false;
6100      boolean keyCertSign = false;
6101      boolean crlSign = false;
6102      boolean encipherOnly = false;
6103      boolean decipherOnly = false;
6104
6105      for (final String value : keyUsageArgument.getValues())
6106      {
6107        if (value.equalsIgnoreCase("digital-signature") ||
6108             value.equalsIgnoreCase("digitalSignature"))
6109        {
6110          digitalSignature = true;
6111        }
6112        else if (value.equalsIgnoreCase("non-repudiation") ||
6113             value.equalsIgnoreCase("nonRepudiation") ||
6114             value.equalsIgnoreCase("content-commitment") ||
6115             value.equalsIgnoreCase("contentCommitment"))
6116        {
6117          nonRepudiation = true;
6118        }
6119        else if (value.equalsIgnoreCase("key-encipherment") ||
6120             value.equalsIgnoreCase("keyEncipherment"))
6121        {
6122          keyEncipherment = true;
6123        }
6124        else if (value.equalsIgnoreCase("data-encipherment") ||
6125             value.equalsIgnoreCase("dataEncipherment"))
6126        {
6127          dataEncipherment = true;
6128        }
6129        else if (value.equalsIgnoreCase("key-agreement") ||
6130             value.equalsIgnoreCase("keyAgreement"))
6131        {
6132          keyAgreement = true;
6133        }
6134        else if (value.equalsIgnoreCase("key-cert-sign") ||
6135             value.equalsIgnoreCase("keyCertSign"))
6136        {
6137          keyCertSign = true;
6138        }
6139        else if (value.equalsIgnoreCase("crl-sign") ||
6140             value.equalsIgnoreCase("crlSign"))
6141        {
6142          crlSign = true;
6143        }
6144        else if (value.equalsIgnoreCase("encipher-only") ||
6145             value.equalsIgnoreCase("encipherOnly"))
6146        {
6147          encipherOnly = true;
6148        }
6149        else if (value.equalsIgnoreCase("decipher-only") ||
6150             value.equalsIgnoreCase("decipherOnly"))
6151        {
6152          decipherOnly = true;
6153        }
6154        else
6155        {
6156          wrapErr(0, WRAP_COLUMN,
6157               ERR_MANAGE_CERTS_GEN_CERT_INVALID_KEY_USAGE.get(value));
6158          return ResultCode.PARAM_ERROR;
6159        }
6160      }
6161
6162      keyUsage = new KeyUsageExtension(false, digitalSignature, nonRepudiation,
6163           keyEncipherment, dataEncipherment, keyAgreement, keyCertSign,
6164           crlSign, encipherOnly, decipherOnly);
6165      extensionList.add(keyUsage);
6166    }
6167
6168
6169    // Build an extended key usage extension, if appropriate.
6170    ExtendedKeyUsageExtension extendedKeyUsage = null;
6171    final StringArgument extendedKeyUsageArgument =
6172         subCommandParser.getStringArgument("extended-key-usage");
6173    if ((extendedKeyUsageArgument != null) &&
6174         extendedKeyUsageArgument.isPresent())
6175    {
6176      final List<String> values = extendedKeyUsageArgument.getValues();
6177      final ArrayList<OID> keyPurposeIDs = new ArrayList<>(values.size());
6178      for (final String value : values)
6179      {
6180        if (value.equalsIgnoreCase("server-auth") ||
6181             value.equalsIgnoreCase("serverAuth") ||
6182             value.equalsIgnoreCase("server-authentication") ||
6183             value.equalsIgnoreCase("serverAuthentication") ||
6184             value.equalsIgnoreCase("tls-server-authentication") ||
6185             value.equalsIgnoreCase("tlsServerAuthentication"))
6186        {
6187          keyPurposeIDs.add(
6188               ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID());
6189        }
6190        else if (value.equalsIgnoreCase("client-auth") ||
6191             value.equalsIgnoreCase("clientAuth") ||
6192             value.equalsIgnoreCase("client-authentication") ||
6193             value.equalsIgnoreCase("clientAuthentication") ||
6194             value.equalsIgnoreCase("tls-client-authentication") ||
6195             value.equalsIgnoreCase("tlsClientAuthentication"))
6196        {
6197          keyPurposeIDs.add(
6198               ExtendedKeyUsageID.TLS_CLIENT_AUTHENTICATION.getOID());
6199        }
6200        else if (value.equalsIgnoreCase("code-signing") ||
6201             value.equalsIgnoreCase("codeSigning"))
6202        {
6203          keyPurposeIDs.add(ExtendedKeyUsageID.CODE_SIGNING.getOID());
6204        }
6205        else if (value.equalsIgnoreCase("email-protection") ||
6206             value.equalsIgnoreCase("emailProtection"))
6207        {
6208          keyPurposeIDs.add(ExtendedKeyUsageID.EMAIL_PROTECTION.getOID());
6209        }
6210        else if (value.equalsIgnoreCase("time-stamping") ||
6211             value.equalsIgnoreCase("timeStamping"))
6212        {
6213          keyPurposeIDs.add(ExtendedKeyUsageID.TIME_STAMPING.getOID());
6214        }
6215        else if (value.equalsIgnoreCase("ocsp-signing") ||
6216             value.equalsIgnoreCase("ocspSigning"))
6217        {
6218          keyPurposeIDs.add(ExtendedKeyUsageID.OCSP_SIGNING.getOID());
6219        }
6220        else if (OID.isStrictlyValidNumericOID(value))
6221        {
6222          keyPurposeIDs.add(new OID(value));
6223        }
6224        else
6225        {
6226          wrapErr(0, WRAP_COLUMN,
6227               ERR_MANAGE_CERTS_GEN_CERT_INVALID_EXTENDED_KEY_USAGE.get(value));
6228          return ResultCode.PARAM_ERROR;
6229        }
6230      }
6231
6232      try
6233      {
6234        extendedKeyUsage = new ExtendedKeyUsageExtension(false, keyPurposeIDs);
6235      }
6236      catch (final Exception e)
6237      {
6238        // This should never happen.
6239        Debug.debugException(e);
6240        wrapErr(0, WRAP_COLUMN,
6241             ERR_MANAGE_CERTS_GEN_CERT_EXTENDED_KEY_USAGE_ERROR.get());
6242        e.printStackTrace(getErr());
6243        return ResultCode.PARAM_ERROR;
6244      }
6245
6246      extensionList.add(extendedKeyUsage);
6247    }
6248
6249
6250    // Build a list of generic extensions.
6251    final ArrayList<X509CertificateExtension> genericExtensions =
6252         new ArrayList<>(5);
6253    final StringArgument extensionArgument =
6254         subCommandParser.getStringArgument("extension");
6255    if ((extensionArgument != null) && extensionArgument.isPresent())
6256    {
6257      for (final String value : extensionArgument.getValues())
6258      {
6259        try
6260        {
6261          final int firstColonPos = value.indexOf(':');
6262          final int secondColonPos = value.indexOf(':', firstColonPos + 1);
6263          final OID oid = new OID(value.substring(0, firstColonPos));
6264          if (! oid.isStrictlyValidNumericOID())
6265          {
6266            wrapErr(0, WRAP_COLUMN,
6267                 ERR_MANAGE_CERTS_GEN_CERT_EXT_MALFORMED_OID.get(value,
6268                      oid.toString()));
6269            return ResultCode.PARAM_ERROR;
6270          }
6271
6272          final boolean criticality;
6273          final String criticalityString =
6274               value.substring(firstColonPos + 1, secondColonPos);
6275          if (criticalityString.equalsIgnoreCase("true") ||
6276               criticalityString.equalsIgnoreCase("t") ||
6277               criticalityString.equalsIgnoreCase("yes") ||
6278               criticalityString.equalsIgnoreCase("y") ||
6279               criticalityString.equalsIgnoreCase("on") ||
6280               criticalityString.equalsIgnoreCase("1"))
6281          {
6282            criticality = true;
6283          }
6284          else if (criticalityString.equalsIgnoreCase("false") ||
6285               criticalityString.equalsIgnoreCase("f") ||
6286               criticalityString.equalsIgnoreCase("no") ||
6287               criticalityString.equalsIgnoreCase("n") ||
6288               criticalityString.equalsIgnoreCase("off") ||
6289               criticalityString.equalsIgnoreCase("0"))
6290          {
6291            criticality = false;
6292          }
6293          else
6294          {
6295            wrapErr(0, WRAP_COLUMN,
6296                 ERR_MANAGE_CERTS_GEN_CERT_EXT_INVALID_CRITICALITY.get(
6297                      value, criticalityString));
6298            return ResultCode.PARAM_ERROR;
6299          }
6300
6301          final byte[] valueBytes;
6302          try
6303          {
6304            valueBytes = StaticUtils.fromHex(value.substring(secondColonPos+1));
6305          }
6306          catch (final Exception e)
6307          {
6308            Debug.debugException(e);
6309            wrapErr(0, WRAP_COLUMN,
6310                 ERR_MANAGE_CERTS_GEN_CERT_EXT_INVALID_VALUE.get(value));
6311            return ResultCode.PARAM_ERROR;
6312          }
6313
6314          final X509CertificateExtension extension =
6315               new X509CertificateExtension(oid, criticality, valueBytes);
6316          genericExtensions.add(extension);
6317          extensionList.add(extension);
6318        }
6319        catch (final Exception e)
6320        {
6321          Debug.debugException(e);
6322          wrapErr(0, WRAP_COLUMN,
6323               ERR_MANAGE_CERTS_GEN_CERT_EXT_MALFORMED.get(value));
6324          return ResultCode.PARAM_ERROR;
6325        }
6326      }
6327    }
6328
6329
6330    final String keystoreType;
6331    try
6332    {
6333      keystoreType = inferKeystoreType(keystorePath);
6334    }
6335    catch (final LDAPException le)
6336    {
6337      Debug.debugException(le);
6338      wrapErr(0, WRAP_COLUMN, le.getMessage());
6339      return le.getResultCode();
6340    }
6341
6342    final char[] keystorePassword;
6343    try
6344    {
6345      keystorePassword = getKeystorePassword(keystorePath);
6346    }
6347    catch (final LDAPException le)
6348    {
6349      Debug.debugException(le);
6350      wrapErr(0, WRAP_COLUMN, le.getMessage());
6351      return le.getResultCode();
6352    }
6353
6354
6355    // Get the keystore.
6356    final KeyStore keystore;
6357    try
6358    {
6359      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
6360    }
6361    catch (final LDAPException le)
6362    {
6363      Debug.debugException(le);
6364      wrapErr(0, WRAP_COLUMN, le.getMessage());
6365      return le.getResultCode();
6366    }
6367
6368
6369    // If there is a private key, then see if we need to use a private key
6370    // password that is different from the keystore password.
6371    final char[] privateKeyPassword;
6372    try
6373    {
6374      privateKeyPassword =
6375           getPrivateKeyPassword(keystore, alias, keystorePassword);
6376    }
6377    catch (final LDAPException le)
6378    {
6379      Debug.debugException(le);
6380      wrapErr(0, WRAP_COLUMN, le.getMessage());
6381      return le.getResultCode();
6382    }
6383
6384
6385    // If we're going to replace an existing certificate in the keystore, then
6386    // perform the appropriate processing for that.
6387    if (replaceExistingCertificate)
6388    {
6389      // Make sure that the keystore already has a private key entry with the
6390      // specified alias.
6391      if (! hasKeyAlias(keystore, alias))
6392      {
6393        if (hasCertificateAlias(keystore, alias))
6394        {
6395          wrapErr(0, WRAP_COLUMN,
6396               ERR_MANAGE_CERTS_GEN_CERT_REPLACE_ALIAS_IS_CERT.get(alias,
6397                    keystorePath.getAbsolutePath()));
6398          return ResultCode.PARAM_ERROR;
6399        }
6400        else
6401        {
6402          wrapErr(0, WRAP_COLUMN,
6403               ERR_MANAGE_CERTS_GEN_CERT_REPLACE_NO_SUCH_ALIAS.get(alias,
6404                    keystorePath.getAbsolutePath()));
6405          return ResultCode.PARAM_ERROR;
6406        }
6407      }
6408
6409
6410      // Get the certificate to replace, along with its key pair.
6411      final X509Certificate certToReplace;
6412      final KeyPair keyPair;
6413      try
6414      {
6415        final Certificate[] chain = keystore.getCertificateChain(alias);
6416        certToReplace = new X509Certificate(chain[0].getEncoded());
6417
6418        final PublicKey publicKey = chain[0].getPublicKey();
6419        final PrivateKey privateKey =
6420             (PrivateKey) keystore.getKey(alias, privateKeyPassword);
6421        keyPair = new KeyPair(publicKey, privateKey);
6422      }
6423      catch (final Exception e)
6424      {
6425        Debug.debugException(e);
6426        wrapErr(0, WRAP_COLUMN,
6427             ERR_MANAGE_CERTS_GEN_CERT_REPLACE_COULD_NOT_GET_CERT.get(alias));
6428        e.printStackTrace(getErr());
6429        return ResultCode.LOCAL_ERROR;
6430      }
6431
6432
6433      // Assign the remaining values using information in the existing
6434      // certificate.
6435      signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forOID(
6436           certToReplace.getSignatureAlgorithmOID());
6437      if (signatureAlgorithmIdentifier == null)
6438      {
6439        wrapErr(0, WRAP_COLUMN,
6440             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG_IN_CERT.get(
6441                  certToReplace.getSignatureAlgorithmOID()));
6442        return ResultCode.PARAM_ERROR;
6443      }
6444      else
6445      {
6446        signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
6447      }
6448
6449      if (subjectDN == null)
6450      {
6451        subjectDN = certToReplace.getSubjectDN();
6452      }
6453
6454      if (inheritExtensions)
6455      {
6456        for (final X509CertificateExtension extension :
6457             certToReplace.getExtensions())
6458        {
6459          if ((extension instanceof AuthorityKeyIdentifierExtension) ||
6460              (extension instanceof IssuerAlternativeNameExtension))
6461          {
6462            // This extension applies to the issuer.  We won't include this in
6463            // the set of inherited extensions.
6464          }
6465          else if (extension instanceof SubjectKeyIdentifierExtension)
6466          {
6467            // The generated certificate will automatically include a subject
6468            // key identifier extension, so we don't need to include it.
6469          }
6470          else if (extension instanceof BasicConstraintsExtension)
6471          {
6472            // Don't override a value already provided on the command line.
6473            if (basicConstraints == null)
6474            {
6475              basicConstraints = (BasicConstraintsExtension) extension;
6476              extensionList.add(basicConstraints);
6477            }
6478          }
6479          else if (extension instanceof ExtendedKeyUsageExtension)
6480          {
6481            // Don't override a value already provided on the command line.
6482            if (extendedKeyUsage == null)
6483            {
6484              extendedKeyUsage = (ExtendedKeyUsageExtension) extension;
6485              extensionList.add(extendedKeyUsage);
6486            }
6487          }
6488          else if (extension instanceof KeyUsageExtension)
6489          {
6490            // Don't override a value already provided on the command line.
6491            if (keyUsage == null)
6492            {
6493              keyUsage = (KeyUsageExtension) extension;
6494              extensionList.add(keyUsage);
6495            }
6496          }
6497          else if (extension instanceof SubjectAlternativeNameExtension)
6498          {
6499            // Although we could merge values, it's safer to not do that if any
6500            // subject alternative name values were provided on the command
6501            // line.
6502            if (sanValues.isEmpty())
6503            {
6504              final SubjectAlternativeNameExtension e =
6505                   (SubjectAlternativeNameExtension) extension;
6506              for (final String dnsName : e.getDNSNames())
6507              {
6508                sanValues.add("DNS:" + dnsName);
6509              }
6510
6511              for (final InetAddress ipAddress : e.getIPAddresses())
6512              {
6513                sanValues.add("IP:" + ipAddress.getHostAddress());
6514              }
6515
6516              for (final String emailAddress : e.getRFC822Names())
6517              {
6518                sanValues.add("EMAIL:" + emailAddress);
6519              }
6520
6521              for (final String uri : e.getUniformResourceIdentifiers())
6522              {
6523                sanValues.add("URI:" + uri);
6524              }
6525
6526              for (final OID oid : e.getRegisteredIDs())
6527              {
6528                sanValues.add("OID:" + oid.toString());
6529              }
6530
6531              extensionList.add(extension);
6532            }
6533          }
6534          else
6535          {
6536            genericExtensions.add(extension);
6537            extensionList.add(extension);
6538          }
6539        }
6540      }
6541
6542
6543      // Create an array with the final set of extensions to include in the
6544      // certificate or certificate signing request.
6545      final X509CertificateExtension[] extensions =
6546           new X509CertificateExtension[extensionList.size()];
6547      extensionList.toArray(extensions);
6548
6549
6550      // If we're generating a self-signed certificate or a certificate signing
6551      // request, then we should now have everything we need to do that.  Build
6552      // a keytool command that we could use to accomplish it.
6553      if (isGenerateCertificate)
6554      {
6555        if (displayKeytoolCommand)
6556        {
6557          final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6558          keytoolArguments.add("-selfcert");
6559          keytoolArguments.add("-keystore");
6560          keytoolArguments.add(keystorePath.getAbsolutePath());
6561          keytoolArguments.add("-storetype");
6562          keytoolArguments.add(keystoreType);
6563          keytoolArguments.add("-storepass");
6564          keytoolArguments.add("*****REDACTED*****");
6565          keytoolArguments.add("-keypass");
6566          keytoolArguments.add("*****REDACTED*****");
6567          keytoolArguments.add("-alias");
6568          keytoolArguments.add(alias);
6569          keytoolArguments.add("-dname");
6570          keytoolArguments.add(subjectDN.toString());
6571          keytoolArguments.add("-sigalg");
6572          keytoolArguments.add(signatureAlgorithmName);
6573          keytoolArguments.add("-validity");
6574          keytoolArguments.add(String.valueOf(daysValid));
6575
6576          if (validityStartTime != null)
6577          {
6578            keytoolArguments.add("-startdate");
6579            keytoolArguments.add(formatValidityStartTime(validityStartTime));
6580          }
6581
6582          addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
6583               extendedKeyUsage, sanValues, ianValues, genericExtensions);
6584
6585          displayKeytoolCommand(keytoolArguments);
6586        }
6587
6588
6589        // Generate the self-signed certificate.
6590        final long notBefore;
6591        if (validityStartTime == null)
6592        {
6593          notBefore = System.currentTimeMillis();
6594        }
6595        else
6596        {
6597          notBefore = validityStartTime.getTime();
6598        }
6599
6600        final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
6601
6602        final X509Certificate certificate;
6603        final Certificate[] chain;
6604        try
6605        {
6606          certificate = X509Certificate.generateSelfSignedCertificate(
6607               signatureAlgorithmIdentifier, keyPair, subjectDN, notBefore,
6608               notAfter, extensions);
6609          chain = new Certificate[] { certificate.toCertificate() };
6610        }
6611        catch (final Exception e)
6612        {
6613          Debug.debugException(e);
6614          wrapErr(0, WRAP_COLUMN,
6615               ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CERT.get());
6616          e.printStackTrace(getErr());
6617          return ResultCode.LOCAL_ERROR;
6618        }
6619
6620
6621        // Update the keystore with the new certificate.
6622        try
6623        {
6624          keystore.setKeyEntry(alias, keyPair.getPrivate(), privateKeyPassword,
6625               chain);
6626          writeKeystore(keystore, keystorePath, keystorePassword);
6627        }
6628        catch (final Exception e)
6629        {
6630          Debug.debugException(e);
6631          wrapErr(0, WRAP_COLUMN,
6632               ERR_MANAGE_CERTS_GEN_CERT_ERROR_UPDATING_KEYSTORE.get());
6633          e.printStackTrace(getErr());
6634          return ResultCode.LOCAL_ERROR;
6635        }
6636
6637
6638        // Display the certificate we just generated to the end user.
6639        out();
6640        wrapOut(0, WRAP_COLUMN,
6641             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_SELF_CERT.
6642                  get());
6643        printCertificate(certificate, "", false);
6644        return ResultCode.SUCCESS;
6645      }
6646      else
6647      {
6648        // Build the keytool command used to generate the certificate signing
6649        // request.
6650        Validator.ensureTrue(isGenerateCSR);
6651        if (displayKeytoolCommand)
6652        {
6653          final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6654          keytoolArguments.add("-certreq");
6655          keytoolArguments.add("-keystore");
6656          keytoolArguments.add(keystorePath.getAbsolutePath());
6657          keytoolArguments.add("-storetype");
6658          keytoolArguments.add(keystoreType);
6659          keytoolArguments.add("-storepass");
6660          keytoolArguments.add("*****REDACTED*****");
6661          keytoolArguments.add("-keypass");
6662          keytoolArguments.add("*****REDACTED*****");
6663          keytoolArguments.add("-alias");
6664          keytoolArguments.add(alias);
6665          keytoolArguments.add("-dname");
6666          keytoolArguments.add(subjectDN.toString());
6667          keytoolArguments.add("-sigalg");
6668          keytoolArguments.add(signatureAlgorithmName);
6669
6670          addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
6671               extendedKeyUsage, sanValues, ianValues, genericExtensions);
6672
6673          if (outputFile != null)
6674          {
6675            keytoolArguments.add("-file");
6676            keytoolArguments.add(outputFile.getAbsolutePath());
6677          }
6678
6679          displayKeytoolCommand(keytoolArguments);
6680        }
6681
6682
6683        // Generate the certificate signing request.
6684        final PKCS10CertificateSigningRequest certificateSigningRequest;
6685        try
6686        {
6687          certificateSigningRequest = PKCS10CertificateSigningRequest.
6688               generateCertificateSigningRequest(signatureAlgorithmIdentifier,
6689                    keyPair, subjectDN, extensions);
6690        }
6691        catch (final Exception e)
6692        {
6693          Debug.debugException(e);
6694          wrapErr(0, WRAP_COLUMN,
6695               ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CSR.get());
6696          e.printStackTrace(getErr());
6697          return ResultCode.LOCAL_ERROR;
6698        }
6699
6700
6701        // Write the generated certificate signing request to the appropriate
6702        // location.
6703        try
6704        {
6705          final PrintStream ps;
6706          if (outputFile == null)
6707          {
6708            ps = getOut();
6709          }
6710          else
6711          {
6712            ps = new PrintStream(outputFile);
6713          }
6714
6715          if (outputPEM)
6716          {
6717            writePEMCertificateSigningRequest(ps,
6718                 certificateSigningRequest.
6719                      getPKCS10CertificateSigningRequestBytes());
6720          }
6721          else
6722          {
6723            ps.write(certificateSigningRequest.
6724                 getPKCS10CertificateSigningRequestBytes());
6725          }
6726
6727          if (outputFile != null)
6728          {
6729            ps.close();
6730          }
6731        }
6732        catch (final Exception e)
6733        {
6734          Debug.debugException(e);
6735          wrapErr(0, WRAP_COLUMN,
6736               ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CSR.get());
6737          e.printStackTrace(getErr());
6738          return ResultCode.LOCAL_ERROR;
6739        }
6740
6741
6742        // If the certificate signing request was written to an output file,
6743        // then let the user know that it was successful.  If it was written to
6744        // standard output, then we don't need to tell them because they'll be
6745        // able to see it.
6746        if (outputFile != null)
6747        {
6748          out();
6749          wrapOut(0, WRAP_COLUMN,
6750               INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_CSR.get(
6751                    outputFile.getAbsolutePath()));
6752        }
6753
6754        return ResultCode.SUCCESS;
6755      }
6756    }
6757
6758
6759    // If we've gotten here, then we know we're not replacing an existing
6760    // certificate.  Perform any remaining argument assignment and validation.
6761    if ((subjectDN == null) && (! isSignCSR))
6762    {
6763      wrapErr(0, WRAP_COLUMN,
6764           ERR_MANAGE_CERTS_GEN_CERT_NO_SUBJECT_DN_WITHOUT_REPLACE.get());
6765      return ResultCode.PARAM_ERROR;
6766    }
6767
6768    if (keyAlgorithmIdentifier == null)
6769    {
6770      keyAlgorithmIdentifier = PublicKeyAlgorithmIdentifier.RSA;
6771      keyAlgorithmName = keyAlgorithmIdentifier.getName();
6772    }
6773
6774    if (keySizeBits == null)
6775    {
6776      keySizeBits = 2048;
6777    }
6778
6779    if ((signatureAlgorithmIdentifier == null) && (! isSignCSR))
6780    {
6781      signatureAlgorithmIdentifier =
6782           SignatureAlgorithmIdentifier.SHA_256_WITH_RSA;
6783      signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
6784    }
6785
6786
6787    // If we're going to generate a self-signed certificate or a certificate
6788    // signing request, then we first need to generate a key pair.  Put together
6789    // the appropriate set of keytool arguments and then generate a self-signed
6790    // certificate.
6791    if (isGenerateCertificate || isGenerateCSR)
6792    {
6793      // Make sure that the specified alias is not already in use in the
6794      // keystore.
6795      if (hasKeyAlias(keystore, alias) || hasCertificateAlias(keystore, alias))
6796      {
6797        wrapErr(0, WRAP_COLUMN,
6798             ERR_MANAGE_CERTS_GEN_CERT_ALIAS_EXISTS_WITHOUT_REPLACE.get(alias));
6799        return ResultCode.PARAM_ERROR;
6800      }
6801
6802
6803      if (displayKeytoolCommand)
6804      {
6805        final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6806        keytoolArguments.add("-genkeypair");
6807        keytoolArguments.add("-keystore");
6808        keytoolArguments.add(keystorePath.getAbsolutePath());
6809        keytoolArguments.add("-storetype");
6810        keytoolArguments.add(keystoreType);
6811        keytoolArguments.add("-storepass");
6812        keytoolArguments.add("*****REDACTED*****");
6813        keytoolArguments.add("-keypass");
6814        keytoolArguments.add("*****REDACTED*****");
6815        keytoolArguments.add("-alias");
6816        keytoolArguments.add(alias);
6817        keytoolArguments.add("-dname");
6818        keytoolArguments.add(subjectDN.toString());
6819        keytoolArguments.add("-keyalg");
6820        keytoolArguments.add(keyAlgorithmName);
6821        keytoolArguments.add("-keysize");
6822        keytoolArguments.add(String.valueOf(keySizeBits));
6823        keytoolArguments.add("-sigalg");
6824        keytoolArguments.add(signatureAlgorithmName);
6825        keytoolArguments.add("-validity");
6826        keytoolArguments.add(String.valueOf(daysValid));
6827
6828        if (validityStartTime != null)
6829        {
6830          keytoolArguments.add("-startdate");
6831          keytoolArguments.add(formatValidityStartTime(validityStartTime));
6832        }
6833
6834        addExtensionArguments(keytoolArguments, basicConstraints,
6835             keyUsage, extendedKeyUsage, sanValues, ianValues,
6836             genericExtensions);
6837
6838        displayKeytoolCommand(keytoolArguments);
6839      }
6840
6841
6842      // Generate the self-signed certificate.
6843      final long notBefore;
6844      if (validityStartTime == null)
6845      {
6846        notBefore = System.currentTimeMillis();
6847      }
6848      else
6849      {
6850        notBefore = validityStartTime.getTime();
6851      }
6852
6853      final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
6854
6855      final X509CertificateExtension[] extensions =
6856           new X509CertificateExtension[extensionList.size()];
6857      extensionList.toArray(extensions);
6858
6859      final Certificate[] chain;
6860      final KeyPair keyPair;
6861      final X509Certificate certificate;
6862      try
6863      {
6864        final ObjectPair<X509Certificate,KeyPair> p =
6865             X509Certificate.generateSelfSignedCertificate(
6866                  signatureAlgorithmIdentifier, keyAlgorithmIdentifier,
6867                  keySizeBits, subjectDN, notBefore, notAfter, extensions);
6868        certificate = p.getFirst();
6869        chain = new Certificate[] { certificate.toCertificate() };
6870        keyPair = p.getSecond();
6871      }
6872      catch (final Exception e)
6873      {
6874        Debug.debugException(e);
6875        wrapErr(0, WRAP_COLUMN,
6876             ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CERT.get());
6877        e.printStackTrace(getErr());
6878        return ResultCode.LOCAL_ERROR;
6879      }
6880
6881
6882      // Update the keystore with the new certificate.
6883      try
6884      {
6885        keystore.setKeyEntry(alias, keyPair.getPrivate(), privateKeyPassword,
6886             chain);
6887        writeKeystore(keystore, keystorePath, keystorePassword);
6888      }
6889      catch (final Exception e)
6890      {
6891        Debug.debugException(e);
6892        wrapErr(0, WRAP_COLUMN,
6893             ERR_MANAGE_CERTS_GEN_CERT_ERROR_UPDATING_KEYSTORE.get());
6894        e.printStackTrace(getErr());
6895        return ResultCode.LOCAL_ERROR;
6896      }
6897
6898      if (isNewKeystore)
6899      {
6900        out();
6901        wrapOut(0, WRAP_COLUMN,
6902             INFO_MANAGE_CERTS_GEN_CERT_CERT_CREATED_KEYSTORE.get(
6903                  getUserFriendlyKeystoreType(keystoreType)));
6904      }
6905
6906
6907      // If we're just generating a self-signed certificate, then display the
6908      // certificate that we generated.
6909      if (isGenerateCertificate)
6910      {
6911        out();
6912        wrapOut(0, WRAP_COLUMN,
6913             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_SELF_CERT.get());
6914        printCertificate(certificate, "", false);
6915
6916        return ResultCode.SUCCESS;
6917      }
6918
6919
6920      // If we're generating a certificate signing request, then put together
6921      // the appropriate set of arguments for that.
6922      Validator.ensureTrue(isGenerateCSR);
6923      out();
6924      wrapOut(0, WRAP_COLUMN,
6925           INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_KEYPAIR.get());
6926
6927      if (displayKeytoolCommand)
6928      {
6929        final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6930        keytoolArguments.add("-certreq");
6931        keytoolArguments.add("-keystore");
6932        keytoolArguments.add(keystorePath.getAbsolutePath());
6933        keytoolArguments.add("-storetype");
6934        keytoolArguments.add(keystoreType);
6935        keytoolArguments.add("-storepass");
6936        keytoolArguments.add("*****REDACTED*****");
6937        keytoolArguments.add("-keypass");
6938        keytoolArguments.add("*****REDACTED*****");
6939        keytoolArguments.add("-alias");
6940        keytoolArguments.add(alias);
6941        keytoolArguments.add("-dname");
6942        keytoolArguments.add(subjectDN.toString());
6943        keytoolArguments.add("-sigalg");
6944        keytoolArguments.add(signatureAlgorithmName);
6945
6946        addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
6947             extendedKeyUsage, sanValues, ianValues, genericExtensions);
6948
6949        if (outputFile != null)
6950        {
6951          keytoolArguments.add("-file");
6952          keytoolArguments.add(outputFile.getAbsolutePath());
6953        }
6954
6955        displayKeytoolCommand(keytoolArguments);
6956      }
6957
6958
6959      // Generate the certificate signing request.
6960      final PKCS10CertificateSigningRequest certificateSigningRequest;
6961      try
6962      {
6963        certificateSigningRequest = PKCS10CertificateSigningRequest.
6964             generateCertificateSigningRequest(signatureAlgorithmIdentifier,
6965                  keyPair, subjectDN, extensions);
6966      }
6967      catch (final Exception e)
6968      {
6969        Debug.debugException(e);
6970        wrapErr(0, WRAP_COLUMN,
6971             ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CSR.get());
6972        e.printStackTrace(getErr());
6973        return ResultCode.LOCAL_ERROR;
6974      }
6975
6976
6977      // Write the generated certificate signing request to the appropriate
6978      // location.
6979      try
6980      {
6981        final PrintStream ps;
6982        if (outputFile == null)
6983        {
6984          ps = getOut();
6985        }
6986        else
6987        {
6988          ps = new PrintStream(outputFile);
6989        }
6990
6991        if (outputPEM)
6992        {
6993          writePEMCertificateSigningRequest(ps,
6994               certificateSigningRequest.
6995                    getPKCS10CertificateSigningRequestBytes());
6996        }
6997        else
6998        {
6999          ps.write(certificateSigningRequest.
7000               getPKCS10CertificateSigningRequestBytes());
7001        }
7002
7003        if (outputFile != null)
7004        {
7005          ps.close();
7006        }
7007      }
7008      catch (final Exception e)
7009      {
7010        Debug.debugException(e);
7011        wrapErr(0, WRAP_COLUMN,
7012             ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CSR.get());
7013        e.printStackTrace(getErr());
7014        return ResultCode.LOCAL_ERROR;
7015      }
7016
7017
7018      // If the certificate signing request was written to an output file,
7019      // then let the user know that it was successful.  If it was written to
7020      // standard output, then we don't need to tell them because they'll be
7021      // able to see it.
7022      if (outputFile != null)
7023      {
7024        out();
7025        wrapOut(0, WRAP_COLUMN,
7026             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_CSR.get(
7027                  outputFile.getAbsolutePath()));
7028      }
7029
7030      return ResultCode.SUCCESS;
7031    }
7032
7033
7034    // If we've gotten here, then we should be signing a certificate signing
7035    // request.  Make sure that the keystore already has a private key entry
7036    // with the specified alias.
7037    Validator.ensureTrue(isSignCSR);
7038    if (! hasKeyAlias(keystore, alias))
7039    {
7040      if (hasCertificateAlias(keystore, alias))
7041      {
7042        wrapErr(0, WRAP_COLUMN,
7043             ERR_MANAGE_CERTS_GEN_CERT_SIGN_ALIAS_IS_CERT.get(alias,
7044                  keystorePath.getAbsolutePath()));
7045        return ResultCode.PARAM_ERROR;
7046      }
7047      else
7048      {
7049        wrapErr(0, WRAP_COLUMN,
7050             ERR_MANAGE_CERTS_GEN_CERT_SIGN_NO_SUCH_ALIAS.get(alias,
7051                  keystorePath.getAbsolutePath()));
7052        return ResultCode.PARAM_ERROR;
7053      }
7054    }
7055
7056
7057    // Get the signing certificate and its key pair.
7058    final PrivateKey issuerPrivateKey;
7059    final X509Certificate issuerCertificate;
7060    try
7061    {
7062      final Certificate[] chain = keystore.getCertificateChain(alias);
7063      issuerCertificate = new X509Certificate(chain[0].getEncoded());
7064
7065      issuerPrivateKey =
7066           (PrivateKey) keystore.getKey(alias, privateKeyPassword);
7067    }
7068    catch (final Exception e)
7069    {
7070      Debug.debugException(e);
7071      wrapErr(0, WRAP_COLUMN,
7072           ERR_MANAGE_CERTS_GEN_CERT_SIGN_CANNOT_GET_SIGNING_CERT.get(alias));
7073      e.printStackTrace(getErr());
7074      return ResultCode.LOCAL_ERROR;
7075    }
7076
7077
7078    // Make sure that we can decode the certificate signing request.
7079    final PKCS10CertificateSigningRequest csr;
7080    try
7081    {
7082      csr = readCertificateSigningRequestFromFile(inputFile);
7083    }
7084    catch (final LDAPException le)
7085    {
7086      Debug.debugException(le);
7087      wrapErr(0, WRAP_COLUMN, le.getMessage());
7088      return le.getResultCode();
7089    }
7090
7091
7092    // Make sure that we can verify the certificate signing request's signature.
7093    try
7094    {
7095      csr.verifySignature();
7096    }
7097    catch (final CertException ce)
7098    {
7099      Debug.debugException(ce);
7100      wrapErr(0, WRAP_COLUMN, ce.getMessage());
7101      return ResultCode.PARAM_ERROR;
7102    }
7103
7104
7105    // Prompt about whether to sign the request, if appropriate.
7106    if (! noPrompt)
7107    {
7108      out();
7109      wrapOut(0, WRAP_COLUMN,
7110           INFO_MANAGE_CERTS_GEN_CERT_SIGN_CONFIRM.get());
7111      out();
7112      printCertificateSigningRequest(csr, false, "");
7113      out();
7114
7115      try
7116      {
7117        if (! promptForYesNo(
7118             INFO_MANAGE_CERTS_GEN_CERT_PROMPT_SIGN.get()))
7119        {
7120          wrapErr(0, WRAP_COLUMN,
7121               ERR_MANAGE_CERTS_GEN_CERT_SIGN_CANCELED.get());
7122          return ResultCode.USER_CANCELED;
7123        }
7124      }
7125      catch (final LDAPException le)
7126      {
7127        Debug.debugException(le);
7128        err();
7129        wrapErr(0, WRAP_COLUMN, le.getMessage());
7130        return le.getResultCode();
7131      }
7132    }
7133
7134
7135    // Read the certificate signing request and see if we need to take values
7136    // from it.
7137    if ((subjectDN == null) || (signatureAlgorithmIdentifier == null) ||
7138        includeRequestedExtensions)
7139    {
7140      if (subjectDN == null)
7141      {
7142        subjectDN = csr.getSubjectDN();
7143      }
7144
7145      if (signatureAlgorithmIdentifier == null)
7146      {
7147        signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forOID(
7148             csr.getSignatureAlgorithmOID());
7149        if (signatureAlgorithmIdentifier == null)
7150        {
7151          wrapErr(0, WRAP_COLUMN,
7152               ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG_IN_CSR.get(
7153                    csr.getSignatureAlgorithmOID()));
7154          return ResultCode.PARAM_ERROR;
7155        }
7156        else
7157        {
7158          signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
7159        }
7160      }
7161
7162      if (includeRequestedExtensions)
7163      {
7164        for (final X509CertificateExtension extension : csr.getExtensions())
7165        {
7166          if ((extension instanceof AuthorityKeyIdentifierExtension) ||
7167              (extension instanceof IssuerAlternativeNameExtension))
7168          {
7169            // This extension applies to the issuer.  We won't include this in
7170            // the set of inherited extensions.
7171          }
7172          else if (extension instanceof SubjectKeyIdentifierExtension)
7173          {
7174            // The generated certificate will automatically include a subject
7175            // key identifier extension, so we don't need to include it.
7176          }
7177          else if (extension instanceof BasicConstraintsExtension)
7178          {
7179            // Don't override a value already provided on the command line.
7180            if (basicConstraints == null)
7181            {
7182              basicConstraints = (BasicConstraintsExtension) extension;
7183              extensionList.add(basicConstraints);
7184            }
7185          }
7186          else if (extension instanceof ExtendedKeyUsageExtension)
7187          {
7188            // Don't override a value already provided on the command line.
7189            if (extendedKeyUsage == null)
7190            {
7191              extendedKeyUsage = (ExtendedKeyUsageExtension) extension;
7192              extensionList.add(extendedKeyUsage);
7193            }
7194          }
7195          else if (extension instanceof KeyUsageExtension)
7196          {
7197            // Don't override a value already provided on the command line.
7198            if (keyUsage == null)
7199            {
7200              keyUsage = (KeyUsageExtension) extension;
7201              extensionList.add(keyUsage);
7202            }
7203          }
7204          else if (extension instanceof SubjectAlternativeNameExtension)
7205          {
7206            // Although we could merge values, it's safer to not do that if any
7207            // subject alternative name values were provided on the command
7208            // line.
7209            if (sanValues.isEmpty())
7210            {
7211              final SubjectAlternativeNameExtension e =
7212                   (SubjectAlternativeNameExtension) extension;
7213              for (final String dnsName : e.getDNSNames())
7214              {
7215                sanBuilder.addDNSName(dnsName);
7216                sanValues.add("DNS:" + dnsName);
7217              }
7218
7219              for (final InetAddress ipAddress : e.getIPAddresses())
7220              {
7221                sanBuilder.addIPAddress(ipAddress);
7222                sanValues.add("IP:" + ipAddress.getHostAddress());
7223              }
7224
7225              for (final String emailAddress : e.getRFC822Names())
7226              {
7227                sanBuilder.addRFC822Name(emailAddress);
7228                sanValues.add("EMAIL:" + emailAddress);
7229              }
7230
7231              for (final String uri : e.getUniformResourceIdentifiers())
7232              {
7233                sanBuilder.addUniformResourceIdentifier(uri);
7234                sanValues.add("URI:" + uri);
7235              }
7236
7237              for (final OID oid : e.getRegisteredIDs())
7238              {
7239                sanBuilder.addRegisteredID(oid);
7240                sanValues.add("OID:" + oid.toString());
7241              }
7242
7243              try
7244              {
7245                extensionList.add(
7246                     new SubjectAlternativeNameExtension(false,
7247                          sanBuilder.build()));
7248              }
7249              catch (final Exception ex)
7250              {
7251                // This should never happen.
7252                Debug.debugException(ex);
7253                throw new RuntimeException(ex);
7254              }
7255            }
7256          }
7257          else
7258          {
7259            genericExtensions.add(extension);
7260            extensionList.add(extension);
7261          }
7262        }
7263      }
7264    }
7265
7266
7267    // Generate the keytool arguments to use to sign the requested certificate.
7268    final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7269    keytoolArguments.add("-gencert");
7270    keytoolArguments.add("-keystore");
7271    keytoolArguments.add(keystorePath.getAbsolutePath());
7272    keytoolArguments.add("-storetype");
7273    keytoolArguments.add(keystoreType);
7274    keytoolArguments.add("-storepass");
7275    keytoolArguments.add("*****REDACTED*****");
7276    keytoolArguments.add("-keypass");
7277    keytoolArguments.add("*****REDACTED*****");
7278    keytoolArguments.add("-alias");
7279    keytoolArguments.add(alias);
7280    keytoolArguments.add("-dname");
7281    keytoolArguments.add(subjectDN.toString());
7282    keytoolArguments.add("-sigalg");
7283    keytoolArguments.add(signatureAlgorithmName);
7284    keytoolArguments.add("-validity");
7285    keytoolArguments.add(String.valueOf(daysValid));
7286
7287    if (validityStartTime != null)
7288    {
7289      keytoolArguments.add("-startdate");
7290      keytoolArguments.add(formatValidityStartTime(validityStartTime));
7291    }
7292
7293    addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
7294         extendedKeyUsage, sanValues, ianValues, genericExtensions);
7295
7296    keytoolArguments.add("-infile");
7297    keytoolArguments.add(inputFile.getAbsolutePath());
7298
7299    if (outputFile != null)
7300    {
7301      keytoolArguments.add("-outfile");
7302      keytoolArguments.add(outputFile.getAbsolutePath());
7303    }
7304
7305    if (outputPEM)
7306    {
7307      keytoolArguments.add("-rfc");
7308    }
7309
7310    if (displayKeytoolCommand)
7311    {
7312      displayKeytoolCommand(keytoolArguments);
7313    }
7314
7315
7316    // Generate the signed certificate.
7317    final long notBefore;
7318    if (validityStartTime == null)
7319    {
7320      notBefore = System.currentTimeMillis();
7321    }
7322    else
7323    {
7324      notBefore = validityStartTime.getTime();
7325    }
7326
7327    final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
7328
7329    final X509CertificateExtension[] extensions =
7330         new X509CertificateExtension[extensionList.size()];
7331    extensionList.toArray(extensions);
7332
7333    final X509Certificate signedCertificate;
7334    try
7335    {
7336      signedCertificate = X509Certificate.generateIssuerSignedCertificate(
7337           signatureAlgorithmIdentifier, issuerCertificate, issuerPrivateKey,
7338           csr.getPublicKeyAlgorithmOID(),
7339           csr.getPublicKeyAlgorithmParameters(), csr.getEncodedPublicKey(),
7340           csr.getDecodedPublicKey(), subjectDN, notBefore, notAfter,
7341           extensions);
7342    }
7343    catch (final Exception e)
7344    {
7345      Debug.debugException(e);
7346      wrapErr(0, WRAP_COLUMN,
7347           ERR_MANAGE_CERTS_GEN_CERT_ERROR_SIGNING_CERT.get());
7348      e.printStackTrace(getErr());
7349      return ResultCode.LOCAL_ERROR;
7350    }
7351
7352
7353    // Write the signed certificate signing request to the appropriate location.
7354    try
7355    {
7356      final PrintStream ps;
7357      if (outputFile == null)
7358      {
7359        ps = getOut();
7360      }
7361      else
7362      {
7363        ps = new PrintStream(outputFile);
7364      }
7365
7366      if (outputPEM)
7367      {
7368        writePEMCertificate(ps, signedCertificate.getX509CertificateBytes());
7369      }
7370      else
7371      {
7372        ps.write(signedCertificate.getX509CertificateBytes());
7373      }
7374
7375      if (outputFile != null)
7376      {
7377        ps.close();
7378      }
7379    }
7380    catch (final Exception e)
7381    {
7382      Debug.debugException(e);
7383      wrapErr(0, WRAP_COLUMN,
7384           ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_SIGNED_CERT.get());
7385      e.printStackTrace(getErr());
7386      return ResultCode.LOCAL_ERROR;
7387    }
7388
7389
7390    // If the certificate signing request was written to an output file,
7391    // then let the user know that it was successful.  If it was written to
7392    // standard output, then we don't need to tell them because they'll be
7393    // able to see it.
7394    if (outputFile != null)
7395    {
7396      out();
7397      wrapOut(0, WRAP_COLUMN,
7398           INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_SIGNED_CERT.get(
7399                outputFile.getAbsolutePath()));
7400    }
7401
7402    return ResultCode.SUCCESS;
7403  }
7404
7405
7406
7407  /**
7408   * Performs the necessary processing for the change-certificate-alias
7409   * subcommand.
7410   *
7411   * @return  A result code that indicates whether the processing completed
7412   *          successfully.
7413   */
7414  private ResultCode doChangeCertificateAlias()
7415  {
7416    // Get the values of a number of configured arguments.
7417    final StringArgument currentAliasArgument =
7418         subCommandParser.getStringArgument("current-alias");
7419    final String currentAlias = currentAliasArgument.getValue();
7420
7421    final StringArgument newAliasArgument =
7422         subCommandParser.getStringArgument("new-alias");
7423    final String newAlias = newAliasArgument.getValue();
7424
7425    final String keystoreType;
7426    final File keystorePath = getKeystorePath();
7427    try
7428    {
7429      keystoreType = inferKeystoreType(keystorePath);
7430    }
7431    catch (final LDAPException le)
7432    {
7433      Debug.debugException(le);
7434      wrapErr(0, WRAP_COLUMN, le.getMessage());
7435      return le.getResultCode();
7436    }
7437
7438    final char[] keystorePassword;
7439    try
7440    {
7441      keystorePassword = getKeystorePassword(keystorePath);
7442    }
7443    catch (final LDAPException le)
7444    {
7445      Debug.debugException(le);
7446      wrapErr(0, WRAP_COLUMN, le.getMessage());
7447      return le.getResultCode();
7448    }
7449
7450
7451    // Get the keystore.
7452    final KeyStore keystore;
7453    try
7454    {
7455      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7456    }
7457    catch (final LDAPException le)
7458    {
7459      Debug.debugException(le);
7460      wrapErr(0, WRAP_COLUMN, le.getMessage());
7461      return le.getResultCode();
7462    }
7463
7464
7465    // See if we need to use a private key password that is different from the
7466    // keystore password.
7467    final char[] privateKeyPassword;
7468    try
7469    {
7470      privateKeyPassword =
7471           getPrivateKeyPassword(keystore, currentAlias, keystorePassword);
7472    }
7473    catch (final LDAPException le)
7474    {
7475      Debug.debugException(le);
7476      wrapErr(0, WRAP_COLUMN, le.getMessage());
7477      return le.getResultCode();
7478    }
7479
7480
7481    // Make sure that the keystore has an existing entry with the current alias.
7482    // It must be either a certificate entry or a private key entry.
7483    final Certificate existingCertificate;
7484    final Certificate[] existingCertificateChain;
7485    final PrivateKey existingPrivateKey;
7486    try
7487    {
7488      if (hasCertificateAlias(keystore, currentAlias))
7489      {
7490        existingCertificate = keystore.getCertificate(currentAlias);
7491        existingCertificateChain = null;
7492        existingPrivateKey = null;
7493      }
7494      else if (hasKeyAlias(keystore, currentAlias))
7495      {
7496        existingCertificateChain = keystore.getCertificateChain(currentAlias);
7497        existingPrivateKey =
7498             (PrivateKey) keystore.getKey(currentAlias, privateKeyPassword);
7499        existingCertificate = null;
7500      }
7501      else
7502      {
7503        wrapErr(0, WRAP_COLUMN,
7504             ERR_MANAGE_CERTS_CHANGE_ALIAS_NO_SUCH_ALIAS.get(currentAlias));
7505        return ResultCode.PARAM_ERROR;
7506      }
7507    }
7508    catch (final Exception e)
7509    {
7510      Debug.debugException(e);
7511      wrapErr(0, WRAP_COLUMN,
7512           ERR_MANAGE_CERTS_CHANGE_ALIAS_CANNOT_GET_EXISTING_ENTRY.get(
7513                currentAlias));
7514      e.printStackTrace(getErr());
7515      return ResultCode.LOCAL_ERROR;
7516    }
7517
7518
7519    // Make sure that the keystore does not have an entry with the new alias.
7520    if (hasCertificateAlias(keystore, newAlias) ||
7521         hasKeyAlias(keystore, newAlias))
7522    {
7523      wrapErr(0, WRAP_COLUMN,
7524           ERR_MANAGE_CERTS_CHANGE_ALIAS_NEW_ALIAS_IN_USE.get(newAlias));
7525      return ResultCode.PARAM_ERROR;
7526    }
7527
7528
7529    // Generate the keytool arguments to use to change the certificate alias.
7530    final BooleanArgument displayKeytoolCommandArgument =
7531         subCommandParser.getBooleanArgument("display-keytool-command");
7532    if ((displayKeytoolCommandArgument != null) &&
7533          displayKeytoolCommandArgument.isPresent())
7534    {
7535      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7536      keytoolArguments.add("-changealias");
7537      keytoolArguments.add("-keystore");
7538      keytoolArguments.add(keystorePath.getAbsolutePath());
7539      keytoolArguments.add("-storetype");
7540      keytoolArguments.add(keystoreType);
7541      keytoolArguments.add("-storepass");
7542      keytoolArguments.add("*****REDACTED*****");
7543      keytoolArguments.add("-keypass");
7544      keytoolArguments.add("*****REDACTED*****");
7545      keytoolArguments.add("-alias");
7546      keytoolArguments.add(currentAlias);
7547      keytoolArguments.add("-destalias");
7548      keytoolArguments.add(newAlias);
7549
7550      displayKeytoolCommand(keytoolArguments);
7551    }
7552
7553
7554    // Update the keystore to remove the entry with the current alias and
7555    // re-write it with the new alias.
7556    try
7557    {
7558      keystore.deleteEntry(currentAlias);
7559      if (existingCertificate != null)
7560      {
7561        keystore.setCertificateEntry(newAlias, existingCertificate);
7562      }
7563      else
7564      {
7565        keystore.setKeyEntry(newAlias, existingPrivateKey,
7566             privateKeyPassword, existingCertificateChain);
7567      }
7568
7569      writeKeystore(keystore, keystorePath, keystorePassword);
7570    }
7571    catch (final Exception e)
7572    {
7573      Debug.debugException(e);
7574      wrapErr(0, WRAP_COLUMN,
7575           ERR_MANAGE_CERTS_CHANGE_ALIAS_CANNOT_UPDATE_KEYSTORE.get());
7576      e.printStackTrace(getErr());
7577      return ResultCode.LOCAL_ERROR;
7578    }
7579
7580    wrapOut(0, WRAP_COLUMN,
7581         INFO_MANAGE_CERTS_CHANGE_ALIAS_SUCCESSFUL.get(currentAlias,
7582              newAlias));
7583    return ResultCode.SUCCESS;
7584  }
7585
7586
7587
7588  /**
7589   * Performs the necessary processing for the change-keystore-password
7590   * subcommand.
7591   *
7592   * @return  A result code that indicates whether the processing completed
7593   *          successfully.
7594   */
7595  private ResultCode doChangeKeystorePassword()
7596  {
7597    // Get the values of a number of configured arguments.
7598    final String keystoreType;
7599    final File keystorePath = getKeystorePath();
7600    try
7601    {
7602      keystoreType = inferKeystoreType(keystorePath);
7603    }
7604    catch (final LDAPException le)
7605    {
7606      Debug.debugException(le);
7607      wrapErr(0, WRAP_COLUMN, le.getMessage());
7608      return le.getResultCode();
7609    }
7610
7611    final char[] currentKeystorePassword;
7612    try
7613    {
7614      currentKeystorePassword = getKeystorePassword(keystorePath, "current");
7615    }
7616    catch (final LDAPException le)
7617    {
7618      Debug.debugException(le);
7619      wrapErr(0, WRAP_COLUMN, le.getMessage());
7620      return le.getResultCode();
7621    }
7622
7623    final char[] newKeystorePassword;
7624    try
7625    {
7626      newKeystorePassword = getKeystorePassword(keystorePath, "new");
7627    }
7628    catch (final LDAPException le)
7629    {
7630      Debug.debugException(le);
7631      wrapErr(0, WRAP_COLUMN, le.getMessage());
7632      return le.getResultCode();
7633    }
7634
7635
7636    // Get the keystore.
7637    final KeyStore keystore;
7638    try
7639    {
7640      keystore = getKeystore(keystoreType, keystorePath,
7641           currentKeystorePassword);
7642    }
7643    catch (final LDAPException le)
7644    {
7645      Debug.debugException(le);
7646      wrapErr(0, WRAP_COLUMN, le.getMessage());
7647      return le.getResultCode();
7648    }
7649
7650
7651    // Generate the keytool arguments to use to change the keystore password.
7652    final BooleanArgument displayKeytoolCommandArgument =
7653         subCommandParser.getBooleanArgument("display-keytool-command");
7654    if ((displayKeytoolCommandArgument != null) &&
7655          displayKeytoolCommandArgument.isPresent())
7656    {
7657      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7658      keytoolArguments.add("-storepasswd");
7659      keytoolArguments.add("-keystore");
7660      keytoolArguments.add(keystorePath.getAbsolutePath());
7661      keytoolArguments.add("-storetype");
7662      keytoolArguments.add(keystoreType);
7663      keytoolArguments.add("-storepass");
7664      keytoolArguments.add("*****REDACTED*****");
7665      keytoolArguments.add("-new");
7666      keytoolArguments.add("*****REDACTED*****");
7667
7668      displayKeytoolCommand(keytoolArguments);
7669    }
7670
7671
7672    // Rewrite the keystore with the new password.
7673    try
7674    {
7675      writeKeystore(keystore, keystorePath, newKeystorePassword);
7676    }
7677    catch (final LDAPException le)
7678    {
7679      Debug.debugException(le);
7680      wrapErr(0, WRAP_COLUMN, le.getMessage());
7681      return le.getResultCode();
7682    }
7683
7684    wrapOut(0, WRAP_COLUMN,
7685         INFO_MANAGE_CERTS_CHANGE_KS_PW_SUCCESSFUL.get(
7686              keystorePath.getAbsolutePath()));
7687    return ResultCode.SUCCESS;
7688  }
7689
7690
7691
7692  /**
7693   * Performs the necessary processing for the change-private-key-password
7694   * subcommand.
7695   *
7696   * @return  A result code that indicates whether the processing completed
7697   *          successfully.
7698   */
7699  private ResultCode doChangePrivateKeyPassword()
7700  {
7701    // Get the values of a number of configured arguments.
7702    final StringArgument aliasArgument =
7703         subCommandParser.getStringArgument("alias");
7704    final String alias = aliasArgument.getValue();
7705
7706    final String keystoreType;
7707    final File keystorePath = getKeystorePath();
7708    try
7709    {
7710      keystoreType = inferKeystoreType(keystorePath);
7711    }
7712    catch (final LDAPException le)
7713    {
7714      Debug.debugException(le);
7715      wrapErr(0, WRAP_COLUMN, le.getMessage());
7716      return le.getResultCode();
7717    }
7718
7719    final char[] keystorePassword;
7720    try
7721    {
7722      keystorePassword = getKeystorePassword(keystorePath);
7723    }
7724    catch (final LDAPException le)
7725    {
7726      Debug.debugException(le);
7727      wrapErr(0, WRAP_COLUMN, le.getMessage());
7728      return le.getResultCode();
7729    }
7730
7731
7732    // Get the keystore.
7733    final KeyStore keystore;
7734    try
7735    {
7736      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7737    }
7738    catch (final LDAPException le)
7739    {
7740      Debug.debugException(le);
7741      wrapErr(0, WRAP_COLUMN, le.getMessage());
7742      return le.getResultCode();
7743    }
7744
7745
7746    // Make sure that the keystore has a key entry with the specified alias.
7747    if (hasCertificateAlias(keystore, alias))
7748    {
7749      wrapErr(0, WRAP_COLUMN,
7750           ERR_MANAGE_CERTS_CHANGE_PK_PW_ALIAS_IS_CERT.get(alias));
7751      return ResultCode.PARAM_ERROR;
7752    }
7753    else if (! hasKeyAlias(keystore, alias))
7754    {
7755      wrapErr(0, WRAP_COLUMN,
7756           ERR_MANAGE_CERTS_CHANGE_PK_PW_NO_SUCH_ALIAS.get(alias));
7757      return ResultCode.PARAM_ERROR;
7758    }
7759
7760
7761    // Get the current and new private key passwords.
7762    final char[] currentPrivateKeyPassword;
7763    try
7764    {
7765      currentPrivateKeyPassword =
7766           getPrivateKeyPassword(keystore, alias, "current", keystorePassword);
7767    }
7768    catch (final LDAPException le)
7769    {
7770      Debug.debugException(le);
7771      wrapErr(0, WRAP_COLUMN, le.getMessage());
7772      return le.getResultCode();
7773    }
7774
7775    final char[] newPrivateKeyPassword;
7776    try
7777    {
7778      newPrivateKeyPassword =
7779           getPrivateKeyPassword(keystore, alias, "new", keystorePassword);
7780    }
7781    catch (final LDAPException le)
7782    {
7783      Debug.debugException(le);
7784      wrapErr(0, WRAP_COLUMN, le.getMessage());
7785      return le.getResultCode();
7786    }
7787
7788
7789    // Generate the keytool arguments to use to change the private key.
7790    final BooleanArgument displayKeytoolCommandArgument =
7791         subCommandParser.getBooleanArgument("display-keytool-command");
7792    if ((displayKeytoolCommandArgument != null) &&
7793          displayKeytoolCommandArgument.isPresent())
7794    {
7795      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7796      keytoolArguments.add("-keypasswd");
7797      keytoolArguments.add("-keystore");
7798      keytoolArguments.add(keystorePath.getAbsolutePath());
7799      keytoolArguments.add("-storetype");
7800      keytoolArguments.add(keystoreType);
7801      keytoolArguments.add("-storepass");
7802      keytoolArguments.add("*****REDACTED*****");
7803      keytoolArguments.add("-alias");
7804      keytoolArguments.add(alias);
7805      keytoolArguments.add("-keypass");
7806      keytoolArguments.add("*****REDACTED*****");
7807      keytoolArguments.add("-new");
7808      keytoolArguments.add("*****REDACTED*****");
7809
7810      displayKeytoolCommand(keytoolArguments);
7811    }
7812
7813
7814    // Get the contents of the private key entry.
7815    final Certificate[] chain;
7816    final PrivateKey privateKey;
7817    try
7818    {
7819      chain = keystore.getCertificateChain(alias);
7820      privateKey =
7821           (PrivateKey) keystore.getKey(alias, currentPrivateKeyPassword);
7822    }
7823    catch (final UnrecoverableKeyException e)
7824    {
7825      Debug.debugException(e);
7826      wrapErr(0, WRAP_COLUMN,
7827           ERR_MANAGE_CERTS_CHANGE_PK_PW_WRONG_PK_PW.get(alias));
7828      return ResultCode.PARAM_ERROR;
7829    }
7830    catch (final Exception e)
7831    {
7832      Debug.debugException(e);
7833      wrapErr(0, WRAP_COLUMN,
7834           ERR_MANAGE_CERTS_CHANGE_PK_PW_CANNOT_GET_PK.get(alias));
7835      e.printStackTrace(getErr());
7836      return ResultCode.LOCAL_ERROR;
7837    }
7838
7839
7840    // Remove the existing key entry and re-add it with the new password.
7841    try
7842    {
7843      keystore.deleteEntry(alias);
7844      keystore.setKeyEntry(alias, privateKey, newPrivateKeyPassword, chain);
7845      writeKeystore(keystore, keystorePath, keystorePassword);
7846    }
7847    catch (final Exception e)
7848    {
7849      Debug.debugException(e);
7850      wrapErr(0, WRAP_COLUMN,
7851           ERR_MANAGE_CERTS_CHANGE_PK_PW_CANNOT_UPDATE_KS.get());
7852      e.printStackTrace(getErr());
7853      return ResultCode.LOCAL_ERROR;
7854    }
7855
7856    wrapOut(0, WRAP_COLUMN,
7857         INFO_MANAGE_CERTS_CHANGE_PK_PW_SUCCESSFUL.get(alias));
7858    return ResultCode.SUCCESS;
7859  }
7860
7861
7862
7863  /**
7864   * Performs the necessary processing for the trust-server-certificate
7865   * subcommand.
7866   *
7867   * @return  A result code that indicates whether the processing completed
7868   *          successfully.
7869   */
7870  private ResultCode doTrustServerCertificate()
7871  {
7872    // Get the values of a number of configured arguments.
7873    final StringArgument hostnameArgument =
7874         subCommandParser.getStringArgument("hostname");
7875    final String hostname = hostnameArgument.getValue();
7876
7877    final IntegerArgument portArgument =
7878         subCommandParser.getIntegerArgument("port");
7879    final int port = portArgument.getValue();
7880
7881    final String alias;
7882    final StringArgument aliasArgument =
7883         subCommandParser.getStringArgument("alias");
7884    if ((aliasArgument != null) && aliasArgument.isPresent())
7885    {
7886      alias = aliasArgument.getValue();
7887    }
7888    else
7889    {
7890      alias = hostname + ':' + port;
7891    }
7892
7893    final BooleanArgument useLDAPStartTLSArgument =
7894         subCommandParser.getBooleanArgument("use-ldap-start-tls");
7895    final boolean useLDAPStartTLS =
7896         ((useLDAPStartTLSArgument != null) &&
7897          useLDAPStartTLSArgument.isPresent());
7898
7899    final BooleanArgument issuersOnlyArgument =
7900         subCommandParser.getBooleanArgument("issuers-only");
7901    final boolean issuersOnly =
7902         ((issuersOnlyArgument != null) && issuersOnlyArgument.isPresent());
7903
7904    final BooleanArgument noPromptArgument =
7905         subCommandParser.getBooleanArgument("no-prompt");
7906    final boolean noPrompt =
7907         ((noPromptArgument != null) && noPromptArgument.isPresent());
7908
7909    final BooleanArgument verboseArgument =
7910         subCommandParser.getBooleanArgument("verbose");
7911    final boolean verbose =
7912         ((verboseArgument != null) && verboseArgument.isPresent());
7913
7914    final String keystoreType;
7915    final File keystorePath = getKeystorePath();
7916    final boolean isNewKeystore = (! keystorePath.exists());
7917    try
7918    {
7919      keystoreType = inferKeystoreType(keystorePath);
7920    }
7921    catch (final LDAPException le)
7922    {
7923      Debug.debugException(le);
7924      wrapErr(0, WRAP_COLUMN, le.getMessage());
7925      return le.getResultCode();
7926    }
7927
7928    final char[] keystorePassword;
7929    try
7930    {
7931      keystorePassword = getKeystorePassword(keystorePath);
7932    }
7933    catch (final LDAPException le)
7934    {
7935      Debug.debugException(le);
7936      wrapErr(0, WRAP_COLUMN, le.getMessage());
7937      return le.getResultCode();
7938    }
7939
7940
7941    // Get the keystore.
7942    final KeyStore keystore;
7943    try
7944    {
7945      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7946    }
7947    catch (final LDAPException le)
7948    {
7949      Debug.debugException(le);
7950      wrapErr(0, WRAP_COLUMN, le.getMessage());
7951      return le.getResultCode();
7952    }
7953
7954
7955    // Make sure that the specified alias is not already in use.
7956    if (hasCertificateAlias(keystore, alias) ||
7957         hasKeyAlias(keystore, alias))
7958    {
7959      wrapErr(0, WRAP_COLUMN,
7960           ERR_MANAGE_CERTS_TRUST_SERVER_ALIAS_IN_USE.get(alias));
7961      return ResultCode.PARAM_ERROR;
7962    }
7963
7964
7965    // Spawn a background thread to establish a connection and get the
7966    // certificate chain from the target server.
7967    final LinkedBlockingQueue<Object> responseQueue =
7968         new LinkedBlockingQueue<>(10);
7969    final ManageCertificatesServerCertificateCollector certificateCollector =
7970         new ManageCertificatesServerCertificateCollector(this, hostname, port,
7971              useLDAPStartTLS, verbose, responseQueue);
7972    certificateCollector.start();
7973
7974    Object responseObject =
7975         ERR_MANAGE_CERTS_TRUST_SERVER_NO_CERT_CHAIN_RECEIVED.get(
7976              hostname + ':' + port);
7977    try
7978    {
7979      responseObject = responseQueue.poll(90L, TimeUnit.SECONDS);
7980    }
7981    catch (final Exception e)
7982    {
7983      Debug.debugException(e);
7984    }
7985
7986    final X509Certificate[] chain;
7987    if (responseObject instanceof  X509Certificate[])
7988    {
7989      chain = (X509Certificate[]) responseObject;
7990    }
7991    else if (responseObject instanceof CertException)
7992    {
7993      // The error message will have already been recorded by the collector
7994      // thread, so we can just return a non-success result.
7995      return ResultCode.LOCAL_ERROR;
7996    }
7997    else
7998    {
7999      wrapErr(0, WRAP_COLUMN, String.valueOf(responseObject));
8000      return ResultCode.LOCAL_ERROR;
8001    }
8002
8003
8004    // If we should prompt the user about whether to trust the certificates,
8005    // then do so now.
8006    if (! noPrompt)
8007    {
8008      out();
8009      wrapOut(0, WRAP_COLUMN,
8010           INFO_MANAGE_CERTS_TRUST_SERVER_RETRIEVED_CHAIN.get(
8011                hostname + ':' + port));
8012
8013      boolean isFirst = true;
8014      for (final X509Certificate c : chain)
8015      {
8016        out();
8017
8018        if (isFirst)
8019        {
8020          isFirst = false;
8021          if (issuersOnly && (chain.length > 1))
8022          {
8023            wrapOut(0, WRAP_COLUMN,
8024                 INFO_MANAGE_CERTS_TRUST_SERVER_NOTE_OMITTED.get());
8025            out();
8026          }
8027        }
8028
8029        printCertificate(c, "", verbose);
8030      }
8031
8032      out();
8033
8034      try
8035      {
8036        if (! promptForYesNo(INFO_MANAGE_CERTS_TRUST_SERVER_PROMPT_TRUST.get()))
8037        {
8038          wrapErr(0, WRAP_COLUMN,
8039               ERR_MANAGE_CERTS_TRUST_SERVER_CHAIN_REJECTED.get());
8040          return ResultCode.USER_CANCELED;
8041        }
8042      }
8043      catch (final LDAPException le)
8044      {
8045        Debug.debugException(le);
8046        err();
8047        wrapErr(0, WRAP_COLUMN, le.getMessage());
8048        return le.getResultCode();
8049      }
8050    }
8051
8052
8053    // Add the certificates to the keystore.
8054    final LinkedHashMap<String,X509Certificate> certsByAlias =
8055         new LinkedHashMap<>(chain.length);
8056    for (int i=0; i < chain.length; i++)
8057    {
8058      if (i == 0)
8059      {
8060        if (issuersOnly && (chain.length > 1))
8061        {
8062          continue;
8063        }
8064
8065        certsByAlias.put(alias, chain[i]);
8066      }
8067      else if ((i == 1) && (chain.length == 2))
8068      {
8069        certsByAlias.put(alias + "-issuer", chain[i]);
8070      }
8071      else
8072      {
8073        certsByAlias.put(alias + "-issuer-" + i, chain[i]);
8074      }
8075    }
8076
8077    for (final Map.Entry<String,X509Certificate> e : certsByAlias.entrySet())
8078    {
8079      final String certAlias = e.getKey();
8080      final X509Certificate cert = e.getValue();
8081
8082      try
8083      {
8084        Validator.ensureFalse(
8085             (hasCertificateAlias(keystore, certAlias) ||
8086                  hasKeyAlias(keystore, certAlias)),
8087             "ERROR:  Alias '" + certAlias + "' is already in use in the " +
8088                  "keystore.");
8089        keystore.setCertificateEntry(certAlias, cert.toCertificate());
8090      }
8091      catch (final Exception ex)
8092      {
8093        Debug.debugException(ex);
8094        wrapErr(0, WRAP_COLUMN,
8095             ERR_MANAGE_CERTS_TRUST_SERVER_ERROR_ADDING_CERT_TO_KS.get(
8096                  cert.getSubjectDN()));
8097        ex.printStackTrace(getErr());
8098        return ResultCode.LOCAL_ERROR;
8099      }
8100    }
8101
8102
8103    // Save the updated keystore.
8104    try
8105    {
8106      writeKeystore(keystore, keystorePath, keystorePassword);
8107    }
8108    catch (final LDAPException le)
8109    {
8110      Debug.debugException(le);
8111      wrapErr(0, WRAP_COLUMN, le.getMessage());
8112      return le.getResultCode();
8113    }
8114
8115    if (isNewKeystore)
8116    {
8117      out();
8118      wrapOut(0, WRAP_COLUMN,
8119           INFO_MANAGE_CERTS_TRUST_SERVER_CERT_CREATED_KEYSTORE.get(
8120                getUserFriendlyKeystoreType(keystoreType)));
8121    }
8122
8123    out();
8124    if (certsByAlias.size() == 1)
8125    {
8126      wrapOut(0, WRAP_COLUMN,
8127           INFO_MANAGE_CERTS_TRUST_SERVER_ADDED_CERT_TO_KS.get());
8128    }
8129    else
8130    {
8131      wrapOut(0, WRAP_COLUMN,
8132           INFO_MANAGE_CERTS_TRUST_SERVER_ADDED_CERTS_TO_KS.get(
8133                certsByAlias.size()));
8134    }
8135
8136    return ResultCode.SUCCESS;
8137  }
8138
8139
8140
8141  /**
8142   * Performs the necessary processing for the check-certificate-usability
8143   * subcommand.
8144   *
8145   * @return  A result code that indicates whether the processing completed
8146   *          successfully.
8147   */
8148  private ResultCode doCheckCertificateUsability()
8149  {
8150    // Get the values of a number of configured arguments.
8151    final StringArgument aliasArgument =
8152         subCommandParser.getStringArgument("alias");
8153    final String alias = aliasArgument.getValue();
8154
8155    final String keystoreType;
8156    final File keystorePath = getKeystorePath();
8157    try
8158    {
8159      keystoreType = inferKeystoreType(keystorePath);
8160    }
8161    catch (final LDAPException le)
8162    {
8163      Debug.debugException(le);
8164      wrapErr(0, WRAP_COLUMN, le.getMessage());
8165      return le.getResultCode();
8166    }
8167
8168    final char[] keystorePassword;
8169    try
8170    {
8171      keystorePassword = getKeystorePassword(keystorePath);
8172    }
8173    catch (final LDAPException le)
8174    {
8175      Debug.debugException(le);
8176      wrapErr(0, WRAP_COLUMN, le.getMessage());
8177      return le.getResultCode();
8178    }
8179
8180
8181    // Get the keystore.
8182    final KeyStore keystore;
8183    try
8184    {
8185      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
8186    }
8187    catch (final LDAPException le)
8188    {
8189      Debug.debugException(le);
8190      wrapErr(0, WRAP_COLUMN, le.getMessage());
8191      return le.getResultCode();
8192    }
8193
8194
8195    // Make sure that the specified entry exists in the keystore and is
8196    // associated with a certificate chain and a private key.
8197    final X509Certificate[] chain;
8198    if (hasKeyAlias(keystore, alias))
8199    {
8200      try
8201      {
8202        final Certificate[] genericChain = keystore.getCertificateChain(alias);
8203        Validator.ensureTrue((genericChain.length > 0),
8204             "ERROR:  The keystore has a private key entry for alias '" +
8205                  alias + "', but the associated certificate chain is empty.");
8206
8207        chain = new X509Certificate[genericChain.length];
8208        for (int i=0; i < genericChain.length; i++)
8209        {
8210          chain[i] = new X509Certificate(genericChain[i].getEncoded());
8211        }
8212
8213        out();
8214        wrapOut(0, WRAP_COLUMN,
8215             INFO_MANAGE_CERTS_CHECK_USABILITY_GOT_CHAIN.get(alias));
8216
8217        for (final X509Certificate c : chain)
8218        {
8219          out();
8220          printCertificate(c, "", false);
8221        }
8222      }
8223      catch (final Exception e)
8224      {
8225        Debug.debugException(e);
8226        wrapErr(0, WRAP_COLUMN,
8227             ERR_MANAGE_CERTS_CHECK_USABILITY_CANNOT_GET_CHAIN.get(alias));
8228        e.printStackTrace(getErr());
8229        return ResultCode.LOCAL_ERROR;
8230      }
8231    }
8232    else if (hasCertificateAlias(keystore, alias))
8233    {
8234      wrapErr(0, WRAP_COLUMN,
8235           ERR_MANAGE_CERTS_CHECK_USABILITY_NO_PRIVATE_KEY.get(alias));
8236      return ResultCode.PARAM_ERROR;
8237    }
8238    else
8239    {
8240      wrapErr(0, WRAP_COLUMN,
8241           ERR_MANAGE_CERTS_CHECK_USABILITY_NO_SUCH_ALIAS.get(alias));
8242      return ResultCode.PARAM_ERROR;
8243    }
8244
8245
8246    // Check to see if the certificate is self-signed.  If so, then that's a
8247    // warning.  If not, then make sure that the chain is complete and that each
8248    // subsequent certificate is the issuer of the previous.
8249    int numWarnings = 0;
8250    int numErrors = 0;
8251    if (chain[0].isSelfSigned())
8252    {
8253      err();
8254      wrapErr(0, WRAP_COLUMN,
8255           WARN_MANAGE_CERTS_CHECK_USABILITY_CERT_IS_SELF_SIGNED.get(
8256                chain[0].getSubjectDN()));
8257      numWarnings++;
8258    }
8259    else if ((chain.length == 1) || (! chain[chain.length - 1].isSelfSigned()))
8260    {
8261      err();
8262      wrapErr(0, WRAP_COLUMN,
8263           ERR_MANAGE_CERTS_CHECK_USABILITY_END_OF_CHAIN_NOT_SELF_SIGNED.get(
8264                alias));
8265      numErrors++;
8266    }
8267    else
8268    {
8269      boolean chainError = false;
8270      final StringBuilder nonMatchReason = new StringBuilder();
8271      for (int i=1; i < chain.length; i++)
8272      {
8273        if (! chain[i].isIssuerFor(chain[i-1], nonMatchReason))
8274        {
8275          err();
8276          wrapErr(0, WRAP_COLUMN,
8277               ERR_MANAGE_CERTS_CHECK_USABILITY_CHAIN_ISSUER_MISMATCH.get(
8278                    alias, chain[i].getSubjectDN(), chain[i-1].getSubjectDN(),
8279                    nonMatchReason));
8280          numErrors++;
8281          chainError = true;
8282        }
8283      }
8284
8285      if (! chainError)
8286      {
8287        out();
8288        wrapOut(0, WRAP_COLUMN,
8289             INFO_MANAGE_CERTS_CHECK_USABILITY_CHAIN_COMPLETE.get());
8290      }
8291    }
8292
8293
8294    // Make sure that the signature is valid for each certificate in the
8295    // chain.  If any certificate has an invalid signature, then that's an
8296    // error.
8297    for (int i=0; i < chain.length; i++)
8298    {
8299      final X509Certificate c = chain[i];
8300
8301      try
8302      {
8303        if (c.isSelfSigned())
8304        {
8305          c.verifySignature(null);
8306        }
8307        else if ((i + 1) < chain.length)
8308        {
8309          c.verifySignature(chain[i+1]);
8310        }
8311
8312        out();
8313        wrapOut(0, WRAP_COLUMN,
8314             INFO_MANAGE_CERTS_CHECK_USABILITY_CERT_SIGNATURE_VALID.get(
8315                  c.getSubjectDN()));
8316      }
8317      catch (final CertException ce)
8318      {
8319        err();
8320        wrapErr(0, WRAP_COLUMN, ce.getMessage());
8321        numErrors++;
8322      }
8323    }
8324
8325
8326    // Check the validity window for each certificate in the chain.  If any of
8327    // them is expired or not yet valid, then that's an error.  If any of them
8328    // will expire in the near future, then that's a warning.
8329    final long currentTime = System.currentTimeMillis();
8330    final long thirtyDaysFromNow =
8331         currentTime + (30L * 24L * 60L * 60L * 1000L);
8332    for (int i=0; i < chain.length; i++)
8333    {
8334      final X509Certificate c = chain[i];
8335      if (c.getNotBeforeTime() > currentTime)
8336      {
8337        err();
8338        if (i == 0)
8339        {
8340          wrapErr(0, WRAP_COLUMN,
8341               ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NOT_YET_VALID.get(
8342                    c.getSubjectDN(), formatDateAndTime(c.getNotBeforeDate())));
8343        }
8344        else
8345        {
8346          wrapErr(0, WRAP_COLUMN,
8347               ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_NOT_YET_VALID.get(
8348                    c.getSubjectDN(), formatDateAndTime(c.getNotBeforeDate())));
8349        }
8350
8351        numErrors++;
8352      }
8353      else if (c.getNotAfterTime() < currentTime)
8354      {
8355        err();
8356        if (i == 0)
8357        {
8358          wrapErr(0, WRAP_COLUMN,
8359               ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_EXPIRED.get(
8360                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8361        }
8362        else
8363        {
8364          wrapErr(0, WRAP_COLUMN,
8365               ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_EXPIRED.get(
8366                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8367        }
8368
8369        numErrors++;
8370      }
8371      else if (c.getNotAfterTime() < thirtyDaysFromNow)
8372      {
8373        err();
8374        if (i == 0)
8375        {
8376          wrapErr(0, WRAP_COLUMN,
8377               WARN_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NEAR_EXPIRATION.get(
8378                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8379        }
8380        else
8381        {
8382          wrapErr(0, WRAP_COLUMN,
8383               WARN_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_NEAR_EXPIRATION.
8384                    get(c.getSubjectDN(),
8385                         formatDateAndTime(c.getNotAfterDate())));
8386        }
8387
8388        numWarnings++;
8389      }
8390      else
8391      {
8392        if (i == 0)
8393        {
8394          out();
8395          wrapOut(0, WRAP_COLUMN,
8396               INFO_MANAGE_CERTS_CHECK_USABILITY_END_CERT_VALIDITY_OK.get(
8397                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8398        }
8399        else
8400        {
8401          out();
8402          wrapOut(0, WRAP_COLUMN,
8403               INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_VALIDITY_OK.get(
8404                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8405        }
8406      }
8407    }
8408
8409
8410    // Look at all of the extensions for all of the certificates and perform the
8411    // following validation:
8412    // - If the certificate at the head of the chain has an extended key usage
8413    //   extension, then make sure it includes the serverAuth usage.  If it
8414    //   does not include an extended key usage extension, then warn that it
8415    //   should.
8416    // - If any of the issuer certificates has a basic constraints extension,
8417    //   then make sure it indicates that the associated certificate is a
8418    //   certification authority.  Further, if it has a path length constraint,
8419    //   then make sure the chain does not exceed that length.  If any issuer
8420    //   certificate does not have a basic constraints extension, then warn that
8421    //   it should.
8422    // - If any of the issuer certificates has a key usage extension, then
8423    //   make sure it has the certSign usage.  If any issuer certificate does
8424    //   not have a key usage extension, then warn that it should.
8425    // - TODO:  If any certificate has a CRL distribution points extension, then
8426    //   retrieve the CRL and make sure the certificate hasn't been revoked.
8427    // - TODO:  If any certificate has an authority information access
8428    //   extension that points to an OCSP service, then consult that service to
8429    //   determine whether the certificate has been revoked.
8430    for (int i=0; i < chain.length; i++)
8431    {
8432      boolean basicConstraintsFound = false;
8433      boolean extendedKeyUsageFound = false;
8434      boolean keyUsageFound = false;
8435      final X509Certificate c = chain[i];
8436      for (final X509CertificateExtension extension : c.getExtensions())
8437      {
8438        if (extension instanceof ExtendedKeyUsageExtension)
8439        {
8440          extendedKeyUsageFound = true;
8441          if (i == 0)
8442          {
8443            final ExtendedKeyUsageExtension e =
8444                 (ExtendedKeyUsageExtension) extension;
8445            if (!e.getKeyPurposeIDs().contains(
8446                 ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID()))
8447            {
8448              err();
8449              wrapErr(0, WRAP_COLUMN,
8450                   ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_BAD_EKU.get(
8451                        c.getSubjectDN()));
8452              numErrors++;
8453            }
8454            else
8455            {
8456              out();
8457              wrapOut(0, WRAP_COLUMN,
8458                   INFO_MANAGE_CERTS_CHECK_USABILITY_END_CERT_GOOD_EKU.get(
8459                        c.getSubjectDN()));
8460            }
8461          }
8462        }
8463        else if (extension instanceof BasicConstraintsExtension)
8464        {
8465          basicConstraintsFound = true;
8466          if (i > 0)
8467          {
8468            final BasicConstraintsExtension e =
8469                 (BasicConstraintsExtension) extension;
8470            if (!e.isCA())
8471            {
8472              err();
8473              wrapErr(0, WRAP_COLUMN,
8474                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_BAD_BC_CA.get(
8475                        c.getSubjectDN()));
8476              numErrors++;
8477            }
8478            else if ((e.getPathLengthConstraint() != null) &&
8479                 (chain.length > e.getPathLengthConstraint()))
8480            {
8481              err();
8482              wrapErr(0, WRAP_COLUMN,
8483                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_BAD_BC_LENGTH.
8484                        get(c.getSubjectDN(), e.getPathLengthConstraint(),
8485                             chain.length));
8486              numErrors++;
8487            }
8488            else
8489            {
8490              out();
8491              wrapOut(0, WRAP_COLUMN,
8492                   INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_GOOD_BC.get(
8493                        c.getSubjectDN()));
8494            }
8495          }
8496        }
8497        else if (extension instanceof KeyUsageExtension)
8498        {
8499          keyUsageFound = true;
8500          if (i > 0)
8501          {
8502            final KeyUsageExtension e = (KeyUsageExtension) extension;
8503            if (! e.isKeyCertSignBitSet())
8504            {
8505              err();
8506              wrapErr(0, WRAP_COLUMN,
8507                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_NO_CERT_SIGN_KU.get(
8508                        c.getSubjectDN()));
8509              numErrors++;
8510            }
8511            else
8512            {
8513              out();
8514              wrapOut(0, WRAP_COLUMN,
8515                   INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_GOOD_KU.get(
8516                        c.getSubjectDN()));
8517            }
8518          }
8519        }
8520      }
8521
8522      if (i == 0)
8523      {
8524        if (! extendedKeyUsageFound)
8525        {
8526          err();
8527          wrapErr(0, WRAP_COLUMN,
8528               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_EKU.get(
8529                    c.getSubjectDN()));
8530          numWarnings++;
8531        }
8532      }
8533      else
8534      {
8535        if (! basicConstraintsFound)
8536        {
8537          err();
8538          wrapErr(0, WRAP_COLUMN,
8539               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_BC.get(
8540                    c.getSubjectDN()));
8541          numWarnings++;
8542        }
8543
8544        if (! keyUsageFound)
8545        {
8546          err();
8547          wrapErr(0, WRAP_COLUMN,
8548               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_KU.get(
8549                    c.getSubjectDN()));
8550          numWarnings++;
8551        }
8552      }
8553    }
8554
8555
8556    // Make sure that none of the certificates has a signature algorithm that
8557    // uses MD5 or SHA-1.  If it uses an unrecognized signature algorithm, then
8558    // that's a warning.
8559    for (final X509Certificate c : chain)
8560    {
8561      final OID signatureAlgorithmOID = c.getSignatureAlgorithmOID();
8562      final SignatureAlgorithmIdentifier id =
8563           SignatureAlgorithmIdentifier.forOID(signatureAlgorithmOID);
8564      if (id == null)
8565      {
8566        err();
8567        wrapErr(0, WRAP_COLUMN,
8568             WARN_MANAGE_CERTS_CHECK_USABILITY_UNKNOWN_SIG_ALG.get(
8569                  c.getSubjectDN(), signatureAlgorithmOID));
8570        numWarnings++;
8571      }
8572      else
8573      {
8574        switch (id)
8575        {
8576          case MD2_WITH_RSA:
8577          case MD5_WITH_RSA:
8578          case SHA_1_WITH_RSA:
8579          case SHA_1_WITH_DSA:
8580          case SHA_1_WITH_ECDSA:
8581            err();
8582            wrapErr(0, WRAP_COLUMN,
8583                 ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_SIG_ALG.get(
8584                      c.getSubjectDN(), id.getUserFriendlyName()));
8585            numErrors++;
8586            break;
8587          case SHA_224_WITH_RSA:
8588          case SHA_224_WITH_DSA:
8589          case SHA_224_WITH_ECDSA:
8590          case SHA_256_WITH_RSA:
8591          case SHA_256_WITH_DSA:
8592          case SHA_256_WITH_ECDSA:
8593          case SHA_384_WITH_RSA:
8594          case SHA_384_WITH_ECDSA:
8595          case SHA_512_WITH_RSA:
8596          case SHA_512_WITH_ECDSA:
8597            out();
8598            wrapOut(0, WRAP_COLUMN,
8599                 INFO_MANAGE_CERTS_CHECK_USABILITY_SIG_ALG_OK.get(
8600                      c.getSubjectDN(), id.getUserFriendlyName()));
8601            break;
8602        }
8603      }
8604    }
8605
8606
8607    // Make sure that none of the certificates that uses the RSA key algorithm
8608    // has a public modulus size smaller than 2048 bits.
8609    for (final X509Certificate c : chain)
8610    {
8611      if ((c.getDecodedPublicKey() != null) &&
8612          (c.getDecodedPublicKey() instanceof RSAPublicKey))
8613      {
8614        final RSAPublicKey rsaPublicKey =
8615             (RSAPublicKey) c.getDecodedPublicKey();
8616        final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
8617        int modulusSizeBits = modulusBytes.length * 8;
8618        if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
8619        {
8620          modulusSizeBits -= 8;
8621        }
8622
8623        if (modulusSizeBits < 2048)
8624        {
8625          err();
8626          wrapErr(0, WRAP_COLUMN,
8627               ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_RSA_MODULUS.get(
8628                    c.getSubjectDN(), modulusSizeBits));
8629          numErrors++;
8630        }
8631        else
8632        {
8633          out();
8634          wrapOut(0, WRAP_COLUMN,
8635               INFO_MANAGE_CERTS_CHECK_USABILITY_RSA_MODULUS_OK.get(
8636                    c.getSubjectDN(), modulusSizeBits));
8637        }
8638      }
8639    }
8640
8641
8642    switch (numErrors)
8643    {
8644      case 0:
8645        break;
8646      case 1:
8647        err();
8648        wrapErr(0, WRAP_COLUMN,
8649             ERR_MANAGE_CERTS_CHECK_USABILITY_ONE_ERROR.get());
8650        return ResultCode.PARAM_ERROR;
8651      default:
8652        err();
8653        wrapErr(0, WRAP_COLUMN,
8654             ERR_MANAGE_CERTS_CHECK_USABILITY_MULTIPLE_ERRORS.get(numErrors));
8655        return ResultCode.PARAM_ERROR;
8656    }
8657
8658    switch (numWarnings)
8659    {
8660      case 0:
8661        out();
8662        wrapOut(0, WRAP_COLUMN,
8663             INFO_MANAGE_CERTS_CHECK_USABILITY_NO_ERRORS_OR_WARNINGS.get());
8664        return ResultCode.SUCCESS;
8665      case 1:
8666        err();
8667        wrapErr(0, WRAP_COLUMN,
8668             ERR_MANAGE_CERTS_CHECK_USABILITY_ONE_WARNING.get());
8669        return ResultCode.PARAM_ERROR;
8670      default:
8671        err();
8672        wrapErr(0, WRAP_COLUMN,
8673             ERR_MANAGE_CERTS_CHECK_USABILITY_MULTIPLE_WARNINGS.get(
8674                  numWarnings));
8675        return ResultCode.PARAM_ERROR;
8676    }
8677  }
8678
8679
8680
8681  /**
8682   * Performs the necessary processing for the display-certificate-file
8683   * subcommand.
8684   *
8685   * @return  A result code that indicates whether the processing completed
8686   *          successfully.
8687   */
8688  private ResultCode doDisplayCertificateFile()
8689  {
8690    // Get the values of a number of configured arguments.
8691    final FileArgument certificateFileArgument =
8692         subCommandParser.getFileArgument("certificate-file");
8693    final File certificateFile = certificateFileArgument.getValue();
8694
8695    final BooleanArgument verboseArgument =
8696         subCommandParser.getBooleanArgument("verbose");
8697    final boolean verbose =
8698         ((verboseArgument != null) && verboseArgument.isPresent());
8699
8700    final BooleanArgument displayKeytoolCommandArgument =
8701         subCommandParser.getBooleanArgument("display-keytool-command");
8702    if ((displayKeytoolCommandArgument != null) &&
8703        displayKeytoolCommandArgument.isPresent())
8704    {
8705      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
8706      keytoolArgs.add("-printcert");
8707      keytoolArgs.add("-file");
8708      keytoolArgs.add(certificateFile.getAbsolutePath());
8709
8710      if (verbose)
8711      {
8712        keytoolArgs.add("-v");
8713      }
8714
8715      displayKeytoolCommand(keytoolArgs);
8716    }
8717
8718
8719    // Read the certificates from the specified file.
8720    final List<X509Certificate> certificates;
8721    try
8722    {
8723      certificates = readCertificatesFromFile(certificateFile);
8724    }
8725    catch (final LDAPException le)
8726    {
8727      Debug.debugException(le);
8728      wrapErr(0, WRAP_COLUMN, le.getMessage());
8729      return le.getResultCode();
8730    }
8731
8732
8733    // If there aren't any certificates in the file, print that.
8734    if (certificates.isEmpty())
8735    {
8736      wrapOut(0, WRAP_COLUMN, INFO_MANAGE_CERTS_DISPLAY_CERT_NO_CERTS.get(
8737           certificateFile.getAbsolutePath()));
8738    }
8739    else
8740    {
8741      for (final X509Certificate c : certificates)
8742      {
8743        out();
8744        printCertificate(c, "", verbose);
8745      }
8746    }
8747
8748    return ResultCode.SUCCESS;
8749  }
8750
8751
8752
8753  /**
8754   * Performs the necessary processing for the
8755   * display-certificate-signing-request-file subcommand.
8756   *
8757   * @return  A result code that indicates whether the processing completed
8758   *          successfully.
8759   */
8760  private ResultCode doDisplayCertificateSigningRequestFile()
8761  {
8762    // Get the values of a number of configured arguments.
8763    final FileArgument csrFileArgument =
8764         subCommandParser.getFileArgument("certificate-signing-request-file");
8765    final File csrFile = csrFileArgument.getValue();
8766
8767    final BooleanArgument verboseArgument =
8768         subCommandParser.getBooleanArgument("verbose");
8769    final boolean verbose =
8770         ((verboseArgument != null) && verboseArgument.isPresent());
8771
8772    final BooleanArgument displayKeytoolCommandArgument =
8773         subCommandParser.getBooleanArgument("display-keytool-command");
8774    if ((displayKeytoolCommandArgument != null) &&
8775        displayKeytoolCommandArgument.isPresent())
8776    {
8777      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
8778      keytoolArgs.add("-printcertreq");
8779      keytoolArgs.add("-file");
8780      keytoolArgs.add(csrFile.getAbsolutePath());
8781      keytoolArgs.add("-v");
8782
8783      displayKeytoolCommand(keytoolArgs);
8784    }
8785
8786
8787    // Read the certificate signing request from the specified file.
8788    final PKCS10CertificateSigningRequest csr;
8789    try
8790    {
8791      csr = readCertificateSigningRequestFromFile(csrFile);
8792    }
8793    catch (final LDAPException le)
8794    {
8795      Debug.debugException(le);
8796      wrapErr(0, WRAP_COLUMN, le.getMessage());
8797      return le.getResultCode();
8798    }
8799
8800    out();
8801    printCertificateSigningRequest(csr, verbose, "");
8802
8803    return ResultCode.SUCCESS;
8804  }
8805
8806
8807
8808  /**
8809   * Prints a string representation of the provided certificate to standard
8810   * output.
8811   *
8812   * @param  certificate  The certificate to be printed.
8813   * @param  indent       The string to place at the beginning of each line to
8814   *                      indent that line.
8815   * @param  verbose      Indicates whether to display verbose information about
8816   *                      the certificate.
8817   */
8818  private void printCertificate(final X509Certificate certificate,
8819                                final String indent, final boolean verbose)
8820  {
8821    if (verbose)
8822    {
8823      out(indent +
8824           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VERSION.get(
8825                certificate.getVersion().getName()));
8826    }
8827
8828    out(indent +
8829         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_DN.get(
8830              certificate.getSubjectDN()));
8831    out(indent +
8832         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ISSUER_DN.get(
8833              certificate.getIssuerDN()));
8834
8835    if (verbose)
8836    {
8837      out(indent +
8838           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SERIAL_NUMBER.get(
8839                toColonDelimitedHex(
8840                     certificate.getSerialNumber().toByteArray())));
8841    }
8842
8843    out(indent +
8844         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_START.get(
8845              formatDateAndTime(certificate.getNotBeforeDate())));
8846    out(indent +
8847         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_END.get(
8848              formatDateAndTime(certificate.getNotAfterDate())));
8849
8850    final long currentTime = System.currentTimeMillis();
8851    if (currentTime < certificate.getNotBeforeTime())
8852    {
8853      out(indent +
8854           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_NOT_YET_VALID.
8855                get());
8856    }
8857    else if (currentTime > certificate.getNotAfterTime())
8858    {
8859      out(indent +
8860           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_EXPIRED.get());
8861    }
8862    else
8863    {
8864      out(indent +
8865           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_VALID.get());
8866    }
8867
8868    out(indent +
8869         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_ALG.get(
8870              certificate.getSignatureAlgorithmNameOrOID()));
8871    if (verbose)
8872    {
8873      String signatureString;
8874      try
8875      {
8876        signatureString =
8877             toColonDelimitedHex(certificate.getSignatureValue().getBytes());
8878      }
8879      catch (final Exception e)
8880      {
8881        Debug.debugException(e);
8882        signatureString = certificate.getSignatureValue().toString();
8883      }
8884      out(indent +
8885           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_VALUE.get());
8886      for (final String line : StaticUtils.wrapLine(signatureString, 78))
8887      {
8888        out(indent + "     " + line);
8889      }
8890    }
8891
8892    final String pkAlg;
8893    final String pkSummary = getPublicKeySummary(
8894         certificate.getPublicKeyAlgorithmOID(),
8895         certificate.getDecodedPublicKey(),
8896         certificate.getPublicKeyAlgorithmParameters());
8897    if (pkSummary == null)
8898    {
8899      pkAlg = certificate.getPublicKeyAlgorithmNameOrOID();
8900    }
8901    else
8902    {
8903      pkAlg = certificate.getPublicKeyAlgorithmNameOrOID() + " (" +
8904           pkSummary + ')';
8905    }
8906    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_PK_ALG.get(pkAlg));
8907
8908    if (verbose)
8909    {
8910      printPublicKey(certificate.getEncodedPublicKey(),
8911           certificate.getDecodedPublicKey(),
8912           certificate.getPublicKeyAlgorithmParameters(), indent);
8913
8914      if (certificate.getSubjectUniqueID() != null)
8915      {
8916        String subjectUniqueID;
8917        try
8918        {
8919          subjectUniqueID = toColonDelimitedHex(
8920               certificate.getSubjectUniqueID().getBytes());
8921        }
8922        catch (final Exception e)
8923        {
8924          Debug.debugException(e);
8925          subjectUniqueID = certificate.getSubjectUniqueID().toString();
8926        }
8927
8928        out(indent +
8929             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_UNIQUE_ID.get());
8930        for (final String line : StaticUtils.wrapLine(subjectUniqueID, 78))
8931        {
8932          out(indent + "     " + line);
8933        }
8934      }
8935
8936      if (certificate.getIssuerUniqueID() != null)
8937      {
8938        String issuerUniqueID;
8939        try
8940        {
8941          issuerUniqueID = toColonDelimitedHex(
8942               certificate.getIssuerUniqueID().getBytes());
8943        }
8944        catch (final Exception e)
8945        {
8946          Debug.debugException(e);
8947          issuerUniqueID = certificate.getIssuerUniqueID().toString();
8948        }
8949
8950        out(indent +
8951             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ISSUER_UNIQUE_ID.get());
8952        for (final String line : StaticUtils.wrapLine(issuerUniqueID, 78))
8953        {
8954          out(indent + "     " + line);
8955        }
8956      }
8957
8958      printExtensions(certificate.getExtensions(), indent);
8959    }
8960
8961    try
8962    {
8963      out(indent +
8964           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_FINGERPRINT.get("SHA-1",
8965                toColonDelimitedHex(certificate.getSHA1Fingerprint())));
8966    }
8967    catch (final Exception e)
8968    {
8969      Debug.debugException(e);
8970    }
8971
8972    try
8973    {
8974      out(indent +
8975           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_FINGERPRINT.get("SHA-256",
8976                toColonDelimitedHex(certificate.getSHA256Fingerprint())));
8977    }
8978    catch (final Exception e)
8979    {
8980      Debug.debugException(e);
8981    }
8982  }
8983
8984
8985
8986  /**
8987   * Prints a string representation of the provided certificate signing request
8988   * to standard output.
8989   *
8990   * @param  csr      The certificate signing request to be printed.
8991   * @param  verbose  Indicates whether to display verbose information about
8992   *                  the contents of the request.
8993   * @param  indent   The string to place at the beginning of each line to
8994   *                  indent that line.
8995   */
8996  private void printCertificateSigningRequest(
8997                    final PKCS10CertificateSigningRequest csr,
8998                    final boolean verbose, final String indent)
8999  {
9000    out(indent +
9001         INFO_MANAGE_CERTS_PRINT_CSR_LABEL_VERSION.get(
9002              csr.getVersion().getName()));
9003    out(indent +
9004         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_DN.get(
9005              csr.getSubjectDN()));
9006    out(indent +
9007         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_ALG.get(
9008              csr.getSignatureAlgorithmNameOrOID()));
9009
9010    if (verbose)
9011    {
9012      String signatureString;
9013      try
9014      {
9015        signatureString =
9016             toColonDelimitedHex(csr.getSignatureValue().getBytes());
9017      }
9018      catch (final Exception e)
9019      {
9020        Debug.debugException(e);
9021        signatureString = csr.getSignatureValue().toString();
9022      }
9023      out(indent +
9024           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_VALUE.get());
9025      for (final String line : StaticUtils.wrapLine(signatureString, 78))
9026      {
9027        out(indent + "     " + line);
9028      }
9029    }
9030
9031    final String pkAlg;
9032    final String pkSummary = getPublicKeySummary(csr.getPublicKeyAlgorithmOID(),
9033         csr.getDecodedPublicKey(), csr.getPublicKeyAlgorithmParameters());
9034    if (pkSummary == null)
9035    {
9036      pkAlg = csr.getPublicKeyAlgorithmNameOrOID();
9037    }
9038    else
9039    {
9040      pkAlg = csr.getPublicKeyAlgorithmNameOrOID() + " (" +
9041           pkSummary + ')';
9042    }
9043    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_PK_ALG.get(pkAlg));
9044
9045    if (verbose)
9046    {
9047      printPublicKey(csr.getEncodedPublicKey(), csr.getDecodedPublicKey(),
9048           csr.getPublicKeyAlgorithmParameters(), indent);
9049      printExtensions(csr.getExtensions(), indent);
9050    }
9051  }
9052
9053
9054
9055  /**
9056   * Prints information about the provided public key.
9057   *
9058   * @param  encodedPublicKey  The encoded representation of the public key.
9059   *                           This must not be {@code null}.
9060   * @param  decodedPublicKey  The decoded representation of the public key, if
9061   *                           available.
9062   * @param  parameters        The public key algorithm parameters, if any.
9063   * @param  indent            The string to place at the beginning of each
9064   *                           line to indent that line.
9065   */
9066  private void printPublicKey(final ASN1BitString encodedPublicKey,
9067                              final DecodedPublicKey decodedPublicKey,
9068                              final ASN1Element parameters,
9069                              final String indent)
9070  {
9071    if (decodedPublicKey == null)
9072    {
9073      String pkString;
9074      try
9075      {
9076        pkString = toColonDelimitedHex(encodedPublicKey.getBytes());
9077      }
9078      catch (final Exception e)
9079      {
9080        Debug.debugException(e);
9081        pkString = encodedPublicKey.toString();
9082      }
9083
9084      out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ENCODED_PK.get());
9085      for (final String line : StaticUtils.wrapLine(pkString, 78))
9086      {
9087        out(indent + "     " + line);
9088      }
9089
9090      return;
9091    }
9092
9093    if (decodedPublicKey instanceof RSAPublicKey)
9094    {
9095      final RSAPublicKey rsaPublicKey = (RSAPublicKey) decodedPublicKey;
9096      final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
9097
9098      int modulusSizeBits = modulusBytes.length * 8;
9099      if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
9100      {
9101        modulusSizeBits -= 8;
9102      }
9103
9104      out(indent +
9105           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_RSA_MODULUS.get(
9106                modulusSizeBits));
9107      final String modulusHex = toColonDelimitedHex(modulusBytes);
9108      for (final String line : StaticUtils.wrapLine(modulusHex, 78))
9109      {
9110        out(indent + "     " + line);
9111      }
9112
9113      out(indent +
9114           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_RSA_EXPONENT.get(
9115                toColonDelimitedHex(
9116                     rsaPublicKey.getPublicExponent().toByteArray())));
9117    }
9118    else if (decodedPublicKey instanceof EllipticCurvePublicKey)
9119    {
9120      final EllipticCurvePublicKey ecPublicKey =
9121           (EllipticCurvePublicKey) decodedPublicKey;
9122
9123      out(indent +
9124           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_IS_COMPRESSED.get(
9125                String.valueOf(ecPublicKey.usesCompressedForm())));
9126      out(indent +
9127           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_X.get(
9128                String.valueOf(ecPublicKey.getXCoordinate())));
9129      if (ecPublicKey.getYCoordinate() == null)
9130      {
9131        out(indent +
9132             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_Y_IS_EVEN.get(
9133                  String.valueOf(ecPublicKey.yCoordinateIsEven())));
9134      }
9135      else
9136      {
9137        out(indent +
9138             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_Y.get(
9139                  String.valueOf(ecPublicKey.getYCoordinate())));
9140      }
9141    }
9142  }
9143
9144
9145
9146  /**
9147   * Retrieves a short summary of the provided public key, if available.  For
9148   * RSA keys, this will be the modulus size in bits.  For elliptic curve keys,
9149   * this will be the named curve, if available.
9150   *
9151   * @param  publicKeyAlgorithmOID  The OID that identifies the type of public
9152   *                                key.
9153   * @param  publicKey              The decoded public key.  This may be
9154   *                                {@code null} if the decoded public key is
9155   *                                not available.
9156   * @param  parameters             The encoded public key algorithm parameters.
9157   *                                This may be {@code null} if no public key
9158   *                                algorithm parameters are available.
9159   *
9160   * @return  A short summary of the provided public key, or {@code null} if
9161   *          no summary is available.
9162   */
9163  static String getPublicKeySummary(final OID publicKeyAlgorithmOID,
9164                                    final DecodedPublicKey publicKey,
9165                                    final ASN1Element parameters)
9166  {
9167    if (publicKey instanceof RSAPublicKey)
9168    {
9169      final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
9170      final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
9171
9172      int modulusSizeBits = modulusBytes.length * 8;
9173      if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
9174      {
9175        modulusSizeBits -= 8;
9176      }
9177
9178      return INFO_MANAGE_CERTS_GET_PK_SUMMARY_RSA_MODULUS_SIZE.get(
9179           modulusSizeBits);
9180    }
9181    else if ((parameters != null) &&
9182         publicKeyAlgorithmOID.equals(PublicKeyAlgorithmIdentifier.EC.getOID()))
9183    {
9184      try
9185      {
9186        final OID namedCurveOID =
9187             parameters.decodeAsObjectIdentifier().getOID();
9188        return NamedCurve.getNameOrOID(namedCurveOID);
9189      }
9190      catch (final Exception e)
9191      {
9192        Debug.debugException(e);
9193      }
9194    }
9195
9196    return null;
9197  }
9198
9199
9200
9201  /**
9202   * Prints information about the provided extensions.
9203   *
9204   * @param  extensions  The list of extensions to be printed.
9205   * @param  indent      The string to place at the beginning of each line to
9206   *                     indent that line.
9207   */
9208  void printExtensions(final List<X509CertificateExtension> extensions,
9209                       final String indent)
9210  {
9211    if (extensions.isEmpty())
9212    {
9213      return;
9214    }
9215
9216    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXTENSIONS.get());
9217    for (final X509CertificateExtension extension : extensions)
9218    {
9219      if (extension instanceof AuthorityKeyIdentifierExtension)
9220      {
9221        out(indent + "     " +
9222             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_EXT.get());
9223        out(indent + "          " +
9224             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9225                  extension.getOID().toString()));
9226        out(indent + "          " +
9227             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9228                  String.valueOf(extension.isCritical())));
9229
9230        final AuthorityKeyIdentifierExtension e =
9231             (AuthorityKeyIdentifierExtension) extension;
9232        if (e.getKeyIdentifier() != null)
9233        {
9234          out(indent + "          " +
9235               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_ID.get());
9236          final String idHex =
9237               toColonDelimitedHex(e.getKeyIdentifier().getValue());
9238          for (final String line : StaticUtils.wrapLine(idHex, 78))
9239          {
9240            out(indent + "               " + line);
9241          }
9242        }
9243
9244        if (e.getAuthorityCertIssuer() != null)
9245        {
9246          out(indent + "          " +
9247               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_ISSUER.
9248                    get());
9249          printGeneralNames(e.getAuthorityCertIssuer(),
9250               indent + "               ");
9251        }
9252
9253        if (e.getAuthorityCertSerialNumber() != null)
9254        {
9255          out(indent + "          " +
9256               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_SERIAL.get(
9257                    toColonDelimitedHex(e.getAuthorityCertSerialNumber().
9258                         toByteArray())));
9259        }
9260      }
9261      else if (extension instanceof BasicConstraintsExtension)
9262      {
9263        out(indent + "     " +
9264             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_EXT.get());
9265        out(indent + "          " +
9266             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9267                  extension.getOID().toString()));
9268        out(indent + "          " +
9269             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9270                  String.valueOf(extension.isCritical())));
9271
9272        final BasicConstraintsExtension e =
9273             (BasicConstraintsExtension) extension;
9274        out(indent + "          " +
9275             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_IS_CA.get(
9276                  String.valueOf(e.isCA())));
9277
9278        if (e.getPathLengthConstraint() != null)
9279        {
9280          out(indent + "          " +
9281               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_LENGTH.get(
9282                    e.getPathLengthConstraint()));
9283        }
9284      }
9285      else if (extension instanceof CRLDistributionPointsExtension)
9286      {
9287        out(indent + "     " +
9288             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_EXT.get());
9289        out(indent + "          " +
9290             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9291                  extension.getOID().toString()));
9292        out(indent + "          " +
9293             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9294                  String.valueOf(extension.isCritical())));
9295
9296        final CRLDistributionPointsExtension crlDPE =
9297             (CRLDistributionPointsExtension) extension;
9298        for (final CRLDistributionPoint dp :
9299             crlDPE.getCRLDistributionPoints())
9300        {
9301          out(indent + "          " +
9302               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_HEADER.get());
9303          if (dp.getFullName() != null)
9304          {
9305            out(indent + "               " +
9306                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_FULL_NAME.
9307                      get());
9308            printGeneralNames(dp.getFullName(),
9309                 indent + "                    ");
9310          }
9311
9312          if (dp.getNameRelativeToCRLIssuer() != null)
9313          {
9314            out(indent + "               " +
9315                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_REL_NAME.get(
9316                      dp.getNameRelativeToCRLIssuer()));
9317          }
9318
9319          if (! dp.getPotentialRevocationReasons().isEmpty())
9320          {
9321            out(indent + "               " +
9322                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_REASON.get());
9323            for (final CRLDistributionPointRevocationReason r :
9324                 dp.getPotentialRevocationReasons())
9325            {
9326              out(indent + "                    " + r.getName());
9327            }
9328          }
9329
9330          if (dp.getCRLIssuer() != null)
9331          {
9332            out(indent + "              " +
9333                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_CRL_ISSUER.
9334                      get());
9335            printGeneralNames(dp.getCRLIssuer(),
9336                 indent + "                    ");
9337          }
9338        }
9339      }
9340      else if (extension instanceof ExtendedKeyUsageExtension)
9341      {
9342        out(indent + "     " +
9343             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_EXT.get());
9344        out(indent + "          " +
9345             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9346                  extension.getOID().toString()));
9347        out(indent + "          " +
9348             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9349                  String.valueOf(extension.isCritical())));
9350
9351        final ExtendedKeyUsageExtension e =
9352             (ExtendedKeyUsageExtension) extension;
9353        for (final OID oid : e.getKeyPurposeIDs())
9354        {
9355          final ExtendedKeyUsageID id = ExtendedKeyUsageID.forOID(oid);
9356          if (id == null)
9357          {
9358            out(indent + "          " +
9359                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_ID.get(oid));
9360          }
9361          else
9362          {
9363            out(indent + "          " +
9364                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_ID.get(
9365                      id.getName()));
9366          }
9367        }
9368      }
9369      else if (extension instanceof IssuerAlternativeNameExtension)
9370      {
9371        out(indent + "     " +
9372             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IAN_EXT.get());
9373        out(indent + "          " +
9374             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9375                  extension.getOID().toString()));
9376        out(indent + "          " +
9377             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9378                  String.valueOf(extension.isCritical())));
9379
9380        final IssuerAlternativeNameExtension e =
9381             (IssuerAlternativeNameExtension) extension;
9382        printGeneralNames(e.getGeneralNames(), indent + "          ");
9383      }
9384      else if (extension instanceof KeyUsageExtension)
9385      {
9386        out(indent + "     " +
9387             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_EXT.get());
9388        out(indent + "          " +
9389             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9390                  extension.getOID().toString()));
9391        out(indent + "          " +
9392             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9393                  String.valueOf(extension.isCritical())));
9394
9395        out(indent + "          " +
9396             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_USAGES.get());
9397        final KeyUsageExtension kue = (KeyUsageExtension) extension;
9398        if (kue.isDigitalSignatureBitSet())
9399        {
9400          out(indent + "               " +
9401               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DS.get());
9402        }
9403
9404        if (kue.isNonRepudiationBitSet())
9405        {
9406          out(indent + "               " +
9407               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_NR.get());
9408        }
9409
9410        if (kue.isKeyEnciphermentBitSet())
9411        {
9412          out(indent + "               " +
9413               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KE.get());
9414        }
9415
9416        if (kue.isDataEnciphermentBitSet())
9417        {
9418          out(indent + "               " +
9419               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DE.get());
9420        }
9421
9422        if (kue.isKeyCertSignBitSet())
9423        {
9424          out(indent + "               " +
9425               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KCS.get());
9426        }
9427
9428        if (kue.isCRLSignBitSet())
9429        {
9430          out(indent + "               " +
9431               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_CRL_SIGN.get());
9432        }
9433
9434        if (kue.isEncipherOnlyBitSet())
9435        {
9436          out(indent + "               " +
9437               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_EO.get());
9438        }
9439
9440        if (kue.isDecipherOnlyBitSet())
9441        {
9442          out(indent + "               " +
9443               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DO.get());
9444        }
9445      }
9446      else if (extension instanceof SubjectAlternativeNameExtension)
9447      {
9448        out(indent + "     " +
9449             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SAN_EXT.get());
9450        out(indent + "          " +
9451             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9452                  extension.getOID().toString()));
9453        out(indent + "          " +
9454             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9455                  String.valueOf(extension.isCritical())));
9456
9457        final SubjectAlternativeNameExtension e =
9458             (SubjectAlternativeNameExtension) extension;
9459        printGeneralNames(e.getGeneralNames(), indent + "          ");
9460      }
9461      else if (extension instanceof SubjectKeyIdentifierExtension)
9462      {
9463        out(indent + "     " +
9464             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SKI_EXT.get());
9465        out(indent + "          " +
9466             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9467                  extension.getOID().toString()));
9468        out(indent + "          " +
9469             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9470                  String.valueOf(extension.isCritical())));
9471
9472        final SubjectKeyIdentifierExtension e =
9473             (SubjectKeyIdentifierExtension) extension;
9474        final String idHex =
9475             toColonDelimitedHex(e.getKeyIdentifier().getValue());
9476        out(indent + "          " +
9477             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SKI_ID.get());
9478        for (final String line  : StaticUtils.wrapLine(idHex, 78))
9479        {
9480          out(indent + "               " + line);
9481        }
9482      }
9483      else
9484      {
9485        out(indent + "     " +
9486             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_GENERIC.get());
9487        out(indent + "          " +
9488             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9489                  extension.getOID().toString()));
9490        out(indent + "          " +
9491             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9492                  String.valueOf(extension.isCritical())));
9493
9494        final String valueHex = toColonDelimitedHex(extension.getValue());
9495        out(indent + "          " +
9496             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_VALUE.get());
9497        getOut().print(StaticUtils.toHexPlusASCII(extension.getValue(),
9498             (indent.length() + 15)));
9499      }
9500    }
9501  }
9502
9503
9504
9505  /**
9506   * Prints information about the contents of the provided general names object.
9507   *
9508   * @param  generalNames  The general names object to print.
9509   * @param  indent        The string to place at the beginning of each line to
9510   *                       indent that line.
9511   */
9512  private void printGeneralNames(final GeneralNames generalNames,
9513                                 final String indent)
9514  {
9515    for (final String dnsName : generalNames.getDNSNames())
9516    {
9517      out(indent + INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_DNS.get(dnsName));
9518    }
9519
9520    for (final InetAddress ipAddress : generalNames.getIPAddresses())
9521    {
9522      out(indent +
9523           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_IP.get(
9524                ipAddress.getHostAddress()));
9525    }
9526
9527    for (final String name : generalNames.getRFC822Names())
9528    {
9529      out(indent +
9530           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_RFC_822_NAME.get(name));
9531    }
9532
9533    for (final DN dn : generalNames.getDirectoryNames())
9534    {
9535      out(indent +
9536           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_DIRECTORY_NAME.get(
9537                String.valueOf(dn)));
9538    }
9539
9540    for (final String uri : generalNames.getUniformResourceIdentifiers())
9541    {
9542      out(indent + INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_URI.get(uri));
9543    }
9544
9545    for (final OID oid : generalNames.getRegisteredIDs())
9546    {
9547      out(indent +
9548           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_REGISTERED_ID.get(
9549                oid.toString()));
9550    }
9551
9552    if (! generalNames.getOtherNames().isEmpty())
9553    {
9554      out(indent +
9555           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_OTHER_NAME_COUNT.get(
9556                generalNames.getOtherNames().size()));
9557    }
9558
9559    if (! generalNames.getX400Addresses().isEmpty())
9560    {
9561      out(indent +
9562           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_X400_ADDR_COUNT.get(
9563                generalNames.getX400Addresses().size()));
9564    }
9565
9566    if (! generalNames.getEDIPartyNames().isEmpty())
9567    {
9568      out(indent +
9569           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_EDI_PARTY_NAME_COUNT.get(
9570                generalNames.getEDIPartyNames().size()));
9571    }
9572  }
9573
9574
9575
9576  /**
9577   * Writes a PEM-encoded representation of the provided encoded certificate to
9578   * the given print stream.
9579   *
9580   * @param  printStream         The print stream to which the PEM-encoded
9581   *                             certificate should be written.  It must not be
9582   *                             {@code null}.
9583   * @param  encodedCertificate  The bytes that comprise the encoded
9584   *                             certificate.  It must not be {@code null}.
9585   */
9586  private static void writePEMCertificate(final PrintStream printStream,
9587                                          final byte[] encodedCertificate)
9588  {
9589    final String certBase64 = Base64.encode(encodedCertificate);
9590    printStream.println("-----BEGIN CERTIFICATE-----");
9591    for (final String line : StaticUtils.wrapLine(certBase64, 64))
9592    {
9593      printStream.println(line);
9594    }
9595    printStream.println("-----END CERTIFICATE-----");
9596  }
9597
9598
9599
9600  /**
9601   * Writes a PEM-encoded representation of the provided encoded certificate
9602   * signing request to the given print stream.
9603   *
9604   * @param  printStream  The print stream to which the PEM-encoded certificate
9605   *                      signing request should be written.  It must not be
9606   *                      {@code null}.
9607   * @param  encodedCSR   The bytes that comprise the encoded certificate
9608   *                      signing request.  It must not be {@code null}.
9609   */
9610  private static void writePEMCertificateSigningRequest(
9611                           final PrintStream printStream,
9612                           final byte[] encodedCSR)
9613  {
9614    final String certBase64 = Base64.encode(encodedCSR);
9615    printStream.println("-----BEGIN CERTIFICATE REQUEST-----");
9616    for (final String line : StaticUtils.wrapLine(certBase64, 64))
9617    {
9618      printStream.println(line);
9619    }
9620    printStream.println("-----END CERTIFICATE REQUEST-----");
9621  }
9622
9623
9624
9625  /**
9626   * Writes a PEM-encoded representation of the provided encoded private key to
9627   * the given print stream.
9628   *
9629   * @param  printStream        The print stream to which the PEM-encoded
9630   *                            private key should be written.  It must not be
9631   *                            {@code null}.
9632   * @param  encodedPrivateKey  The bytes that comprise the encoded private key.
9633   *                            It must not be {@code null}.
9634   */
9635  private static void writePEMPrivateKey(final PrintStream printStream,
9636                                         final byte[] encodedPrivateKey)
9637  {
9638    final String certBase64 = Base64.encode(encodedPrivateKey);
9639    printStream.println("-----BEGIN PRIVATE KEY-----");
9640    for (final String line : StaticUtils.wrapLine(certBase64, 64))
9641    {
9642      printStream.println(line);
9643    }
9644    printStream.println("-----END PRIVATE KEY-----");
9645  }
9646
9647
9648
9649  /**
9650   * Displays the keytool command that can be invoked to produce approximately
9651   * equivalent functionality.
9652   *
9653   * @param  keytoolArgs  The arguments to provide to the keytool command.
9654   */
9655  private void displayKeytoolCommand(final List<String> keytoolArgs)
9656  {
9657    final StringBuilder buffer = new StringBuilder();
9658    buffer.append("#      keytool");
9659
9660    boolean lastWasArgName = false;
9661    for (final String arg : keytoolArgs)
9662    {
9663      if (arg.startsWith("-"))
9664      {
9665        buffer.append(" \\");
9666        buffer.append(StaticUtils.EOL);
9667        buffer.append("#           ");
9668        buffer.append(arg);
9669        lastWasArgName = true;
9670      }
9671      else if (lastWasArgName)
9672      {
9673        buffer.append(' ');
9674        buffer.append(StaticUtils.cleanExampleCommandLineArgument(arg));
9675        lastWasArgName = false;
9676      }
9677      else
9678      {
9679        buffer.append(" \\");
9680        buffer.append(StaticUtils.EOL);
9681        buffer.append("#           ");
9682        buffer.append(arg);
9683        lastWasArgName = false;
9684      }
9685    }
9686
9687    out();
9688    out(INFO_MANAGE_CERTS_APPROXIMATE_KEYTOOL_COMMAND.get());
9689    out(buffer);
9690    out();
9691  }
9692
9693
9694
9695  /**
9696   * Retrieves the path to the target keystore file.
9697   *
9698   * @return  The path to the target keystore file, or {@code null} if no
9699   *          keystore path was configured.
9700   */
9701  private File getKeystorePath()
9702  {
9703    final FileArgument keystoreArgument =
9704         subCommandParser.getFileArgument("keystore");
9705    if (keystoreArgument != null)
9706    {
9707      return keystoreArgument.getValue();
9708    }
9709
9710    return null;
9711  }
9712
9713
9714
9715  /**
9716   * Retrieves the password needed to access the keystore.
9717   *
9718   * @param  keystoreFile  The path to the keystore file for which to get the
9719   *                       password.
9720   *
9721   * @return  The password needed to access the keystore, or {@code null} if
9722   *          no keystore password was configured.
9723   *
9724   * @throws  LDAPException  If a problem is encountered while trying to get the
9725   *                         keystore password.
9726   */
9727  private char[] getKeystorePassword(final File keystoreFile)
9728          throws LDAPException
9729  {
9730    return getKeystorePassword(keystoreFile, null);
9731  }
9732
9733
9734
9735  /**
9736   * Retrieves the password needed to access the keystore.
9737   *
9738   * @param  keystoreFile  The path to the keystore file for which to get the
9739   *                       password.
9740   * @param  prefix        The prefix string to use for the arguments.  This may
9741   *                       be {@code null} if no prefix is needed.
9742   *
9743   * @return  The password needed to access the keystore, or {@code null} if
9744   *          no keystore password was configured.
9745   *
9746   * @throws  LDAPException  If a problem is encountered while trying to get the
9747   *                         keystore password.
9748   */
9749  private char[] getKeystorePassword(final File keystoreFile,
9750                                     final String prefix)
9751          throws LDAPException
9752  {
9753    final String prefixDash;
9754    if (prefix == null)
9755    {
9756      prefixDash = "";
9757    }
9758    else
9759    {
9760      prefixDash = prefix + '-';
9761    }
9762
9763    final StringArgument keystorePasswordArgument =
9764         subCommandParser.getStringArgument(prefixDash + "keystore-password");
9765    if ((keystorePasswordArgument != null) &&
9766         keystorePasswordArgument.isPresent())
9767    {
9768      final char[] keystorePWChars =
9769           keystorePasswordArgument.getValue().toCharArray();
9770      if ((! keystoreFile.exists()) && (keystorePWChars.length < 6))
9771      {
9772        throw new LDAPException(ResultCode.PARAM_ERROR,
9773             ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
9774      }
9775
9776      return keystorePWChars;
9777    }
9778
9779
9780    final FileArgument keystorePasswordFileArgument =
9781         subCommandParser.getFileArgument(
9782              prefixDash + "keystore-password-file");
9783    if ((keystorePasswordFileArgument != null) &&
9784        keystorePasswordFileArgument.isPresent())
9785    {
9786      final File f = keystorePasswordFileArgument.getValue();
9787      try (BufferedReader r = new BufferedReader(new FileReader(f)))
9788      {
9789        final String line = r.readLine();
9790        if (line == null)
9791        {
9792          throw new LDAPException(ResultCode.PARAM_ERROR,
9793               ERR_MANAGE_CERTS_GET_KS_PW_EMPTY_FILE.get(f.getAbsolutePath()));
9794        }
9795        else if (r.readLine() != null)
9796        {
9797          throw new LDAPException(ResultCode.PARAM_ERROR,
9798               ERR_MANAGE_CERTS_GET_KS_PW_MULTI_LINE_FILE.get(
9799                    f.getAbsolutePath()));
9800        }
9801        else if (line.isEmpty())
9802        {
9803          throw new LDAPException(ResultCode.PARAM_ERROR,
9804               ERR_MANAGE_CERTS_GET_KS_PW_EMPTY_FILE.get(f.getAbsolutePath()));
9805        }
9806        else
9807        {
9808          if ((! keystoreFile.exists()) && (line.length() < 6))
9809          {
9810            throw new LDAPException(ResultCode.PARAM_ERROR,
9811                 ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
9812          }
9813
9814          return line.toCharArray();
9815        }
9816      }
9817      catch(final LDAPException le)
9818      {
9819        Debug.debugException(le);
9820        throw le;
9821      }
9822      catch (final Exception e)
9823      {
9824        Debug.debugException(e);
9825        throw new LDAPException(ResultCode.LOCAL_ERROR,
9826             ERR_MANAGE_CERTS_GET_KS_PW_ERROR_READING_FILE.get(
9827                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
9828             e);
9829      }
9830    }
9831
9832
9833    final BooleanArgument promptArgument = subCommandParser.getBooleanArgument(
9834         "prompt-for-" + prefixDash + "keystore-password");
9835    if ((promptArgument != null) && promptArgument.isPresent())
9836    {
9837      out();
9838      if (keystoreFile.exists() && (! "new".equals(prefix)))
9839      {
9840        // We're only going to prompt once.
9841        if ((prefix != null) && prefix.equals("current"))
9842        {
9843          return promptForPassword(
9844               INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_CURRENT_PROMPT.get(
9845                    keystoreFile.getAbsolutePath()),
9846               false);
9847        }
9848        else
9849        {
9850          return promptForPassword(
9851               INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_PROMPT.get(
9852                    keystoreFile.getAbsolutePath()),
9853               false);
9854        }
9855      }
9856      else
9857      {
9858        // We're creating a new keystore, so we should prompt for the password
9859        // twice to prevent setting the wrong password because of a typo.
9860        while (true)
9861        {
9862          final String prompt1;
9863          if ("new".equals(prefix))
9864          {
9865            prompt1 = INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_NEW_PROMPT.get();
9866          }
9867          else
9868          {
9869            prompt1 = INFO_MANAGE_CERTS_KEY_KS_PW_NEW_PROMPT_1.get(
9870                 keystoreFile.getAbsolutePath());
9871          }
9872          final char[] pwChars = promptForPassword(prompt1, false);
9873
9874          if (pwChars.length < 6)
9875          {
9876            wrapErr(0, WRAP_COLUMN,
9877                 ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
9878            err();
9879            continue;
9880          }
9881
9882          final char[] confirmChars = promptForPassword(
9883               INFO_MANAGE_CERTS_KEY_KS_PW_NEW_PROMPT_2.get(), true);
9884
9885          if (Arrays.equals(pwChars, confirmChars))
9886          {
9887            Arrays.fill(confirmChars, '\u0000');
9888            return pwChars;
9889          }
9890          else
9891          {
9892            wrapErr(0, WRAP_COLUMN,
9893                 ERR_MANAGE_CERTS_KEY_KS_PW_PROMPT_MISMATCH.get());
9894            err();
9895          }
9896        }
9897      }
9898    }
9899
9900
9901    return null;
9902  }
9903
9904
9905
9906  /**
9907   * Prompts for a password and retrieves the value that the user entered.
9908   *
9909   * @param  prompt      The prompt to display to the user.
9910   * @param  allowEmpty  Indicates whether to allow the password to be empty.
9911   *
9912   * @return  The password that was read, or an empty array if the user did not
9913   *          type a password before pressing ENTER.
9914   *
9915   * @throws  LDAPException  If a problem is encountered while reading the
9916   *                         password.
9917   */
9918  private char[] promptForPassword(final String prompt,
9919                                   final boolean allowEmpty)
9920          throws LDAPException
9921  {
9922    final Iterator<String> iterator =
9923         StaticUtils.wrapLine(prompt, WRAP_COLUMN).iterator();
9924    while (iterator.hasNext())
9925    {
9926      final String line = iterator.next();
9927      if (iterator.hasNext())
9928      {
9929        out(line);
9930      }
9931      else
9932      {
9933        getOut().print(line);
9934      }
9935    }
9936
9937    final char[] passwordChars = PasswordReader.readPasswordChars();
9938    if ((passwordChars.length == 0) && (! allowEmpty))
9939    {
9940      wrapErr(0, WRAP_COLUMN,
9941           ERR_MANAGE_CERTS_PROMPT_FOR_PW_EMPTY_PW.get());
9942      err();
9943      return promptForPassword(prompt, allowEmpty);
9944    }
9945
9946    return passwordChars;
9947  }
9948
9949
9950
9951  /**
9952   * Prompts the user for a yes or no response.
9953   *
9954   * @param  prompt  The prompt to display to the end user.
9955   *
9956   * @return  {@code true} if the user chooses the "yes" response, or
9957   *          {@code false} if the user chooses the "no" throws.
9958   *
9959   * @throws  LDAPException  If a problem is encountered while reading data from
9960   *                         the client.
9961   */
9962  private boolean promptForYesNo(final String prompt)
9963          throws LDAPException
9964  {
9965    while (true)
9966    {
9967      final List<String> lines =
9968           StaticUtils.wrapLine((prompt + ' '), WRAP_COLUMN);
9969
9970      final Iterator<String> lineIterator = lines.iterator();
9971      while (lineIterator.hasNext())
9972      {
9973        final String line = lineIterator.next();
9974        if (lineIterator.hasNext())
9975        {
9976          out(line);
9977        }
9978        else
9979        {
9980          getOut().print(line);
9981        }
9982      }
9983
9984      try
9985      {
9986        final String response = readLineFromIn();
9987        if (response.equalsIgnoreCase("yes") || response.equalsIgnoreCase("y"))
9988        {
9989          return true;
9990        }
9991        else if (response.equalsIgnoreCase("no") ||
9992             response.equalsIgnoreCase("n"))
9993        {
9994          return false;
9995        }
9996        else
9997        {
9998          err();
9999          wrapErr(0, WRAP_COLUMN,
10000               ERR_MANAGE_CERTS_PROMPT_FOR_YES_NO_INVALID_RESPONSE.get());
10001          err();
10002        }
10003      }
10004      catch (final Exception e)
10005      {
10006        Debug.debugException(e);
10007        throw new LDAPException(ResultCode.LOCAL_ERROR,
10008             ERR_MANAGE_CERTS_PROMPT_FOR_YES_NO_READ_ERROR.get(
10009                  StaticUtils.getExceptionMessage(e)),
10010             e);
10011      }
10012    }
10013  }
10014
10015
10016
10017  /**
10018   * Reads a line of input from standard input.
10019   *
10020   * @return  The line read from standard input.
10021   *
10022   * @throws  IOException  If a problem is encountered while reading from
10023   *                       standard input.
10024   */
10025  private String readLineFromIn()
10026          throws IOException
10027  {
10028    final ByteStringBuffer buffer = new ByteStringBuffer();
10029    while (true)
10030    {
10031      final int byteRead = in.read();
10032      if (byteRead < 0)
10033      {
10034        if (buffer.isEmpty())
10035        {
10036          return null;
10037        }
10038        else
10039        {
10040          return buffer.toString();
10041        }
10042      }
10043
10044      if (byteRead == '\n')
10045      {
10046        return buffer.toString();
10047      }
10048      else if (byteRead == '\r')
10049      {
10050        final int nextByteRead = in.read();
10051        Validator.ensureTrue(((nextByteRead < 0) || (nextByteRead == '\n')),
10052             "ERROR:  Read a carriage return from standard input that was " +
10053                  "not followed by a new line.");
10054        return buffer.toString();
10055      }
10056      else
10057      {
10058        buffer.append((byte) (byteRead & 0xFF));
10059      }
10060    }
10061  }
10062
10063
10064
10065  /**
10066   * Retrieves the password needed to access the private key.
10067   *
10068   * @param  keystore          The keystore that contains the target private
10069   *                           key.  This must not be {@code null}.
10070   * @param  alias             The alias of the target private key.  This must
10071   *                           not be {@code null}.
10072   * @param  keystorePassword  The keystore password to use if no specific
10073   *                           private key password was provided.
10074   *
10075   * @return  The password needed to access the private key, or the provided
10076   *          keystore password if no arguments were provided to specify a
10077   *          different private key password.
10078   *
10079   * @throws  LDAPException  If a problem is encountered while trying to get the
10080   *                         private key password.
10081   */
10082  private char[] getPrivateKeyPassword(final KeyStore keystore,
10083                                       final String alias,
10084                                       final char[] keystorePassword)
10085          throws LDAPException
10086  {
10087    return getPrivateKeyPassword(keystore, alias, null, keystorePassword);
10088  }
10089
10090
10091
10092  /**
10093   * Retrieves the password needed to access the private key.
10094   *
10095   * @param  keystore          The keystore that contains the target private
10096   *                           key.  This must not be {@code null}.
10097   * @param  alias             The alias of the target private key.  This must
10098   *                           not be {@code null}.
10099   * @param  prefix            The prefix string to use for the arguments.  This
10100   *                           may be {@code null} if no prefix is needed.
10101   * @param  keystorePassword  The keystore password to use if no specific
10102   *                           private key password was provided.
10103   *
10104   * @return  The password needed to access the private key, or the provided
10105   *          keystore password if no arguments were provided to specify a
10106   *          different private key password.
10107   *
10108   * @throws  LDAPException  If a problem is encountered while trying to get the
10109   *                         private key password.
10110   */
10111  private char[] getPrivateKeyPassword(final KeyStore keystore,
10112                                       final String alias, final String prefix,
10113                                       final char[] keystorePassword)
10114          throws LDAPException
10115  {
10116    final String prefixDash;
10117    if (prefix == null)
10118    {
10119      prefixDash = "";
10120    }
10121    else
10122    {
10123      prefixDash = prefix + '-';
10124    }
10125
10126    final StringArgument privateKeyPasswordArgument =
10127         subCommandParser.getStringArgument(
10128              prefixDash + "private-key-password");
10129    if ((privateKeyPasswordArgument != null) &&
10130         privateKeyPasswordArgument.isPresent())
10131    {
10132      final char[] pkPasswordChars =
10133           privateKeyPasswordArgument.getValue().toCharArray();
10134      if ((pkPasswordChars.length < 6) &&
10135          (! (hasCertificateAlias(keystore, alias) ||
10136              hasKeyAlias(keystore, alias))))
10137      {
10138        throw new LDAPException(ResultCode.PARAM_ERROR,
10139             ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
10140      }
10141
10142      return pkPasswordChars;
10143    }
10144
10145
10146    final FileArgument privateKeyPasswordFileArgument =
10147         subCommandParser.getFileArgument(
10148              prefixDash + "private-key-password-file");
10149    if ((privateKeyPasswordFileArgument != null) &&
10150        privateKeyPasswordFileArgument.isPresent())
10151    {
10152      final File f = privateKeyPasswordFileArgument.getValue();
10153      try (BufferedReader r = new BufferedReader(new FileReader(f)))
10154      {
10155        final String line = r.readLine();
10156        if (line == null)
10157        {
10158          throw new LDAPException(ResultCode.PARAM_ERROR,
10159               ERR_MANAGE_CERTS_GET_PK_PW_EMPTY_FILE.get(f.getAbsolutePath()));
10160        }
10161        else if (r.readLine() != null)
10162        {
10163          throw new LDAPException(ResultCode.PARAM_ERROR,
10164               ERR_MANAGE_CERTS_GET_PK_PW_MULTI_LINE_FILE.get(
10165                    f.getAbsolutePath()));
10166        }
10167        else if (line.isEmpty())
10168        {
10169          throw new LDAPException(ResultCode.PARAM_ERROR,
10170               ERR_MANAGE_CERTS_GET_PK_PW_EMPTY_FILE.get(f.getAbsolutePath()));
10171        }
10172        else
10173        {
10174          if ((line.length() < 6) &&
10175              (! (hasCertificateAlias(keystore, alias) ||
10176                  hasKeyAlias(keystore, alias))))
10177          {
10178            throw new LDAPException(ResultCode.PARAM_ERROR,
10179                 ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
10180          }
10181
10182          return line.toCharArray();
10183        }
10184      }
10185      catch(final LDAPException le)
10186      {
10187        Debug.debugException(le);
10188        throw le;
10189      }
10190      catch (final Exception e)
10191      {
10192        Debug.debugException(e);
10193        throw new LDAPException(ResultCode.LOCAL_ERROR,
10194             ERR_MANAGE_CERTS_GET_PK_PW_ERROR_READING_FILE.get(
10195                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10196             e);
10197      }
10198    }
10199
10200
10201    final BooleanArgument promptArgument =
10202         subCommandParser.getBooleanArgument(
10203              "prompt-for-" + prefixDash + "private-key-password");
10204    if ((promptArgument != null) && promptArgument.isPresent())
10205    {
10206      out();
10207
10208      try
10209      {
10210        if ((hasKeyAlias(keystore, alias) ||
10211             hasCertificateAlias(keystore, alias)) &&
10212            (! "new".equals(prefix)))
10213        {
10214          // This means that the private key already exists, so we just need to
10215          // prompt once.
10216          final String prompt;
10217          if ("current".equals(prefix))
10218          {
10219            prompt =
10220                 INFO_MANAGE_CERTS_GET_PK_PW_CURRENT_PROMPT.get(alias);
10221          }
10222          else
10223          {
10224            prompt =
10225                 INFO_MANAGE_CERTS_GET_PK_PW_EXISTING_PROMPT.get(alias);
10226          }
10227
10228          return promptForPassword(prompt, false);
10229        }
10230        else
10231        {
10232          // This means that we'll be creating a new private key, so we need to
10233          // prompt twice.
10234          while (true)
10235          {
10236            final String prompt;
10237            if ("new".equals(prefix))
10238            {
10239              prompt = INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT.get();
10240            }
10241            else
10242            {
10243              prompt = INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT_1.get(alias);
10244            }
10245
10246            final char[] pwChars = promptForPassword(prompt, false);
10247            if (pwChars.length < 6)
10248            {
10249              wrapErr(0, WRAP_COLUMN,
10250                   ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
10251              err();
10252              continue;
10253            }
10254
10255            final char[] confirmChars = promptForPassword(
10256                 INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT_2.get(), true);
10257
10258            if (Arrays.equals(pwChars, confirmChars))
10259            {
10260              Arrays.fill(confirmChars, '\u0000');
10261              return pwChars;
10262            }
10263            else
10264            {
10265              wrapErr(0, WRAP_COLUMN,
10266                   ERR_MANAGE_CERTS_GET_PK_PW_PROMPT_MISMATCH.get());
10267              err();
10268            }
10269          }
10270        }
10271      }
10272      catch (final LDAPException le)
10273      {
10274        Debug.debugException(le);
10275        throw le;
10276      }
10277      catch (final Exception e)
10278      {
10279        Debug.debugException(e);
10280        throw new LDAPException(ResultCode.LOCAL_ERROR,
10281             ERR_MANAGE_CERTS_GET_PK_PW_PROMPT_ERROR.get(alias,
10282                  StaticUtils.getExceptionMessage(e)),
10283             e);
10284      }
10285    }
10286
10287
10288    return keystorePassword;
10289  }
10290
10291
10292
10293  /**
10294   * Infers the keystore type from the provided keystore file.
10295   *
10296   * @param  keystorePath  The path to the file to examine.
10297   *
10298   * @return  The keystore type inferred from the provided keystore file, or
10299   *          {@code null} if the specified file does not exist.
10300   *
10301   * @throws  LDAPException  If a problem is encountered while trying to infer
10302   *                         the keystore type.
10303   */
10304  private String inferKeystoreType(final File keystorePath)
10305          throws LDAPException
10306  {
10307    if (! keystorePath.exists())
10308    {
10309      final StringArgument keystoreTypeArgument =
10310           subCommandParser.getStringArgument("keystore-type");
10311      if ((keystoreTypeArgument != null) && keystoreTypeArgument.isPresent())
10312      {
10313        final String ktaValue = keystoreTypeArgument.getValue();
10314        if (ktaValue.equalsIgnoreCase("PKCS12") ||
10315            ktaValue.equalsIgnoreCase("PKCS 12") ||
10316            ktaValue.equalsIgnoreCase("PKCS#12") ||
10317            ktaValue.equalsIgnoreCase("PKCS #12"))
10318        {
10319          return "PKCS12";
10320        }
10321        else
10322        {
10323          return "JKS";
10324        }
10325      }
10326
10327      return DEFAULT_KEYSTORE_TYPE;
10328    }
10329
10330
10331    try (FileInputStream inputStream = new FileInputStream(keystorePath))
10332    {
10333      final int firstByte = inputStream.read();
10334      if (firstByte < 0)
10335      {
10336        throw new LDAPException(ResultCode.PARAM_ERROR,
10337             ERR_MANAGE_CERTS_INFER_KS_TYPE_EMPTY_FILE.get(
10338                  keystorePath.getAbsolutePath()));
10339      }
10340
10341      if (firstByte == 0x30)
10342      {
10343        // This is the correct first byte of a DER sequence, and a PKCS #12
10344        // file is encoded as a DER sequence.
10345        return "PKCS12";
10346      }
10347      else if (firstByte == 0xFE)
10348      {
10349        // This is the correct first byte of a Java JKS keystore, which starts
10350        // with bytes 0xFEEDFEED.
10351        return "JKS";
10352      }
10353      else
10354      {
10355        throw new LDAPException(ResultCode.PARAM_ERROR,
10356             ERR_MANAGE_CERTS_INFER_KS_TYPE_UNEXPECTED_FIRST_BYTE.get(
10357                  keystorePath.getAbsolutePath(),
10358                  StaticUtils.toHex((byte) (firstByte & 0xFF))));
10359      }
10360    }
10361    catch (final LDAPException e)
10362    {
10363      Debug.debugException(e);
10364      throw e;
10365    }
10366    catch (final Exception e)
10367    {
10368      Debug.debugException(e);
10369      throw new LDAPException(ResultCode.LOCAL_ERROR,
10370           ERR_MANAGE_CERTS_INFER_KS_TYPE_ERROR_READING_FILE.get(
10371                keystorePath.getAbsolutePath(),
10372                StaticUtils.getExceptionMessage(e)),
10373           e);
10374    }
10375  }
10376
10377
10378
10379  /**
10380   * Retrieves a user-friendly representation of the provided keystore type.
10381   *
10382   * @param  keystoreType  The keystore type for which to get the user-friendly
10383   *                       name.
10384   *
10385   * @return  "JKS" if the provided keystore type is for a JKS keystore,
10386   *          "PKCS #12" if the provided keystore type is for a PKCS #12
10387   *          keystore, or the provided string if it is for some other keystore
10388   *          type.
10389   */
10390  static String getUserFriendlyKeystoreType(final String keystoreType)
10391  {
10392    if (keystoreType.equalsIgnoreCase("JKS"))
10393    {
10394      return "JKS";
10395    }
10396    else if (keystoreType.equalsIgnoreCase("PKCS12") ||
10397         keystoreType.equalsIgnoreCase("PKCS 12") ||
10398         keystoreType.equalsIgnoreCase("PKCS#12") ||
10399         keystoreType.equalsIgnoreCase("PKCS #12"))
10400    {
10401      return "PKCS #12";
10402    }
10403    else
10404    {
10405      return keystoreType;
10406    }
10407  }
10408
10409
10410
10411  /**
10412   * Gets access to a keystore based on information included in command-line
10413   * arguments.
10414   *
10415   * @param  keystoreType      The keystore type for the keystore to access.
10416   * @param  keystorePath      The path to the keystore file.
10417   * @param  keystorePassword  The password to use to access the keystore.
10418   *
10419   * @return  The configured keystore instance.
10420   *
10421   * @throws  LDAPException  If it is not possible to access the keystore.
10422   */
10423  static KeyStore getKeystore(final String keystoreType,
10424                              final File keystorePath,
10425                              final char[] keystorePassword)
10426          throws LDAPException
10427  {
10428    // Instantiate a keystore instance of the desired keystore type.
10429    final KeyStore keystore;
10430    try
10431    {
10432      keystore = KeyStore.getInstance(keystoreType);
10433    }
10434    catch (final Exception e)
10435    {
10436      Debug.debugException(e);
10437      throw new LDAPException(ResultCode.LOCAL_ERROR,
10438           ERR_MANAGE_CERTS_CANNOT_INSTANTIATE_KS_TYPE.get(keystoreType,
10439                StaticUtils.getExceptionMessage(e)),
10440           e);
10441    }
10442
10443
10444    // Get an input stream that may be used to access the keystore.
10445    final InputStream inputStream;
10446    try
10447    {
10448      if (keystorePath.exists())
10449      {
10450        inputStream = new FileInputStream(keystorePath);
10451      }
10452      else
10453      {
10454        inputStream = null;
10455      }
10456    }
10457    catch (final Exception e)
10458    {
10459      Debug.debugException(e);
10460      throw new LDAPException(ResultCode.LOCAL_ERROR,
10461           ERR_MANAGE_CERTS_CANNOT_OPEN_KS_FILE_FOR_READING.get(
10462                keystorePath.getAbsolutePath(),
10463                StaticUtils.getExceptionMessage(e)),
10464           e);
10465    }
10466
10467    try
10468    {
10469      keystore.load(inputStream, keystorePassword);
10470    }
10471    catch (final Exception e)
10472    {
10473      Debug.debugException(e);
10474      final Throwable cause = e.getCause();
10475      if ((e instanceof IOException) && (cause != null) &&
10476          (cause instanceof UnrecoverableKeyException) &&
10477          (keystorePassword != null))
10478      {
10479        throw new LDAPException(ResultCode.PARAM_ERROR,
10480             ERR_MANAGE_CERTS_CANNOT_LOAD_KS_WRONG_PW.get(
10481                  keystorePath.getAbsolutePath()),
10482             e);
10483      }
10484      else
10485      {
10486        throw new LDAPException(ResultCode.PARAM_ERROR,
10487             ERR_MANAGE_CERTS_ERROR_CANNOT_LOAD_KS.get(
10488                  keystorePath.getAbsolutePath(),
10489                  StaticUtils.getExceptionMessage(e)),
10490             e);
10491      }
10492    }
10493    finally
10494    {
10495      try
10496      {
10497        if (inputStream != null)
10498        {
10499          inputStream.close();
10500        }
10501      }
10502      catch (final Exception e)
10503      {
10504        Debug.debugException(e);
10505      }
10506    }
10507
10508    return keystore;
10509  }
10510
10511
10512
10513  /**
10514   * Reads all of the certificates contained in the specified file.  The file
10515   * must exist and may contain zero or more certificates that are either all in
10516   * PEM format or all in DER format.
10517   *
10518   * @param  f  The path to the certificate file to read.  It must not be
10519   *            {@code null}.
10520   *
10521   * @return  A list of the certificates read from the specified file.
10522   *
10523   * @throws  LDAPException  If a problem is encountered while reading
10524   *                         certificates from the specified file.
10525   */
10526  static List<X509Certificate> readCertificatesFromFile(final File f)
10527         throws LDAPException
10528  {
10529    // Read the first byte of the file to see if it contains DER-formatted data,
10530    // which we can determine by seeing if the first byte is 0x30.
10531    try (BufferedInputStream inputStream =
10532              new BufferedInputStream(new FileInputStream(f)))
10533    {
10534      inputStream.mark(1);
10535      final int firstByte = inputStream.read();
10536
10537      if (firstByte < 0)
10538      {
10539        // This means that the file is empty.
10540        return Collections.emptyList();
10541      }
10542      else
10543      {
10544        inputStream.reset();
10545      }
10546
10547      final ArrayList<X509Certificate> certList = new ArrayList<>(5);
10548      if ((firstByte & 0xFF) == 0x30)
10549      {
10550        // It is a DER-encoded file.  Read ASN.1 elements and decode them as
10551        // X.509 certificates.
10552        while (true)
10553        {
10554          final ASN1Element certElement;
10555          try
10556          {
10557            certElement = ASN1Element.readFrom(inputStream);
10558          }
10559          catch (final Exception e)
10560          {
10561            Debug.debugException(e);
10562            throw new LDAPException(ResultCode.LOCAL_ERROR,
10563                 ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DER_NOT_VALID_ASN1.get(
10564                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10565                 e);
10566          }
10567
10568          if (certElement == null)
10569          {
10570            // We've reached the end of the input stream.
10571            return certList;
10572          }
10573
10574          try
10575          {
10576            certList.add(new X509Certificate(certElement.encode()));
10577          }
10578          catch (final CertException e)
10579          {
10580            Debug.debugException(e);
10581            throw new LDAPException(ResultCode.PARAM_ERROR,
10582                 ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DER_NOT_VALID_CERT.get(
10583                      f.getAbsolutePath(), e.getMessage()),
10584                 e);
10585          }
10586        }
10587      }
10588      else
10589      {
10590        try (BufferedReader reader =
10591                  new BufferedReader(new InputStreamReader(inputStream)))
10592        {
10593          boolean inCert = false;
10594          final StringBuilder buffer = new StringBuilder();
10595          while (true)
10596          {
10597            String line = reader.readLine();
10598            if (line == null)
10599            {
10600              if (inCert)
10601              {
10602                throw new LDAPException(ResultCode.PARAM_ERROR,
10603                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_EOF_WITHOUT_END.get(
10604                          f.getAbsolutePath()));
10605              }
10606
10607              return certList;
10608            }
10609
10610            line = line.trim();
10611            if (line.isEmpty() || line.startsWith("#"))
10612            {
10613              continue;
10614            }
10615
10616            if (line.equals("-----BEGIN CERTIFICATE-----"))
10617            {
10618              if (inCert)
10619              {
10620                throw new LDAPException(ResultCode.PARAM_ERROR,
10621                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_MULTIPLE_BEGIN.get(
10622                          f.getAbsolutePath()));
10623              }
10624              else
10625              {
10626                inCert = true;
10627              }
10628            }
10629            else if (line.equals("-----END CERTIFICATE-----"))
10630            {
10631              if (! inCert)
10632              {
10633                throw new LDAPException(ResultCode.PARAM_ERROR,
10634                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_END_WITHOUT_BEGIN.
10635                          get(f.getAbsolutePath()));
10636              }
10637
10638              inCert = false;
10639              final byte[] certBytes;
10640              try
10641              {
10642                certBytes = Base64.decode(buffer.toString());
10643              }
10644              catch (final Exception e)
10645              {
10646                Debug.debugException(e);
10647                throw new LDAPException(ResultCode.PARAM_ERROR,
10648                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_PEM_CERT_NOT_BASE64.
10649                          get(f.getAbsolutePath(),
10650                               StaticUtils.getExceptionMessage(e)),
10651                     e);
10652              }
10653
10654              try
10655              {
10656                certList.add(new X509Certificate(certBytes));
10657              }
10658              catch (final CertException e)
10659              {
10660                Debug.debugException(e);
10661                throw new LDAPException(ResultCode.PARAM_ERROR,
10662                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_PEM_CERT_NOT_CERT.
10663                          get(f.getAbsolutePath(), e.getMessage()),
10664                     e);
10665              }
10666
10667              buffer.setLength(0);
10668            }
10669            else if (inCert)
10670            {
10671              buffer.append(line);
10672            }
10673            else
10674            {
10675              throw new LDAPException(ResultCode.PARAM_ERROR,
10676                   ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DATA_WITHOUT_BEGIN.get(
10677                        f.getAbsolutePath()));
10678            }
10679          }
10680        }
10681      }
10682    }
10683    catch (final LDAPException le)
10684    {
10685      Debug.debugException(le);
10686      throw le;
10687    }
10688    catch (final Exception e)
10689    {
10690      Debug.debugException(e);
10691      throw new LDAPException(ResultCode.LOCAL_ERROR,
10692           ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_READ_ERROR.get(
10693                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10694           e);
10695    }
10696  }
10697
10698
10699
10700  /**
10701   * Reads a private key from the specified file.  The file must exist and must
10702   * contain exactly one PEM-encoded or DER-encoded PKCS #8 private key.
10703   *
10704   * @param  f  The path to the private key file to read.  It must not be
10705   *            {@code null}.
10706   *
10707   * @return  The private key read from the file.
10708   *
10709   * @throws  LDAPException  If a problem is encountered while reading the
10710   *                         private key.
10711   */
10712  static PKCS8PrivateKey readPrivateKeyFromFile(final File f)
10713         throws LDAPException
10714  {
10715    // Read the first byte of the file to see if it contains DER-formatted data,
10716    // which we can determine by seeing if the first byte is 0x30.
10717    try (BufferedInputStream inputStream =
10718              new BufferedInputStream(new FileInputStream(f)))
10719    {
10720      inputStream.mark(1);
10721      final int firstByte = inputStream.read();
10722
10723      if (firstByte < 0)
10724      {
10725        // This means that the file is empty.
10726        throw new LDAPException(ResultCode.PARAM_ERROR,
10727             ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
10728                  f.getAbsolutePath()));
10729      }
10730      else
10731      {
10732        inputStream.reset();
10733      }
10734
10735      PKCS8PrivateKey privateKey = null;
10736      if ((firstByte & 0xFF) == 0x30)
10737      {
10738        // It is a DER-encoded file.  Read an ASN.1 element and decode it as a
10739        // certificate.
10740        while (true)
10741        {
10742          final ASN1Element pkElement;
10743          try
10744          {
10745            pkElement = ASN1Element.readFrom(inputStream);
10746          }
10747          catch (final Exception e)
10748          {
10749            Debug.debugException(e);
10750            throw new LDAPException(ResultCode.LOCAL_ERROR,
10751                 ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DER_NOT_VALID_ASN1.get(
10752                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10753                 e);
10754          }
10755
10756          if (pkElement == null)
10757          {
10758            // We've reached the end of the input stream.
10759            if (privateKey == null)
10760            {
10761              throw new LDAPException(ResultCode.PARAM_ERROR,
10762                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
10763                        f.getAbsolutePath()));
10764            }
10765            else
10766            {
10767              return privateKey;
10768            }
10769          }
10770          else if (privateKey == null)
10771          {
10772            try
10773            {
10774              privateKey = new PKCS8PrivateKey(pkElement.encode());
10775            }
10776            catch (final Exception e)
10777            {
10778              Debug.debugException(e);
10779              throw new LDAPException(ResultCode.PARAM_ERROR,
10780                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DER_NOT_VALID_PK.get(
10781                        f.getAbsolutePath(), e.getMessage()),
10782                   e);
10783            }
10784          }
10785          else
10786          {
10787            throw new LDAPException(ResultCode.PARAM_ERROR,
10788                 ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_KEYS.get(
10789                      f.getAbsolutePath()));
10790          }
10791        }
10792      }
10793      else
10794      {
10795        try (BufferedReader reader =
10796                  new BufferedReader(new InputStreamReader(inputStream)))
10797        {
10798          boolean inKey = false;
10799          final StringBuilder buffer = new StringBuilder();
10800          while (true)
10801          {
10802            String line = reader.readLine();
10803            if (line == null)
10804            {
10805              if (inKey)
10806              {
10807                throw new LDAPException(ResultCode.PARAM_ERROR,
10808                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EOF_WITHOUT_END.get(
10809                          f.getAbsolutePath()));
10810              }
10811
10812              if (privateKey == null)
10813              {
10814                throw new LDAPException(ResultCode.PARAM_ERROR,
10815                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
10816                          f.getAbsolutePath()));
10817              }
10818              else
10819              {
10820                return privateKey;
10821              }
10822            }
10823
10824            line = line.trim();
10825            if (line.isEmpty() || line.startsWith("#"))
10826            {
10827              continue;
10828            }
10829
10830            if (line.equals("-----BEGIN PRIVATE KEY-----"))
10831            {
10832              if (inKey)
10833              {
10834                throw new LDAPException(ResultCode.PARAM_ERROR,
10835                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_BEGIN.get(
10836                          f.getAbsolutePath()));
10837              }
10838              else if (privateKey != null)
10839              {
10840                throw new LDAPException(ResultCode.PARAM_ERROR,
10841                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_KEYS.get(
10842                          f.getAbsolutePath()));
10843              }
10844              else
10845              {
10846                inKey = true;
10847              }
10848            }
10849            else if (line.equals("-----END PRIVATE KEY-----"))
10850            {
10851              if (! inKey)
10852              {
10853                throw new LDAPException(ResultCode.PARAM_ERROR,
10854                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_END_WITHOUT_BEGIN.get(
10855                          f.getAbsolutePath()));
10856              }
10857
10858              inKey = false;
10859              final byte[] pkBytes;
10860              try
10861              {
10862                pkBytes = Base64.decode(buffer.toString());
10863              }
10864              catch (final Exception e)
10865              {
10866                Debug.debugException(e);
10867                throw new LDAPException(ResultCode.PARAM_ERROR,
10868                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PEM_PK_NOT_BASE64.get(
10869                          f.getAbsolutePath(),
10870                          StaticUtils.getExceptionMessage(e)),
10871                     e);
10872              }
10873
10874              try
10875              {
10876                privateKey = new PKCS8PrivateKey(pkBytes);
10877              }
10878              catch (final CertException e)
10879              {
10880                Debug.debugException(e);
10881                throw new LDAPException(ResultCode.PARAM_ERROR,
10882                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PEM_PK_NOT_PK.get(
10883                          f.getAbsolutePath(), e.getMessage()),
10884                     e);
10885              }
10886
10887              buffer.setLength(0);
10888            }
10889            else if (inKey)
10890            {
10891              buffer.append(line);
10892            }
10893            else
10894            {
10895              throw new LDAPException(ResultCode.PARAM_ERROR,
10896                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DATA_WITHOUT_BEGIN.get(
10897                        f.getAbsolutePath()));
10898            }
10899          }
10900        }
10901      }
10902    }
10903    catch (final LDAPException le)
10904    {
10905      Debug.debugException(le);
10906      throw le;
10907    }
10908    catch (final Exception e)
10909    {
10910      Debug.debugException(e);
10911      throw new LDAPException(ResultCode.LOCAL_ERROR,
10912           ERR_MANAGE_CERTS_READ_PK_FROM_FILE_READ_ERROR.get(
10913                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10914           e);
10915    }
10916  }
10917
10918
10919
10920  /**
10921   * Reads a certificate signing request from the specified file.  The file must
10922   * exist and must contain exactly one PEM-encoded or DER-encoded PKCS #10
10923   * certificate signing request.
10924   *
10925   * @param  f  The path to the private key file to read.  It must not be
10926   *            {@code null}.
10927   *
10928   * @return  The certificate signing request read from the file.
10929   *
10930   * @throws  LDAPException  If a problem is encountered while reading the
10931   *                         certificate signing request.
10932   */
10933  static PKCS10CertificateSigningRequest
10934              readCertificateSigningRequestFromFile(final File f)
10935         throws LDAPException
10936  {
10937    // Read the first byte of the file to see if it contains DER-formatted data,
10938    // which we can determine by seeing if the first byte is 0x30.
10939    try (BufferedInputStream inputStream =
10940              new BufferedInputStream(new FileInputStream(f)))
10941    {
10942      inputStream.mark(1);
10943      final int firstByte = inputStream.read();
10944
10945      if (firstByte < 0)
10946      {
10947        // This means that the file is empty.
10948        throw new LDAPException(ResultCode.PARAM_ERROR,
10949             ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
10950                  f.getAbsolutePath()));
10951      }
10952      else
10953      {
10954        inputStream.reset();
10955      }
10956
10957      PKCS10CertificateSigningRequest csr = null;
10958      if ((firstByte & 0xFF) == 0x30)
10959      {
10960        // It is a DER-encoded file.  Read an ASN.1 element and decode it as a
10961        // certificate.
10962        while (true)
10963        {
10964          final ASN1Element csrElement;
10965          try
10966          {
10967            csrElement = ASN1Element.readFrom(inputStream);
10968          }
10969          catch (final Exception e)
10970          {
10971            Debug.debugException(e);
10972            throw new LDAPException(ResultCode.LOCAL_ERROR,
10973                 ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DER_NOT_VALID_ASN1.get(
10974                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10975                 e);
10976          }
10977
10978          if (csrElement == null)
10979          {
10980            // We've reached the end of the input stream.
10981            if (csr == null)
10982            {
10983              throw new LDAPException(ResultCode.PARAM_ERROR,
10984                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
10985                        f.getAbsolutePath()));
10986            }
10987            else
10988            {
10989              return csr;
10990            }
10991          }
10992          else if (csr == null)
10993          {
10994            try
10995            {
10996              csr = new PKCS10CertificateSigningRequest(csrElement.encode());
10997            }
10998            catch (final Exception e)
10999            {
11000              Debug.debugException(e);
11001              throw new LDAPException(ResultCode.PARAM_ERROR,
11002                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DER_NOT_VALID_CSR.get(
11003                        f.getAbsolutePath(), e.getMessage()),
11004                   e);
11005            }
11006          }
11007          else
11008          {
11009            throw new LDAPException(ResultCode.PARAM_ERROR,
11010                 ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_CSRS.get(
11011                      f.getAbsolutePath()));
11012          }
11013        }
11014      }
11015      else
11016      {
11017        try (BufferedReader reader =
11018                  new BufferedReader(new InputStreamReader(inputStream)))
11019        {
11020          boolean inCSR = false;
11021          final StringBuilder buffer = new StringBuilder();
11022          while (true)
11023          {
11024            String line = reader.readLine();
11025            if (line == null)
11026            {
11027              if (inCSR)
11028              {
11029                throw new LDAPException(ResultCode.PARAM_ERROR,
11030                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EOF_WITHOUT_END.get(
11031                          f.getAbsolutePath()));
11032              }
11033
11034              if (csr == null)
11035              {
11036                throw new LDAPException(ResultCode.PARAM_ERROR,
11037                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
11038                          f.getAbsolutePath()));
11039              }
11040              else
11041              {
11042                return csr;
11043              }
11044            }
11045
11046            line = line.trim();
11047            if (line.isEmpty() || line.startsWith("#"))
11048            {
11049              continue;
11050            }
11051
11052            if (line.equals("-----BEGIN CERTIFICATE REQUEST-----") ||
11053                line.equals("-----BEGIN NEW CERTIFICATE REQUEST-----"))
11054            {
11055              if (inCSR)
11056              {
11057                throw new LDAPException(ResultCode.PARAM_ERROR,
11058                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_BEGIN.get(
11059                          f.getAbsolutePath()));
11060              }
11061              else if (csr != null)
11062              {
11063                throw new LDAPException(ResultCode.PARAM_ERROR,
11064                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_CSRS.get(
11065                          f.getAbsolutePath()));
11066              }
11067              else
11068              {
11069                inCSR = true;
11070              }
11071            }
11072            else if (line.equals("-----END CERTIFICATE REQUEST-----") ||
11073                 line.equals("-----END NEW CERTIFICATE REQUEST-----"))
11074            {
11075              if (! inCSR)
11076              {
11077                throw new LDAPException(ResultCode.PARAM_ERROR,
11078                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_END_WITHOUT_BEGIN.get(
11079                          f.getAbsolutePath()));
11080              }
11081
11082              inCSR = false;
11083              final byte[] csrBytes;
11084              try
11085              {
11086                csrBytes = Base64.decode(buffer.toString());
11087              }
11088              catch (final Exception e)
11089              {
11090                Debug.debugException(e);
11091                throw new LDAPException(ResultCode.PARAM_ERROR,
11092                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_PEM_CSR_NOT_BASE64.get(
11093                          f.getAbsolutePath(),
11094                          StaticUtils.getExceptionMessage(e)),
11095                     e);
11096              }
11097
11098              try
11099              {
11100                csr = new PKCS10CertificateSigningRequest(csrBytes);
11101              }
11102              catch (final CertException e)
11103              {
11104                Debug.debugException(e);
11105                throw new LDAPException(ResultCode.PARAM_ERROR,
11106                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_PEM_CSR_NOT_CSR.get(
11107                          f.getAbsolutePath(), e.getMessage()),
11108                     e);
11109              }
11110
11111              buffer.setLength(0);
11112            }
11113            else if (inCSR)
11114            {
11115              buffer.append(line);
11116            }
11117            else
11118            {
11119              throw new LDAPException(ResultCode.PARAM_ERROR,
11120                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DATA_WITHOUT_BEGIN.get(
11121                        f.getAbsolutePath()));
11122            }
11123          }
11124        }
11125      }
11126    }
11127    catch (final LDAPException le)
11128    {
11129      Debug.debugException(le);
11130      throw le;
11131    }
11132    catch (final Exception e)
11133    {
11134      Debug.debugException(e);
11135      throw new LDAPException(ResultCode.LOCAL_ERROR,
11136           ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_READ_ERROR.get(
11137                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
11138           e);
11139    }
11140  }
11141
11142
11143
11144  /**
11145   * Retrieves a colon-delimited hexadecimal representation of the contents of
11146   * the provided byte array.
11147   *
11148   * @param  bytes  The byte array for which to get the hexadecimal
11149   *                representation.  It must not be {@code null}.
11150   *
11151   * @return  A colon-delimited hexadecimal representation of the contents of
11152   *          the provided byte array.
11153   */
11154  private static String toColonDelimitedHex(final byte... bytes)
11155  {
11156    final StringBuilder buffer = new StringBuilder(bytes.length * 3);
11157    StaticUtils.toHex(bytes, ":", buffer);
11158    return buffer.toString();
11159  }
11160
11161
11162
11163  /**
11164   * Retrieves a formatted representation of the provided date in a
11165   * human-readable format that includes an offset from the current time.
11166   *
11167   * @param  d  The date to format.  It must not be {@code null}.
11168   *
11169   * @return  A formatted representation of the provided date.
11170   */
11171  private static String formatDateAndTime(final Date d)
11172  {
11173    // Example:  Sunday, January 1, 2017
11174    final String dateFormatString = "EEEE, MMMM d, yyyy";
11175    final String formattedDate =
11176         new SimpleDateFormat(dateFormatString).format(d);
11177
11178    // Example:  12:34:56 AM CDT
11179    final String timeFormatString = "hh:mm:ss aa z";
11180    final String formattedTime =
11181         new SimpleDateFormat(timeFormatString).format(d);
11182
11183    final long providedTime = d.getTime();
11184    final long currentTime = System.currentTimeMillis();
11185    if (providedTime > currentTime)
11186    {
11187      final long secondsInFuture = ((providedTime - currentTime) / 1000L);
11188      final String durationInFuture =
11189           StaticUtils.secondsToHumanReadableDuration(secondsInFuture);
11190      return INFO_MANAGE_CERTS_FORMAT_DATE_AND_TIME_IN_FUTURE.get(formattedDate,
11191           formattedTime, durationInFuture);
11192    }
11193    else
11194    {
11195      final long secondsInPast = ((currentTime - providedTime) / 1000L);
11196      final String durationInPast =
11197           StaticUtils.secondsToHumanReadableDuration(secondsInPast);
11198      return INFO_MANAGE_CERTS_FORMAT_DATE_AND_TIME_IN_PAST.get(formattedDate,
11199           formattedTime, durationInPast);
11200    }
11201  }
11202
11203
11204
11205  /**
11206   * Retrieves a formatted representation of the provided date in a format
11207   * suitable for use as the validity start time value provided to the keytool
11208   * command.
11209   *
11210   * @param  d  The date to format.  It must not be {@code null}.
11211   *
11212   * @return  A formatted representation of the provided date.
11213   */
11214  private static String formatValidityStartTime(final Date d)
11215  {
11216    // Example:  2017/01/01 01:23:45
11217    final String dateFormatString = "yyyy'/'MM'/'dd HH':'mm':'ss";
11218    return new SimpleDateFormat(dateFormatString).format(d);
11219  }
11220
11221
11222
11223  /**
11224   * Retrieves the certificate chain for the specified certificate from the
11225   * given keystore.  If any issuer certificate is not in the provided keystore,
11226   * then the JVM-default trust store will be checked to see if it can be found
11227   * there.
11228   *
11229   * @param  alias             The alias of the certificate for which to get the
11230   *                           certificate chain.  This must not be
11231   *                           {@code null}.
11232   * @param  keystore          The keystore from which to get the certificate
11233   *                           chain.  This must not be {@code null}.
11234   * @param  missingIssuerRef  A reference that will be updated with the DN of a
11235   *                           missing issuer certificate, if any certificate in
11236   *                           the chain cannot be located.  This must not be
11237   *                           {@code null}.
11238   *
11239   * @return  The certificate chain for the specified certificate, or an empty
11240   *          array if no certificate exists with the specified alias.
11241   *
11242   * @throws  LDAPException  If a problem is encountered while getting the
11243   *                         certificate chain.
11244   */
11245  private static X509Certificate[] getCertificateChain(final String alias,
11246                      final KeyStore keystore,
11247                      final AtomicReference<DN> missingIssuerRef)
11248          throws LDAPException
11249  {
11250    try
11251    {
11252      // First, see if the keystore will give us the certificate chain.  This
11253      // will only happen if the alias references an entry that includes the
11254      // private key, but it will save us a lot of work.
11255      final Certificate[] chain = keystore.getCertificateChain(alias);
11256      if ((chain != null) && (chain.length > 0))
11257      {
11258        final X509Certificate[] x509Chain = new X509Certificate[chain.length];
11259        for (int i=0; i < chain.length; i++)
11260        {
11261          x509Chain[i] = new X509Certificate(chain[i].getEncoded());
11262        }
11263        return x509Chain;
11264      }
11265
11266
11267      // We couldn't get the keystore to give us the chain, but see if we can
11268      // get a certificate with the specified alias.
11269      final Certificate endCert = keystore.getCertificate(alias);
11270      if (endCert == null)
11271      {
11272        // This means there isn't any certificate with the specified alias.
11273        // Return an empty chain.
11274        return new X509Certificate[0];
11275      }
11276
11277      final ArrayList<X509Certificate> chainList = new ArrayList<>(5);
11278      X509Certificate certificate = new X509Certificate(endCert.getEncoded());
11279      chainList.add(certificate);
11280
11281      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
11282           new AtomicReference<>();
11283      while (true)
11284      {
11285        final X509Certificate issuerCertificate =
11286             getIssuerCertificate(certificate, keystore,
11287                  jvmDefaultTrustStoreRef, missingIssuerRef);
11288        if (issuerCertificate == null)
11289        {
11290          break;
11291        }
11292
11293        chainList.add(issuerCertificate);
11294        certificate = issuerCertificate;
11295      }
11296
11297      final X509Certificate[] x509Chain = new X509Certificate[chainList.size()];
11298      return chainList.toArray(x509Chain);
11299    }
11300    catch (final Exception e)
11301    {
11302      Debug.debugException(e);
11303      throw new LDAPException(ResultCode.LOCAL_ERROR,
11304           ERR_MANAGE_CERTS_GET_CHAIN_ERROR.get(alias,
11305                StaticUtils.getExceptionMessage(e)),
11306           e);
11307    }
11308  }
11309
11310
11311
11312  /**
11313   * Attempts to retrieve the issuer certificate for the provided certificate
11314   * from the given keystore or the JVM-default trust store.
11315   *
11316   * @param  certificate              The certificate for which to retrieve the
11317   *                                  issuer certificate.
11318   * @param  keystore                 The keystore in which to look for the
11319   *                                  issuer certificate.
11320   * @param  jvmDefaultTrustStoreRef  A reference that will be used to hold the
11321   *                                  JVM-default trust store if it is obtained
11322   *                                  in the process of retrieving the issuer
11323   *                                  certificate.
11324   * @param  missingIssuerRef         A reference that will be updated with the
11325   *                                  DN of a missing issuer certificate, if any
11326   *                                  certificate in the chain cannot be
11327   *                                  located.  This must not be {@code null}.
11328   *
11329   * @return  The issuer certificate for the provided certificate, or
11330   *          {@code null} if the issuer certificate could not be retrieved.
11331   *
11332   * @throws  Exception   If a problem is encountered while trying to retrieve
11333   *                      the issuer certificate.
11334   */
11335  private static X509Certificate getIssuerCertificate(
11336                      final X509Certificate certificate,
11337                      final KeyStore keystore,
11338                      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef,
11339                      final AtomicReference<DN> missingIssuerRef)
11340          throws Exception
11341  {
11342    final DN subjectDN = certificate.getSubjectDN();
11343    final DN issuerDN = certificate.getIssuerDN();
11344    if (subjectDN.equals(issuerDN))
11345    {
11346      // This means that the certificate is self-signed, so there is no issuer.
11347      return null;
11348    }
11349
11350
11351    // See if we can find the issuer certificate in the provided keystore.
11352    X509Certificate issuerCertificate = getIssuerCertificate(certificate,
11353         keystore);
11354    if (issuerCertificate != null)
11355    {
11356      return issuerCertificate;
11357    }
11358
11359
11360    // See if we can get the JVM-default trust store.
11361    KeyStore jvmDefaultTrustStore = jvmDefaultTrustStoreRef.get();
11362    if (jvmDefaultTrustStore == null)
11363    {
11364      if (JVM_DEFAULT_CACERTS_FILE == null)
11365      {
11366        missingIssuerRef.set(issuerDN);
11367        return null;
11368      }
11369
11370      for (final String keystoreType : new String[] { "JKS", "PKCS12" })
11371      {
11372        final KeyStore ks = KeyStore.getInstance(keystoreType);
11373        try (FileInputStream inputStream =
11374                  new FileInputStream(JVM_DEFAULT_CACERTS_FILE))
11375        {
11376          ks.load(inputStream, null);
11377          jvmDefaultTrustStore = ks;
11378          jvmDefaultTrustStoreRef.set(jvmDefaultTrustStore);
11379          break;
11380        }
11381        catch (final Exception e)
11382        {
11383          Debug.debugException(e);
11384        }
11385      }
11386    }
11387
11388    if (jvmDefaultTrustStore != null)
11389    {
11390      issuerCertificate = getIssuerCertificate(certificate,
11391           jvmDefaultTrustStore);
11392    }
11393
11394    if (issuerCertificate == null)
11395    {
11396      missingIssuerRef.set(issuerDN);
11397    }
11398
11399    return issuerCertificate;
11400  }
11401
11402
11403
11404  /**
11405   * Attempts to retrieve the issuer certificate for the provided certificate
11406   * from the given keystore.
11407   *
11408   * @param  certificate  The certificate for which to retrieve the issuer
11409   *                      certificate.
11410   * @param  keystore     The keystore in which to look for the issuer
11411   *                      certificate.
11412   *
11413   * @return  The issuer certificate for the provided certificate, or
11414   *          {@code null} if the issuer certificate could not be retrieved.
11415   *
11416   * @throws  Exception   If a problem is encountered while trying to retrieve
11417   *                      the issuer certificate.
11418   */
11419  private static X509Certificate getIssuerCertificate(
11420                                      final X509Certificate certificate,
11421                                      final KeyStore keystore)
11422          throws Exception
11423  {
11424    final Enumeration<String> aliases = keystore.aliases();
11425    while (aliases.hasMoreElements())
11426    {
11427      final String alias = aliases.nextElement();
11428
11429      Certificate[] certs = null;
11430      if (hasCertificateAlias(keystore, alias))
11431      {
11432        final Certificate c = keystore.getCertificate(alias);
11433        if (c == null)
11434        {
11435          continue;
11436        }
11437
11438        certs = new Certificate[] { c };
11439      }
11440      else if (hasKeyAlias(keystore, alias))
11441      {
11442        certs = keystore.getCertificateChain(alias);
11443      }
11444
11445      if (certs != null)
11446      {
11447        for (final Certificate c : certs)
11448        {
11449          final X509Certificate xc = new X509Certificate(c.getEncoded());
11450          if (xc.isIssuerFor(certificate))
11451          {
11452            return xc;
11453          }
11454        }
11455      }
11456    }
11457
11458    return null;
11459  }
11460
11461
11462
11463  /**
11464   * Retrieves the authority key identifier value for the provided certificate,
11465   * if present.
11466   *
11467   * @param  c  The certificate for which to retrieve the authority key
11468   *            identifier.
11469   *
11470   * @return  The authority key identifier value for the provided certificate,
11471   *          or {@code null} if the certificate does not have an authority
11472   *          key identifier.
11473   */
11474  private static byte[] getAuthorityKeyIdentifier(final X509Certificate c)
11475  {
11476    for (final X509CertificateExtension extension : c.getExtensions())
11477    {
11478      if (extension instanceof AuthorityKeyIdentifierExtension)
11479      {
11480        final AuthorityKeyIdentifierExtension e =
11481             (AuthorityKeyIdentifierExtension) extension;
11482        if (e.getKeyIdentifier() != null)
11483        {
11484          return e.getKeyIdentifier().getValue();
11485        }
11486      }
11487    }
11488
11489    return null;
11490  }
11491
11492
11493
11494  /**
11495   * Writes the provided keystore to the specified file.  If the keystore file
11496   * already exists, a new temporary file will be created, the old file renamed
11497   * out of the way, the new file renamed into place, and the old file deleted.
11498   * If the keystore file does not exist, then it will simply be created in the
11499   * correct place.
11500   *
11501   * @param  keystore          The keystore to be written.
11502   * @param  keystorePath      The path to the keystore file to be written.
11503   * @param  keystorePassword  The password to use for the keystore.
11504   *
11505   * @throws  LDAPException  If a problem is encountered while writing the
11506   *                         keystore.
11507   */
11508  static void writeKeystore(final KeyStore keystore, final File keystorePath,
11509                            final char[] keystorePassword)
11510          throws LDAPException
11511  {
11512    if (keystorePath.exists())
11513    {
11514      final String timestamp =
11515           StaticUtils.encodeGeneralizedTime(System.currentTimeMillis());
11516      final File newFile =
11517           new File(keystorePath.getAbsolutePath() + ".new-" + timestamp);
11518      try (FileOutputStream outputStream = new FileOutputStream(newFile))
11519      {
11520        keystore.store(outputStream, keystorePassword);
11521      }
11522      catch (final Exception e)
11523      {
11524        Debug.debugException(e);
11525        throw new LDAPException(ResultCode.LOCAL_ERROR,
11526             ERR_MANAGE_CERTS_WRITE_KS_ERROR_WRITING_TO_TEMP_FILE.get(
11527                  newFile.getAbsolutePath(),
11528                  StaticUtils.getExceptionMessage(e)),
11529             e);
11530      }
11531
11532      final File oldFile =
11533           new File(keystorePath.getAbsolutePath() + ".old-" + timestamp);
11534      if (! keystorePath.renameTo(oldFile))
11535      {
11536        throw new LDAPException(ResultCode.LOCAL_ERROR,
11537             ERR_MANAGE_CERTS_WRITE_KS_ERROR_RENAMING_EXISTING_FILE.get(
11538                  keystorePath.getAbsolutePath(), oldFile.getAbsolutePath(),
11539                  newFile.getAbsolutePath()));
11540      }
11541
11542      if (! newFile.renameTo(keystorePath))
11543      {
11544        throw new LDAPException(ResultCode.LOCAL_ERROR,
11545             ERR_MANAGE_CERTS_WRITE_KS_ERROR_RENAMING_NEW_FILE.get(
11546                  newFile.getAbsolutePath(), keystorePath.getAbsolutePath(),
11547                  oldFile.getAbsolutePath()));
11548      }
11549
11550      if (! oldFile.delete())
11551      {
11552        throw new LDAPException(ResultCode.LOCAL_ERROR,
11553             ERR_MANAGE_CERTS_WRITE_KS_ERROR_DELETING_FILE.get(
11554                  oldFile.getAbsolutePath()));
11555      }
11556    }
11557    else
11558    {
11559      try (FileOutputStream outputStream = new FileOutputStream(keystorePath))
11560      {
11561        keystore.store(outputStream, keystorePassword);
11562      }
11563      catch (final Exception e)
11564      {
11565        Debug.debugException(e);
11566        throw new LDAPException(ResultCode.LOCAL_ERROR,
11567             ERR_MANAGE_CERTS_WRITE_KS_ERROR_WRITING_TO_NEW_FILE.get(
11568                  keystorePath.getAbsolutePath(),
11569                  StaticUtils.getExceptionMessage(e)),
11570             e);
11571      }
11572    }
11573  }
11574
11575
11576
11577  /**
11578   * Indicates whether the provided keystore has a certificate entry with the
11579   * specified alias.
11580   *
11581   * @param  keystore  The keystore to examine.
11582   * @param  alias     The alias for which to make the determination.
11583   *
11584   * @return  {@code true} if the keystore has a certificate entry with the
11585   *          specified alias, or {@code false} if the alias doesn't exist or
11586   *          is associated with some other type of entry (like a key).
11587   */
11588  private static boolean hasCertificateAlias(final KeyStore keystore,
11589                                             final String alias)
11590  {
11591    try
11592    {
11593      return keystore.isCertificateEntry(alias);
11594    }
11595    catch (final Exception e)
11596    {
11597      // This should never happen.  If it does, then we'll assume the alias
11598      // doesn't exist or isn't associated with a certificate.
11599      Debug.debugException(e);
11600      return false;
11601    }
11602  }
11603
11604
11605
11606  /**
11607   * Indicates whether the provided keystore has a key entry with the specified
11608   * alias.
11609   *
11610   * @param  keystore  The keystore to examine.
11611   * @param  alias     The alias for which to make the determination.
11612   *
11613   * @return  {@code true} if the keystore has a key entry with the specified
11614   *          alias, or {@code false} if the alias doesn't exist or is
11615   *          associated with some other type of entry (like a certificate).
11616   */
11617  private static boolean hasKeyAlias(final KeyStore keystore,
11618                                     final String alias)
11619  {
11620    try
11621    {
11622      return keystore.isKeyEntry(alias);
11623    }
11624    catch (final Exception e)
11625    {
11626      // This should never happen.  If it does, then we'll assume the alias
11627      // doesn't exist or isn't associated with a key.
11628      Debug.debugException(e);
11629      return false;
11630    }
11631  }
11632
11633
11634
11635  /**
11636   * Adds arguments for each of the provided extensions to the given list.
11637   *
11638   * @param  keytoolArguments   The list to which the extension arguments should
11639   *                            be added.
11640   * @param  basicConstraints   The basic constraints extension to include.  It
11641   *                            may be {@code null} if this extension should not
11642   *                            be included.
11643   * @param  keyUsage           The key usage extension to include.  It may be
11644   *                            {@code null} if this extension should not be
11645   *                            included.
11646   * @param  extendedKeyUsage   The extended key usage extension to include.  It
11647   *                            may be {@code null} if this extension should not
11648   *                            be included.
11649   * @param  sanValues          The list of subject alternative name values to
11650   *                            include.  It must not be {@code null} but may be
11651   *                            empty.
11652   * @param  ianValues          The list of issuer alternative name values to
11653   *                            include.  It must not be {@code null} but may be
11654   *                            empty.
11655   * @param  genericExtensions  The list of generic extensions to include.  It
11656   *                            must not be {@code null} but may be empty.
11657   */
11658  private static void addExtensionArguments(final List<String> keytoolArguments,
11659               final BasicConstraintsExtension basicConstraints,
11660               final KeyUsageExtension keyUsage,
11661               final ExtendedKeyUsageExtension extendedKeyUsage,
11662               final Set<String> sanValues,
11663               final Set<String> ianValues,
11664               final List<X509CertificateExtension> genericExtensions)
11665  {
11666    if (basicConstraints != null)
11667    {
11668      final StringBuilder basicConstraintsValue = new StringBuilder();
11669      basicConstraintsValue.append("ca:");
11670      basicConstraintsValue.append(basicConstraints.isCA());
11671
11672      if (basicConstraints.getPathLengthConstraint() != null)
11673      {
11674        basicConstraintsValue.append(",pathlen:");
11675        basicConstraintsValue.append(
11676             basicConstraints.getPathLengthConstraint());
11677      }
11678
11679      keytoolArguments.add("-ext");
11680      keytoolArguments.add("BasicConstraints=" + basicConstraintsValue);
11681    }
11682
11683    if (keyUsage != null)
11684    {
11685      final StringBuilder keyUsageValue = new StringBuilder();
11686      if (keyUsage.isDigitalSignatureBitSet())
11687      {
11688        commaAppend(keyUsageValue, "digitalSignature");
11689      }
11690
11691      if (keyUsage.isNonRepudiationBitSet())
11692      {
11693        commaAppend(keyUsageValue, "nonRepudiation");
11694      }
11695
11696      if (keyUsage.isKeyEnciphermentBitSet())
11697      {
11698        commaAppend(keyUsageValue, "keyEncipherment");
11699      }
11700
11701      if (keyUsage.isDataEnciphermentBitSet())
11702      {
11703        commaAppend(keyUsageValue, "dataEncipherment");
11704      }
11705
11706      if (keyUsage.isKeyAgreementBitSet())
11707      {
11708        commaAppend(keyUsageValue, "keyAgreement");
11709      }
11710
11711      if (keyUsage.isKeyCertSignBitSet())
11712      {
11713        commaAppend(keyUsageValue, "keyCertSign");
11714      }
11715
11716      if (keyUsage.isCRLSignBitSet())
11717      {
11718        commaAppend(keyUsageValue, "cRLSign");
11719      }
11720
11721      if (keyUsage.isEncipherOnlyBitSet())
11722      {
11723        commaAppend(keyUsageValue, "encipherOnly");
11724      }
11725
11726      if (keyUsage.isEncipherOnlyBitSet())
11727      {
11728        commaAppend(keyUsageValue, "decipherOnly");
11729      }
11730
11731      keytoolArguments.add("-ext");
11732      keytoolArguments.add("KeyUsage=" + keyUsageValue);
11733    }
11734
11735    if (extendedKeyUsage != null)
11736    {
11737      final StringBuilder extendedKeyUsageValue = new StringBuilder();
11738      for (final OID oid : extendedKeyUsage.getKeyPurposeIDs())
11739      {
11740        final ExtendedKeyUsageID id = ExtendedKeyUsageID.forOID(oid);
11741        if (id == null)
11742        {
11743          commaAppend(extendedKeyUsageValue, oid.toString());
11744        }
11745        else
11746        {
11747          switch (id)
11748          {
11749            case TLS_SERVER_AUTHENTICATION:
11750              commaAppend(extendedKeyUsageValue, "serverAuth");
11751              break;
11752            case TLS_CLIENT_AUTHENTICATION:
11753              commaAppend(extendedKeyUsageValue, "clientAuth");
11754              break;
11755            case CODE_SIGNING:
11756              commaAppend(extendedKeyUsageValue, "codeSigning");
11757              break;
11758            case EMAIL_PROTECTION:
11759              commaAppend(extendedKeyUsageValue, "emailProtection");
11760              break;
11761            case TIME_STAMPING:
11762              commaAppend(extendedKeyUsageValue, "timeStamping");
11763              break;
11764            case OCSP_SIGNING:
11765              commaAppend(extendedKeyUsageValue, "OCSPSigning");
11766              break;
11767            default:
11768              // This should never happen.
11769              commaAppend(extendedKeyUsageValue, id.getOID().toString());
11770              break;
11771          }
11772        }
11773      }
11774
11775      keytoolArguments.add("-ext");
11776      keytoolArguments.add("ExtendedKeyUsage=" + extendedKeyUsageValue);
11777    }
11778
11779    if (! sanValues.isEmpty())
11780    {
11781      final StringBuilder subjectAltNameValue = new StringBuilder();
11782      for (final String sanValue : sanValues)
11783      {
11784        commaAppend(subjectAltNameValue, sanValue);
11785      }
11786
11787      keytoolArguments.add("-ext");
11788      keytoolArguments.add("SAN=" + subjectAltNameValue);
11789    }
11790
11791    if (! ianValues.isEmpty())
11792    {
11793      final StringBuilder issuerAltNameValue = new StringBuilder();
11794      for (final String ianValue : ianValues)
11795      {
11796        commaAppend(issuerAltNameValue, ianValue);
11797      }
11798
11799      keytoolArguments.add("-ext");
11800      keytoolArguments.add("IAN=" + issuerAltNameValue);
11801    }
11802
11803    for (final X509CertificateExtension e : genericExtensions)
11804    {
11805      keytoolArguments.add("-ext");
11806      if (e.isCritical())
11807      {
11808        keytoolArguments.add(e.getOID().toString() + ":critical=" +
11809             toColonDelimitedHex(e.getValue()));
11810      }
11811      else
11812      {
11813        keytoolArguments.add(e.getOID().toString() + '=' +
11814             toColonDelimitedHex(e.getValue()));
11815      }
11816    }
11817  }
11818
11819
11820
11821  /**
11822   * Appends the provided value to the given buffer.  If the buffer is not
11823   * empty, the new value will be preceded by a comma.  There will not be any
11824   * spaces on either side of the comma.
11825   *
11826   * @param  buffer  The buffer to which the value should be appended.
11827   * @param  value   The value to append to the buffer.
11828   */
11829  private static void commaAppend(final StringBuilder buffer,
11830                                  final String value)
11831  {
11832    if (buffer.length() > 0)
11833    {
11834      buffer.append(',');
11835    }
11836
11837    buffer.append(value);
11838  }
11839
11840
11841
11842  /**
11843   * Retrieves a set of information that may be used to generate example usage
11844   * information.  Each element in the returned map should consist of a map
11845   * between an example set of arguments and a string that describes the
11846   * behavior of the tool when invoked with that set of arguments.
11847   *
11848   * @return  A set of information that may be used to generate example usage
11849   *          information.  It may be {@code null} or empty if no example usage
11850   *          information is available.
11851   */
11852  @Override()
11853  public LinkedHashMap<String[],String> getExampleUsages()
11854  {
11855    final String keystorePath = getPlatformSpecificPath("config", "keystore");
11856    final String keystorePWPath =
11857         getPlatformSpecificPath("config", "keystore.pin");
11858    final String privateKeyPWPath =
11859         getPlatformSpecificPath("config", "server-cert-private-key.pin");
11860    final String exportCertOutputFile =
11861         getPlatformSpecificPath("server-cert.crt");
11862    final String exportKeyOutputFile =
11863         getPlatformSpecificPath("server-cert.private-key");
11864    final String genCSROutputFile = getPlatformSpecificPath("server-cert.csr");
11865
11866    final LinkedHashMap<String[],String> examples = new LinkedHashMap<>(20);
11867
11868    examples.put(
11869         new String[]
11870         {
11871           "list-certificates",
11872           "--keystore", keystorePath,
11873           "--keystore-password-file", keystorePWPath,
11874           "--verbose",
11875           "--display-keytool-command"
11876         },
11877         INFO_MANAGE_CERTS_EXAMPLE_LIST_1.get(keystorePath));
11878
11879    examples.put(
11880         new String[]
11881         {
11882           "export-certificate",
11883           "--keystore", keystorePath,
11884           "--keystore-password-file", keystorePWPath,
11885           "--alias", "server-cert",
11886           "--output-file", exportCertOutputFile,
11887           "--output-format", "PEM",
11888           "--verbose",
11889           "--display-keytool-command"
11890         },
11891         INFO_MANAGE_CERTS_EXAMPLE_EXPORT_CERT_1.get(keystorePath,
11892              exportCertOutputFile));
11893
11894    examples.put(
11895         new String[]
11896         {
11897           "export-private-key",
11898           "--keystore", keystorePath,
11899           "--keystore-password-file", keystorePWPath,
11900           "--private-key-password-file", privateKeyPWPath,
11901           "--alias", "server-cert",
11902           "--output-file", exportKeyOutputFile,
11903           "--output-format", "PEM",
11904           "--verbose",
11905           "--display-keytool-command"
11906         },
11907         INFO_MANAGE_CERTS_EXAMPLE_EXPORT_KEY_1.get(keystorePath,
11908              exportKeyOutputFile));
11909
11910    examples.put(
11911         new String[]
11912         {
11913           "import-certificate",
11914           "--keystore", keystorePath,
11915           "--keystore-type", "JKS",
11916           "--keystore-password-file", keystorePWPath,
11917           "--alias", "server-cert",
11918           "--certificate-file", exportCertOutputFile,
11919           "--private-key-file", exportKeyOutputFile,
11920           "--display-keytool-command"
11921         },
11922         INFO_MANAGE_CERTS_EXAMPLE_IMPORT_1.get(exportCertOutputFile,
11923              exportKeyOutputFile, keystorePath));
11924
11925    examples.put(
11926         new String[]
11927         {
11928           "delete-certificate",
11929           "--keystore", keystorePath,
11930           "--keystore-password-file", keystorePWPath,
11931           "--alias", "server-cert"
11932         },
11933         INFO_MANAGE_CERTS_EXAMPLE_DELETE_1.get(keystorePath));
11934
11935    examples.put(
11936         new String[]
11937         {
11938           "generate-self-signed-certificate",
11939           "--keystore", keystorePath,
11940           "--keystore-type", "PKCS12",
11941           "--keystore-password-file", keystorePWPath,
11942           "--alias", "ca-cert",
11943           "--subject-dn", "CN=Example Authority,O=Example Corporation,C=US",
11944           "--days-valid", "7300",
11945           "--validity-start-time", "20170101000000",
11946           "--key-algorithm", "RSA",
11947           "--key-size-bits", "4096",
11948           "--signature-algorithm", "SHA256withRSA",
11949           "--basic-constraints-is-ca", "true",
11950           "--key-usage", "key-cert-sign",
11951           "--key-usage", "crl-sign",
11952           "--display-keytool-command"
11953         },
11954         INFO_MANAGE_CERTS_EXAMPLE_GEN_CERT_1.get(keystorePath));
11955
11956    examples.put(
11957         new String[]
11958         {
11959           "generate-certificate-signing-request",
11960           "--keystore", keystorePath,
11961           "--keystore-type", "PKCS12",
11962           "--keystore-password-file", keystorePWPath,
11963           "--output-file", genCSROutputFile,
11964           "--alias", "server-cert",
11965           "--subject-dn", "CN=ldap.example.com,O=Example Corporation,C=US",
11966           "--key-algorithm", "EC",
11967           "--key-size-bits", "256",
11968           "--signature-algorithm", "SHA256withECDSA",
11969           "--subject-alternative-name-dns", "ldap1.example.com",
11970           "--subject-alternative-name-dns", "ldap2.example.com",
11971           "--extended-key-usage", "server-auth",
11972           "--extended-key-usage", "client-auth",
11973           "--display-keytool-command"
11974         },
11975         INFO_MANAGE_CERTS_EXAMPLE_GEN_CSR_1.get(keystorePath,
11976              genCSROutputFile));
11977
11978    examples.put(
11979         new String[]
11980         {
11981           "generate-certificate-signing-request",
11982           "--keystore", keystorePath,
11983           "--keystore-password-file", keystorePWPath,
11984           "--alias", "server-cert",
11985           "--use-existing-key-pair",
11986           "--inherit-extensions",
11987           "--display-keytool-command"
11988         },
11989         INFO_MANAGE_CERTS_EXAMPLE_GEN_CSR_2.get(keystorePath));
11990
11991    examples.put(
11992         new String[]
11993         {
11994           "sign-certificate-signing-request",
11995           "--keystore", keystorePath,
11996           "--keystore-password-file", keystorePWPath,
11997           "--request-input-file", genCSROutputFile,
11998           "--certificate-output-file", exportCertOutputFile,
11999           "--alias", "ca-cert",
12000           "--days-valid", "730",
12001           "--include-requested-extensions",
12002           "--display-keytool-command"
12003         },
12004         INFO_MANAGE_CERTS_EXAMPLE_SIGN_CERT_1.get(keystorePath,
12005              genCSROutputFile, exportCertOutputFile));
12006
12007    examples.put(
12008         new String[]
12009         {
12010           "change-certificate-alias",
12011           "--keystore", keystorePath,
12012           "--keystore-password-file", keystorePWPath,
12013           "--current-alias", "server-cert",
12014           "--new-alias", "server-certificate",
12015           "--display-keytool-command"
12016         },
12017         INFO_MANAGE_CERTS_EXAMPLE_CHANGE_ALIAS_1.get(keystorePath,
12018              genCSROutputFile, exportCertOutputFile));
12019
12020    examples.put(
12021         new String[]
12022         {
12023           "change-keystore-password",
12024           "--keystore", getPlatformSpecificPath("config", "keystore"),
12025           "--current-keystore-password-file",
12026                getPlatformSpecificPath("config", "current.pin"),
12027           "--new-keystore-password-file",
12028                getPlatformSpecificPath("config", "new.pin"),
12029           "--display-keytool-command"
12030         },
12031         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_EXAMPLE_1.get(
12032              getPlatformSpecificPath("config", "keystore"),
12033              getPlatformSpecificPath("config", "current.pin"),
12034              getPlatformSpecificPath("config", "new.pin")));
12035
12036    examples.put(
12037         new String[]
12038         {
12039           "trust-server-certificate",
12040           "--hostname", "ldap.example.com",
12041           "--port", "636",
12042           "--keystore", keystorePath,
12043           "--keystore-password-file", keystorePWPath,
12044           "--alias", "ldap.example.com:636"
12045         },
12046         INFO_MANAGE_CERTS_EXAMPLE_TRUST_SERVER_1.get(keystorePath));
12047
12048    examples.put(
12049         new String[]
12050         {
12051           "check-certificate-usability",
12052           "--keystore", keystorePath,
12053           "--keystore-password-file", keystorePWPath,
12054           "--alias", "server-cert"
12055         },
12056         INFO_MANAGE_CERTS_EXAMPLE_CHECK_USABILITY_1.get(keystorePath));
12057
12058    examples.put(
12059         new String[]
12060         {
12061           "display-certificate-file",
12062           "--certificate-file", exportCertOutputFile,
12063           "--verbose",
12064           "--display-keytool-command"
12065         },
12066         INFO_MANAGE_CERTS_EXAMPLE_DISPLAY_CERT_1.get(keystorePath));
12067
12068    examples.put(
12069         new String[]
12070         {
12071           "display-certificate-signing-request-file",
12072           "--certificate-signing-request-file", genCSROutputFile,
12073           "--display-keytool-command"
12074         },
12075         INFO_MANAGE_CERTS_EXAMPLE_DISPLAY_CSR_1.get(keystorePath));
12076
12077    examples.put(
12078         new String[]
12079         {
12080           "--help-subcommands"
12081         },
12082         INFO_MANAGE_CERTS_EXAMPLE_HELP_SUBCOMMANDS_1.get(keystorePath));
12083
12084    return examples;
12085  }
12086}