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