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