001/*
002 * Copyright 2017-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util.ssl.cert;
022
023
024
025import java.io.BufferedInputStream;
026import java.io.BufferedReader;
027import java.io.ByteArrayInputStream;
028import java.io.File;
029import java.io.FileInputStream;
030import java.io.FileOutputStream;
031import java.io.FileReader;
032import java.io.InputStream;
033import java.io.InputStreamReader;
034import java.io.IOException;
035import java.io.OutputStream;
036import java.io.PrintStream;
037import java.nio.file.Files;
038import java.net.InetAddress;
039import java.security.Key;
040import java.security.KeyPair;
041import java.security.KeyStore;
042import java.security.PrivateKey;
043import java.security.PublicKey;
044import java.security.UnrecoverableKeyException;
045import java.security.cert.Certificate;
046import java.text.SimpleDateFormat;
047import java.util.ArrayList;
048import java.util.Arrays;
049import java.util.Collections;
050import java.util.Date;
051import java.util.Enumeration;
052import java.util.Iterator;
053import java.util.LinkedHashMap;
054import java.util.LinkedHashSet;
055import java.util.List;
056import java.util.Map;
057import java.util.Set;
058import java.util.concurrent.LinkedBlockingQueue;
059import java.util.concurrent.TimeUnit;
060import java.util.concurrent.atomic.AtomicReference;
061
062import com.unboundid.asn1.ASN1BitString;
063import com.unboundid.asn1.ASN1Element;
064import com.unboundid.ldap.sdk.DN;
065import com.unboundid.ldap.sdk.LDAPException;
066import com.unboundid.ldap.sdk.ResultCode;
067import com.unboundid.ldap.sdk.Version;
068import com.unboundid.util.Base64;
069import com.unboundid.util.ByteStringBuffer;
070import com.unboundid.util.CommandLineTool;
071import com.unboundid.util.Debug;
072import com.unboundid.util.OID;
073import com.unboundid.util.ObjectPair;
074import com.unboundid.util.PasswordReader;
075import com.unboundid.util.StaticUtils;
076import com.unboundid.util.ThreadSafety;
077import com.unboundid.util.ThreadSafetyLevel;
078import com.unboundid.util.Validator;
079import com.unboundid.util.args.ArgumentException;
080import com.unboundid.util.args.ArgumentParser;
081import com.unboundid.util.args.BooleanArgument;
082import com.unboundid.util.args.BooleanValueArgument;
083import com.unboundid.util.args.DNArgument;
084import com.unboundid.util.args.FileArgument;
085import com.unboundid.util.args.IPAddressArgumentValueValidator;
086import com.unboundid.util.args.IntegerArgument;
087import com.unboundid.util.args.OIDArgumentValueValidator;
088import com.unboundid.util.args.StringArgument;
089import com.unboundid.util.args.TimestampArgument;
090import com.unboundid.util.args.SubCommand;
091import com.unboundid.util.ssl.JVMDefaultTrustManager;
092
093import static com.unboundid.util.ssl.cert.CertMessages.*;
094
095
096
097/**
098 * This class provides a tool that can be used to manage X.509 certificates for
099 * use in TLS communication.
100 */
101@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
102public final class ManageCertificates
103       extends CommandLineTool
104{
105  /**
106   * The path to the keystore with the JVM's set of default trusted issuer
107   * certificates.
108   */
109  private static final File JVM_DEFAULT_CACERTS_FILE;
110  static
111  {
112    File caCertsFile;
113    try
114    {
115      caCertsFile = JVMDefaultTrustManager.getInstance().getCACertsFile();
116    }
117    catch (final Exception e)
118    {
119      Debug.debugException(e);
120      caCertsFile = null;
121    }
122
123    JVM_DEFAULT_CACERTS_FILE = caCertsFile;
124  }
125
126
127
128  /**
129   * The name of a system property that can be used to specify the default
130   * keystore type for new keystores.
131   */
132  private static final String PROPERTY_DEFAULT_KEYSTORE_TYPE =
133       ManageCertificates.class.getName() + ".defaultKeystoreType";
134
135
136
137  /**
138   * The default keystore type that will be used for new keystores when the
139   * type is not specified.
140   */
141  private static final String DEFAULT_KEYSTORE_TYPE;
142  static
143  {
144    final String propertyValue =
145         System.getProperty(PROPERTY_DEFAULT_KEYSTORE_TYPE);
146    if ((propertyValue != null) &&
147        (propertyValue.equalsIgnoreCase("PKCS12") ||
148         propertyValue.equalsIgnoreCase("PKCS#12") ||
149         propertyValue.equalsIgnoreCase("PKCS #12") ||
150         propertyValue.equalsIgnoreCase("PKCS 12")))
151    {
152      DEFAULT_KEYSTORE_TYPE = "PKCS12";
153    }
154    else
155    {
156      DEFAULT_KEYSTORE_TYPE = "JKS";
157    }
158  }
159
160
161
162  /**
163   * The column at which to wrap long lines of output.
164   */
165  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
166
167
168
169  // The global argument parser used by this tool.
170  private volatile ArgumentParser globalParser = null;
171
172  // The argument parser for the selected subcommand.
173  private volatile ArgumentParser subCommandParser = null;
174
175  // The input stream to use for standard input.
176  private final InputStream in;
177
178
179
180  /**
181   * Invokes this tool with the default standard output and standard error and
182   * the provided set of arguments.
183   *
184   * @param  args  The command-line arguments provided to this program.
185   */
186  public static void main(final String... args)
187  {
188    final ResultCode resultCode = main(System.in, System.out, System.err, args);
189    if (resultCode != ResultCode.SUCCESS)
190    {
191      System.exit(Math.max(1, Math.min(resultCode.intValue(), 255)));
192    }
193  }
194
195
196
197  /**
198   * Invokes this tool with the provided output and error streams and set of
199   * arguments.
200   *
201   * @param  in    The input stream to use for standard input.  It may be
202   *               {@code null} if no input stream should be available.
203   * @param  out   The output stream to use for standard output.  It may be
204   *               {@code null} if standard output should be suppressed.
205   * @param  err   The output stream to use for standard error.  It may be
206   *               {@code null} if standard error should be suppressed.
207   * @param  args  The command-line arguments provided to this program.
208   *
209   * @return  The result code obtained from tool processing.
210   */
211  public static ResultCode main(final InputStream in, final OutputStream out,
212                                final OutputStream err, final String... args)
213  {
214    final ManageCertificates manageCertificates =
215         new ManageCertificates(in, out, err);
216    return manageCertificates.runTool(args);
217  }
218
219
220
221  /**
222   * Creates a new instance of this tool with the provided output and error
223   * streams.
224   *
225   * @param  in   The input stream to use for standard input.  It may be
226   *              {@code null} if no input stream should be available.
227   * @param  out  The output stream to use for standard output.  It may be
228   *              {@code null} if standard output should be suppressed.
229   * @param  err  The output stream to use for standard error.  It may be
230   *              {@code null} if standard error should be suppressed.
231   */
232  public ManageCertificates(final InputStream in, final OutputStream out,
233                            final OutputStream err)
234  {
235    super(out, err);
236
237    if (in == null)
238    {
239      this.in = new ByteArrayInputStream(StaticUtils.NO_BYTES);
240    }
241    else
242    {
243      this.in = in;
244    }
245  }
246
247
248
249  /**
250   * Retrieves the name of this tool.  It should be the name of the command used
251   * to invoke this tool.
252   *
253   * @return  The name for this tool.
254   */
255  @Override()
256  public String getToolName()
257  {
258    return "manage-certificates";
259  }
260
261
262
263  /**
264   * Retrieves a human-readable description for this tool.
265   *
266   * @return  A human-readable description for this tool.
267   */
268  @Override()
269  public String getToolDescription()
270  {
271    return INFO_MANAGE_CERTS_TOOL_DESC.get();
272  }
273
274
275
276  /**
277   * Retrieves a version string for this tool, if available.
278   *
279   * @return  A version string for this tool, or {@code null} if none is
280   *          available.
281   */
282  @Override()
283  public String getToolVersion()
284  {
285    return Version.NUMERIC_VERSION_STRING;
286  }
287
288
289
290  /**
291   * Indicates whether this tool should provide support for an interactive mode,
292   * in which the tool offers a mode in which the arguments can be provided in
293   * a text-driven menu rather than requiring them to be given on the command
294   * line.  If interactive mode is supported, it may be invoked using the
295   * "--interactive" argument.  Alternately, if interactive mode is supported
296   * and {@link #defaultsToInteractiveMode()} returns {@code true}, then
297   * interactive mode may be invoked by simply launching the tool without any
298   * arguments.
299   *
300   * @return  {@code true} if this tool supports interactive mode, or
301   *          {@code false} if not.
302   */
303  @Override()
304  public boolean supportsInteractiveMode()
305  {
306    return true;
307  }
308
309
310
311  /**
312   * Indicates whether this tool defaults to launching in interactive mode if
313   * the tool is invoked without any command-line arguments.  This will only be
314   * used if {@link #supportsInteractiveMode()} returns {@code true}.
315   *
316   * @return  {@code true} if this tool defaults to using interactive mode if
317   *          launched without any command-line arguments, or {@code false} if
318   *          not.
319   */
320  @Override()
321  public boolean defaultsToInteractiveMode()
322  {
323    return true;
324  }
325
326
327
328  /**
329   * Indicates whether this tool supports the use of a properties file for
330   * specifying default values for arguments that aren't specified on the
331   * command line.
332   *
333   * @return  {@code true} if this tool supports the use of a properties file
334   *          for specifying default values for arguments that aren't specified
335   *          on the command line, or {@code false} if not.
336   */
337  @Override()
338  public boolean supportsPropertiesFile()
339  {
340    return true;
341  }
342
343
344
345  /**
346   * Indicates whether this tool should provide arguments for redirecting output
347   * to a file.  If this method returns {@code true}, then the tool will offer
348   * an "--outputFile" argument that will specify the path to a file to which
349   * all standard output and standard error content will be written, and it will
350   * also offer a "--teeToStandardOut" argument that can only be used if the
351   * "--outputFile" argument is present and will cause all output to be written
352   * to both the specified output file and to standard output.
353   *
354   * @return  {@code true} if this tool should provide arguments for redirecting
355   *          output to a file, or {@code false} if not.
356   */
357  @Override()
358  protected boolean supportsOutputFile()
359  {
360    return false;
361  }
362
363
364
365  /**
366   * Indicates whether to log messages about the launch and completion of this
367   * tool into the invocation log of Ping Identity server products that may
368   * include it.  This method is not needed for tools that are not expected to
369   * be part of the Ping Identity server products suite.  Further, this value
370   * may be overridden by settings in the server's
371   * tool-invocation-logging.properties file.
372   * <BR><BR>
373   * This method should generally return {@code true} for tools that may alter
374   * the server configuration, data, or other state information, and
375   * {@code false} for tools that do not make any changes.
376   *
377   * @return  {@code true} if Ping Identity server products should include
378   *          messages about the launch and completion of this tool in tool
379   *          invocation log files by default, or {@code false} if not.
380   */
381  @Override()
382  protected boolean logToolInvocationByDefault()
383  {
384    return true;
385  }
386
387
388
389  /**
390   * Adds the command-line arguments supported for use with this tool to the
391   * provided argument parser.  The tool may need to retain references to the
392   * arguments (and/or the argument parser, if trailing arguments are allowed)
393   * to it in order to obtain their values for use in later processing.
394   *
395   * @param  parser  The argument parser to which the arguments are to be added.
396   *
397   * @throws  ArgumentException  If a problem occurs while adding any of the
398   *                             tool-specific arguments to the provided
399   *                             argument parser.
400   */
401  @Override()
402  public void addToolArguments(final ArgumentParser parser)
403         throws ArgumentException
404  {
405    globalParser = parser;
406
407
408    // Define the "list-certificates" subcommand and all of its arguments.
409    final ArgumentParser listCertsParser = new ArgumentParser(
410         "list-certificates", INFO_MANAGE_CERTS_SC_LIST_CERTS_DESC.get());
411
412    final FileArgument listCertsKeystore = new FileArgument(null, "keystore",
413         true, 1, null, INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_DESC.get(),
414         true, true,  true, false);
415    listCertsKeystore.addLongIdentifier("keystore-path", true);
416    listCertsKeystore.addLongIdentifier("keystorePath", true);
417    listCertsKeystore.addLongIdentifier("keystore-file", true);
418    listCertsKeystore.addLongIdentifier("keystoreFile", true);
419    listCertsParser.addArgument(listCertsKeystore);
420
421    final StringArgument listCertsKeystorePassword = new StringArgument(null,
422         "keystore-password", false, 1,
423         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
424         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_PW_DESC.get());
425    listCertsKeystorePassword.addLongIdentifier("keystorePassword", true);
426    listCertsKeystorePassword.addLongIdentifier("keystore-passphrase", true);
427    listCertsKeystorePassword.addLongIdentifier("keystorePassphrase", true);
428    listCertsKeystorePassword.addLongIdentifier("keystore-pin", true);
429    listCertsKeystorePassword.addLongIdentifier("keystorePIN", true);
430    listCertsKeystorePassword.addLongIdentifier("storepass", true);
431    listCertsKeystorePassword.setSensitive(true);
432    listCertsParser.addArgument(listCertsKeystorePassword);
433
434    final FileArgument listCertsKeystorePasswordFile = new FileArgument(null,
435         "keystore-password-file", false, 1, null,
436         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_PW_FILE_DESC.get(), true, true,
437         true, false);
438    listCertsKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
439         true);
440    listCertsKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
441         true);
442    listCertsKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
443         true);
444    listCertsKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
445         true);
446    listCertsKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
447    listCertsParser.addArgument(listCertsKeystorePasswordFile);
448
449    final BooleanArgument listCertsPromptForKeystorePassword =
450         new BooleanArgument(null, "prompt-for-keystore-password",
451        INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_PROMPT_FOR_KS_PW_DESC.get());
452    listCertsPromptForKeystorePassword.addLongIdentifier(
453         "promptForKeystorePassword", true);
454    listCertsPromptForKeystorePassword.addLongIdentifier(
455         "prompt-for-keystore-passphrase", true);
456    listCertsPromptForKeystorePassword.addLongIdentifier(
457         "promptForKeystorePassphrase", true);
458    listCertsPromptForKeystorePassword.addLongIdentifier(
459         "prompt-for-keystore-pin", true);
460    listCertsPromptForKeystorePassword.addLongIdentifier(
461         "promptForKeystorePIN", true);
462    listCertsParser.addArgument(listCertsPromptForKeystorePassword);
463
464    final StringArgument listCertsAlias = new StringArgument(null, "alias",
465         false, 0, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
466         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_ALIAS_DESC.get());
467    listCertsAlias.addLongIdentifier("nickname", true);
468    listCertsParser.addArgument(listCertsAlias);
469
470    final BooleanArgument listCertsDisplayPEM = new BooleanArgument(null,
471         "display-pem-certificate", 1,
472         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_DISPLAY_PEM_DESC.get());
473    listCertsDisplayPEM.addLongIdentifier("displayPEMCertificate", true);
474    listCertsDisplayPEM.addLongIdentifier("display-pem", true);
475    listCertsDisplayPEM.addLongIdentifier("displayPEM", true);
476    listCertsDisplayPEM.addLongIdentifier("show-pem-certificate", true);
477    listCertsDisplayPEM.addLongIdentifier("showPEMCertificate", true);
478    listCertsDisplayPEM.addLongIdentifier("show-pem", true);
479    listCertsDisplayPEM.addLongIdentifier("showPEM", true);
480    listCertsDisplayPEM.addLongIdentifier("pem", true);
481    listCertsDisplayPEM.addLongIdentifier("rfc", true);
482    listCertsParser.addArgument(listCertsDisplayPEM);
483
484    final BooleanArgument listCertsVerbose = new BooleanArgument(null,
485         "verbose", 1, INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_VERBOSE_DESC.get());
486    listCertsParser.addArgument(listCertsVerbose);
487
488    final BooleanArgument listCertsDisplayCommand = new BooleanArgument(null,
489         "display-keytool-command", 1,
490         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_DISPLAY_COMMAND_DESC.get());
491    listCertsDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
492    listCertsDisplayCommand.addLongIdentifier("show-keytool-command", true);
493    listCertsDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
494    listCertsParser.addArgument(listCertsDisplayCommand);
495
496    listCertsParser.addExclusiveArgumentSet(listCertsKeystorePassword,
497         listCertsKeystorePasswordFile, listCertsPromptForKeystorePassword);
498
499    final LinkedHashMap<String[],String> listCertsExamples =
500         new LinkedHashMap<>(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(InetAddress.getByName(value));
5882          sanValues.add("IP:" + value);
5883        }
5884        catch (final Exception e)
5885        {
5886          // This should never happen.
5887          Debug.debugException(e);
5888          throw new RuntimeException(e);
5889        }
5890      }
5891    }
5892
5893    final StringArgument sanEmailArgument = subCommandParser.getStringArgument(
5894         "subject-alternative-name-email-address");
5895    if ((sanEmailArgument != null) && sanEmailArgument.isPresent())
5896    {
5897      for (final String value : sanEmailArgument.getValues())
5898      {
5899        sanBuilder.addRFC822Name(value);
5900        sanValues.add("EMAIL:" + value);
5901      }
5902    }
5903
5904    final StringArgument sanURIArgument =
5905         subCommandParser.getStringArgument("subject-alternative-name-uri");
5906    if ((sanURIArgument != null) && sanURIArgument.isPresent())
5907    {
5908      for (final String value : sanURIArgument.getValues())
5909      {
5910        sanBuilder.addUniformResourceIdentifier(value);
5911        sanValues.add("URI:" + value);
5912      }
5913    }
5914
5915    final StringArgument sanOIDArgument =
5916         subCommandParser.getStringArgument("subject-alternative-name-oid");
5917    if ((sanOIDArgument != null) && sanOIDArgument.isPresent())
5918    {
5919      for (final String value : sanOIDArgument.getValues())
5920      {
5921        sanBuilder.addRegisteredID(new OID(value));
5922        sanValues.add("OID:" + value);
5923      }
5924    }
5925
5926    if (! sanValues.isEmpty())
5927    {
5928      try
5929      {
5930        extensionList.add(
5931             new SubjectAlternativeNameExtension(false, sanBuilder.build()));
5932      }
5933      catch (final Exception e)
5934      {
5935        // This should never happen.
5936        Debug.debugException(e);
5937        throw new RuntimeException(e);
5938      }
5939    }
5940
5941    // Build a set of issuer alternative name extension values.
5942    final GeneralNamesBuilder ianBuilder = new GeneralNamesBuilder();
5943    final LinkedHashSet<String> ianValues =
5944         new LinkedHashSet<>(StaticUtils.computeMapCapacity(10));
5945    final StringArgument ianDNSArgument =
5946         subCommandParser.getStringArgument("issuer-alternative-name-dns");
5947    if ((ianDNSArgument != null) && ianDNSArgument.isPresent())
5948    {
5949      for (final String value : ianDNSArgument.getValues())
5950      {
5951        ianBuilder.addDNSName(value);
5952        ianValues.add("DNS:" + value);
5953      }
5954    }
5955
5956    final StringArgument ianIPArgument = subCommandParser.getStringArgument(
5957         "issuer-alternative-name-ip-address");
5958    if ((ianIPArgument != null) && ianIPArgument.isPresent())
5959    {
5960      for (final String value : ianIPArgument.getValues())
5961      {
5962        try
5963        {
5964          ianBuilder.addIPAddress(InetAddress.getByName(value));
5965          ianValues.add("IP:" + value);
5966        }
5967        catch (final Exception e)
5968        {
5969          // This should never happen.
5970          Debug.debugException(e);
5971          throw new RuntimeException(e);
5972        }
5973      }
5974    }
5975
5976    final StringArgument ianEmailArgument = subCommandParser.getStringArgument(
5977         "issuer-alternative-name-email-address");
5978    if ((ianEmailArgument != null) && ianEmailArgument.isPresent())
5979    {
5980      for (final String value : ianEmailArgument.getValues())
5981      {
5982        ianBuilder.addRFC822Name(value);
5983        ianValues.add("EMAIL:" + value);
5984      }
5985    }
5986
5987    final StringArgument ianURIArgument =
5988         subCommandParser.getStringArgument("issuer-alternative-name-uri");
5989    if ((ianURIArgument != null) && ianURIArgument.isPresent())
5990    {
5991      for (final String value : ianURIArgument.getValues())
5992      {
5993        ianBuilder.addUniformResourceIdentifier(value);
5994        ianValues.add("URI:" + value);
5995      }
5996    }
5997
5998    final StringArgument ianOIDArgument =
5999         subCommandParser.getStringArgument("issuer-alternative-name-oid");
6000    if ((ianOIDArgument != null) && ianOIDArgument.isPresent())
6001    {
6002      for (final String value : ianOIDArgument.getValues())
6003      {
6004        ianBuilder.addRegisteredID(new OID(value));
6005        ianValues.add("OID:" + value);
6006      }
6007    }
6008
6009    if (! ianValues.isEmpty())
6010    {
6011      try
6012      {
6013        extensionList.add(
6014             new IssuerAlternativeNameExtension(false, ianBuilder.build()));
6015      }
6016      catch (final Exception e)
6017      {
6018        // This should never happen.
6019        Debug.debugException(e);
6020        throw new RuntimeException(e);
6021      }
6022    }
6023
6024
6025    // Build a basic constraints extension, if appropriate.
6026    BasicConstraintsExtension basicConstraints = null;
6027    final BooleanValueArgument basicConstraintsIsCAArgument =
6028         subCommandParser.getBooleanValueArgument("basic-constraints-is-ca");
6029    if ((basicConstraintsIsCAArgument != null) &&
6030         basicConstraintsIsCAArgument.isPresent())
6031    {
6032      final boolean isCA = basicConstraintsIsCAArgument.getValue();
6033
6034      Integer pathLength = null;
6035      final IntegerArgument pathLengthArgument =
6036           subCommandParser.getIntegerArgument(
6037                "basic-constraints-maximum-path-length");
6038      if ((pathLengthArgument != null) && pathLengthArgument.isPresent())
6039      {
6040        if (isCA)
6041        {
6042          pathLength = pathLengthArgument.getValue();
6043        }
6044        else
6045        {
6046          wrapErr(0, WRAP_COLUMN,
6047               ERR_MANAGE_CERTS_GEN_CERT_BC_PATH_LENGTH_WITHOUT_CA.get());
6048          return ResultCode.PARAM_ERROR;
6049        }
6050      }
6051
6052      basicConstraints = new BasicConstraintsExtension(false, isCA, pathLength);
6053      extensionList.add(basicConstraints);
6054    }
6055
6056
6057    // Build a key usage extension, if appropriate.
6058    KeyUsageExtension keyUsage = null;
6059    final StringArgument keyUsageArgument =
6060         subCommandParser.getStringArgument("key-usage");
6061    if ((keyUsageArgument != null) && keyUsageArgument.isPresent())
6062    {
6063      boolean digitalSignature = false;
6064      boolean nonRepudiation = false;
6065      boolean keyEncipherment = false;
6066      boolean dataEncipherment = false;
6067      boolean keyAgreement = false;
6068      boolean keyCertSign = false;
6069      boolean crlSign = false;
6070      boolean encipherOnly = false;
6071      boolean decipherOnly = false;
6072
6073      for (final String value : keyUsageArgument.getValues())
6074      {
6075        if (value.equalsIgnoreCase("digital-signature") ||
6076             value.equalsIgnoreCase("digitalSignature"))
6077        {
6078          digitalSignature = true;
6079        }
6080        else if (value.equalsIgnoreCase("non-repudiation") ||
6081             value.equalsIgnoreCase("nonRepudiation") ||
6082             value.equalsIgnoreCase("content-commitment") ||
6083             value.equalsIgnoreCase("contentCommitment"))
6084        {
6085          nonRepudiation = true;
6086        }
6087        else if (value.equalsIgnoreCase("key-encipherment") ||
6088             value.equalsIgnoreCase("keyEncipherment"))
6089        {
6090          keyEncipherment = true;
6091        }
6092        else if (value.equalsIgnoreCase("data-encipherment") ||
6093             value.equalsIgnoreCase("dataEncipherment"))
6094        {
6095          dataEncipherment = true;
6096        }
6097        else if (value.equalsIgnoreCase("key-agreement") ||
6098             value.equalsIgnoreCase("keyAgreement"))
6099        {
6100          keyAgreement = true;
6101        }
6102        else if (value.equalsIgnoreCase("key-cert-sign") ||
6103             value.equalsIgnoreCase("keyCertSign"))
6104        {
6105          keyCertSign = true;
6106        }
6107        else if (value.equalsIgnoreCase("crl-sign") ||
6108             value.equalsIgnoreCase("crlSign"))
6109        {
6110          crlSign = true;
6111        }
6112        else if (value.equalsIgnoreCase("encipher-only") ||
6113             value.equalsIgnoreCase("encipherOnly"))
6114        {
6115          encipherOnly = true;
6116        }
6117        else if (value.equalsIgnoreCase("decipher-only") ||
6118             value.equalsIgnoreCase("decipherOnly"))
6119        {
6120          decipherOnly = true;
6121        }
6122        else
6123        {
6124          wrapErr(0, WRAP_COLUMN,
6125               ERR_MANAGE_CERTS_GEN_CERT_INVALID_KEY_USAGE.get(value));
6126          return ResultCode.PARAM_ERROR;
6127        }
6128      }
6129
6130      keyUsage = new KeyUsageExtension(false, digitalSignature, nonRepudiation,
6131           keyEncipherment, dataEncipherment, keyAgreement, keyCertSign,
6132           crlSign, encipherOnly, decipherOnly);
6133      extensionList.add(keyUsage);
6134    }
6135
6136
6137    // Build an extended key usage extension, if appropriate.
6138    ExtendedKeyUsageExtension extendedKeyUsage = null;
6139    final StringArgument extendedKeyUsageArgument =
6140         subCommandParser.getStringArgument("extended-key-usage");
6141    if ((extendedKeyUsageArgument != null) &&
6142         extendedKeyUsageArgument.isPresent())
6143    {
6144      final List<String> values = extendedKeyUsageArgument.getValues();
6145      final ArrayList<OID> keyPurposeIDs = new ArrayList<>(values.size());
6146      for (final String value : values)
6147      {
6148        if (value.equalsIgnoreCase("server-auth") ||
6149             value.equalsIgnoreCase("serverAuth") ||
6150             value.equalsIgnoreCase("server-authentication") ||
6151             value.equalsIgnoreCase("serverAuthentication") ||
6152             value.equalsIgnoreCase("tls-server-authentication") ||
6153             value.equalsIgnoreCase("tlsServerAuthentication"))
6154        {
6155          keyPurposeIDs.add(
6156               ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID());
6157        }
6158        else if (value.equalsIgnoreCase("client-auth") ||
6159             value.equalsIgnoreCase("clientAuth") ||
6160             value.equalsIgnoreCase("client-authentication") ||
6161             value.equalsIgnoreCase("clientAuthentication") ||
6162             value.equalsIgnoreCase("tls-client-authentication") ||
6163             value.equalsIgnoreCase("tlsClientAuthentication"))
6164        {
6165          keyPurposeIDs.add(
6166               ExtendedKeyUsageID.TLS_CLIENT_AUTHENTICATION.getOID());
6167        }
6168        else if (value.equalsIgnoreCase("code-signing") ||
6169             value.equalsIgnoreCase("codeSigning"))
6170        {
6171          keyPurposeIDs.add(ExtendedKeyUsageID.CODE_SIGNING.getOID());
6172        }
6173        else if (value.equalsIgnoreCase("email-protection") ||
6174             value.equalsIgnoreCase("emailProtection"))
6175        {
6176          keyPurposeIDs.add(ExtendedKeyUsageID.EMAIL_PROTECTION.getOID());
6177        }
6178        else if (value.equalsIgnoreCase("time-stamping") ||
6179             value.equalsIgnoreCase("timeStamping"))
6180        {
6181          keyPurposeIDs.add(ExtendedKeyUsageID.TIME_STAMPING.getOID());
6182        }
6183        else if (value.equalsIgnoreCase("ocsp-signing") ||
6184             value.equalsIgnoreCase("ocspSigning"))
6185        {
6186          keyPurposeIDs.add(ExtendedKeyUsageID.OCSP_SIGNING.getOID());
6187        }
6188        else if (OID.isStrictlyValidNumericOID(value))
6189        {
6190          keyPurposeIDs.add(new OID(value));
6191        }
6192        else
6193        {
6194          wrapErr(0, WRAP_COLUMN,
6195               ERR_MANAGE_CERTS_GEN_CERT_INVALID_EXTENDED_KEY_USAGE.get(value));
6196          return ResultCode.PARAM_ERROR;
6197        }
6198      }
6199
6200      try
6201      {
6202        extendedKeyUsage = new ExtendedKeyUsageExtension(false, keyPurposeIDs);
6203      }
6204      catch (final Exception e)
6205      {
6206        // This should never happen.
6207        Debug.debugException(e);
6208        wrapErr(0, WRAP_COLUMN,
6209             ERR_MANAGE_CERTS_GEN_CERT_EXTENDED_KEY_USAGE_ERROR.get());
6210        e.printStackTrace(getErr());
6211        return ResultCode.PARAM_ERROR;
6212      }
6213
6214      extensionList.add(extendedKeyUsage);
6215    }
6216
6217
6218    // Build a list of generic extensions.
6219    final ArrayList<X509CertificateExtension> genericExtensions =
6220         new ArrayList<>(5);
6221    final StringArgument extensionArgument =
6222         subCommandParser.getStringArgument("extension");
6223    if ((extensionArgument != null) && extensionArgument.isPresent())
6224    {
6225      for (final String value : extensionArgument.getValues())
6226      {
6227        try
6228        {
6229          final int firstColonPos = value.indexOf(':');
6230          final int secondColonPos = value.indexOf(':', firstColonPos + 1);
6231          final OID oid = new OID(value.substring(0, firstColonPos));
6232          if (! oid.isStrictlyValidNumericOID())
6233          {
6234            wrapErr(0, WRAP_COLUMN,
6235                 ERR_MANAGE_CERTS_GEN_CERT_EXT_MALFORMED_OID.get(value,
6236                      oid.toString()));
6237            return ResultCode.PARAM_ERROR;
6238          }
6239
6240          final boolean criticality;
6241          final String criticalityString =
6242               value.substring(firstColonPos + 1, secondColonPos);
6243          if (criticalityString.equalsIgnoreCase("true") ||
6244               criticalityString.equalsIgnoreCase("t") ||
6245               criticalityString.equalsIgnoreCase("yes") ||
6246               criticalityString.equalsIgnoreCase("y") ||
6247               criticalityString.equalsIgnoreCase("on") ||
6248               criticalityString.equalsIgnoreCase("1"))
6249          {
6250            criticality = true;
6251          }
6252          else if (criticalityString.equalsIgnoreCase("false") ||
6253               criticalityString.equalsIgnoreCase("f") ||
6254               criticalityString.equalsIgnoreCase("no") ||
6255               criticalityString.equalsIgnoreCase("n") ||
6256               criticalityString.equalsIgnoreCase("off") ||
6257               criticalityString.equalsIgnoreCase("0"))
6258          {
6259            criticality = false;
6260          }
6261          else
6262          {
6263            wrapErr(0, WRAP_COLUMN,
6264                 ERR_MANAGE_CERTS_GEN_CERT_EXT_INVALID_CRITICALITY.get(
6265                      value, criticalityString));
6266            return ResultCode.PARAM_ERROR;
6267          }
6268
6269          final byte[] valueBytes;
6270          try
6271          {
6272            valueBytes = StaticUtils.fromHex(value.substring(secondColonPos+1));
6273          }
6274          catch (final Exception e)
6275          {
6276            Debug.debugException(e);
6277            wrapErr(0, WRAP_COLUMN,
6278                 ERR_MANAGE_CERTS_GEN_CERT_EXT_INVALID_VALUE.get(value));
6279            return ResultCode.PARAM_ERROR;
6280          }
6281
6282          final X509CertificateExtension extension =
6283               new X509CertificateExtension(oid, criticality, valueBytes);
6284          genericExtensions.add(extension);
6285          extensionList.add(extension);
6286        }
6287        catch (final Exception e)
6288        {
6289          Debug.debugException(e);
6290          wrapErr(0, WRAP_COLUMN,
6291               ERR_MANAGE_CERTS_GEN_CERT_EXT_MALFORMED.get(value));
6292          return ResultCode.PARAM_ERROR;
6293        }
6294      }
6295    }
6296
6297
6298    final String keystoreType;
6299    try
6300    {
6301      keystoreType = inferKeystoreType(keystorePath);
6302    }
6303    catch (final LDAPException le)
6304    {
6305      Debug.debugException(le);
6306      wrapErr(0, WRAP_COLUMN, le.getMessage());
6307      return le.getResultCode();
6308    }
6309
6310    final char[] keystorePassword;
6311    try
6312    {
6313      keystorePassword = getKeystorePassword(keystorePath);
6314    }
6315    catch (final LDAPException le)
6316    {
6317      Debug.debugException(le);
6318      wrapErr(0, WRAP_COLUMN, le.getMessage());
6319      return le.getResultCode();
6320    }
6321
6322
6323    // Get the keystore.
6324    final KeyStore keystore;
6325    try
6326    {
6327      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
6328    }
6329    catch (final LDAPException le)
6330    {
6331      Debug.debugException(le);
6332      wrapErr(0, WRAP_COLUMN, le.getMessage());
6333      return le.getResultCode();
6334    }
6335
6336
6337    // If there is a private key, then see if we need to use a private key
6338    // password that is different from the keystore password.
6339    final char[] privateKeyPassword;
6340    try
6341    {
6342      privateKeyPassword =
6343           getPrivateKeyPassword(keystore, alias, keystorePassword);
6344    }
6345    catch (final LDAPException le)
6346    {
6347      Debug.debugException(le);
6348      wrapErr(0, WRAP_COLUMN, le.getMessage());
6349      return le.getResultCode();
6350    }
6351
6352
6353    // If we're going to replace an existing certificate in the keystore, then
6354    // perform the appropriate processing for that.
6355    if (replaceExistingCertificate)
6356    {
6357      // Make sure that the keystore already has a private key entry with the
6358      // specified alias.
6359      if (! hasKeyAlias(keystore, alias))
6360      {
6361        if (hasCertificateAlias(keystore, alias))
6362        {
6363          wrapErr(0, WRAP_COLUMN,
6364               ERR_MANAGE_CERTS_GEN_CERT_REPLACE_ALIAS_IS_CERT.get(alias,
6365                    keystorePath.getAbsolutePath()));
6366          return ResultCode.PARAM_ERROR;
6367        }
6368        else
6369        {
6370          wrapErr(0, WRAP_COLUMN,
6371               ERR_MANAGE_CERTS_GEN_CERT_REPLACE_NO_SUCH_ALIAS.get(alias,
6372                    keystorePath.getAbsolutePath()));
6373          return ResultCode.PARAM_ERROR;
6374        }
6375      }
6376
6377
6378      // Get the certificate to replace, along with its key pair.
6379      final X509Certificate certToReplace;
6380      final KeyPair keyPair;
6381      try
6382      {
6383        final Certificate[] chain = keystore.getCertificateChain(alias);
6384        certToReplace = new X509Certificate(chain[0].getEncoded());
6385
6386        final PublicKey publicKey = chain[0].getPublicKey();
6387        final PrivateKey privateKey =
6388             (PrivateKey) keystore.getKey(alias, privateKeyPassword);
6389        keyPair = new KeyPair(publicKey, privateKey);
6390      }
6391      catch (final Exception e)
6392      {
6393        Debug.debugException(e);
6394        wrapErr(0, WRAP_COLUMN,
6395             ERR_MANAGE_CERTS_GEN_CERT_REPLACE_COULD_NOT_GET_CERT.get(alias));
6396        e.printStackTrace(getErr());
6397        return ResultCode.LOCAL_ERROR;
6398      }
6399
6400
6401      // Assign the remaining values using information in the existing
6402      // certificate.
6403      signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forOID(
6404           certToReplace.getSignatureAlgorithmOID());
6405      if (signatureAlgorithmIdentifier == null)
6406      {
6407        wrapErr(0, WRAP_COLUMN,
6408             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG_IN_CERT.get(
6409                  certToReplace.getSignatureAlgorithmOID()));
6410        return ResultCode.PARAM_ERROR;
6411      }
6412      else
6413      {
6414        signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
6415      }
6416
6417      if (subjectDN == null)
6418      {
6419        subjectDN = certToReplace.getSubjectDN();
6420      }
6421
6422      if (inheritExtensions)
6423      {
6424        for (final X509CertificateExtension extension :
6425             certToReplace.getExtensions())
6426        {
6427          if ((extension instanceof AuthorityKeyIdentifierExtension) ||
6428              (extension instanceof IssuerAlternativeNameExtension))
6429          {
6430            // This extension applies to the issuer.  We won't include this in
6431            // the set of inherited extensions.
6432          }
6433          else if (extension instanceof SubjectKeyIdentifierExtension)
6434          {
6435            // The generated certificate will automatically include a subject
6436            // key identifier extension, so we don't need to include it.
6437          }
6438          else if (extension instanceof BasicConstraintsExtension)
6439          {
6440            // Don't override a value already provided on the command line.
6441            if (basicConstraints == null)
6442            {
6443              basicConstraints = (BasicConstraintsExtension) extension;
6444              extensionList.add(basicConstraints);
6445            }
6446          }
6447          else if (extension instanceof ExtendedKeyUsageExtension)
6448          {
6449            // Don't override a value already provided on the command line.
6450            if (extendedKeyUsage == null)
6451            {
6452              extendedKeyUsage = (ExtendedKeyUsageExtension) extension;
6453              extensionList.add(extendedKeyUsage);
6454            }
6455          }
6456          else if (extension instanceof KeyUsageExtension)
6457          {
6458            // Don't override a value already provided on the command line.
6459            if (keyUsage == null)
6460            {
6461              keyUsage = (KeyUsageExtension) extension;
6462              extensionList.add(keyUsage);
6463            }
6464          }
6465          else if (extension instanceof SubjectAlternativeNameExtension)
6466          {
6467            // Although we could merge values, it's safer to not do that if any
6468            // subject alternative name values were provided on the command
6469            // line.
6470            if (sanValues.isEmpty())
6471            {
6472              final SubjectAlternativeNameExtension e =
6473                   (SubjectAlternativeNameExtension) extension;
6474              for (final String dnsName : e.getDNSNames())
6475              {
6476                sanValues.add("DNS:" + dnsName);
6477              }
6478
6479              for (final InetAddress ipAddress : e.getIPAddresses())
6480              {
6481                sanValues.add("IP:" + ipAddress.getHostAddress());
6482              }
6483
6484              for (final String emailAddress : e.getRFC822Names())
6485              {
6486                sanValues.add("EMAIL:" + emailAddress);
6487              }
6488
6489              for (final String uri : e.getUniformResourceIdentifiers())
6490              {
6491                sanValues.add("URI:" + uri);
6492              }
6493
6494              for (final OID oid : e.getRegisteredIDs())
6495              {
6496                sanValues.add("OID:" + oid.toString());
6497              }
6498
6499              extensionList.add(extension);
6500            }
6501          }
6502          else
6503          {
6504            genericExtensions.add(extension);
6505            extensionList.add(extension);
6506          }
6507        }
6508      }
6509
6510
6511      // Create an array with the final set of extensions to include in the
6512      // certificate or certificate signing request.
6513      final X509CertificateExtension[] extensions =
6514           new X509CertificateExtension[extensionList.size()];
6515      extensionList.toArray(extensions);
6516
6517
6518      // If we're generating a self-signed certificate or a certificate signing
6519      // request, then we should now have everything we need to do that.  Build
6520      // a keytool command that we could use to accomplish it.
6521      if (isGenerateCertificate)
6522      {
6523        if (displayKeytoolCommand)
6524        {
6525          final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6526          keytoolArguments.add("-selfcert");
6527          keytoolArguments.add("-keystore");
6528          keytoolArguments.add(keystorePath.getAbsolutePath());
6529          keytoolArguments.add("-storetype");
6530          keytoolArguments.add(keystoreType);
6531          keytoolArguments.add("-storepass");
6532          keytoolArguments.add("*****REDACTED*****");
6533          keytoolArguments.add("-keypass");
6534          keytoolArguments.add("*****REDACTED*****");
6535          keytoolArguments.add("-alias");
6536          keytoolArguments.add(alias);
6537          keytoolArguments.add("-dname");
6538          keytoolArguments.add(subjectDN.toString());
6539          keytoolArguments.add("-sigalg");
6540          keytoolArguments.add(signatureAlgorithmName);
6541          keytoolArguments.add("-validity");
6542          keytoolArguments.add(String.valueOf(daysValid));
6543
6544          if (validityStartTime != null)
6545          {
6546            keytoolArguments.add("-startdate");
6547            keytoolArguments.add(formatValidityStartTime(validityStartTime));
6548          }
6549
6550          addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
6551               extendedKeyUsage, sanValues, ianValues, genericExtensions);
6552
6553          displayKeytoolCommand(keytoolArguments);
6554        }
6555
6556
6557        // Generate the self-signed certificate.
6558        final long notBefore;
6559        if (validityStartTime == null)
6560        {
6561          notBefore = System.currentTimeMillis();
6562        }
6563        else
6564        {
6565          notBefore = validityStartTime.getTime();
6566        }
6567
6568        final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
6569
6570        final X509Certificate certificate;
6571        final Certificate[] chain;
6572        try
6573        {
6574          certificate = X509Certificate.generateSelfSignedCertificate(
6575               signatureAlgorithmIdentifier, keyPair, subjectDN, notBefore,
6576               notAfter, extensions);
6577          chain = new Certificate[] { certificate.toCertificate() };
6578        }
6579        catch (final Exception e)
6580        {
6581          Debug.debugException(e);
6582          wrapErr(0, WRAP_COLUMN,
6583               ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CERT.get());
6584          e.printStackTrace(getErr());
6585          return ResultCode.LOCAL_ERROR;
6586        }
6587
6588
6589        // Update the keystore with the new certificate.
6590        try
6591        {
6592          keystore.setKeyEntry(alias, keyPair.getPrivate(), privateKeyPassword,
6593               chain);
6594          writeKeystore(keystore, keystorePath, keystorePassword);
6595        }
6596        catch (final Exception e)
6597        {
6598          Debug.debugException(e);
6599          wrapErr(0, WRAP_COLUMN,
6600               ERR_MANAGE_CERTS_GEN_CERT_ERROR_UPDATING_KEYSTORE.get());
6601          e.printStackTrace(getErr());
6602          return ResultCode.LOCAL_ERROR;
6603        }
6604
6605
6606        // Display the certificate we just generated to the end user.
6607        out();
6608        wrapOut(0, WRAP_COLUMN,
6609             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_SELF_CERT.
6610                  get());
6611        printCertificate(certificate, "", false);
6612        return ResultCode.SUCCESS;
6613      }
6614      else
6615      {
6616        // Build the keytool command used to generate the certificate signing
6617        // request.
6618        Validator.ensureTrue(isGenerateCSR);
6619        if (displayKeytoolCommand)
6620        {
6621          final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6622          keytoolArguments.add("-certreq");
6623          keytoolArguments.add("-keystore");
6624          keytoolArguments.add(keystorePath.getAbsolutePath());
6625          keytoolArguments.add("-storetype");
6626          keytoolArguments.add(keystoreType);
6627          keytoolArguments.add("-storepass");
6628          keytoolArguments.add("*****REDACTED*****");
6629          keytoolArguments.add("-keypass");
6630          keytoolArguments.add("*****REDACTED*****");
6631          keytoolArguments.add("-alias");
6632          keytoolArguments.add(alias);
6633          keytoolArguments.add("-dname");
6634          keytoolArguments.add(subjectDN.toString());
6635          keytoolArguments.add("-sigalg");
6636          keytoolArguments.add(signatureAlgorithmName);
6637
6638          addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
6639               extendedKeyUsage, sanValues, ianValues, genericExtensions);
6640
6641          if (outputFile != null)
6642          {
6643            keytoolArguments.add("-file");
6644            keytoolArguments.add(outputFile.getAbsolutePath());
6645          }
6646
6647          displayKeytoolCommand(keytoolArguments);
6648        }
6649
6650
6651        // Generate the certificate signing request.
6652        final PKCS10CertificateSigningRequest certificateSigningRequest;
6653        try
6654        {
6655          certificateSigningRequest = PKCS10CertificateSigningRequest.
6656               generateCertificateSigningRequest(signatureAlgorithmIdentifier,
6657                    keyPair, subjectDN, extensions);
6658        }
6659        catch (final Exception e)
6660        {
6661          Debug.debugException(e);
6662          wrapErr(0, WRAP_COLUMN,
6663               ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CSR.get());
6664          e.printStackTrace(getErr());
6665          return ResultCode.LOCAL_ERROR;
6666        }
6667
6668
6669        // Write the generated certificate signing request to the appropriate
6670        // location.
6671        try
6672        {
6673          final PrintStream ps;
6674          if (outputFile == null)
6675          {
6676            ps = getOut();
6677          }
6678          else
6679          {
6680            ps = new PrintStream(outputFile);
6681          }
6682
6683          if (outputPEM)
6684          {
6685            writePEMCertificateSigningRequest(ps,
6686                 certificateSigningRequest.
6687                      getPKCS10CertificateSigningRequestBytes());
6688          }
6689          else
6690          {
6691            ps.write(certificateSigningRequest.
6692                 getPKCS10CertificateSigningRequestBytes());
6693          }
6694
6695          if (outputFile != null)
6696          {
6697            ps.close();
6698          }
6699        }
6700        catch (final Exception e)
6701        {
6702          Debug.debugException(e);
6703          wrapErr(0, WRAP_COLUMN,
6704               ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CSR.get());
6705          e.printStackTrace(getErr());
6706          return ResultCode.LOCAL_ERROR;
6707        }
6708
6709
6710        // If the certificate signing request was written to an output file,
6711        // then let the user know that it was successful.  If it was written to
6712        // standard output, then we don't need to tell them because they'll be
6713        // able to see it.
6714        if (outputFile != null)
6715        {
6716          out();
6717          wrapOut(0, WRAP_COLUMN,
6718               INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_CSR.get(
6719                    outputFile.getAbsolutePath()));
6720        }
6721
6722        return ResultCode.SUCCESS;
6723      }
6724    }
6725
6726
6727    // If we've gotten here, then we know we're not replacing an existing
6728    // certificate.  Perform any remaining argument assignment and validation.
6729    if ((subjectDN == null) && (! isSignCSR))
6730    {
6731      wrapErr(0, WRAP_COLUMN,
6732           ERR_MANAGE_CERTS_GEN_CERT_NO_SUBJECT_DN_WITHOUT_REPLACE.get());
6733      return ResultCode.PARAM_ERROR;
6734    }
6735
6736    if (keyAlgorithmIdentifier == null)
6737    {
6738      keyAlgorithmIdentifier = PublicKeyAlgorithmIdentifier.RSA;
6739      keyAlgorithmName = keyAlgorithmIdentifier.getName();
6740    }
6741
6742    if (keySizeBits == null)
6743    {
6744      keySizeBits = 2048;
6745    }
6746
6747    if ((signatureAlgorithmIdentifier == null) && (! isSignCSR))
6748    {
6749      signatureAlgorithmIdentifier =
6750           SignatureAlgorithmIdentifier.SHA_256_WITH_RSA;
6751      signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
6752    }
6753
6754
6755    // If we're going to generate a self-signed certificate or a certificate
6756    // signing request, then we first need to generate a key pair.  Put together
6757    // the appropriate set of keytool arguments and then generate a self-signed
6758    // certificate.
6759    if (isGenerateCertificate || isGenerateCSR)
6760    {
6761      // Make sure that the specified alias is not already in use in the
6762      // keystore.
6763      if (hasKeyAlias(keystore, alias) || hasCertificateAlias(keystore, alias))
6764      {
6765        wrapErr(0, WRAP_COLUMN,
6766             ERR_MANAGE_CERTS_GEN_CERT_ALIAS_EXISTS_WITHOUT_REPLACE.get(alias));
6767        return ResultCode.PARAM_ERROR;
6768      }
6769
6770
6771      if (displayKeytoolCommand)
6772      {
6773        final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6774        keytoolArguments.add("-genkeypair");
6775        keytoolArguments.add("-keystore");
6776        keytoolArguments.add(keystorePath.getAbsolutePath());
6777        keytoolArguments.add("-storetype");
6778        keytoolArguments.add(keystoreType);
6779        keytoolArguments.add("-storepass");
6780        keytoolArguments.add("*****REDACTED*****");
6781        keytoolArguments.add("-keypass");
6782        keytoolArguments.add("*****REDACTED*****");
6783        keytoolArguments.add("-alias");
6784        keytoolArguments.add(alias);
6785        keytoolArguments.add("-dname");
6786        keytoolArguments.add(subjectDN.toString());
6787        keytoolArguments.add("-keyalg");
6788        keytoolArguments.add(keyAlgorithmName);
6789        keytoolArguments.add("-keysize");
6790        keytoolArguments.add(String.valueOf(keySizeBits));
6791        keytoolArguments.add("-sigalg");
6792        keytoolArguments.add(signatureAlgorithmName);
6793        keytoolArguments.add("-validity");
6794        keytoolArguments.add(String.valueOf(daysValid));
6795
6796        if (validityStartTime != null)
6797        {
6798          keytoolArguments.add("-startdate");
6799          keytoolArguments.add(formatValidityStartTime(validityStartTime));
6800        }
6801
6802        addExtensionArguments(keytoolArguments, basicConstraints,
6803             keyUsage, extendedKeyUsage, sanValues, ianValues,
6804             genericExtensions);
6805
6806        displayKeytoolCommand(keytoolArguments);
6807      }
6808
6809
6810      // Generate the self-signed certificate.
6811      final long notBefore;
6812      if (validityStartTime == null)
6813      {
6814        notBefore = System.currentTimeMillis();
6815      }
6816      else
6817      {
6818        notBefore = validityStartTime.getTime();
6819      }
6820
6821      final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
6822
6823      final X509CertificateExtension[] extensions =
6824           new X509CertificateExtension[extensionList.size()];
6825      extensionList.toArray(extensions);
6826
6827      final Certificate[] chain;
6828      final KeyPair keyPair;
6829      final X509Certificate certificate;
6830      try
6831      {
6832        final ObjectPair<X509Certificate,KeyPair> p =
6833             X509Certificate.generateSelfSignedCertificate(
6834                  signatureAlgorithmIdentifier, keyAlgorithmIdentifier,
6835                  keySizeBits, subjectDN, notBefore, notAfter, extensions);
6836        certificate = p.getFirst();
6837        chain = new Certificate[] { certificate.toCertificate() };
6838        keyPair = p.getSecond();
6839      }
6840      catch (final Exception e)
6841      {
6842        Debug.debugException(e);
6843        wrapErr(0, WRAP_COLUMN,
6844             ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CERT.get());
6845        e.printStackTrace(getErr());
6846        return ResultCode.LOCAL_ERROR;
6847      }
6848
6849
6850      // Update the keystore with the new certificate.
6851      try
6852      {
6853        keystore.setKeyEntry(alias, keyPair.getPrivate(), privateKeyPassword,
6854             chain);
6855        writeKeystore(keystore, keystorePath, keystorePassword);
6856      }
6857      catch (final Exception e)
6858      {
6859        Debug.debugException(e);
6860        wrapErr(0, WRAP_COLUMN,
6861             ERR_MANAGE_CERTS_GEN_CERT_ERROR_UPDATING_KEYSTORE.get());
6862        e.printStackTrace(getErr());
6863        return ResultCode.LOCAL_ERROR;
6864      }
6865
6866      if (isNewKeystore)
6867      {
6868        out();
6869        wrapOut(0, WRAP_COLUMN,
6870             INFO_MANAGE_CERTS_GEN_CERT_CERT_CREATED_KEYSTORE.get(
6871                  getUserFriendlyKeystoreType(keystoreType)));
6872      }
6873
6874
6875      // If we're just generating a self-signed certificate, then display the
6876      // certificate that we generated.
6877      if (isGenerateCertificate)
6878      {
6879        out();
6880        wrapOut(0, WRAP_COLUMN,
6881             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_SELF_CERT.get());
6882        printCertificate(certificate, "", false);
6883
6884        return ResultCode.SUCCESS;
6885      }
6886
6887
6888      // If we're generating a certificate signing request, then put together
6889      // the appropriate set of arguments for that.
6890      Validator.ensureTrue(isGenerateCSR);
6891      out();
6892      wrapOut(0, WRAP_COLUMN,
6893           INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_KEYPAIR.get());
6894
6895      if (displayKeytoolCommand)
6896      {
6897        final ArrayList<String> keytoolArguments = new ArrayList<>(30);
6898        keytoolArguments.add("-certreq");
6899        keytoolArguments.add("-keystore");
6900        keytoolArguments.add(keystorePath.getAbsolutePath());
6901        keytoolArguments.add("-storetype");
6902        keytoolArguments.add(keystoreType);
6903        keytoolArguments.add("-storepass");
6904        keytoolArguments.add("*****REDACTED*****");
6905        keytoolArguments.add("-keypass");
6906        keytoolArguments.add("*****REDACTED*****");
6907        keytoolArguments.add("-alias");
6908        keytoolArguments.add(alias);
6909        keytoolArguments.add("-dname");
6910        keytoolArguments.add(subjectDN.toString());
6911        keytoolArguments.add("-sigalg");
6912        keytoolArguments.add(signatureAlgorithmName);
6913
6914        addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
6915             extendedKeyUsage, sanValues, ianValues, genericExtensions);
6916
6917        if (outputFile != null)
6918        {
6919          keytoolArguments.add("-file");
6920          keytoolArguments.add(outputFile.getAbsolutePath());
6921        }
6922
6923        displayKeytoolCommand(keytoolArguments);
6924      }
6925
6926
6927      // Generate the certificate signing request.
6928      final PKCS10CertificateSigningRequest certificateSigningRequest;
6929      try
6930      {
6931        certificateSigningRequest = PKCS10CertificateSigningRequest.
6932             generateCertificateSigningRequest(signatureAlgorithmIdentifier,
6933                  keyPair, subjectDN, extensions);
6934      }
6935      catch (final Exception e)
6936      {
6937        Debug.debugException(e);
6938        wrapErr(0, WRAP_COLUMN,
6939             ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CSR.get());
6940        e.printStackTrace(getErr());
6941        return ResultCode.LOCAL_ERROR;
6942      }
6943
6944
6945      // Write the generated certificate signing request to the appropriate
6946      // location.
6947      try
6948      {
6949        final PrintStream ps;
6950        if (outputFile == null)
6951        {
6952          ps = getOut();
6953        }
6954        else
6955        {
6956          ps = new PrintStream(outputFile);
6957        }
6958
6959        if (outputPEM)
6960        {
6961          writePEMCertificateSigningRequest(ps,
6962               certificateSigningRequest.
6963                    getPKCS10CertificateSigningRequestBytes());
6964        }
6965        else
6966        {
6967          ps.write(certificateSigningRequest.
6968               getPKCS10CertificateSigningRequestBytes());
6969        }
6970
6971        if (outputFile != null)
6972        {
6973          ps.close();
6974        }
6975      }
6976      catch (final Exception e)
6977      {
6978        Debug.debugException(e);
6979        wrapErr(0, WRAP_COLUMN,
6980             ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CSR.get());
6981        e.printStackTrace(getErr());
6982        return ResultCode.LOCAL_ERROR;
6983      }
6984
6985
6986      // If the certificate signing request was written to an output file,
6987      // then let the user know that it was successful.  If it was written to
6988      // standard output, then we don't need to tell them because they'll be
6989      // able to see it.
6990      if (outputFile != null)
6991      {
6992        out();
6993        wrapOut(0, WRAP_COLUMN,
6994             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_CSR.get(
6995                  outputFile.getAbsolutePath()));
6996      }
6997
6998      return ResultCode.SUCCESS;
6999    }
7000
7001
7002    // If we've gotten here, then we should be signing a certificate signing
7003    // request.  Make sure that the keystore already has a private key entry
7004    // with the specified alias.
7005    Validator.ensureTrue(isSignCSR);
7006    if (! hasKeyAlias(keystore, alias))
7007    {
7008      if (hasCertificateAlias(keystore, alias))
7009      {
7010        wrapErr(0, WRAP_COLUMN,
7011             ERR_MANAGE_CERTS_GEN_CERT_SIGN_ALIAS_IS_CERT.get(alias,
7012                  keystorePath.getAbsolutePath()));
7013        return ResultCode.PARAM_ERROR;
7014      }
7015      else
7016      {
7017        wrapErr(0, WRAP_COLUMN,
7018             ERR_MANAGE_CERTS_GEN_CERT_SIGN_NO_SUCH_ALIAS.get(alias,
7019                  keystorePath.getAbsolutePath()));
7020        return ResultCode.PARAM_ERROR;
7021      }
7022    }
7023
7024
7025    // Get the signing certificate and its key pair.
7026    final PrivateKey issuerPrivateKey;
7027    final X509Certificate issuerCertificate;
7028    try
7029    {
7030      final Certificate[] chain = keystore.getCertificateChain(alias);
7031      issuerCertificate = new X509Certificate(chain[0].getEncoded());
7032
7033      issuerPrivateKey =
7034           (PrivateKey) keystore.getKey(alias, privateKeyPassword);
7035    }
7036    catch (final Exception e)
7037    {
7038      Debug.debugException(e);
7039      wrapErr(0, WRAP_COLUMN,
7040           ERR_MANAGE_CERTS_GEN_CERT_SIGN_CANNOT_GET_SIGNING_CERT.get(alias));
7041      e.printStackTrace(getErr());
7042      return ResultCode.LOCAL_ERROR;
7043    }
7044
7045
7046    // Make sure that we can decode the certificate signing request.
7047    final PKCS10CertificateSigningRequest csr;
7048    try
7049    {
7050      csr = readCertificateSigningRequestFromFile(inputFile);
7051    }
7052    catch (final LDAPException le)
7053    {
7054      Debug.debugException(le);
7055      wrapErr(0, WRAP_COLUMN, le.getMessage());
7056      return le.getResultCode();
7057    }
7058
7059
7060    // Make sure that we can verify the certificate signing request's signature.
7061    try
7062    {
7063      csr.verifySignature();
7064    }
7065    catch (final CertException ce)
7066    {
7067      Debug.debugException(ce);
7068      wrapErr(0, WRAP_COLUMN, ce.getMessage());
7069      return ResultCode.PARAM_ERROR;
7070    }
7071
7072
7073    // Prompt about whether to sign the request, if appropriate.
7074    if (! noPrompt)
7075    {
7076      out();
7077      wrapOut(0, WRAP_COLUMN,
7078           INFO_MANAGE_CERTS_GEN_CERT_SIGN_CONFIRM.get());
7079      out();
7080      printCertificateSigningRequest(csr, false, "");
7081      out();
7082
7083      try
7084      {
7085        if (! promptForYesNo(
7086             INFO_MANAGE_CERTS_GEN_CERT_PROMPT_SIGN.get()))
7087        {
7088          wrapErr(0, WRAP_COLUMN,
7089               ERR_MANAGE_CERTS_GEN_CERT_SIGN_CANCELED.get());
7090          return ResultCode.USER_CANCELED;
7091        }
7092      }
7093      catch (final LDAPException le)
7094      {
7095        Debug.debugException(le);
7096        err();
7097        wrapErr(0, WRAP_COLUMN, le.getMessage());
7098        return le.getResultCode();
7099      }
7100    }
7101
7102
7103    // Read the certificate signing request and see if we need to take values
7104    // from it.
7105    if ((subjectDN == null) || (signatureAlgorithmIdentifier == null) ||
7106        includeRequestedExtensions)
7107    {
7108      if (subjectDN == null)
7109      {
7110        subjectDN = csr.getSubjectDN();
7111      }
7112
7113      if (signatureAlgorithmIdentifier == null)
7114      {
7115        signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forOID(
7116             csr.getSignatureAlgorithmOID());
7117        if (signatureAlgorithmIdentifier == null)
7118        {
7119          wrapErr(0, WRAP_COLUMN,
7120               ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG_IN_CSR.get(
7121                    csr.getSignatureAlgorithmOID()));
7122          return ResultCode.PARAM_ERROR;
7123        }
7124        else
7125        {
7126          signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
7127        }
7128      }
7129
7130      if (includeRequestedExtensions)
7131      {
7132        for (final X509CertificateExtension extension : csr.getExtensions())
7133        {
7134          if ((extension instanceof AuthorityKeyIdentifierExtension) ||
7135              (extension instanceof IssuerAlternativeNameExtension))
7136          {
7137            // This extension applies to the issuer.  We won't include this in
7138            // the set of inherited extensions.
7139          }
7140          else if (extension instanceof SubjectKeyIdentifierExtension)
7141          {
7142            // The generated certificate will automatically include a subject
7143            // key identifier extension, so we don't need to include it.
7144          }
7145          else if (extension instanceof BasicConstraintsExtension)
7146          {
7147            // Don't override a value already provided on the command line.
7148            if (basicConstraints == null)
7149            {
7150              basicConstraints = (BasicConstraintsExtension) extension;
7151              extensionList.add(basicConstraints);
7152            }
7153          }
7154          else if (extension instanceof ExtendedKeyUsageExtension)
7155          {
7156            // Don't override a value already provided on the command line.
7157            if (extendedKeyUsage == null)
7158            {
7159              extendedKeyUsage = (ExtendedKeyUsageExtension) extension;
7160              extensionList.add(extendedKeyUsage);
7161            }
7162          }
7163          else if (extension instanceof KeyUsageExtension)
7164          {
7165            // Don't override a value already provided on the command line.
7166            if (keyUsage == null)
7167            {
7168              keyUsage = (KeyUsageExtension) extension;
7169              extensionList.add(keyUsage);
7170            }
7171          }
7172          else if (extension instanceof SubjectAlternativeNameExtension)
7173          {
7174            // Although we could merge values, it's safer to not do that if any
7175            // subject alternative name values were provided on the command
7176            // line.
7177            if (sanValues.isEmpty())
7178            {
7179              final SubjectAlternativeNameExtension e =
7180                   (SubjectAlternativeNameExtension) extension;
7181              for (final String dnsName : e.getDNSNames())
7182              {
7183                sanBuilder.addDNSName(dnsName);
7184                sanValues.add("DNS:" + dnsName);
7185              }
7186
7187              for (final InetAddress ipAddress : e.getIPAddresses())
7188              {
7189                sanBuilder.addIPAddress(ipAddress);
7190                sanValues.add("IP:" + ipAddress.getHostAddress());
7191              }
7192
7193              for (final String emailAddress : e.getRFC822Names())
7194              {
7195                sanBuilder.addRFC822Name(emailAddress);
7196                sanValues.add("EMAIL:" + emailAddress);
7197              }
7198
7199              for (final String uri : e.getUniformResourceIdentifiers())
7200              {
7201                sanBuilder.addUniformResourceIdentifier(uri);
7202                sanValues.add("URI:" + uri);
7203              }
7204
7205              for (final OID oid : e.getRegisteredIDs())
7206              {
7207                sanBuilder.addRegisteredID(oid);
7208                sanValues.add("OID:" + oid.toString());
7209              }
7210
7211              try
7212              {
7213                extensionList.add(
7214                     new SubjectAlternativeNameExtension(false,
7215                          sanBuilder.build()));
7216              }
7217              catch (final Exception ex)
7218              {
7219                // This should never happen.
7220                Debug.debugException(ex);
7221                throw new RuntimeException(ex);
7222              }
7223            }
7224          }
7225          else
7226          {
7227            genericExtensions.add(extension);
7228            extensionList.add(extension);
7229          }
7230        }
7231      }
7232    }
7233
7234
7235    // Generate the keytool arguments to use to sign the requested certificate.
7236    final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7237    keytoolArguments.add("-gencert");
7238    keytoolArguments.add("-keystore");
7239    keytoolArguments.add(keystorePath.getAbsolutePath());
7240    keytoolArguments.add("-storetype");
7241    keytoolArguments.add(keystoreType);
7242    keytoolArguments.add("-storepass");
7243    keytoolArguments.add("*****REDACTED*****");
7244    keytoolArguments.add("-keypass");
7245    keytoolArguments.add("*****REDACTED*****");
7246    keytoolArguments.add("-alias");
7247    keytoolArguments.add(alias);
7248    keytoolArguments.add("-dname");
7249    keytoolArguments.add(subjectDN.toString());
7250    keytoolArguments.add("-sigalg");
7251    keytoolArguments.add(signatureAlgorithmName);
7252    keytoolArguments.add("-validity");
7253    keytoolArguments.add(String.valueOf(daysValid));
7254
7255    if (validityStartTime != null)
7256    {
7257      keytoolArguments.add("-startdate");
7258      keytoolArguments.add(formatValidityStartTime(validityStartTime));
7259    }
7260
7261    addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
7262         extendedKeyUsage, sanValues, ianValues, genericExtensions);
7263
7264    keytoolArguments.add("-infile");
7265    keytoolArguments.add(inputFile.getAbsolutePath());
7266
7267    if (outputFile != null)
7268    {
7269      keytoolArguments.add("-outfile");
7270      keytoolArguments.add(outputFile.getAbsolutePath());
7271    }
7272
7273    if (outputPEM)
7274    {
7275      keytoolArguments.add("-rfc");
7276    }
7277
7278    if (displayKeytoolCommand)
7279    {
7280      displayKeytoolCommand(keytoolArguments);
7281    }
7282
7283
7284    // Generate the signed certificate.
7285    final long notBefore;
7286    if (validityStartTime == null)
7287    {
7288      notBefore = System.currentTimeMillis();
7289    }
7290    else
7291    {
7292      notBefore = validityStartTime.getTime();
7293    }
7294
7295    final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
7296
7297    final X509CertificateExtension[] extensions =
7298         new X509CertificateExtension[extensionList.size()];
7299    extensionList.toArray(extensions);
7300
7301    final X509Certificate signedCertificate;
7302    try
7303    {
7304      signedCertificate = X509Certificate.generateIssuerSignedCertificate(
7305           signatureAlgorithmIdentifier, issuerCertificate, issuerPrivateKey,
7306           csr.getPublicKeyAlgorithmOID(),
7307           csr.getPublicKeyAlgorithmParameters(), csr.getEncodedPublicKey(),
7308           csr.getDecodedPublicKey(), subjectDN, notBefore, notAfter,
7309           extensions);
7310    }
7311    catch (final Exception e)
7312    {
7313      Debug.debugException(e);
7314      wrapErr(0, WRAP_COLUMN,
7315           ERR_MANAGE_CERTS_GEN_CERT_ERROR_SIGNING_CERT.get());
7316      e.printStackTrace(getErr());
7317      return ResultCode.LOCAL_ERROR;
7318    }
7319
7320
7321    // Write the signed certificate signing request to the appropriate location.
7322    try
7323    {
7324      final PrintStream ps;
7325      if (outputFile == null)
7326      {
7327        ps = getOut();
7328      }
7329      else
7330      {
7331        ps = new PrintStream(outputFile);
7332      }
7333
7334      if (outputPEM)
7335      {
7336        writePEMCertificate(ps, signedCertificate.getX509CertificateBytes());
7337      }
7338      else
7339      {
7340        ps.write(signedCertificate.getX509CertificateBytes());
7341      }
7342
7343      if (outputFile != null)
7344      {
7345        ps.close();
7346      }
7347    }
7348    catch (final Exception e)
7349    {
7350      Debug.debugException(e);
7351      wrapErr(0, WRAP_COLUMN,
7352           ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_SIGNED_CERT.get());
7353      e.printStackTrace(getErr());
7354      return ResultCode.LOCAL_ERROR;
7355    }
7356
7357
7358    // If the certificate signing request was written to an output file,
7359    // then let the user know that it was successful.  If it was written to
7360    // standard output, then we don't need to tell them because they'll be
7361    // able to see it.
7362    if (outputFile != null)
7363    {
7364      out();
7365      wrapOut(0, WRAP_COLUMN,
7366           INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_SIGNED_CERT.get(
7367                outputFile.getAbsolutePath()));
7368    }
7369
7370    return ResultCode.SUCCESS;
7371  }
7372
7373
7374
7375  /**
7376   * Performs the necessary processing for the change-certificate-alias
7377   * subcommand.
7378   *
7379   * @return  A result code that indicates whether the processing completed
7380   *          successfully.
7381   */
7382  private ResultCode doChangeCertificateAlias()
7383  {
7384    // Get the values of a number of configured arguments.
7385    final StringArgument currentAliasArgument =
7386         subCommandParser.getStringArgument("current-alias");
7387    final String currentAlias = currentAliasArgument.getValue();
7388
7389    final StringArgument newAliasArgument =
7390         subCommandParser.getStringArgument("new-alias");
7391    final String newAlias = newAliasArgument.getValue();
7392
7393    final String keystoreType;
7394    final File keystorePath = getKeystorePath();
7395    try
7396    {
7397      keystoreType = inferKeystoreType(keystorePath);
7398    }
7399    catch (final LDAPException le)
7400    {
7401      Debug.debugException(le);
7402      wrapErr(0, WRAP_COLUMN, le.getMessage());
7403      return le.getResultCode();
7404    }
7405
7406    final char[] keystorePassword;
7407    try
7408    {
7409      keystorePassword = getKeystorePassword(keystorePath);
7410    }
7411    catch (final LDAPException le)
7412    {
7413      Debug.debugException(le);
7414      wrapErr(0, WRAP_COLUMN, le.getMessage());
7415      return le.getResultCode();
7416    }
7417
7418
7419    // Get the keystore.
7420    final KeyStore keystore;
7421    try
7422    {
7423      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7424    }
7425    catch (final LDAPException le)
7426    {
7427      Debug.debugException(le);
7428      wrapErr(0, WRAP_COLUMN, le.getMessage());
7429      return le.getResultCode();
7430    }
7431
7432
7433    // See if we need to use a private key password that is different from the
7434    // keystore password.
7435    final char[] privateKeyPassword;
7436    try
7437    {
7438      privateKeyPassword =
7439           getPrivateKeyPassword(keystore, currentAlias, keystorePassword);
7440    }
7441    catch (final LDAPException le)
7442    {
7443      Debug.debugException(le);
7444      wrapErr(0, WRAP_COLUMN, le.getMessage());
7445      return le.getResultCode();
7446    }
7447
7448
7449    // Make sure that the keystore has an existing entry with the current alias.
7450    // It must be either a certificate entry or a private key entry.
7451    final Certificate existingCertificate;
7452    final Certificate[] existingCertificateChain;
7453    final PrivateKey existingPrivateKey;
7454    try
7455    {
7456      if (hasCertificateAlias(keystore, currentAlias))
7457      {
7458        existingCertificate = keystore.getCertificate(currentAlias);
7459        existingCertificateChain = null;
7460        existingPrivateKey = null;
7461      }
7462      else if (hasKeyAlias(keystore, currentAlias))
7463      {
7464        existingCertificateChain = keystore.getCertificateChain(currentAlias);
7465        existingPrivateKey =
7466             (PrivateKey) keystore.getKey(currentAlias, privateKeyPassword);
7467        existingCertificate = null;
7468      }
7469      else
7470      {
7471        wrapErr(0, WRAP_COLUMN,
7472             ERR_MANAGE_CERTS_CHANGE_ALIAS_NO_SUCH_ALIAS.get(currentAlias));
7473        return ResultCode.PARAM_ERROR;
7474      }
7475    }
7476    catch (final Exception e)
7477    {
7478      Debug.debugException(e);
7479      wrapErr(0, WRAP_COLUMN,
7480           ERR_MANAGE_CERTS_CHANGE_ALIAS_CANNOT_GET_EXISTING_ENTRY.get(
7481                currentAlias));
7482      e.printStackTrace(getErr());
7483      return ResultCode.LOCAL_ERROR;
7484    }
7485
7486
7487    // Make sure that the keystore does not have an entry with the new alias.
7488    if (hasCertificateAlias(keystore, newAlias) ||
7489         hasKeyAlias(keystore, newAlias))
7490    {
7491      wrapErr(0, WRAP_COLUMN,
7492           ERR_MANAGE_CERTS_CHANGE_ALIAS_NEW_ALIAS_IN_USE.get(newAlias));
7493      return ResultCode.PARAM_ERROR;
7494    }
7495
7496
7497    // Generate the keytool arguments to use to change the certificate alias.
7498    final BooleanArgument displayKeytoolCommandArgument =
7499         subCommandParser.getBooleanArgument("display-keytool-command");
7500    if ((displayKeytoolCommandArgument != null) &&
7501          displayKeytoolCommandArgument.isPresent())
7502    {
7503      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7504      keytoolArguments.add("-changealias");
7505      keytoolArguments.add("-keystore");
7506      keytoolArguments.add(keystorePath.getAbsolutePath());
7507      keytoolArguments.add("-storetype");
7508      keytoolArguments.add(keystoreType);
7509      keytoolArguments.add("-storepass");
7510      keytoolArguments.add("*****REDACTED*****");
7511      keytoolArguments.add("-keypass");
7512      keytoolArguments.add("*****REDACTED*****");
7513      keytoolArguments.add("-alias");
7514      keytoolArguments.add(currentAlias);
7515      keytoolArguments.add("-destalias");
7516      keytoolArguments.add(newAlias);
7517
7518      displayKeytoolCommand(keytoolArguments);
7519    }
7520
7521
7522    // Update the keystore to remove the entry with the current alias and
7523    // re-write it with the new alias.
7524    try
7525    {
7526      keystore.deleteEntry(currentAlias);
7527      if (existingCertificate != null)
7528      {
7529        keystore.setCertificateEntry(newAlias, existingCertificate);
7530      }
7531      else
7532      {
7533        keystore.setKeyEntry(newAlias, existingPrivateKey,
7534             privateKeyPassword, existingCertificateChain);
7535      }
7536
7537      writeKeystore(keystore, keystorePath, keystorePassword);
7538    }
7539    catch (final Exception e)
7540    {
7541      Debug.debugException(e);
7542      wrapErr(0, WRAP_COLUMN,
7543           ERR_MANAGE_CERTS_CHANGE_ALIAS_CANNOT_UPDATE_KEYSTORE.get());
7544      e.printStackTrace(getErr());
7545      return ResultCode.LOCAL_ERROR;
7546    }
7547
7548    wrapOut(0, WRAP_COLUMN,
7549         INFO_MANAGE_CERTS_CHANGE_ALIAS_SUCCESSFUL.get(currentAlias,
7550              newAlias));
7551    return ResultCode.SUCCESS;
7552  }
7553
7554
7555
7556  /**
7557   * Performs the necessary processing for the change-keystore-password
7558   * subcommand.
7559   *
7560   * @return  A result code that indicates whether the processing completed
7561   *          successfully.
7562   */
7563  private ResultCode doChangeKeystorePassword()
7564  {
7565    // Get the values of a number of configured arguments.
7566    final String keystoreType;
7567    final File keystorePath = getKeystorePath();
7568    try
7569    {
7570      keystoreType = inferKeystoreType(keystorePath);
7571    }
7572    catch (final LDAPException le)
7573    {
7574      Debug.debugException(le);
7575      wrapErr(0, WRAP_COLUMN, le.getMessage());
7576      return le.getResultCode();
7577    }
7578
7579    final char[] currentKeystorePassword;
7580    try
7581    {
7582      currentKeystorePassword = getKeystorePassword(keystorePath, "current");
7583    }
7584    catch (final LDAPException le)
7585    {
7586      Debug.debugException(le);
7587      wrapErr(0, WRAP_COLUMN, le.getMessage());
7588      return le.getResultCode();
7589    }
7590
7591    final char[] newKeystorePassword;
7592    try
7593    {
7594      newKeystorePassword = getKeystorePassword(keystorePath, "new");
7595    }
7596    catch (final LDAPException le)
7597    {
7598      Debug.debugException(le);
7599      wrapErr(0, WRAP_COLUMN, le.getMessage());
7600      return le.getResultCode();
7601    }
7602
7603
7604    // Get the keystore.
7605    final KeyStore keystore;
7606    try
7607    {
7608      keystore = getKeystore(keystoreType, keystorePath,
7609           currentKeystorePassword);
7610    }
7611    catch (final LDAPException le)
7612    {
7613      Debug.debugException(le);
7614      wrapErr(0, WRAP_COLUMN, le.getMessage());
7615      return le.getResultCode();
7616    }
7617
7618
7619    // Generate the keytool arguments to use to change the keystore password.
7620    final BooleanArgument displayKeytoolCommandArgument =
7621         subCommandParser.getBooleanArgument("display-keytool-command");
7622    if ((displayKeytoolCommandArgument != null) &&
7623          displayKeytoolCommandArgument.isPresent())
7624    {
7625      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7626      keytoolArguments.add("-storepasswd");
7627      keytoolArguments.add("-keystore");
7628      keytoolArguments.add(keystorePath.getAbsolutePath());
7629      keytoolArguments.add("-storetype");
7630      keytoolArguments.add(keystoreType);
7631      keytoolArguments.add("-storepass");
7632      keytoolArguments.add("*****REDACTED*****");
7633      keytoolArguments.add("-new");
7634      keytoolArguments.add("*****REDACTED*****");
7635
7636      displayKeytoolCommand(keytoolArguments);
7637    }
7638
7639
7640    // Rewrite the keystore with the new password.
7641    try
7642    {
7643      writeKeystore(keystore, keystorePath, newKeystorePassword);
7644    }
7645    catch (final LDAPException le)
7646    {
7647      Debug.debugException(le);
7648      wrapErr(0, WRAP_COLUMN, le.getMessage());
7649      return le.getResultCode();
7650    }
7651
7652    wrapOut(0, WRAP_COLUMN,
7653         INFO_MANAGE_CERTS_CHANGE_KS_PW_SUCCESSFUL.get(
7654              keystorePath.getAbsolutePath()));
7655    return ResultCode.SUCCESS;
7656  }
7657
7658
7659
7660  /**
7661   * Performs the necessary processing for the change-private-key-password
7662   * subcommand.
7663   *
7664   * @return  A result code that indicates whether the processing completed
7665   *          successfully.
7666   */
7667  private ResultCode doChangePrivateKeyPassword()
7668  {
7669    // Get the values of a number of configured arguments.
7670    final StringArgument aliasArgument =
7671         subCommandParser.getStringArgument("alias");
7672    final String alias = aliasArgument.getValue();
7673
7674    final String keystoreType;
7675    final File keystorePath = getKeystorePath();
7676    try
7677    {
7678      keystoreType = inferKeystoreType(keystorePath);
7679    }
7680    catch (final LDAPException le)
7681    {
7682      Debug.debugException(le);
7683      wrapErr(0, WRAP_COLUMN, le.getMessage());
7684      return le.getResultCode();
7685    }
7686
7687    final char[] keystorePassword;
7688    try
7689    {
7690      keystorePassword = getKeystorePassword(keystorePath);
7691    }
7692    catch (final LDAPException le)
7693    {
7694      Debug.debugException(le);
7695      wrapErr(0, WRAP_COLUMN, le.getMessage());
7696      return le.getResultCode();
7697    }
7698
7699
7700    // Get the keystore.
7701    final KeyStore keystore;
7702    try
7703    {
7704      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7705    }
7706    catch (final LDAPException le)
7707    {
7708      Debug.debugException(le);
7709      wrapErr(0, WRAP_COLUMN, le.getMessage());
7710      return le.getResultCode();
7711    }
7712
7713
7714    // Make sure that the keystore has a key entry with the specified alias.
7715    if (hasCertificateAlias(keystore, alias))
7716    {
7717      wrapErr(0, WRAP_COLUMN,
7718           ERR_MANAGE_CERTS_CHANGE_PK_PW_ALIAS_IS_CERT.get(alias));
7719      return ResultCode.PARAM_ERROR;
7720    }
7721    else if (! hasKeyAlias(keystore, alias))
7722    {
7723      wrapErr(0, WRAP_COLUMN,
7724           ERR_MANAGE_CERTS_CHANGE_PK_PW_NO_SUCH_ALIAS.get(alias));
7725      return ResultCode.PARAM_ERROR;
7726    }
7727
7728
7729    // Get the current and new private key passwords.
7730    final char[] currentPrivateKeyPassword;
7731    try
7732    {
7733      currentPrivateKeyPassword =
7734           getPrivateKeyPassword(keystore, alias, "current", keystorePassword);
7735    }
7736    catch (final LDAPException le)
7737    {
7738      Debug.debugException(le);
7739      wrapErr(0, WRAP_COLUMN, le.getMessage());
7740      return le.getResultCode();
7741    }
7742
7743    final char[] newPrivateKeyPassword;
7744    try
7745    {
7746      newPrivateKeyPassword =
7747           getPrivateKeyPassword(keystore, alias, "new", keystorePassword);
7748    }
7749    catch (final LDAPException le)
7750    {
7751      Debug.debugException(le);
7752      wrapErr(0, WRAP_COLUMN, le.getMessage());
7753      return le.getResultCode();
7754    }
7755
7756
7757    // Generate the keytool arguments to use to change the private key.
7758    final BooleanArgument displayKeytoolCommandArgument =
7759         subCommandParser.getBooleanArgument("display-keytool-command");
7760    if ((displayKeytoolCommandArgument != null) &&
7761          displayKeytoolCommandArgument.isPresent())
7762    {
7763      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7764      keytoolArguments.add("-keypasswd");
7765      keytoolArguments.add("-keystore");
7766      keytoolArguments.add(keystorePath.getAbsolutePath());
7767      keytoolArguments.add("-storetype");
7768      keytoolArguments.add(keystoreType);
7769      keytoolArguments.add("-storepass");
7770      keytoolArguments.add("*****REDACTED*****");
7771      keytoolArguments.add("-alias");
7772      keytoolArguments.add(alias);
7773      keytoolArguments.add("-keypass");
7774      keytoolArguments.add("*****REDACTED*****");
7775      keytoolArguments.add("-new");
7776      keytoolArguments.add("*****REDACTED*****");
7777
7778      displayKeytoolCommand(keytoolArguments);
7779    }
7780
7781
7782    // Get the contents of the private key entry.
7783    final Certificate[] chain;
7784    final PrivateKey privateKey;
7785    try
7786    {
7787      chain = keystore.getCertificateChain(alias);
7788      privateKey =
7789           (PrivateKey) keystore.getKey(alias, currentPrivateKeyPassword);
7790    }
7791    catch (final UnrecoverableKeyException e)
7792    {
7793      Debug.debugException(e);
7794      wrapErr(0, WRAP_COLUMN,
7795           ERR_MANAGE_CERTS_CHANGE_PK_PW_WRONG_PK_PW.get(alias));
7796      return ResultCode.PARAM_ERROR;
7797    }
7798    catch (final Exception e)
7799    {
7800      Debug.debugException(e);
7801      wrapErr(0, WRAP_COLUMN,
7802           ERR_MANAGE_CERTS_CHANGE_PK_PW_CANNOT_GET_PK.get(alias));
7803      e.printStackTrace(getErr());
7804      return ResultCode.LOCAL_ERROR;
7805    }
7806
7807
7808    // Remove the existing key entry and re-add it with the new password.
7809    try
7810    {
7811      keystore.deleteEntry(alias);
7812      keystore.setKeyEntry(alias, privateKey, newPrivateKeyPassword, chain);
7813      writeKeystore(keystore, keystorePath, keystorePassword);
7814    }
7815    catch (final Exception e)
7816    {
7817      Debug.debugException(e);
7818      wrapErr(0, WRAP_COLUMN,
7819           ERR_MANAGE_CERTS_CHANGE_PK_PW_CANNOT_UPDATE_KS.get());
7820      e.printStackTrace(getErr());
7821      return ResultCode.LOCAL_ERROR;
7822    }
7823
7824    wrapOut(0, WRAP_COLUMN,
7825         INFO_MANAGE_CERTS_CHANGE_PK_PW_SUCCESSFUL.get(alias));
7826    return ResultCode.SUCCESS;
7827  }
7828
7829
7830
7831  /**
7832   * Performs the necessary processing for the trust-server-certificate
7833   * subcommand.
7834   *
7835   * @return  A result code that indicates whether the processing completed
7836   *          successfully.
7837   */
7838  private ResultCode doTrustServerCertificate()
7839  {
7840    // Get the values of a number of configured arguments.
7841    final StringArgument hostnameArgument =
7842         subCommandParser.getStringArgument("hostname");
7843    final String hostname = hostnameArgument.getValue();
7844
7845    final IntegerArgument portArgument =
7846         subCommandParser.getIntegerArgument("port");
7847    final int port = portArgument.getValue();
7848
7849    final String alias;
7850    final StringArgument aliasArgument =
7851         subCommandParser.getStringArgument("alias");
7852    if ((aliasArgument != null) && aliasArgument.isPresent())
7853    {
7854      alias = aliasArgument.getValue();
7855    }
7856    else
7857    {
7858      alias = hostname + ':' + port;
7859    }
7860
7861    final BooleanArgument useLDAPStartTLSArgument =
7862         subCommandParser.getBooleanArgument("use-ldap-start-tls");
7863    final boolean useLDAPStartTLS =
7864         ((useLDAPStartTLSArgument != null) &&
7865          useLDAPStartTLSArgument.isPresent());
7866
7867    final BooleanArgument issuersOnlyArgument =
7868         subCommandParser.getBooleanArgument("issuers-only");
7869    final boolean issuersOnly =
7870         ((issuersOnlyArgument != null) && issuersOnlyArgument.isPresent());
7871
7872    final BooleanArgument noPromptArgument =
7873         subCommandParser.getBooleanArgument("no-prompt");
7874    final boolean noPrompt =
7875         ((noPromptArgument != null) && noPromptArgument.isPresent());
7876
7877    final BooleanArgument verboseArgument =
7878         subCommandParser.getBooleanArgument("verbose");
7879    final boolean verbose =
7880         ((verboseArgument != null) && verboseArgument.isPresent());
7881
7882    final String keystoreType;
7883    final File keystorePath = getKeystorePath();
7884    final boolean isNewKeystore = (! keystorePath.exists());
7885    try
7886    {
7887      keystoreType = inferKeystoreType(keystorePath);
7888    }
7889    catch (final LDAPException le)
7890    {
7891      Debug.debugException(le);
7892      wrapErr(0, WRAP_COLUMN, le.getMessage());
7893      return le.getResultCode();
7894    }
7895
7896    final char[] keystorePassword;
7897    try
7898    {
7899      keystorePassword = getKeystorePassword(keystorePath);
7900    }
7901    catch (final LDAPException le)
7902    {
7903      Debug.debugException(le);
7904      wrapErr(0, WRAP_COLUMN, le.getMessage());
7905      return le.getResultCode();
7906    }
7907
7908
7909    // Get the keystore.
7910    final KeyStore keystore;
7911    try
7912    {
7913      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7914    }
7915    catch (final LDAPException le)
7916    {
7917      Debug.debugException(le);
7918      wrapErr(0, WRAP_COLUMN, le.getMessage());
7919      return le.getResultCode();
7920    }
7921
7922
7923    // Make sure that the specified alias is not already in use.
7924    if (hasCertificateAlias(keystore, alias) ||
7925         hasKeyAlias(keystore, alias))
7926    {
7927      wrapErr(0, WRAP_COLUMN,
7928           ERR_MANAGE_CERTS_TRUST_SERVER_ALIAS_IN_USE.get(alias));
7929      return ResultCode.PARAM_ERROR;
7930    }
7931
7932
7933    // Spawn a background thread to establish a connection and get the
7934    // certificate chain from the target server.
7935    final LinkedBlockingQueue<Object> responseQueue =
7936         new LinkedBlockingQueue<>(10);
7937    final ManageCertificatesServerCertificateCollector certificateCollector =
7938         new ManageCertificatesServerCertificateCollector(this, hostname, port,
7939              useLDAPStartTLS, verbose, responseQueue);
7940    certificateCollector.start();
7941
7942    Object responseObject =
7943         ERR_MANAGE_CERTS_TRUST_SERVER_NO_CERT_CHAIN_RECEIVED.get(
7944              hostname + ':' + port);
7945    try
7946    {
7947      responseObject = responseQueue.poll(90L, TimeUnit.SECONDS);
7948    }
7949    catch (final Exception e)
7950    {
7951      Debug.debugException(e);
7952    }
7953
7954    final X509Certificate[] chain;
7955    if (responseObject instanceof  X509Certificate[])
7956    {
7957      chain = (X509Certificate[]) responseObject;
7958    }
7959    else if (responseObject instanceof CertException)
7960    {
7961      // The error message will have already been recorded by the collector
7962      // thread, so we can just return a non-success result.
7963      return ResultCode.LOCAL_ERROR;
7964    }
7965    else
7966    {
7967      wrapErr(0, WRAP_COLUMN, String.valueOf(responseObject));
7968      return ResultCode.LOCAL_ERROR;
7969    }
7970
7971
7972    // If we should prompt the user about whether to trust the certificates,
7973    // then do so now.
7974    if (! noPrompt)
7975    {
7976      out();
7977      wrapOut(0, WRAP_COLUMN,
7978           INFO_MANAGE_CERTS_TRUST_SERVER_RETRIEVED_CHAIN.get(
7979                hostname + ':' + port));
7980
7981      boolean isFirst = true;
7982      for (final X509Certificate c : chain)
7983      {
7984        out();
7985
7986        if (isFirst)
7987        {
7988          isFirst = false;
7989          if (issuersOnly && (chain.length > 1))
7990          {
7991            wrapOut(0, WRAP_COLUMN,
7992                 INFO_MANAGE_CERTS_TRUST_SERVER_NOTE_OMITTED.get());
7993            out();
7994          }
7995        }
7996
7997        printCertificate(c, "", verbose);
7998      }
7999
8000      out();
8001
8002      try
8003      {
8004        if (! promptForYesNo(INFO_MANAGE_CERTS_TRUST_SERVER_PROMPT_TRUST.get()))
8005        {
8006          wrapErr(0, WRAP_COLUMN,
8007               ERR_MANAGE_CERTS_TRUST_SERVER_CHAIN_REJECTED.get());
8008          return ResultCode.USER_CANCELED;
8009        }
8010      }
8011      catch (final LDAPException le)
8012      {
8013        Debug.debugException(le);
8014        err();
8015        wrapErr(0, WRAP_COLUMN, le.getMessage());
8016        return le.getResultCode();
8017      }
8018    }
8019
8020
8021    // Add the certificates to the keystore.
8022    final LinkedHashMap<String,X509Certificate> certsByAlias =
8023         new LinkedHashMap<>(StaticUtils.computeMapCapacity(chain.length));
8024    for (int i=0; i < chain.length; i++)
8025    {
8026      if (i == 0)
8027      {
8028        if (issuersOnly && (chain.length > 1))
8029        {
8030          continue;
8031        }
8032
8033        certsByAlias.put(alias, chain[i]);
8034      }
8035      else if ((i == 1) && (chain.length == 2))
8036      {
8037        certsByAlias.put(alias + "-issuer", chain[i]);
8038      }
8039      else
8040      {
8041        certsByAlias.put(alias + "-issuer-" + i, chain[i]);
8042      }
8043    }
8044
8045    for (final Map.Entry<String,X509Certificate> e : certsByAlias.entrySet())
8046    {
8047      final String certAlias = e.getKey();
8048      final X509Certificate cert = e.getValue();
8049
8050      try
8051      {
8052        Validator.ensureFalse(
8053             (hasCertificateAlias(keystore, certAlias) ||
8054                  hasKeyAlias(keystore, certAlias)),
8055             "ERROR:  Alias '" + certAlias + "' is already in use in the " +
8056                  "keystore.");
8057        keystore.setCertificateEntry(certAlias, cert.toCertificate());
8058      }
8059      catch (final Exception ex)
8060      {
8061        Debug.debugException(ex);
8062        wrapErr(0, WRAP_COLUMN,
8063             ERR_MANAGE_CERTS_TRUST_SERVER_ERROR_ADDING_CERT_TO_KS.get(
8064                  cert.getSubjectDN()));
8065        ex.printStackTrace(getErr());
8066        return ResultCode.LOCAL_ERROR;
8067      }
8068    }
8069
8070
8071    // Save the updated keystore.
8072    try
8073    {
8074      writeKeystore(keystore, keystorePath, keystorePassword);
8075    }
8076    catch (final LDAPException le)
8077    {
8078      Debug.debugException(le);
8079      wrapErr(0, WRAP_COLUMN, le.getMessage());
8080      return le.getResultCode();
8081    }
8082
8083    if (isNewKeystore)
8084    {
8085      out();
8086      wrapOut(0, WRAP_COLUMN,
8087           INFO_MANAGE_CERTS_TRUST_SERVER_CERT_CREATED_KEYSTORE.get(
8088                getUserFriendlyKeystoreType(keystoreType)));
8089    }
8090
8091    out();
8092    if (certsByAlias.size() == 1)
8093    {
8094      wrapOut(0, WRAP_COLUMN,
8095           INFO_MANAGE_CERTS_TRUST_SERVER_ADDED_CERT_TO_KS.get());
8096    }
8097    else
8098    {
8099      wrapOut(0, WRAP_COLUMN,
8100           INFO_MANAGE_CERTS_TRUST_SERVER_ADDED_CERTS_TO_KS.get(
8101                certsByAlias.size()));
8102    }
8103
8104    return ResultCode.SUCCESS;
8105  }
8106
8107
8108
8109  /**
8110   * Performs the necessary processing for the check-certificate-usability
8111   * subcommand.
8112   *
8113   * @return  A result code that indicates whether the processing completed
8114   *          successfully.
8115   */
8116  private ResultCode doCheckCertificateUsability()
8117  {
8118    // Get the values of a number of configured arguments.
8119    final StringArgument aliasArgument =
8120         subCommandParser.getStringArgument("alias");
8121    final String alias = aliasArgument.getValue();
8122
8123    final String keystoreType;
8124    final File keystorePath = getKeystorePath();
8125    try
8126    {
8127      keystoreType = inferKeystoreType(keystorePath);
8128    }
8129    catch (final LDAPException le)
8130    {
8131      Debug.debugException(le);
8132      wrapErr(0, WRAP_COLUMN, le.getMessage());
8133      return le.getResultCode();
8134    }
8135
8136    final char[] keystorePassword;
8137    try
8138    {
8139      keystorePassword = getKeystorePassword(keystorePath);
8140    }
8141    catch (final LDAPException le)
8142    {
8143      Debug.debugException(le);
8144      wrapErr(0, WRAP_COLUMN, le.getMessage());
8145      return le.getResultCode();
8146    }
8147
8148
8149    // Get the keystore.
8150    final KeyStore keystore;
8151    try
8152    {
8153      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
8154    }
8155    catch (final LDAPException le)
8156    {
8157      Debug.debugException(le);
8158      wrapErr(0, WRAP_COLUMN, le.getMessage());
8159      return le.getResultCode();
8160    }
8161
8162
8163    // Make sure that the specified entry exists in the keystore and is
8164    // associated with a certificate chain and a private key.
8165    final X509Certificate[] chain;
8166    if (hasKeyAlias(keystore, alias))
8167    {
8168      try
8169      {
8170        final Certificate[] genericChain = keystore.getCertificateChain(alias);
8171        Validator.ensureTrue((genericChain.length > 0),
8172             "ERROR:  The keystore has a private key entry for alias '" +
8173                  alias + "', but the associated certificate chain is empty.");
8174
8175        chain = new X509Certificate[genericChain.length];
8176        for (int i=0; i < genericChain.length; i++)
8177        {
8178          chain[i] = new X509Certificate(genericChain[i].getEncoded());
8179        }
8180
8181        out();
8182        wrapOut(0, WRAP_COLUMN,
8183             INFO_MANAGE_CERTS_CHECK_USABILITY_GOT_CHAIN.get(alias));
8184
8185        for (final X509Certificate c : chain)
8186        {
8187          out();
8188          printCertificate(c, "", false);
8189        }
8190      }
8191      catch (final Exception e)
8192      {
8193        Debug.debugException(e);
8194        wrapErr(0, WRAP_COLUMN,
8195             ERR_MANAGE_CERTS_CHECK_USABILITY_CANNOT_GET_CHAIN.get(alias));
8196        e.printStackTrace(getErr());
8197        return ResultCode.LOCAL_ERROR;
8198      }
8199    }
8200    else if (hasCertificateAlias(keystore, alias))
8201    {
8202      wrapErr(0, WRAP_COLUMN,
8203           ERR_MANAGE_CERTS_CHECK_USABILITY_NO_PRIVATE_KEY.get(alias));
8204      return ResultCode.PARAM_ERROR;
8205    }
8206    else
8207    {
8208      wrapErr(0, WRAP_COLUMN,
8209           ERR_MANAGE_CERTS_CHECK_USABILITY_NO_SUCH_ALIAS.get(alias));
8210      return ResultCode.PARAM_ERROR;
8211    }
8212
8213
8214    // Check to see if the certificate is self-signed.  If so, then that's a
8215    // warning.  If not, then make sure that the chain is complete and that each
8216    // subsequent certificate is the issuer of the previous.
8217    int numWarnings = 0;
8218    int numErrors = 0;
8219    if (chain[0].isSelfSigned())
8220    {
8221      err();
8222      wrapErr(0, WRAP_COLUMN,
8223           WARN_MANAGE_CERTS_CHECK_USABILITY_CERT_IS_SELF_SIGNED.get(
8224                chain[0].getSubjectDN()));
8225      numWarnings++;
8226    }
8227    else if ((chain.length == 1) || (! chain[chain.length - 1].isSelfSigned()))
8228    {
8229      err();
8230      wrapErr(0, WRAP_COLUMN,
8231           ERR_MANAGE_CERTS_CHECK_USABILITY_END_OF_CHAIN_NOT_SELF_SIGNED.get(
8232                alias));
8233      numErrors++;
8234    }
8235    else
8236    {
8237      boolean chainError = false;
8238      final StringBuilder nonMatchReason = new StringBuilder();
8239      for (int i=1; i < chain.length; i++)
8240      {
8241        if (! chain[i].isIssuerFor(chain[i-1], nonMatchReason))
8242        {
8243          err();
8244          wrapErr(0, WRAP_COLUMN,
8245               ERR_MANAGE_CERTS_CHECK_USABILITY_CHAIN_ISSUER_MISMATCH.get(
8246                    alias, chain[i].getSubjectDN(), chain[i-1].getSubjectDN(),
8247                    nonMatchReason));
8248          numErrors++;
8249          chainError = true;
8250        }
8251      }
8252
8253      if (! chainError)
8254      {
8255        out();
8256        wrapOut(0, WRAP_COLUMN,
8257             INFO_MANAGE_CERTS_CHECK_USABILITY_CHAIN_COMPLETE.get());
8258      }
8259    }
8260
8261
8262    // Make sure that the signature is valid for each certificate in the
8263    // chain.  If any certificate has an invalid signature, then that's an
8264    // error.
8265    for (int i=0; i < chain.length; i++)
8266    {
8267      final X509Certificate c = chain[i];
8268
8269      try
8270      {
8271        if (c.isSelfSigned())
8272        {
8273          c.verifySignature(null);
8274        }
8275        else if ((i + 1) < chain.length)
8276        {
8277          c.verifySignature(chain[i+1]);
8278        }
8279
8280        out();
8281        wrapOut(0, WRAP_COLUMN,
8282             INFO_MANAGE_CERTS_CHECK_USABILITY_CERT_SIGNATURE_VALID.get(
8283                  c.getSubjectDN()));
8284      }
8285      catch (final CertException ce)
8286      {
8287        err();
8288        wrapErr(0, WRAP_COLUMN, ce.getMessage());
8289        numErrors++;
8290      }
8291    }
8292
8293
8294    // Check the validity window for each certificate in the chain.  If any of
8295    // them is expired or not yet valid, then that's an error.  If any of them
8296    // will expire in the near future, then that's a warning.
8297    final long currentTime = System.currentTimeMillis();
8298    final long thirtyDaysFromNow =
8299         currentTime + (30L * 24L * 60L * 60L * 1000L);
8300    for (int i=0; i < chain.length; i++)
8301    {
8302      final X509Certificate c = chain[i];
8303      if (c.getNotBeforeTime() > currentTime)
8304      {
8305        err();
8306        if (i == 0)
8307        {
8308          wrapErr(0, WRAP_COLUMN,
8309               ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NOT_YET_VALID.get(
8310                    c.getSubjectDN(), formatDateAndTime(c.getNotBeforeDate())));
8311        }
8312        else
8313        {
8314          wrapErr(0, WRAP_COLUMN,
8315               ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_NOT_YET_VALID.get(
8316                    c.getSubjectDN(), formatDateAndTime(c.getNotBeforeDate())));
8317        }
8318
8319        numErrors++;
8320      }
8321      else if (c.getNotAfterTime() < currentTime)
8322      {
8323        err();
8324        if (i == 0)
8325        {
8326          wrapErr(0, WRAP_COLUMN,
8327               ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_EXPIRED.get(
8328                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8329        }
8330        else
8331        {
8332          wrapErr(0, WRAP_COLUMN,
8333               ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_EXPIRED.get(
8334                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8335        }
8336
8337        numErrors++;
8338      }
8339      else if (c.getNotAfterTime() < thirtyDaysFromNow)
8340      {
8341        err();
8342        if (i == 0)
8343        {
8344          wrapErr(0, WRAP_COLUMN,
8345               WARN_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NEAR_EXPIRATION.get(
8346                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8347        }
8348        else
8349        {
8350          wrapErr(0, WRAP_COLUMN,
8351               WARN_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_NEAR_EXPIRATION.
8352                    get(c.getSubjectDN(),
8353                         formatDateAndTime(c.getNotAfterDate())));
8354        }
8355
8356        numWarnings++;
8357      }
8358      else
8359      {
8360        if (i == 0)
8361        {
8362          out();
8363          wrapOut(0, WRAP_COLUMN,
8364               INFO_MANAGE_CERTS_CHECK_USABILITY_END_CERT_VALIDITY_OK.get(
8365                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8366        }
8367        else
8368        {
8369          out();
8370          wrapOut(0, WRAP_COLUMN,
8371               INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_VALIDITY_OK.get(
8372                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
8373        }
8374      }
8375    }
8376
8377
8378    // Look at all of the extensions for all of the certificates and perform the
8379    // following validation:
8380    // - If the certificate at the head of the chain has an extended key usage
8381    //   extension, then make sure it includes the serverAuth usage.  If it
8382    //   does not include an extended key usage extension, then warn that it
8383    //   should.
8384    // - If any of the issuer certificates has a basic constraints extension,
8385    //   then make sure it indicates that the associated certificate is a
8386    //   certification authority.  Further, if it has a path length constraint,
8387    //   then make sure the chain does not exceed that length.  If any issuer
8388    //   certificate does not have a basic constraints extension, then warn that
8389    //   it should.
8390    // - If any of the issuer certificates has a key usage extension, then
8391    //   make sure it has the certSign usage.  If any issuer certificate does
8392    //   not have a key usage extension, then warn that it should.
8393    // - TODO:  If any certificate has a CRL distribution points extension, then
8394    //   retrieve the CRL and make sure the certificate hasn't been revoked.
8395    // - TODO:  If any certificate has an authority information access
8396    //   extension that points to an OCSP service, then consult that service to
8397    //   determine whether the certificate has been revoked.
8398    for (int i=0; i < chain.length; i++)
8399    {
8400      boolean basicConstraintsFound = false;
8401      boolean extendedKeyUsageFound = false;
8402      boolean keyUsageFound = false;
8403      final X509Certificate c = chain[i];
8404      for (final X509CertificateExtension extension : c.getExtensions())
8405      {
8406        if (extension instanceof ExtendedKeyUsageExtension)
8407        {
8408          extendedKeyUsageFound = true;
8409          if (i == 0)
8410          {
8411            final ExtendedKeyUsageExtension e =
8412                 (ExtendedKeyUsageExtension) extension;
8413            if (!e.getKeyPurposeIDs().contains(
8414                 ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID()))
8415            {
8416              err();
8417              wrapErr(0, WRAP_COLUMN,
8418                   ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_BAD_EKU.get(
8419                        c.getSubjectDN()));
8420              numErrors++;
8421            }
8422            else
8423            {
8424              out();
8425              wrapOut(0, WRAP_COLUMN,
8426                   INFO_MANAGE_CERTS_CHECK_USABILITY_END_CERT_GOOD_EKU.get(
8427                        c.getSubjectDN()));
8428            }
8429          }
8430        }
8431        else if (extension instanceof BasicConstraintsExtension)
8432        {
8433          basicConstraintsFound = true;
8434          if (i > 0)
8435          {
8436            final BasicConstraintsExtension e =
8437                 (BasicConstraintsExtension) extension;
8438            if (!e.isCA())
8439            {
8440              err();
8441              wrapErr(0, WRAP_COLUMN,
8442                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_BAD_BC_CA.get(
8443                        c.getSubjectDN()));
8444              numErrors++;
8445            }
8446            else if ((e.getPathLengthConstraint() != null) &&
8447                 ((i - 1) > e.getPathLengthConstraint()))
8448            {
8449              err();
8450              wrapErr(0, WRAP_COLUMN,
8451                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_BAD_BC_LENGTH.
8452                        get(c.getSubjectDN(), e.getPathLengthConstraint(),
8453                             chain[0].getSubjectDN(), (i-1)));
8454              numErrors++;
8455            }
8456            else
8457            {
8458              out();
8459              wrapOut(0, WRAP_COLUMN,
8460                   INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_GOOD_BC.get(
8461                        c.getSubjectDN()));
8462            }
8463          }
8464        }
8465        else if (extension instanceof KeyUsageExtension)
8466        {
8467          keyUsageFound = true;
8468          if (i > 0)
8469          {
8470            final KeyUsageExtension e = (KeyUsageExtension) extension;
8471            if (! e.isKeyCertSignBitSet())
8472            {
8473              err();
8474              wrapErr(0, WRAP_COLUMN,
8475                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_NO_CERT_SIGN_KU.get(
8476                        c.getSubjectDN()));
8477              numErrors++;
8478            }
8479            else
8480            {
8481              out();
8482              wrapOut(0, WRAP_COLUMN,
8483                   INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_GOOD_KU.get(
8484                        c.getSubjectDN()));
8485            }
8486          }
8487        }
8488      }
8489
8490      if (i == 0)
8491      {
8492        if (! extendedKeyUsageFound)
8493        {
8494          err();
8495          wrapErr(0, WRAP_COLUMN,
8496               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_EKU.get(
8497                    c.getSubjectDN()));
8498          numWarnings++;
8499        }
8500      }
8501      else
8502      {
8503        if (! basicConstraintsFound)
8504        {
8505          err();
8506          wrapErr(0, WRAP_COLUMN,
8507               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_BC.get(
8508                    c.getSubjectDN()));
8509          numWarnings++;
8510        }
8511
8512        if (! keyUsageFound)
8513        {
8514          err();
8515          wrapErr(0, WRAP_COLUMN,
8516               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_KU.get(
8517                    c.getSubjectDN()));
8518          numWarnings++;
8519        }
8520      }
8521    }
8522
8523
8524    // Make sure that none of the certificates has a signature algorithm that
8525    // uses MD5 or SHA-1.  If it uses an unrecognized signature algorithm, then
8526    // that's a warning.
8527    boolean isIssuer = false;
8528    final BooleanArgument ignoreSHA1WarningArg =
8529         subCommandParser.getBooleanArgument(
8530              "allow-sha-1-signature-for-issuer-certificates");
8531    final boolean ignoreSHA1SignatureWarningForIssuerCertificates =
8532         ((ignoreSHA1WarningArg != null) && ignoreSHA1WarningArg.isPresent());
8533    for (final X509Certificate c : chain)
8534    {
8535      final OID signatureAlgorithmOID = c.getSignatureAlgorithmOID();
8536      final SignatureAlgorithmIdentifier id =
8537           SignatureAlgorithmIdentifier.forOID(signatureAlgorithmOID);
8538      if (id == null)
8539      {
8540        err();
8541        wrapErr(0, WRAP_COLUMN,
8542             WARN_MANAGE_CERTS_CHECK_USABILITY_UNKNOWN_SIG_ALG.get(
8543                  c.getSubjectDN(), signatureAlgorithmOID));
8544        numWarnings++;
8545      }
8546      else
8547      {
8548        switch (id)
8549        {
8550          case MD2_WITH_RSA:
8551          case MD5_WITH_RSA:
8552            err();
8553            wrapErr(0, WRAP_COLUMN,
8554                 ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_SIG_ALG.get(
8555                      c.getSubjectDN(), id.getUserFriendlyName()));
8556            numErrors++;
8557            break;
8558
8559          case SHA_1_WITH_RSA:
8560          case SHA_1_WITH_DSA:
8561          case SHA_1_WITH_ECDSA:
8562            if (isIssuer && ignoreSHA1SignatureWarningForIssuerCertificates)
8563            {
8564              err();
8565              wrapErr(0, WRAP_COLUMN,
8566                   WARN_MANAGE_CERTS_CHECK_USABILITY_ISSUER_WITH_SHA1_SIG.get(
8567                        c.getSubjectDN(), id.getUserFriendlyName(),
8568                        ignoreSHA1WarningArg.getIdentifierString()));
8569            }
8570            else
8571            {
8572              err();
8573              wrapErr(0, WRAP_COLUMN,
8574                   ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_SIG_ALG.get(
8575                        c.getSubjectDN(), id.getUserFriendlyName()));
8576              numErrors++;
8577            }
8578            break;
8579
8580          case SHA_224_WITH_RSA:
8581          case SHA_224_WITH_DSA:
8582          case SHA_224_WITH_ECDSA:
8583          case SHA_256_WITH_RSA:
8584          case SHA_256_WITH_DSA:
8585          case SHA_256_WITH_ECDSA:
8586          case SHA_384_WITH_RSA:
8587          case SHA_384_WITH_ECDSA:
8588          case SHA_512_WITH_RSA:
8589          case SHA_512_WITH_ECDSA:
8590            out();
8591            wrapOut(0, WRAP_COLUMN,
8592                 INFO_MANAGE_CERTS_CHECK_USABILITY_SIG_ALG_OK.get(
8593                      c.getSubjectDN(), id.getUserFriendlyName()));
8594            break;
8595        }
8596      }
8597
8598      isIssuer = true;
8599    }
8600
8601
8602    // Make sure that none of the certificates that uses the RSA key algorithm
8603    // has a public modulus size smaller than 2048 bits.
8604    for (final X509Certificate c : chain)
8605    {
8606      if ((c.getDecodedPublicKey() != null) &&
8607          (c.getDecodedPublicKey() instanceof RSAPublicKey))
8608      {
8609        final RSAPublicKey rsaPublicKey =
8610             (RSAPublicKey) c.getDecodedPublicKey();
8611        final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
8612        int modulusSizeBits = modulusBytes.length * 8;
8613        if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
8614        {
8615          modulusSizeBits -= 8;
8616        }
8617
8618        if (modulusSizeBits < 2048)
8619        {
8620          err();
8621          wrapErr(0, WRAP_COLUMN,
8622               ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_RSA_MODULUS.get(
8623                    c.getSubjectDN(), modulusSizeBits));
8624          numErrors++;
8625        }
8626        else
8627        {
8628          out();
8629          wrapOut(0, WRAP_COLUMN,
8630               INFO_MANAGE_CERTS_CHECK_USABILITY_RSA_MODULUS_OK.get(
8631                    c.getSubjectDN(), modulusSizeBits));
8632        }
8633      }
8634    }
8635
8636
8637    switch (numErrors)
8638    {
8639      case 0:
8640        break;
8641      case 1:
8642        err();
8643        wrapErr(0, WRAP_COLUMN,
8644             ERR_MANAGE_CERTS_CHECK_USABILITY_ONE_ERROR.get());
8645        return ResultCode.PARAM_ERROR;
8646      default:
8647        err();
8648        wrapErr(0, WRAP_COLUMN,
8649             ERR_MANAGE_CERTS_CHECK_USABILITY_MULTIPLE_ERRORS.get(numErrors));
8650        return ResultCode.PARAM_ERROR;
8651    }
8652
8653    switch (numWarnings)
8654    {
8655      case 0:
8656        out();
8657        wrapOut(0, WRAP_COLUMN,
8658             INFO_MANAGE_CERTS_CHECK_USABILITY_NO_ERRORS_OR_WARNINGS.get());
8659        return ResultCode.SUCCESS;
8660      case 1:
8661        err();
8662        wrapErr(0, WRAP_COLUMN,
8663             ERR_MANAGE_CERTS_CHECK_USABILITY_ONE_WARNING.get());
8664        return ResultCode.PARAM_ERROR;
8665      default:
8666        err();
8667        wrapErr(0, WRAP_COLUMN,
8668             ERR_MANAGE_CERTS_CHECK_USABILITY_MULTIPLE_WARNINGS.get(
8669                  numWarnings));
8670        return ResultCode.PARAM_ERROR;
8671    }
8672  }
8673
8674
8675
8676  /**
8677   * Performs the necessary processing for the display-certificate-file
8678   * subcommand.
8679   *
8680   * @return  A result code that indicates whether the processing completed
8681   *          successfully.
8682   */
8683  private ResultCode doDisplayCertificateFile()
8684  {
8685    // Get the values of a number of configured arguments.
8686    final FileArgument certificateFileArgument =
8687         subCommandParser.getFileArgument("certificate-file");
8688    final File certificateFile = certificateFileArgument.getValue();
8689
8690    final BooleanArgument verboseArgument =
8691         subCommandParser.getBooleanArgument("verbose");
8692    final boolean verbose =
8693         ((verboseArgument != null) && verboseArgument.isPresent());
8694
8695    final BooleanArgument displayKeytoolCommandArgument =
8696         subCommandParser.getBooleanArgument("display-keytool-command");
8697    if ((displayKeytoolCommandArgument != null) &&
8698        displayKeytoolCommandArgument.isPresent())
8699    {
8700      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
8701      keytoolArgs.add("-printcert");
8702      keytoolArgs.add("-file");
8703      keytoolArgs.add(certificateFile.getAbsolutePath());
8704
8705      if (verbose)
8706      {
8707        keytoolArgs.add("-v");
8708      }
8709
8710      displayKeytoolCommand(keytoolArgs);
8711    }
8712
8713
8714    // Read the certificates from the specified file.
8715    final List<X509Certificate> certificates;
8716    try
8717    {
8718      certificates = readCertificatesFromFile(certificateFile);
8719    }
8720    catch (final LDAPException le)
8721    {
8722      Debug.debugException(le);
8723      wrapErr(0, WRAP_COLUMN, le.getMessage());
8724      return le.getResultCode();
8725    }
8726
8727
8728    // If there aren't any certificates in the file, print that.
8729    if (certificates.isEmpty())
8730    {
8731      wrapOut(0, WRAP_COLUMN, INFO_MANAGE_CERTS_DISPLAY_CERT_NO_CERTS.get(
8732           certificateFile.getAbsolutePath()));
8733    }
8734    else
8735    {
8736      for (final X509Certificate c : certificates)
8737      {
8738        out();
8739        printCertificate(c, "", verbose);
8740      }
8741    }
8742
8743    return ResultCode.SUCCESS;
8744  }
8745
8746
8747
8748  /**
8749   * Performs the necessary processing for the
8750   * display-certificate-signing-request-file subcommand.
8751   *
8752   * @return  A result code that indicates whether the processing completed
8753   *          successfully.
8754   */
8755  private ResultCode doDisplayCertificateSigningRequestFile()
8756  {
8757    // Get the values of a number of configured arguments.
8758    final FileArgument csrFileArgument =
8759         subCommandParser.getFileArgument("certificate-signing-request-file");
8760    final File csrFile = csrFileArgument.getValue();
8761
8762    final BooleanArgument verboseArgument =
8763         subCommandParser.getBooleanArgument("verbose");
8764    final boolean verbose =
8765         ((verboseArgument != null) && verboseArgument.isPresent());
8766
8767    final BooleanArgument displayKeytoolCommandArgument =
8768         subCommandParser.getBooleanArgument("display-keytool-command");
8769    if ((displayKeytoolCommandArgument != null) &&
8770        displayKeytoolCommandArgument.isPresent())
8771    {
8772      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
8773      keytoolArgs.add("-printcertreq");
8774      keytoolArgs.add("-file");
8775      keytoolArgs.add(csrFile.getAbsolutePath());
8776      keytoolArgs.add("-v");
8777
8778      displayKeytoolCommand(keytoolArgs);
8779    }
8780
8781
8782    // Read the certificate signing request from the specified file.
8783    final PKCS10CertificateSigningRequest csr;
8784    try
8785    {
8786      csr = readCertificateSigningRequestFromFile(csrFile);
8787    }
8788    catch (final LDAPException le)
8789    {
8790      Debug.debugException(le);
8791      wrapErr(0, WRAP_COLUMN, le.getMessage());
8792      return le.getResultCode();
8793    }
8794
8795    out();
8796    printCertificateSigningRequest(csr, verbose, "");
8797
8798    return ResultCode.SUCCESS;
8799  }
8800
8801
8802
8803  /**
8804   * Prints a string representation of the provided certificate to standard
8805   * output.
8806   *
8807   * @param  certificate  The certificate to be printed.
8808   * @param  indent       The string to place at the beginning of each line to
8809   *                      indent that line.
8810   * @param  verbose      Indicates whether to display verbose information about
8811   *                      the certificate.
8812   */
8813  private void printCertificate(final X509Certificate certificate,
8814                                final String indent, final boolean verbose)
8815  {
8816    if (verbose)
8817    {
8818      out(indent +
8819           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VERSION.get(
8820                certificate.getVersion().getName()));
8821    }
8822
8823    out(indent +
8824         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_DN.get(
8825              certificate.getSubjectDN()));
8826    out(indent +
8827         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ISSUER_DN.get(
8828              certificate.getIssuerDN()));
8829
8830    if (verbose)
8831    {
8832      out(indent +
8833           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SERIAL_NUMBER.get(
8834                toColonDelimitedHex(
8835                     certificate.getSerialNumber().toByteArray())));
8836    }
8837
8838    out(indent +
8839         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_START.get(
8840              formatDateAndTime(certificate.getNotBeforeDate())));
8841    out(indent +
8842         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_END.get(
8843              formatDateAndTime(certificate.getNotAfterDate())));
8844
8845    final long currentTime = System.currentTimeMillis();
8846    if (currentTime < certificate.getNotBeforeTime())
8847    {
8848      out(indent +
8849           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_NOT_YET_VALID.
8850                get());
8851    }
8852    else if (currentTime > certificate.getNotAfterTime())
8853    {
8854      out(indent +
8855           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_EXPIRED.get());
8856    }
8857    else
8858    {
8859      out(indent +
8860           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_VALID.get());
8861    }
8862
8863    out(indent +
8864         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_ALG.get(
8865              certificate.getSignatureAlgorithmNameOrOID()));
8866    if (verbose)
8867    {
8868      String signatureString;
8869      try
8870      {
8871        signatureString =
8872             toColonDelimitedHex(certificate.getSignatureValue().getBytes());
8873      }
8874      catch (final Exception e)
8875      {
8876        Debug.debugException(e);
8877        signatureString = certificate.getSignatureValue().toString();
8878      }
8879      out(indent +
8880           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_VALUE.get());
8881      for (final String line : StaticUtils.wrapLine(signatureString, 78))
8882      {
8883        out(indent + "     " + line);
8884      }
8885    }
8886
8887    final String pkAlg;
8888    final String pkSummary = getPublicKeySummary(
8889         certificate.getPublicKeyAlgorithmOID(),
8890         certificate.getDecodedPublicKey(),
8891         certificate.getPublicKeyAlgorithmParameters());
8892    if (pkSummary == null)
8893    {
8894      pkAlg = certificate.getPublicKeyAlgorithmNameOrOID();
8895    }
8896    else
8897    {
8898      pkAlg = certificate.getPublicKeyAlgorithmNameOrOID() + " (" +
8899           pkSummary + ')';
8900    }
8901    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_PK_ALG.get(pkAlg));
8902
8903    if (verbose)
8904    {
8905      printPublicKey(certificate.getEncodedPublicKey(),
8906           certificate.getDecodedPublicKey(),
8907           certificate.getPublicKeyAlgorithmParameters(), indent);
8908
8909      if (certificate.getSubjectUniqueID() != null)
8910      {
8911        String subjectUniqueID;
8912        try
8913        {
8914          subjectUniqueID = toColonDelimitedHex(
8915               certificate.getSubjectUniqueID().getBytes());
8916        }
8917        catch (final Exception e)
8918        {
8919          Debug.debugException(e);
8920          subjectUniqueID = certificate.getSubjectUniqueID().toString();
8921        }
8922
8923        out(indent +
8924             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_UNIQUE_ID.get());
8925        for (final String line : StaticUtils.wrapLine(subjectUniqueID, 78))
8926        {
8927          out(indent + "     " + line);
8928        }
8929      }
8930
8931      if (certificate.getIssuerUniqueID() != null)
8932      {
8933        String issuerUniqueID;
8934        try
8935        {
8936          issuerUniqueID = toColonDelimitedHex(
8937               certificate.getIssuerUniqueID().getBytes());
8938        }
8939        catch (final Exception e)
8940        {
8941          Debug.debugException(e);
8942          issuerUniqueID = certificate.getIssuerUniqueID().toString();
8943        }
8944
8945        out(indent +
8946             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ISSUER_UNIQUE_ID.get());
8947        for (final String line : StaticUtils.wrapLine(issuerUniqueID, 78))
8948        {
8949          out(indent + "     " + line);
8950        }
8951      }
8952
8953      printExtensions(certificate.getExtensions(), indent);
8954    }
8955
8956    try
8957    {
8958      out(indent +
8959           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_FINGERPRINT.get("SHA-1",
8960                toColonDelimitedHex(certificate.getSHA1Fingerprint())));
8961    }
8962    catch (final Exception e)
8963    {
8964      Debug.debugException(e);
8965    }
8966
8967    try
8968    {
8969      out(indent +
8970           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_FINGERPRINT.get("SHA-256",
8971                toColonDelimitedHex(certificate.getSHA256Fingerprint())));
8972    }
8973    catch (final Exception e)
8974    {
8975      Debug.debugException(e);
8976    }
8977  }
8978
8979
8980
8981  /**
8982   * Prints a string representation of the provided certificate signing request
8983   * to standard output.
8984   *
8985   * @param  csr      The certificate signing request to be printed.
8986   * @param  verbose  Indicates whether to display verbose information about
8987   *                  the contents of the request.
8988   * @param  indent   The string to place at the beginning of each line to
8989   *                  indent that line.
8990   */
8991  private void printCertificateSigningRequest(
8992                    final PKCS10CertificateSigningRequest csr,
8993                    final boolean verbose, final String indent)
8994  {
8995    out(indent +
8996         INFO_MANAGE_CERTS_PRINT_CSR_LABEL_VERSION.get(
8997              csr.getVersion().getName()));
8998    out(indent +
8999         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_DN.get(
9000              csr.getSubjectDN()));
9001    out(indent +
9002         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_ALG.get(
9003              csr.getSignatureAlgorithmNameOrOID()));
9004
9005    if (verbose)
9006    {
9007      String signatureString;
9008      try
9009      {
9010        signatureString =
9011             toColonDelimitedHex(csr.getSignatureValue().getBytes());
9012      }
9013      catch (final Exception e)
9014      {
9015        Debug.debugException(e);
9016        signatureString = csr.getSignatureValue().toString();
9017      }
9018      out(indent +
9019           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_VALUE.get());
9020      for (final String line : StaticUtils.wrapLine(signatureString, 78))
9021      {
9022        out(indent + "     " + line);
9023      }
9024    }
9025
9026    final String pkAlg;
9027    final String pkSummary = getPublicKeySummary(csr.getPublicKeyAlgorithmOID(),
9028         csr.getDecodedPublicKey(), csr.getPublicKeyAlgorithmParameters());
9029    if (pkSummary == null)
9030    {
9031      pkAlg = csr.getPublicKeyAlgorithmNameOrOID();
9032    }
9033    else
9034    {
9035      pkAlg = csr.getPublicKeyAlgorithmNameOrOID() + " (" +
9036           pkSummary + ')';
9037    }
9038    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_PK_ALG.get(pkAlg));
9039
9040    if (verbose)
9041    {
9042      printPublicKey(csr.getEncodedPublicKey(), csr.getDecodedPublicKey(),
9043           csr.getPublicKeyAlgorithmParameters(), indent);
9044      printExtensions(csr.getExtensions(), indent);
9045    }
9046  }
9047
9048
9049
9050  /**
9051   * Prints information about the provided public key.
9052   *
9053   * @param  encodedPublicKey  The encoded representation of the public key.
9054   *                           This must not be {@code null}.
9055   * @param  decodedPublicKey  The decoded representation of the public key, if
9056   *                           available.
9057   * @param  parameters        The public key algorithm parameters, if any.
9058   * @param  indent            The string to place at the beginning of each
9059   *                           line to indent that line.
9060   */
9061  private void printPublicKey(final ASN1BitString encodedPublicKey,
9062                              final DecodedPublicKey decodedPublicKey,
9063                              final ASN1Element parameters,
9064                              final String indent)
9065  {
9066    if (decodedPublicKey == null)
9067    {
9068      String pkString;
9069      try
9070      {
9071        pkString = toColonDelimitedHex(encodedPublicKey.getBytes());
9072      }
9073      catch (final Exception e)
9074      {
9075        Debug.debugException(e);
9076        pkString = encodedPublicKey.toString();
9077      }
9078
9079      out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ENCODED_PK.get());
9080      for (final String line : StaticUtils.wrapLine(pkString, 78))
9081      {
9082        out(indent + "     " + line);
9083      }
9084
9085      return;
9086    }
9087
9088    if (decodedPublicKey instanceof RSAPublicKey)
9089    {
9090      final RSAPublicKey rsaPublicKey = (RSAPublicKey) decodedPublicKey;
9091      final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
9092
9093      int modulusSizeBits = modulusBytes.length * 8;
9094      if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
9095      {
9096        modulusSizeBits -= 8;
9097      }
9098
9099      out(indent +
9100           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_RSA_MODULUS.get(
9101                modulusSizeBits));
9102      final String modulusHex = toColonDelimitedHex(modulusBytes);
9103      for (final String line : StaticUtils.wrapLine(modulusHex, 78))
9104      {
9105        out(indent + "     " + line);
9106      }
9107
9108      out(indent +
9109           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_RSA_EXPONENT.get(
9110                toColonDelimitedHex(
9111                     rsaPublicKey.getPublicExponent().toByteArray())));
9112    }
9113    else if (decodedPublicKey instanceof EllipticCurvePublicKey)
9114    {
9115      final EllipticCurvePublicKey ecPublicKey =
9116           (EllipticCurvePublicKey) decodedPublicKey;
9117
9118      out(indent +
9119           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_IS_COMPRESSED.get(
9120                String.valueOf(ecPublicKey.usesCompressedForm())));
9121      out(indent +
9122           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_X.get(
9123                String.valueOf(ecPublicKey.getXCoordinate())));
9124      if (ecPublicKey.getYCoordinate() == null)
9125      {
9126        out(indent +
9127             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_Y_IS_EVEN.get(
9128                  String.valueOf(ecPublicKey.yCoordinateIsEven())));
9129      }
9130      else
9131      {
9132        out(indent +
9133             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_Y.get(
9134                  String.valueOf(ecPublicKey.getYCoordinate())));
9135      }
9136    }
9137  }
9138
9139
9140
9141  /**
9142   * Retrieves a short summary of the provided public key, if available.  For
9143   * RSA keys, this will be the modulus size in bits.  For elliptic curve keys,
9144   * this will be the named curve, if available.
9145   *
9146   * @param  publicKeyAlgorithmOID  The OID that identifies the type of public
9147   *                                key.
9148   * @param  publicKey              The decoded public key.  This may be
9149   *                                {@code null} if the decoded public key is
9150   *                                not available.
9151   * @param  parameters             The encoded public key algorithm parameters.
9152   *                                This may be {@code null} if no public key
9153   *                                algorithm parameters are available.
9154   *
9155   * @return  A short summary of the provided public key, or {@code null} if
9156   *          no summary is available.
9157   */
9158  private static String getPublicKeySummary(final OID publicKeyAlgorithmOID,
9159                                            final DecodedPublicKey publicKey,
9160                                            final ASN1Element parameters)
9161  {
9162    if (publicKey instanceof RSAPublicKey)
9163    {
9164      final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
9165      final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
9166
9167      int modulusSizeBits = modulusBytes.length * 8;
9168      if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
9169      {
9170        modulusSizeBits -= 8;
9171      }
9172
9173      return INFO_MANAGE_CERTS_GET_PK_SUMMARY_RSA_MODULUS_SIZE.get(
9174           modulusSizeBits);
9175    }
9176    else if ((parameters != null) &&
9177         publicKeyAlgorithmOID.equals(PublicKeyAlgorithmIdentifier.EC.getOID()))
9178    {
9179      try
9180      {
9181        final OID namedCurveOID =
9182             parameters.decodeAsObjectIdentifier().getOID();
9183        return NamedCurve.getNameOrOID(namedCurveOID);
9184      }
9185      catch (final Exception e)
9186      {
9187        Debug.debugException(e);
9188      }
9189    }
9190
9191    return null;
9192  }
9193
9194
9195
9196  /**
9197   * Prints information about the provided extensions.
9198   *
9199   * @param  extensions  The list of extensions to be printed.
9200   * @param  indent      The string to place at the beginning of each line to
9201   *                     indent that line.
9202   */
9203  void printExtensions(final List<X509CertificateExtension> extensions,
9204                       final String indent)
9205  {
9206    if (extensions.isEmpty())
9207    {
9208      return;
9209    }
9210
9211    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXTENSIONS.get());
9212    for (final X509CertificateExtension extension : extensions)
9213    {
9214      if (extension instanceof AuthorityKeyIdentifierExtension)
9215      {
9216        out(indent + "     " +
9217             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_EXT.get());
9218        out(indent + "          " +
9219             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9220                  extension.getOID().toString()));
9221        out(indent + "          " +
9222             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9223                  String.valueOf(extension.isCritical())));
9224
9225        final AuthorityKeyIdentifierExtension e =
9226             (AuthorityKeyIdentifierExtension) extension;
9227        if (e.getKeyIdentifier() != null)
9228        {
9229          out(indent + "          " +
9230               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_ID.get());
9231          final String idHex =
9232               toColonDelimitedHex(e.getKeyIdentifier().getValue());
9233          for (final String line : StaticUtils.wrapLine(idHex, 78))
9234          {
9235            out(indent + "               " + line);
9236          }
9237        }
9238
9239        if (e.getAuthorityCertIssuer() != null)
9240        {
9241          out(indent + "          " +
9242               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_ISSUER.
9243                    get());
9244          printGeneralNames(e.getAuthorityCertIssuer(),
9245               indent + "               ");
9246        }
9247
9248        if (e.getAuthorityCertSerialNumber() != null)
9249        {
9250          out(indent + "          " +
9251               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_SERIAL.get(
9252                    toColonDelimitedHex(e.getAuthorityCertSerialNumber().
9253                         toByteArray())));
9254        }
9255      }
9256      else if (extension instanceof BasicConstraintsExtension)
9257      {
9258        out(indent + "     " +
9259             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_EXT.get());
9260        out(indent + "          " +
9261             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9262                  extension.getOID().toString()));
9263        out(indent + "          " +
9264             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9265                  String.valueOf(extension.isCritical())));
9266
9267        final BasicConstraintsExtension e =
9268             (BasicConstraintsExtension) extension;
9269        out(indent + "          " +
9270             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_IS_CA.get(
9271                  String.valueOf(e.isCA())));
9272
9273        if (e.getPathLengthConstraint() != null)
9274        {
9275          out(indent + "          " +
9276               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_LENGTH.get(
9277                    e.getPathLengthConstraint()));
9278        }
9279      }
9280      else if (extension instanceof CRLDistributionPointsExtension)
9281      {
9282        out(indent + "     " +
9283             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_EXT.get());
9284        out(indent + "          " +
9285             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9286                  extension.getOID().toString()));
9287        out(indent + "          " +
9288             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9289                  String.valueOf(extension.isCritical())));
9290
9291        final CRLDistributionPointsExtension crlDPE =
9292             (CRLDistributionPointsExtension) extension;
9293        for (final CRLDistributionPoint dp :
9294             crlDPE.getCRLDistributionPoints())
9295        {
9296          out(indent + "          " +
9297               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_HEADER.get());
9298          if (dp.getFullName() != null)
9299          {
9300            out(indent + "               " +
9301                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_FULL_NAME.
9302                      get());
9303            printGeneralNames(dp.getFullName(),
9304                 indent + "                    ");
9305          }
9306
9307          if (dp.getNameRelativeToCRLIssuer() != null)
9308          {
9309            out(indent + "               " +
9310                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_REL_NAME.get(
9311                      dp.getNameRelativeToCRLIssuer()));
9312          }
9313
9314          if (! dp.getPotentialRevocationReasons().isEmpty())
9315          {
9316            out(indent + "               " +
9317                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_REASON.get());
9318            for (final CRLDistributionPointRevocationReason r :
9319                 dp.getPotentialRevocationReasons())
9320            {
9321              out(indent + "                    " + r.getName());
9322            }
9323          }
9324
9325          if (dp.getCRLIssuer() != null)
9326          {
9327            out(indent + "              " +
9328                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_CRL_ISSUER.
9329                      get());
9330            printGeneralNames(dp.getCRLIssuer(),
9331                 indent + "                    ");
9332          }
9333        }
9334      }
9335      else if (extension instanceof ExtendedKeyUsageExtension)
9336      {
9337        out(indent + "     " +
9338             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_EXT.get());
9339        out(indent + "          " +
9340             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9341                  extension.getOID().toString()));
9342        out(indent + "          " +
9343             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9344                  String.valueOf(extension.isCritical())));
9345
9346        final ExtendedKeyUsageExtension e =
9347             (ExtendedKeyUsageExtension) extension;
9348        for (final OID oid : e.getKeyPurposeIDs())
9349        {
9350          final ExtendedKeyUsageID id = ExtendedKeyUsageID.forOID(oid);
9351          if (id == null)
9352          {
9353            out(indent + "          " +
9354                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_ID.get(oid));
9355          }
9356          else
9357          {
9358            out(indent + "          " +
9359                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_ID.get(
9360                      id.getName()));
9361          }
9362        }
9363      }
9364      else if (extension instanceof IssuerAlternativeNameExtension)
9365      {
9366        out(indent + "     " +
9367             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IAN_EXT.get());
9368        out(indent + "          " +
9369             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9370                  extension.getOID().toString()));
9371        out(indent + "          " +
9372             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9373                  String.valueOf(extension.isCritical())));
9374
9375        final IssuerAlternativeNameExtension e =
9376             (IssuerAlternativeNameExtension) extension;
9377        printGeneralNames(e.getGeneralNames(), indent + "          ");
9378      }
9379      else if (extension instanceof KeyUsageExtension)
9380      {
9381        out(indent + "     " +
9382             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_EXT.get());
9383        out(indent + "          " +
9384             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9385                  extension.getOID().toString()));
9386        out(indent + "          " +
9387             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9388                  String.valueOf(extension.isCritical())));
9389
9390        out(indent + "          " +
9391             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_USAGES.get());
9392        final KeyUsageExtension kue = (KeyUsageExtension) extension;
9393        if (kue.isDigitalSignatureBitSet())
9394        {
9395          out(indent + "               " +
9396               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DS.get());
9397        }
9398
9399        if (kue.isNonRepudiationBitSet())
9400        {
9401          out(indent + "               " +
9402               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_NR.get());
9403        }
9404
9405        if (kue.isKeyEnciphermentBitSet())
9406        {
9407          out(indent + "               " +
9408               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KE.get());
9409        }
9410
9411        if (kue.isDataEnciphermentBitSet())
9412        {
9413          out(indent + "               " +
9414               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DE.get());
9415        }
9416
9417        if (kue.isKeyCertSignBitSet())
9418        {
9419          out(indent + "               " +
9420               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KCS.get());
9421        }
9422
9423        if (kue.isCRLSignBitSet())
9424        {
9425          out(indent + "               " +
9426               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_CRL_SIGN.get());
9427        }
9428
9429        if (kue.isEncipherOnlyBitSet())
9430        {
9431          out(indent + "               " +
9432               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_EO.get());
9433        }
9434
9435        if (kue.isDecipherOnlyBitSet())
9436        {
9437          out(indent + "               " +
9438               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DO.get());
9439        }
9440      }
9441      else if (extension instanceof SubjectAlternativeNameExtension)
9442      {
9443        out(indent + "     " +
9444             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SAN_EXT.get());
9445        out(indent + "          " +
9446             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9447                  extension.getOID().toString()));
9448        out(indent + "          " +
9449             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9450                  String.valueOf(extension.isCritical())));
9451
9452        final SubjectAlternativeNameExtension e =
9453             (SubjectAlternativeNameExtension) extension;
9454        printGeneralNames(e.getGeneralNames(), indent + "          ");
9455      }
9456      else if (extension instanceof SubjectKeyIdentifierExtension)
9457      {
9458        out(indent + "     " +
9459             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SKI_EXT.get());
9460        out(indent + "          " +
9461             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9462                  extension.getOID().toString()));
9463        out(indent + "          " +
9464             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9465                  String.valueOf(extension.isCritical())));
9466
9467        final SubjectKeyIdentifierExtension e =
9468             (SubjectKeyIdentifierExtension) extension;
9469        final String idHex =
9470             toColonDelimitedHex(e.getKeyIdentifier().getValue());
9471        out(indent + "          " +
9472             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SKI_ID.get());
9473        for (final String line  : StaticUtils.wrapLine(idHex, 78))
9474        {
9475          out(indent + "               " + line);
9476        }
9477      }
9478      else
9479      {
9480        out(indent + "     " +
9481             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_GENERIC.get());
9482        out(indent + "          " +
9483             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
9484                  extension.getOID().toString()));
9485        out(indent + "          " +
9486             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
9487                  String.valueOf(extension.isCritical())));
9488
9489        final String valueHex = toColonDelimitedHex(extension.getValue());
9490        out(indent + "          " +
9491             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_VALUE.get());
9492        getOut().print(StaticUtils.toHexPlusASCII(extension.getValue(),
9493             (indent.length() + 15)));
9494      }
9495    }
9496  }
9497
9498
9499
9500  /**
9501   * Prints information about the contents of the provided general names object.
9502   *
9503   * @param  generalNames  The general names object to print.
9504   * @param  indent        The string to place at the beginning of each line to
9505   *                       indent that line.
9506   */
9507  private void printGeneralNames(final GeneralNames generalNames,
9508                                 final String indent)
9509  {
9510    for (final String dnsName : generalNames.getDNSNames())
9511    {
9512      out(indent + INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_DNS.get(dnsName));
9513    }
9514
9515    for (final InetAddress ipAddress : generalNames.getIPAddresses())
9516    {
9517      out(indent +
9518           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_IP.get(
9519                ipAddress.getHostAddress()));
9520    }
9521
9522    for (final String name : generalNames.getRFC822Names())
9523    {
9524      out(indent +
9525           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_RFC_822_NAME.get(name));
9526    }
9527
9528    for (final DN dn : generalNames.getDirectoryNames())
9529    {
9530      out(indent +
9531           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_DIRECTORY_NAME.get(
9532                String.valueOf(dn)));
9533    }
9534
9535    for (final String uri : generalNames.getUniformResourceIdentifiers())
9536    {
9537      out(indent + INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_URI.get(uri));
9538    }
9539
9540    for (final OID oid : generalNames.getRegisteredIDs())
9541    {
9542      out(indent +
9543           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_REGISTERED_ID.get(
9544                oid.toString()));
9545    }
9546
9547    if (! generalNames.getOtherNames().isEmpty())
9548    {
9549      out(indent +
9550           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_OTHER_NAME_COUNT.get(
9551                generalNames.getOtherNames().size()));
9552    }
9553
9554    if (! generalNames.getX400Addresses().isEmpty())
9555    {
9556      out(indent +
9557           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_X400_ADDR_COUNT.get(
9558                generalNames.getX400Addresses().size()));
9559    }
9560
9561    if (! generalNames.getEDIPartyNames().isEmpty())
9562    {
9563      out(indent +
9564           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_EDI_PARTY_NAME_COUNT.get(
9565                generalNames.getEDIPartyNames().size()));
9566    }
9567  }
9568
9569
9570
9571  /**
9572   * Writes a PEM-encoded representation of the provided encoded certificate to
9573   * the given print stream.
9574   *
9575   * @param  printStream         The print stream to which the PEM-encoded
9576   *                             certificate should be written.  It must not be
9577   *                             {@code null}.
9578   * @param  encodedCertificate  The bytes that comprise the encoded
9579   *                             certificate.  It must not be {@code null}.
9580   */
9581  private static void writePEMCertificate(final PrintStream printStream,
9582                                          final byte[] encodedCertificate)
9583  {
9584    final String certBase64 = Base64.encode(encodedCertificate);
9585    printStream.println("-----BEGIN CERTIFICATE-----");
9586    for (final String line : StaticUtils.wrapLine(certBase64, 64))
9587    {
9588      printStream.println(line);
9589    }
9590    printStream.println("-----END CERTIFICATE-----");
9591  }
9592
9593
9594
9595  /**
9596   * Writes a PEM-encoded representation of the provided encoded certificate
9597   * signing request to the given print stream.
9598   *
9599   * @param  printStream  The print stream to which the PEM-encoded certificate
9600   *                      signing request should be written.  It must not be
9601   *                      {@code null}.
9602   * @param  encodedCSR   The bytes that comprise the encoded certificate
9603   *                      signing request.  It must not be {@code null}.
9604   */
9605  private static void writePEMCertificateSigningRequest(
9606                           final PrintStream printStream,
9607                           final byte[] encodedCSR)
9608  {
9609    final String certBase64 = Base64.encode(encodedCSR);
9610    printStream.println("-----BEGIN CERTIFICATE REQUEST-----");
9611    for (final String line : StaticUtils.wrapLine(certBase64, 64))
9612    {
9613      printStream.println(line);
9614    }
9615    printStream.println("-----END CERTIFICATE REQUEST-----");
9616  }
9617
9618
9619
9620  /**
9621   * Writes a PEM-encoded representation of the provided encoded private key to
9622   * the given print stream.
9623   *
9624   * @param  printStream        The print stream to which the PEM-encoded
9625   *                            private key should be written.  It must not be
9626   *                            {@code null}.
9627   * @param  encodedPrivateKey  The bytes that comprise the encoded private key.
9628   *                            It must not be {@code null}.
9629   */
9630  private static void writePEMPrivateKey(final PrintStream printStream,
9631                                         final byte[] encodedPrivateKey)
9632  {
9633    final String certBase64 = Base64.encode(encodedPrivateKey);
9634    printStream.println("-----BEGIN PRIVATE KEY-----");
9635    for (final String line : StaticUtils.wrapLine(certBase64, 64))
9636    {
9637      printStream.println(line);
9638    }
9639    printStream.println("-----END PRIVATE KEY-----");
9640  }
9641
9642
9643
9644  /**
9645   * Displays the keytool command that can be invoked to produce approximately
9646   * equivalent functionality.
9647   *
9648   * @param  keytoolArgs  The arguments to provide to the keytool command.
9649   */
9650  private void displayKeytoolCommand(final List<String> keytoolArgs)
9651  {
9652    final StringBuilder buffer = new StringBuilder();
9653    buffer.append("#      keytool");
9654
9655    boolean lastWasArgName = false;
9656    for (final String arg : keytoolArgs)
9657    {
9658      if (arg.startsWith("-"))
9659      {
9660        buffer.append(" \\");
9661        buffer.append(StaticUtils.EOL);
9662        buffer.append("#           ");
9663        buffer.append(arg);
9664        lastWasArgName = true;
9665      }
9666      else if (lastWasArgName)
9667      {
9668        buffer.append(' ');
9669        buffer.append(StaticUtils.cleanExampleCommandLineArgument(arg));
9670        lastWasArgName = false;
9671      }
9672      else
9673      {
9674        buffer.append(" \\");
9675        buffer.append(StaticUtils.EOL);
9676        buffer.append("#           ");
9677        buffer.append(arg);
9678        lastWasArgName = false;
9679      }
9680    }
9681
9682    out();
9683    out(INFO_MANAGE_CERTS_APPROXIMATE_KEYTOOL_COMMAND.get());
9684    out(buffer);
9685    out();
9686  }
9687
9688
9689
9690  /**
9691   * Retrieves the path to the target keystore file.
9692   *
9693   * @return  The path to the target keystore file, or {@code null} if no
9694   *          keystore path was configured.
9695   */
9696  private File getKeystorePath()
9697  {
9698    final FileArgument keystoreArgument =
9699         subCommandParser.getFileArgument("keystore");
9700    if (keystoreArgument != null)
9701    {
9702      return keystoreArgument.getValue();
9703    }
9704
9705    return null;
9706  }
9707
9708
9709
9710  /**
9711   * Retrieves the password needed to access the keystore.
9712   *
9713   * @param  keystoreFile  The path to the keystore file for which to get the
9714   *                       password.
9715   *
9716   * @return  The password needed to access the keystore, or {@code null} if
9717   *          no keystore password was configured.
9718   *
9719   * @throws  LDAPException  If a problem is encountered while trying to get the
9720   *                         keystore password.
9721   */
9722  private char[] getKeystorePassword(final File keystoreFile)
9723          throws LDAPException
9724  {
9725    return getKeystorePassword(keystoreFile, null);
9726  }
9727
9728
9729
9730  /**
9731   * Retrieves the password needed to access the keystore.
9732   *
9733   * @param  keystoreFile  The path to the keystore file for which to get the
9734   *                       password.
9735   * @param  prefix        The prefix string to use for the arguments.  This may
9736   *                       be {@code null} if no prefix is needed.
9737   *
9738   * @return  The password needed to access the keystore, or {@code null} if
9739   *          no keystore password was configured.
9740   *
9741   * @throws  LDAPException  If a problem is encountered while trying to get the
9742   *                         keystore password.
9743   */
9744  private char[] getKeystorePassword(final File keystoreFile,
9745                                     final String prefix)
9746          throws LDAPException
9747  {
9748    final String prefixDash;
9749    if (prefix == null)
9750    {
9751      prefixDash = "";
9752    }
9753    else
9754    {
9755      prefixDash = prefix + '-';
9756    }
9757
9758    final StringArgument keystorePasswordArgument =
9759         subCommandParser.getStringArgument(prefixDash + "keystore-password");
9760    if ((keystorePasswordArgument != null) &&
9761         keystorePasswordArgument.isPresent())
9762    {
9763      final char[] keystorePWChars =
9764           keystorePasswordArgument.getValue().toCharArray();
9765      if ((! keystoreFile.exists()) && (keystorePWChars.length < 6))
9766      {
9767        throw new LDAPException(ResultCode.PARAM_ERROR,
9768             ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
9769      }
9770
9771      return keystorePWChars;
9772    }
9773
9774
9775    final FileArgument keystorePasswordFileArgument =
9776         subCommandParser.getFileArgument(
9777              prefixDash + "keystore-password-file");
9778    if ((keystorePasswordFileArgument != null) &&
9779        keystorePasswordFileArgument.isPresent())
9780    {
9781      final File f = keystorePasswordFileArgument.getValue();
9782      try (final BufferedReader r = new BufferedReader(new FileReader(f)))
9783      {
9784        final String line = r.readLine();
9785        if (line == null)
9786        {
9787          throw new LDAPException(ResultCode.PARAM_ERROR,
9788               ERR_MANAGE_CERTS_GET_KS_PW_EMPTY_FILE.get(f.getAbsolutePath()));
9789        }
9790        else if (r.readLine() != null)
9791        {
9792          throw new LDAPException(ResultCode.PARAM_ERROR,
9793               ERR_MANAGE_CERTS_GET_KS_PW_MULTI_LINE_FILE.get(
9794                    f.getAbsolutePath()));
9795        }
9796        else if (line.isEmpty())
9797        {
9798          throw new LDAPException(ResultCode.PARAM_ERROR,
9799               ERR_MANAGE_CERTS_GET_KS_PW_EMPTY_FILE.get(f.getAbsolutePath()));
9800        }
9801        else
9802        {
9803          if ((! keystoreFile.exists()) && (line.length() < 6))
9804          {
9805            throw new LDAPException(ResultCode.PARAM_ERROR,
9806                 ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
9807          }
9808
9809          return line.toCharArray();
9810        }
9811      }
9812      catch(final LDAPException le)
9813      {
9814        Debug.debugException(le);
9815        throw le;
9816      }
9817      catch (final Exception e)
9818      {
9819        Debug.debugException(e);
9820        throw new LDAPException(ResultCode.LOCAL_ERROR,
9821             ERR_MANAGE_CERTS_GET_KS_PW_ERROR_READING_FILE.get(
9822                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
9823             e);
9824      }
9825    }
9826
9827
9828    final BooleanArgument promptArgument = subCommandParser.getBooleanArgument(
9829         "prompt-for-" + prefixDash + "keystore-password");
9830    if ((promptArgument != null) && promptArgument.isPresent())
9831    {
9832      out();
9833      if (keystoreFile.exists() && (! "new".equals(prefix)))
9834      {
9835        // We're only going to prompt once.
9836        if ((prefix != null) && prefix.equals("current"))
9837        {
9838          return promptForPassword(
9839               INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_CURRENT_PROMPT.get(
9840                    keystoreFile.getAbsolutePath()),
9841               false);
9842        }
9843        else
9844        {
9845          return promptForPassword(
9846               INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_PROMPT.get(
9847                    keystoreFile.getAbsolutePath()),
9848               false);
9849        }
9850      }
9851      else
9852      {
9853        // We're creating a new keystore, so we should prompt for the password
9854        // twice to prevent setting the wrong password because of a typo.
9855        while (true)
9856        {
9857          final String prompt1;
9858          if ("new".equals(prefix))
9859          {
9860            prompt1 = INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_NEW_PROMPT.get();
9861          }
9862          else
9863          {
9864            prompt1 = INFO_MANAGE_CERTS_KEY_KS_PW_NEW_PROMPT_1.get(
9865                 keystoreFile.getAbsolutePath());
9866          }
9867          final char[] pwChars = promptForPassword(prompt1, false);
9868
9869          if (pwChars.length < 6)
9870          {
9871            wrapErr(0, WRAP_COLUMN,
9872                 ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
9873            err();
9874            continue;
9875          }
9876
9877          final char[] confirmChars = promptForPassword(
9878               INFO_MANAGE_CERTS_KEY_KS_PW_NEW_PROMPT_2.get(), true);
9879
9880          if (Arrays.equals(pwChars, confirmChars))
9881          {
9882            Arrays.fill(confirmChars, '\u0000');
9883            return pwChars;
9884          }
9885          else
9886          {
9887            wrapErr(0, WRAP_COLUMN,
9888                 ERR_MANAGE_CERTS_KEY_KS_PW_PROMPT_MISMATCH.get());
9889            err();
9890          }
9891        }
9892      }
9893    }
9894
9895
9896    return null;
9897  }
9898
9899
9900
9901  /**
9902   * Prompts for a password and retrieves the value that the user entered.
9903   *
9904   * @param  prompt      The prompt to display to the user.
9905   * @param  allowEmpty  Indicates whether to allow the password to be empty.
9906   *
9907   * @return  The password that was read, or an empty array if the user did not
9908   *          type a password before pressing ENTER.
9909   *
9910   * @throws  LDAPException  If a problem is encountered while reading the
9911   *                         password.
9912   */
9913  private char[] promptForPassword(final String prompt,
9914                                   final boolean allowEmpty)
9915          throws LDAPException
9916  {
9917    final Iterator<String> iterator =
9918         StaticUtils.wrapLine(prompt, WRAP_COLUMN).iterator();
9919    while (iterator.hasNext())
9920    {
9921      final String line = iterator.next();
9922      if (iterator.hasNext())
9923      {
9924        out(line);
9925      }
9926      else
9927      {
9928        getOut().print(line);
9929      }
9930    }
9931
9932    final char[] passwordChars = PasswordReader.readPasswordChars();
9933    if ((passwordChars.length == 0) && (! allowEmpty))
9934    {
9935      wrapErr(0, WRAP_COLUMN,
9936           ERR_MANAGE_CERTS_PROMPT_FOR_PW_EMPTY_PW.get());
9937      err();
9938      return promptForPassword(prompt, allowEmpty);
9939    }
9940
9941    return passwordChars;
9942  }
9943
9944
9945
9946  /**
9947   * Prompts the user for a yes or no response.
9948   *
9949   * @param  prompt  The prompt to display to the end user.
9950   *
9951   * @return  {@code true} if the user chooses the "yes" response, or
9952   *          {@code false} if the user chooses the "no" throws.
9953   *
9954   * @throws  LDAPException  If a problem is encountered while reading data from
9955   *                         the client.
9956   */
9957  private boolean promptForYesNo(final String prompt)
9958          throws LDAPException
9959  {
9960    while (true)
9961    {
9962      final List<String> lines =
9963           StaticUtils.wrapLine((prompt + ' '), WRAP_COLUMN);
9964
9965      final Iterator<String> lineIterator = lines.iterator();
9966      while (lineIterator.hasNext())
9967      {
9968        final String line = lineIterator.next();
9969        if (lineIterator.hasNext())
9970        {
9971          out(line);
9972        }
9973        else
9974        {
9975          getOut().print(line);
9976        }
9977      }
9978
9979      try
9980      {
9981        final String response = readLineFromIn();
9982        if (response.equalsIgnoreCase("yes") || response.equalsIgnoreCase("y"))
9983        {
9984          return true;
9985        }
9986        else if (response.equalsIgnoreCase("no") ||
9987             response.equalsIgnoreCase("n"))
9988        {
9989          return false;
9990        }
9991        else
9992        {
9993          err();
9994          wrapErr(0, WRAP_COLUMN,
9995               ERR_MANAGE_CERTS_PROMPT_FOR_YES_NO_INVALID_RESPONSE.get());
9996          err();
9997        }
9998      }
9999      catch (final Exception e)
10000      {
10001        Debug.debugException(e);
10002        throw new LDAPException(ResultCode.LOCAL_ERROR,
10003             ERR_MANAGE_CERTS_PROMPT_FOR_YES_NO_READ_ERROR.get(
10004                  StaticUtils.getExceptionMessage(e)),
10005             e);
10006      }
10007    }
10008  }
10009
10010
10011
10012  /**
10013   * Reads a line of input from standard input.
10014   *
10015   * @return  The line read from standard input.
10016   *
10017   * @throws  IOException  If a problem is encountered while reading from
10018   *                       standard input.
10019   */
10020  private String readLineFromIn()
10021          throws IOException
10022  {
10023    final ByteStringBuffer buffer = new ByteStringBuffer();
10024    while (true)
10025    {
10026      final int byteRead = in.read();
10027      if (byteRead < 0)
10028      {
10029        if (buffer.isEmpty())
10030        {
10031          return null;
10032        }
10033        else
10034        {
10035          return buffer.toString();
10036        }
10037      }
10038
10039      if (byteRead == '\n')
10040      {
10041        return buffer.toString();
10042      }
10043      else if (byteRead == '\r')
10044      {
10045        final int nextByteRead = in.read();
10046        Validator.ensureTrue(((nextByteRead < 0) || (nextByteRead == '\n')),
10047             "ERROR:  Read a carriage return from standard input that was " +
10048                  "not followed by a new line.");
10049        return buffer.toString();
10050      }
10051      else
10052      {
10053        buffer.append((byte) (byteRead & 0xFF));
10054      }
10055    }
10056  }
10057
10058
10059
10060  /**
10061   * Retrieves the password needed to access the private key.
10062   *
10063   * @param  keystore          The keystore that contains the target private
10064   *                           key.  This must not be {@code null}.
10065   * @param  alias             The alias of the target private key.  This must
10066   *                           not be {@code null}.
10067   * @param  keystorePassword  The keystore password to use if no specific
10068   *                           private key password was provided.
10069   *
10070   * @return  The password needed to access the private key, or the provided
10071   *          keystore password if no arguments were provided to specify a
10072   *          different private key password.
10073   *
10074   * @throws  LDAPException  If a problem is encountered while trying to get the
10075   *                         private key password.
10076   */
10077  private char[] getPrivateKeyPassword(final KeyStore keystore,
10078                                       final String alias,
10079                                       final char[] keystorePassword)
10080          throws LDAPException
10081  {
10082    return getPrivateKeyPassword(keystore, alias, null, keystorePassword);
10083  }
10084
10085
10086
10087  /**
10088   * Retrieves the password needed to access the private key.
10089   *
10090   * @param  keystore          The keystore that contains the target private
10091   *                           key.  This must not be {@code null}.
10092   * @param  alias             The alias of the target private key.  This must
10093   *                           not be {@code null}.
10094   * @param  prefix            The prefix string to use for the arguments.  This
10095   *                           may be {@code null} if no prefix is needed.
10096   * @param  keystorePassword  The keystore password to use if no specific
10097   *                           private key password was provided.
10098   *
10099   * @return  The password needed to access the private key, or the provided
10100   *          keystore password if no arguments were provided to specify a
10101   *          different private key password.
10102   *
10103   * @throws  LDAPException  If a problem is encountered while trying to get the
10104   *                         private key password.
10105   */
10106  private char[] getPrivateKeyPassword(final KeyStore keystore,
10107                                       final String alias, final String prefix,
10108                                       final char[] keystorePassword)
10109          throws LDAPException
10110  {
10111    final String prefixDash;
10112    if (prefix == null)
10113    {
10114      prefixDash = "";
10115    }
10116    else
10117    {
10118      prefixDash = prefix + '-';
10119    }
10120
10121    final StringArgument privateKeyPasswordArgument =
10122         subCommandParser.getStringArgument(
10123              prefixDash + "private-key-password");
10124    if ((privateKeyPasswordArgument != null) &&
10125         privateKeyPasswordArgument.isPresent())
10126    {
10127      final char[] pkPasswordChars =
10128           privateKeyPasswordArgument.getValue().toCharArray();
10129      if ((pkPasswordChars.length < 6) &&
10130          (! (hasCertificateAlias(keystore, alias) ||
10131              hasKeyAlias(keystore, alias))))
10132      {
10133        throw new LDAPException(ResultCode.PARAM_ERROR,
10134             ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
10135      }
10136
10137      return pkPasswordChars;
10138    }
10139
10140
10141    final FileArgument privateKeyPasswordFileArgument =
10142         subCommandParser.getFileArgument(
10143              prefixDash + "private-key-password-file");
10144    if ((privateKeyPasswordFileArgument != null) &&
10145        privateKeyPasswordFileArgument.isPresent())
10146    {
10147      final File f = privateKeyPasswordFileArgument.getValue();
10148      try (final BufferedReader r = new BufferedReader(new FileReader(f)))
10149      {
10150        final String line = r.readLine();
10151        if (line == null)
10152        {
10153          throw new LDAPException(ResultCode.PARAM_ERROR,
10154               ERR_MANAGE_CERTS_GET_PK_PW_EMPTY_FILE.get(f.getAbsolutePath()));
10155        }
10156        else if (r.readLine() != null)
10157        {
10158          throw new LDAPException(ResultCode.PARAM_ERROR,
10159               ERR_MANAGE_CERTS_GET_PK_PW_MULTI_LINE_FILE.get(
10160                    f.getAbsolutePath()));
10161        }
10162        else if (line.isEmpty())
10163        {
10164          throw new LDAPException(ResultCode.PARAM_ERROR,
10165               ERR_MANAGE_CERTS_GET_PK_PW_EMPTY_FILE.get(f.getAbsolutePath()));
10166        }
10167        else
10168        {
10169          if ((line.length() < 6) &&
10170              (! (hasCertificateAlias(keystore, alias) ||
10171                  hasKeyAlias(keystore, alias))))
10172          {
10173            throw new LDAPException(ResultCode.PARAM_ERROR,
10174                 ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
10175          }
10176
10177          return line.toCharArray();
10178        }
10179      }
10180      catch(final LDAPException le)
10181      {
10182        Debug.debugException(le);
10183        throw le;
10184      }
10185      catch (final Exception e)
10186      {
10187        Debug.debugException(e);
10188        throw new LDAPException(ResultCode.LOCAL_ERROR,
10189             ERR_MANAGE_CERTS_GET_PK_PW_ERROR_READING_FILE.get(
10190                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10191             e);
10192      }
10193    }
10194
10195
10196    final BooleanArgument promptArgument =
10197         subCommandParser.getBooleanArgument(
10198              "prompt-for-" + prefixDash + "private-key-password");
10199    if ((promptArgument != null) && promptArgument.isPresent())
10200    {
10201      out();
10202
10203      try
10204      {
10205        if ((hasKeyAlias(keystore, alias) ||
10206             hasCertificateAlias(keystore, alias)) &&
10207            (! "new".equals(prefix)))
10208        {
10209          // This means that the private key already exists, so we just need to
10210          // prompt once.
10211          final String prompt;
10212          if ("current".equals(prefix))
10213          {
10214            prompt =
10215                 INFO_MANAGE_CERTS_GET_PK_PW_CURRENT_PROMPT.get(alias);
10216          }
10217          else
10218          {
10219            prompt =
10220                 INFO_MANAGE_CERTS_GET_PK_PW_EXISTING_PROMPT.get(alias);
10221          }
10222
10223          return promptForPassword(prompt, false);
10224        }
10225        else
10226        {
10227          // This means that we'll be creating a new private key, so we need to
10228          // prompt twice.
10229          while (true)
10230          {
10231            final String prompt;
10232            if ("new".equals(prefix))
10233            {
10234              prompt = INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT.get();
10235            }
10236            else
10237            {
10238              prompt = INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT_1.get(alias);
10239            }
10240
10241            final char[] pwChars = promptForPassword(prompt, false);
10242            if (pwChars.length < 6)
10243            {
10244              wrapErr(0, WRAP_COLUMN,
10245                   ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
10246              err();
10247              continue;
10248            }
10249
10250            final char[] confirmChars = promptForPassword(
10251                 INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT_2.get(), true);
10252
10253            if (Arrays.equals(pwChars, confirmChars))
10254            {
10255              Arrays.fill(confirmChars, '\u0000');
10256              return pwChars;
10257            }
10258            else
10259            {
10260              wrapErr(0, WRAP_COLUMN,
10261                   ERR_MANAGE_CERTS_GET_PK_PW_PROMPT_MISMATCH.get());
10262              err();
10263            }
10264          }
10265        }
10266      }
10267      catch (final LDAPException le)
10268      {
10269        Debug.debugException(le);
10270        throw le;
10271      }
10272      catch (final Exception e)
10273      {
10274        Debug.debugException(e);
10275        throw new LDAPException(ResultCode.LOCAL_ERROR,
10276             ERR_MANAGE_CERTS_GET_PK_PW_PROMPT_ERROR.get(alias,
10277                  StaticUtils.getExceptionMessage(e)),
10278             e);
10279      }
10280    }
10281
10282
10283    return keystorePassword;
10284  }
10285
10286
10287
10288  /**
10289   * Infers the keystore type from the provided keystore file.
10290   *
10291   * @param  keystorePath  The path to the file to examine.
10292   *
10293   * @return  The keystore type inferred from the provided keystore file, or
10294   *          {@code null} if the specified file does not exist.
10295   *
10296   * @throws  LDAPException  If a problem is encountered while trying to infer
10297   *                         the keystore type.
10298   */
10299  private String inferKeystoreType(final File keystorePath)
10300          throws LDAPException
10301  {
10302    if (! keystorePath.exists())
10303    {
10304      final StringArgument keystoreTypeArgument =
10305           subCommandParser.getStringArgument("keystore-type");
10306      if ((keystoreTypeArgument != null) && keystoreTypeArgument.isPresent())
10307      {
10308        final String ktaValue = keystoreTypeArgument.getValue();
10309        if (ktaValue.equalsIgnoreCase("PKCS12") ||
10310            ktaValue.equalsIgnoreCase("PKCS 12") ||
10311            ktaValue.equalsIgnoreCase("PKCS#12") ||
10312            ktaValue.equalsIgnoreCase("PKCS #12"))
10313        {
10314          return "PKCS12";
10315        }
10316        else
10317        {
10318          return "JKS";
10319        }
10320      }
10321
10322      return DEFAULT_KEYSTORE_TYPE;
10323    }
10324
10325
10326    try (final FileInputStream inputStream = new FileInputStream(keystorePath))
10327    {
10328      final int firstByte = inputStream.read();
10329      if (firstByte < 0)
10330      {
10331        throw new LDAPException(ResultCode.PARAM_ERROR,
10332             ERR_MANAGE_CERTS_INFER_KS_TYPE_EMPTY_FILE.get(
10333                  keystorePath.getAbsolutePath()));
10334      }
10335
10336      if (firstByte == 0x30)
10337      {
10338        // This is the correct first byte of a DER sequence, and a PKCS #12
10339        // file is encoded as a DER sequence.
10340        return "PKCS12";
10341      }
10342      else if (firstByte == 0xFE)
10343      {
10344        // This is the correct first byte of a Java JKS keystore, which starts
10345        // with bytes 0xFEEDFEED.
10346        return "JKS";
10347      }
10348      else
10349      {
10350        throw new LDAPException(ResultCode.PARAM_ERROR,
10351             ERR_MANAGE_CERTS_INFER_KS_TYPE_UNEXPECTED_FIRST_BYTE.get(
10352                  keystorePath.getAbsolutePath(),
10353                  StaticUtils.toHex((byte) (firstByte & 0xFF))));
10354      }
10355    }
10356    catch (final LDAPException e)
10357    {
10358      Debug.debugException(e);
10359      throw e;
10360    }
10361    catch (final Exception e)
10362    {
10363      Debug.debugException(e);
10364      throw new LDAPException(ResultCode.LOCAL_ERROR,
10365           ERR_MANAGE_CERTS_INFER_KS_TYPE_ERROR_READING_FILE.get(
10366                keystorePath.getAbsolutePath(),
10367                StaticUtils.getExceptionMessage(e)),
10368           e);
10369    }
10370  }
10371
10372
10373
10374  /**
10375   * Retrieves a user-friendly representation of the provided keystore type.
10376   *
10377   * @param  keystoreType  The keystore type for which to get the user-friendly
10378   *                       name.
10379   *
10380   * @return  "JKS" if the provided keystore type is for a JKS keystore,
10381   *          "PKCS #12" if the provided keystore type is for a PKCS #12
10382   *          keystore, or the provided string if it is for some other keystore
10383   *          type.
10384   */
10385  static String getUserFriendlyKeystoreType(final String keystoreType)
10386  {
10387    if (keystoreType.equalsIgnoreCase("JKS"))
10388    {
10389      return "JKS";
10390    }
10391    else if (keystoreType.equalsIgnoreCase("PKCS12") ||
10392         keystoreType.equalsIgnoreCase("PKCS 12") ||
10393         keystoreType.equalsIgnoreCase("PKCS#12") ||
10394         keystoreType.equalsIgnoreCase("PKCS #12"))
10395    {
10396      return "PKCS #12";
10397    }
10398    else
10399    {
10400      return keystoreType;
10401    }
10402  }
10403
10404
10405
10406  /**
10407   * Gets access to a keystore based on information included in command-line
10408   * arguments.
10409   *
10410   * @param  keystoreType      The keystore type for the keystore to access.
10411   * @param  keystorePath      The path to the keystore file.
10412   * @param  keystorePassword  The password to use to access the keystore.
10413   *
10414   * @return  The configured keystore instance.
10415   *
10416   * @throws  LDAPException  If it is not possible to access the keystore.
10417   */
10418  static KeyStore getKeystore(final String keystoreType,
10419                              final File keystorePath,
10420                              final char[] keystorePassword)
10421          throws LDAPException
10422  {
10423    // Instantiate a keystore instance of the desired keystore type.
10424    final KeyStore keystore;
10425    try
10426    {
10427      keystore = KeyStore.getInstance(keystoreType);
10428    }
10429    catch (final Exception e)
10430    {
10431      Debug.debugException(e);
10432      throw new LDAPException(ResultCode.LOCAL_ERROR,
10433           ERR_MANAGE_CERTS_CANNOT_INSTANTIATE_KS_TYPE.get(keystoreType,
10434                StaticUtils.getExceptionMessage(e)),
10435           e);
10436    }
10437
10438
10439    // Get an input stream that may be used to access the keystore.
10440    final InputStream inputStream;
10441    try
10442    {
10443      if (keystorePath.exists())
10444      {
10445        inputStream = new FileInputStream(keystorePath);
10446      }
10447      else
10448      {
10449        inputStream = null;
10450      }
10451    }
10452    catch (final Exception e)
10453    {
10454      Debug.debugException(e);
10455      throw new LDAPException(ResultCode.LOCAL_ERROR,
10456           ERR_MANAGE_CERTS_CANNOT_OPEN_KS_FILE_FOR_READING.get(
10457                keystorePath.getAbsolutePath(),
10458                StaticUtils.getExceptionMessage(e)),
10459           e);
10460    }
10461
10462    try
10463    {
10464      keystore.load(inputStream, keystorePassword);
10465    }
10466    catch (final Exception e)
10467    {
10468      Debug.debugException(e);
10469      final Throwable cause = e.getCause();
10470      if ((e instanceof IOException) && (cause != null) &&
10471          (cause instanceof UnrecoverableKeyException) &&
10472          (keystorePassword != null))
10473      {
10474        throw new LDAPException(ResultCode.PARAM_ERROR,
10475             ERR_MANAGE_CERTS_CANNOT_LOAD_KS_WRONG_PW.get(
10476                  keystorePath.getAbsolutePath()),
10477             e);
10478      }
10479      else
10480      {
10481        throw new LDAPException(ResultCode.PARAM_ERROR,
10482             ERR_MANAGE_CERTS_ERROR_CANNOT_LOAD_KS.get(
10483                  keystorePath.getAbsolutePath(),
10484                  StaticUtils.getExceptionMessage(e)),
10485             e);
10486      }
10487    }
10488    finally
10489    {
10490      try
10491      {
10492        if (inputStream != null)
10493        {
10494          inputStream.close();
10495        }
10496      }
10497      catch (final Exception e)
10498      {
10499        Debug.debugException(e);
10500      }
10501    }
10502
10503    return keystore;
10504  }
10505
10506
10507
10508  /**
10509   * Reads all of the certificates contained in the specified file.  The file
10510   * must exist and may contain zero or more certificates that are either all in
10511   * PEM format or all in DER format.
10512   *
10513   * @param  f  The path to the certificate file to read.  It must not be
10514   *            {@code null}.
10515   *
10516   * @return  A list of the certificates read from the specified file.
10517   *
10518   * @throws  LDAPException  If a problem is encountered while reading
10519   *                         certificates from the specified file.
10520   */
10521  static List<X509Certificate> readCertificatesFromFile(final File f)
10522         throws LDAPException
10523  {
10524    // Read the first byte of the file to see if it contains DER-formatted data,
10525    // which we can determine by seeing if the first byte is 0x30.
10526    try (final BufferedInputStream inputStream =
10527              new BufferedInputStream(new FileInputStream(f)))
10528    {
10529      inputStream.mark(1);
10530      final int firstByte = inputStream.read();
10531
10532      if (firstByte < 0)
10533      {
10534        // This means that the file is empty.
10535        return Collections.emptyList();
10536      }
10537      else
10538      {
10539        inputStream.reset();
10540      }
10541
10542      final ArrayList<X509Certificate> certList = new ArrayList<>(5);
10543      if ((firstByte & 0xFF) == 0x30)
10544      {
10545        // It is a DER-encoded file.  Read ASN.1 elements and decode them as
10546        // X.509 certificates.
10547        while (true)
10548        {
10549          final ASN1Element certElement;
10550          try
10551          {
10552            certElement = ASN1Element.readFrom(inputStream);
10553          }
10554          catch (final Exception e)
10555          {
10556            Debug.debugException(e);
10557            throw new LDAPException(ResultCode.LOCAL_ERROR,
10558                 ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DER_NOT_VALID_ASN1.get(
10559                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10560                 e);
10561          }
10562
10563          if (certElement == null)
10564          {
10565            // We've reached the end of the input stream.
10566            return certList;
10567          }
10568
10569          try
10570          {
10571            certList.add(new X509Certificate(certElement.encode()));
10572          }
10573          catch (final CertException e)
10574          {
10575            Debug.debugException(e);
10576            throw new LDAPException(ResultCode.PARAM_ERROR,
10577                 ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DER_NOT_VALID_CERT.get(
10578                      f.getAbsolutePath(), e.getMessage()),
10579                 e);
10580          }
10581        }
10582      }
10583      else
10584      {
10585        try (final BufferedReader reader =
10586                  new BufferedReader(new InputStreamReader(inputStream)))
10587        {
10588          boolean inCert = false;
10589          final StringBuilder buffer = new StringBuilder();
10590          while (true)
10591          {
10592            String line = reader.readLine();
10593            if (line == null)
10594            {
10595              if (inCert)
10596              {
10597                throw new LDAPException(ResultCode.PARAM_ERROR,
10598                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_EOF_WITHOUT_END.get(
10599                          f.getAbsolutePath()));
10600              }
10601
10602              return certList;
10603            }
10604
10605            line = line.trim();
10606            if (line.isEmpty() || line.startsWith("#"))
10607            {
10608              continue;
10609            }
10610
10611            if (line.equals("-----BEGIN CERTIFICATE-----"))
10612            {
10613              if (inCert)
10614              {
10615                throw new LDAPException(ResultCode.PARAM_ERROR,
10616                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_MULTIPLE_BEGIN.get(
10617                          f.getAbsolutePath()));
10618              }
10619              else
10620              {
10621                inCert = true;
10622              }
10623            }
10624            else if (line.equals("-----END CERTIFICATE-----"))
10625            {
10626              if (! inCert)
10627              {
10628                throw new LDAPException(ResultCode.PARAM_ERROR,
10629                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_END_WITHOUT_BEGIN.
10630                          get(f.getAbsolutePath()));
10631              }
10632
10633              inCert = false;
10634              final byte[] certBytes;
10635              try
10636              {
10637                certBytes = Base64.decode(buffer.toString());
10638              }
10639              catch (final Exception e)
10640              {
10641                Debug.debugException(e);
10642                throw new LDAPException(ResultCode.PARAM_ERROR,
10643                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_PEM_CERT_NOT_BASE64.
10644                          get(f.getAbsolutePath(),
10645                               StaticUtils.getExceptionMessage(e)),
10646                     e);
10647              }
10648
10649              try
10650              {
10651                certList.add(new X509Certificate(certBytes));
10652              }
10653              catch (final CertException e)
10654              {
10655                Debug.debugException(e);
10656                throw new LDAPException(ResultCode.PARAM_ERROR,
10657                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_PEM_CERT_NOT_CERT.
10658                          get(f.getAbsolutePath(), e.getMessage()),
10659                     e);
10660              }
10661
10662              buffer.setLength(0);
10663            }
10664            else if (inCert)
10665            {
10666              buffer.append(line);
10667            }
10668            else
10669            {
10670              throw new LDAPException(ResultCode.PARAM_ERROR,
10671                   ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DATA_WITHOUT_BEGIN.get(
10672                        f.getAbsolutePath()));
10673            }
10674          }
10675        }
10676      }
10677    }
10678    catch (final LDAPException le)
10679    {
10680      Debug.debugException(le);
10681      throw le;
10682    }
10683    catch (final Exception e)
10684    {
10685      Debug.debugException(e);
10686      throw new LDAPException(ResultCode.LOCAL_ERROR,
10687           ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_READ_ERROR.get(
10688                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10689           e);
10690    }
10691  }
10692
10693
10694
10695  /**
10696   * Reads a private key from the specified file.  The file must exist and must
10697   * contain exactly one PEM-encoded or DER-encoded PKCS #8 private key.
10698   *
10699   * @param  f  The path to the private key file to read.  It must not be
10700   *            {@code null}.
10701   *
10702   * @return  The private key read from the file.
10703   *
10704   * @throws  LDAPException  If a problem is encountered while reading the
10705   *                         private key.
10706   */
10707  static PKCS8PrivateKey readPrivateKeyFromFile(final File f)
10708         throws LDAPException
10709  {
10710    // Read the first byte of the file to see if it contains DER-formatted data,
10711    // which we can determine by seeing if the first byte is 0x30.
10712    try (final BufferedInputStream inputStream =
10713              new BufferedInputStream(new FileInputStream(f)))
10714    {
10715      inputStream.mark(1);
10716      final int firstByte = inputStream.read();
10717
10718      if (firstByte < 0)
10719      {
10720        // This means that the file is empty.
10721        throw new LDAPException(ResultCode.PARAM_ERROR,
10722             ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
10723                  f.getAbsolutePath()));
10724      }
10725      else
10726      {
10727        inputStream.reset();
10728      }
10729
10730      PKCS8PrivateKey privateKey = null;
10731      if ((firstByte & 0xFF) == 0x30)
10732      {
10733        // It is a DER-encoded file.  Read an ASN.1 element and decode it as a
10734        // certificate.
10735        while (true)
10736        {
10737          final ASN1Element pkElement;
10738          try
10739          {
10740            pkElement = ASN1Element.readFrom(inputStream);
10741          }
10742          catch (final Exception e)
10743          {
10744            Debug.debugException(e);
10745            throw new LDAPException(ResultCode.LOCAL_ERROR,
10746                 ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DER_NOT_VALID_ASN1.get(
10747                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10748                 e);
10749          }
10750
10751          if (pkElement == null)
10752          {
10753            // We've reached the end of the input stream.
10754            if (privateKey == null)
10755            {
10756              throw new LDAPException(ResultCode.PARAM_ERROR,
10757                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
10758                        f.getAbsolutePath()));
10759            }
10760            else
10761            {
10762              return privateKey;
10763            }
10764          }
10765          else if (privateKey == null)
10766          {
10767            try
10768            {
10769              privateKey = new PKCS8PrivateKey(pkElement.encode());
10770            }
10771            catch (final Exception e)
10772            {
10773              Debug.debugException(e);
10774              throw new LDAPException(ResultCode.PARAM_ERROR,
10775                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DER_NOT_VALID_PK.get(
10776                        f.getAbsolutePath(), e.getMessage()),
10777                   e);
10778            }
10779          }
10780          else
10781          {
10782            throw new LDAPException(ResultCode.PARAM_ERROR,
10783                 ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_KEYS.get(
10784                      f.getAbsolutePath()));
10785          }
10786        }
10787      }
10788      else
10789      {
10790        try (final BufferedReader reader =
10791                  new BufferedReader(new InputStreamReader(inputStream)))
10792        {
10793          boolean inKey = false;
10794          boolean isRSAKey = false;
10795          final StringBuilder buffer = new StringBuilder();
10796          while (true)
10797          {
10798            String line = reader.readLine();
10799            if (line == null)
10800            {
10801              if (inKey)
10802              {
10803                throw new LDAPException(ResultCode.PARAM_ERROR,
10804                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EOF_WITHOUT_END.get(
10805                          f.getAbsolutePath()));
10806              }
10807
10808              if (privateKey == null)
10809              {
10810                throw new LDAPException(ResultCode.PARAM_ERROR,
10811                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
10812                          f.getAbsolutePath()));
10813              }
10814              else
10815              {
10816                return privateKey;
10817              }
10818            }
10819
10820            line = line.trim();
10821            if (line.isEmpty() || line.startsWith("#"))
10822            {
10823              continue;
10824            }
10825
10826            if (line.equals("-----BEGIN PRIVATE KEY-----") ||
10827                 line.equals("-----BEGIN RSA PRIVATE KEY-----"))
10828            {
10829              if (inKey)
10830              {
10831                throw new LDAPException(ResultCode.PARAM_ERROR,
10832                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_BEGIN.get(
10833                          f.getAbsolutePath()));
10834              }
10835              else if (privateKey != null)
10836              {
10837                throw new LDAPException(ResultCode.PARAM_ERROR,
10838                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_KEYS.get(
10839                          f.getAbsolutePath()));
10840              }
10841              else
10842              {
10843                inKey = true;
10844                if (line.equals("-----BEGIN RSA PRIVATE KEY-----"))
10845                {
10846                  isRSAKey = true;
10847                }
10848              }
10849            }
10850            else if (line.equals("-----END PRIVATE KEY-----") ||
10851                 line.equals("-----END RSA PRIVATE KEY-----"))
10852            {
10853              if (! inKey)
10854              {
10855                throw new LDAPException(ResultCode.PARAM_ERROR,
10856                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_END_WITHOUT_BEGIN.get(
10857                          f.getAbsolutePath()));
10858              }
10859
10860              inKey = false;
10861              byte[] pkBytes;
10862              try
10863              {
10864                pkBytes = Base64.decode(buffer.toString());
10865              }
10866              catch (final Exception e)
10867              {
10868                Debug.debugException(e);
10869                throw new LDAPException(ResultCode.PARAM_ERROR,
10870                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PEM_PK_NOT_BASE64.get(
10871                          f.getAbsolutePath(),
10872                          StaticUtils.getExceptionMessage(e)),
10873                     e);
10874              }
10875
10876              if (isRSAKey)
10877              {
10878                pkBytes = PKCS8PrivateKey.wrapRSAPrivateKey(pkBytes);
10879              }
10880
10881              try
10882              {
10883                privateKey = new PKCS8PrivateKey(pkBytes);
10884              }
10885              catch (final CertException e)
10886              {
10887                Debug.debugException(e);
10888                throw new LDAPException(ResultCode.PARAM_ERROR,
10889                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PEM_PK_NOT_PK.get(
10890                          f.getAbsolutePath(), e.getMessage()),
10891                     e);
10892              }
10893
10894              buffer.setLength(0);
10895            }
10896            else if (inKey)
10897            {
10898              buffer.append(line);
10899            }
10900            else
10901            {
10902              throw new LDAPException(ResultCode.PARAM_ERROR,
10903                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DATA_WITHOUT_BEGIN.get(
10904                        f.getAbsolutePath()));
10905            }
10906          }
10907        }
10908      }
10909    }
10910    catch (final LDAPException le)
10911    {
10912      Debug.debugException(le);
10913      throw le;
10914    }
10915    catch (final Exception e)
10916    {
10917      Debug.debugException(e);
10918      throw new LDAPException(ResultCode.LOCAL_ERROR,
10919           ERR_MANAGE_CERTS_READ_PK_FROM_FILE_READ_ERROR.get(
10920                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10921           e);
10922    }
10923  }
10924
10925
10926
10927  /**
10928   * Reads a certificate signing request from the specified file.  The file must
10929   * exist and must contain exactly one PEM-encoded or DER-encoded PKCS #10
10930   * certificate signing request.
10931   *
10932   * @param  f  The path to the private key file to read.  It must not be
10933   *            {@code null}.
10934   *
10935   * @return  The certificate signing request read from the file.
10936   *
10937   * @throws  LDAPException  If a problem is encountered while reading the
10938   *                         certificate signing request.
10939   */
10940  static PKCS10CertificateSigningRequest
10941              readCertificateSigningRequestFromFile(final File f)
10942         throws LDAPException
10943  {
10944    // Read the first byte of the file to see if it contains DER-formatted data,
10945    // which we can determine by seeing if the first byte is 0x30.
10946    try (final BufferedInputStream inputStream =
10947              new BufferedInputStream(new FileInputStream(f)))
10948    {
10949      inputStream.mark(1);
10950      final int firstByte = inputStream.read();
10951
10952      if (firstByte < 0)
10953      {
10954        // This means that the file is empty.
10955        throw new LDAPException(ResultCode.PARAM_ERROR,
10956             ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
10957                  f.getAbsolutePath()));
10958      }
10959      else
10960      {
10961        inputStream.reset();
10962      }
10963
10964      PKCS10CertificateSigningRequest csr = null;
10965      if ((firstByte & 0xFF) == 0x30)
10966      {
10967        // It is a DER-encoded file.  Read an ASN.1 element and decode it as a
10968        // certificate.
10969        while (true)
10970        {
10971          final ASN1Element csrElement;
10972          try
10973          {
10974            csrElement = ASN1Element.readFrom(inputStream);
10975          }
10976          catch (final Exception e)
10977          {
10978            Debug.debugException(e);
10979            throw new LDAPException(ResultCode.LOCAL_ERROR,
10980                 ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DER_NOT_VALID_ASN1.get(
10981                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
10982                 e);
10983          }
10984
10985          if (csrElement == null)
10986          {
10987            // We've reached the end of the input stream.
10988            if (csr == null)
10989            {
10990              throw new LDAPException(ResultCode.PARAM_ERROR,
10991                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
10992                        f.getAbsolutePath()));
10993            }
10994            else
10995            {
10996              return csr;
10997            }
10998          }
10999          else if (csr == null)
11000          {
11001            try
11002            {
11003              csr = new PKCS10CertificateSigningRequest(csrElement.encode());
11004            }
11005            catch (final Exception e)
11006            {
11007              Debug.debugException(e);
11008              throw new LDAPException(ResultCode.PARAM_ERROR,
11009                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DER_NOT_VALID_CSR.get(
11010                        f.getAbsolutePath(), e.getMessage()),
11011                   e);
11012            }
11013          }
11014          else
11015          {
11016            throw new LDAPException(ResultCode.PARAM_ERROR,
11017                 ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_CSRS.get(
11018                      f.getAbsolutePath()));
11019          }
11020        }
11021      }
11022      else
11023      {
11024        try (final BufferedReader reader =
11025                  new BufferedReader(new InputStreamReader(inputStream)))
11026        {
11027          boolean inCSR = false;
11028          final StringBuilder buffer = new StringBuilder();
11029          while (true)
11030          {
11031            String line = reader.readLine();
11032            if (line == null)
11033            {
11034              if (inCSR)
11035              {
11036                throw new LDAPException(ResultCode.PARAM_ERROR,
11037                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EOF_WITHOUT_END.get(
11038                          f.getAbsolutePath()));
11039              }
11040
11041              if (csr == null)
11042              {
11043                throw new LDAPException(ResultCode.PARAM_ERROR,
11044                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
11045                          f.getAbsolutePath()));
11046              }
11047              else
11048              {
11049                return csr;
11050              }
11051            }
11052
11053            line = line.trim();
11054            if (line.isEmpty() || line.startsWith("#"))
11055            {
11056              continue;
11057            }
11058
11059            if (line.equals("-----BEGIN CERTIFICATE REQUEST-----") ||
11060                line.equals("-----BEGIN NEW CERTIFICATE REQUEST-----"))
11061            {
11062              if (inCSR)
11063              {
11064                throw new LDAPException(ResultCode.PARAM_ERROR,
11065                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_BEGIN.get(
11066                          f.getAbsolutePath()));
11067              }
11068              else if (csr != null)
11069              {
11070                throw new LDAPException(ResultCode.PARAM_ERROR,
11071                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_CSRS.get(
11072                          f.getAbsolutePath()));
11073              }
11074              else
11075              {
11076                inCSR = true;
11077              }
11078            }
11079            else if (line.equals("-----END CERTIFICATE REQUEST-----") ||
11080                 line.equals("-----END NEW CERTIFICATE REQUEST-----"))
11081            {
11082              if (! inCSR)
11083              {
11084                throw new LDAPException(ResultCode.PARAM_ERROR,
11085                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_END_WITHOUT_BEGIN.get(
11086                          f.getAbsolutePath()));
11087              }
11088
11089              inCSR = false;
11090              final byte[] csrBytes;
11091              try
11092              {
11093                csrBytes = Base64.decode(buffer.toString());
11094              }
11095              catch (final Exception e)
11096              {
11097                Debug.debugException(e);
11098                throw new LDAPException(ResultCode.PARAM_ERROR,
11099                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_PEM_CSR_NOT_BASE64.get(
11100                          f.getAbsolutePath(),
11101                          StaticUtils.getExceptionMessage(e)),
11102                     e);
11103              }
11104
11105              try
11106              {
11107                csr = new PKCS10CertificateSigningRequest(csrBytes);
11108              }
11109              catch (final CertException e)
11110              {
11111                Debug.debugException(e);
11112                throw new LDAPException(ResultCode.PARAM_ERROR,
11113                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_PEM_CSR_NOT_CSR.get(
11114                          f.getAbsolutePath(), e.getMessage()),
11115                     e);
11116              }
11117
11118              buffer.setLength(0);
11119            }
11120            else if (inCSR)
11121            {
11122              buffer.append(line);
11123            }
11124            else
11125            {
11126              throw new LDAPException(ResultCode.PARAM_ERROR,
11127                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DATA_WITHOUT_BEGIN.get(
11128                        f.getAbsolutePath()));
11129            }
11130          }
11131        }
11132      }
11133    }
11134    catch (final LDAPException le)
11135    {
11136      Debug.debugException(le);
11137      throw le;
11138    }
11139    catch (final Exception e)
11140    {
11141      Debug.debugException(e);
11142      throw new LDAPException(ResultCode.LOCAL_ERROR,
11143           ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_READ_ERROR.get(
11144                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
11145           e);
11146    }
11147  }
11148
11149
11150
11151  /**
11152   * Retrieves a colon-delimited hexadecimal representation of the contents of
11153   * the provided byte array.
11154   *
11155   * @param  bytes  The byte array for which to get the hexadecimal
11156   *                representation.  It must not be {@code null}.
11157   *
11158   * @return  A colon-delimited hexadecimal representation of the contents of
11159   *          the provided byte array.
11160   */
11161  private static String toColonDelimitedHex(final byte... bytes)
11162  {
11163    final StringBuilder buffer = new StringBuilder(bytes.length * 3);
11164    StaticUtils.toHex(bytes, ":", buffer);
11165    return buffer.toString();
11166  }
11167
11168
11169
11170  /**
11171   * Retrieves a formatted representation of the provided date in a
11172   * human-readable format that includes an offset from the current time.
11173   *
11174   * @param  d  The date to format.  It must not be {@code null}.
11175   *
11176   * @return  A formatted representation of the provided date.
11177   */
11178  private static String formatDateAndTime(final Date d)
11179  {
11180    // Example:  Sunday, January 1, 2017
11181    final String dateFormatString = "EEEE, MMMM d, yyyy";
11182    final String formattedDate =
11183         new SimpleDateFormat(dateFormatString).format(d);
11184
11185    // Example:  12:34:56 AM CDT
11186    final String timeFormatString = "hh:mm:ss aa z";
11187    final String formattedTime =
11188         new SimpleDateFormat(timeFormatString).format(d);
11189
11190    final long providedTime = d.getTime();
11191    final long currentTime = System.currentTimeMillis();
11192    if (providedTime > currentTime)
11193    {
11194      final long secondsInFuture = ((providedTime - currentTime) / 1000L);
11195      final String durationInFuture =
11196           StaticUtils.secondsToHumanReadableDuration(secondsInFuture);
11197      return INFO_MANAGE_CERTS_FORMAT_DATE_AND_TIME_IN_FUTURE.get(formattedDate,
11198           formattedTime, durationInFuture);
11199    }
11200    else
11201    {
11202      final long secondsInPast = ((currentTime - providedTime) / 1000L);
11203      final String durationInPast =
11204           StaticUtils.secondsToHumanReadableDuration(secondsInPast);
11205      return INFO_MANAGE_CERTS_FORMAT_DATE_AND_TIME_IN_PAST.get(formattedDate,
11206           formattedTime, durationInPast);
11207    }
11208  }
11209
11210
11211
11212  /**
11213   * Retrieves a formatted representation of the provided date in a format
11214   * suitable for use as the validity start time value provided to the keytool
11215   * command.
11216   *
11217   * @param  d  The date to format.  It must not be {@code null}.
11218   *
11219   * @return  A formatted representation of the provided date.
11220   */
11221  private static String formatValidityStartTime(final Date d)
11222  {
11223    // Example:  2017/01/01 01:23:45
11224    final String dateFormatString = "yyyy'/'MM'/'dd HH':'mm':'ss";
11225    return new SimpleDateFormat(dateFormatString).format(d);
11226  }
11227
11228
11229
11230  /**
11231   * Retrieves the certificate chain for the specified certificate from the
11232   * given keystore.  If any issuer certificate is not in the provided keystore,
11233   * then the JVM-default trust store will be checked to see if it can be found
11234   * there.
11235   *
11236   * @param  alias             The alias of the certificate for which to get the
11237   *                           certificate chain.  This must not be
11238   *                           {@code null}.
11239   * @param  keystore          The keystore from which to get the certificate
11240   *                           chain.  This must not be {@code null}.
11241   * @param  missingIssuerRef  A reference that will be updated with the DN of a
11242   *                           missing issuer certificate, if any certificate in
11243   *                           the chain cannot be located.  This must not be
11244   *                           {@code null}.
11245   *
11246   * @return  The certificate chain for the specified certificate, or an empty
11247   *          array if no certificate exists with the specified alias.
11248   *
11249   * @throws  LDAPException  If a problem is encountered while getting the
11250   *                         certificate chain.
11251   */
11252  private static X509Certificate[] getCertificateChain(final String alias,
11253                      final KeyStore keystore,
11254                      final AtomicReference<DN> missingIssuerRef)
11255          throws LDAPException
11256  {
11257    try
11258    {
11259      // First, see if the keystore will give us the certificate chain.  This
11260      // will only happen if the alias references an entry that includes the
11261      // private key, but it will save us a lot of work.
11262      final Certificate[] chain = keystore.getCertificateChain(alias);
11263      if ((chain != null) && (chain.length > 0))
11264      {
11265        final X509Certificate[] x509Chain = new X509Certificate[chain.length];
11266        for (int i=0; i < chain.length; i++)
11267        {
11268          x509Chain[i] = new X509Certificate(chain[i].getEncoded());
11269        }
11270        return x509Chain;
11271      }
11272
11273
11274      // We couldn't get the keystore to give us the chain, but see if we can
11275      // get a certificate with the specified alias.
11276      final Certificate endCert = keystore.getCertificate(alias);
11277      if (endCert == null)
11278      {
11279        // This means there isn't any certificate with the specified alias.
11280        // Return an empty chain.
11281        return new X509Certificate[0];
11282      }
11283
11284      final ArrayList<X509Certificate> chainList = new ArrayList<>(5);
11285      X509Certificate certificate = new X509Certificate(endCert.getEncoded());
11286      chainList.add(certificate);
11287
11288      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
11289           new AtomicReference<>();
11290      while (true)
11291      {
11292        final X509Certificate issuerCertificate =
11293             getIssuerCertificate(certificate, keystore,
11294                  jvmDefaultTrustStoreRef, missingIssuerRef);
11295        if (issuerCertificate == null)
11296        {
11297          break;
11298        }
11299
11300        chainList.add(issuerCertificate);
11301        certificate = issuerCertificate;
11302      }
11303
11304      final X509Certificate[] x509Chain = new X509Certificate[chainList.size()];
11305      return chainList.toArray(x509Chain);
11306    }
11307    catch (final Exception e)
11308    {
11309      Debug.debugException(e);
11310      throw new LDAPException(ResultCode.LOCAL_ERROR,
11311           ERR_MANAGE_CERTS_GET_CHAIN_ERROR.get(alias,
11312                StaticUtils.getExceptionMessage(e)),
11313           e);
11314    }
11315  }
11316
11317
11318
11319  /**
11320   * Attempts to retrieve the issuer certificate for the provided certificate
11321   * from the given keystore or the JVM-default trust store.
11322   *
11323   * @param  certificate              The certificate for which to retrieve the
11324   *                                  issuer certificate.
11325   * @param  keystore                 The keystore in which to look for the
11326   *                                  issuer certificate.
11327   * @param  jvmDefaultTrustStoreRef  A reference that will be used to hold the
11328   *                                  JVM-default trust store if it is obtained
11329   *                                  in the process of retrieving the issuer
11330   *                                  certificate.
11331   * @param  missingIssuerRef         A reference that will be updated with the
11332   *                                  DN of a missing issuer certificate, if any
11333   *                                  certificate in the chain cannot be
11334   *                                  located.  This must not be {@code null}.
11335   *
11336   * @return  The issuer certificate for the provided certificate, or
11337   *          {@code null} if the issuer certificate could not be retrieved.
11338   *
11339   * @throws  Exception   If a problem is encountered while trying to retrieve
11340   *                      the issuer certificate.
11341   */
11342  private static X509Certificate getIssuerCertificate(
11343                      final X509Certificate certificate,
11344                      final KeyStore keystore,
11345                      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef,
11346                      final AtomicReference<DN> missingIssuerRef)
11347          throws Exception
11348  {
11349    final DN subjectDN = certificate.getSubjectDN();
11350    final DN issuerDN = certificate.getIssuerDN();
11351    if (subjectDN.equals(issuerDN))
11352    {
11353      // This means that the certificate is self-signed, so there is no issuer.
11354      return null;
11355    }
11356
11357
11358    // See if we can find the issuer certificate in the provided keystore.
11359    X509Certificate issuerCertificate = getIssuerCertificate(certificate,
11360         keystore);
11361    if (issuerCertificate != null)
11362    {
11363      return issuerCertificate;
11364    }
11365
11366
11367    // See if we can get the JVM-default trust store.
11368    KeyStore jvmDefaultTrustStore = jvmDefaultTrustStoreRef.get();
11369    if (jvmDefaultTrustStore == null)
11370    {
11371      if (JVM_DEFAULT_CACERTS_FILE == null)
11372      {
11373        missingIssuerRef.set(issuerDN);
11374        return null;
11375      }
11376
11377      for (final String keystoreType : new String[] { "JKS", "PKCS12" })
11378      {
11379        final KeyStore ks = KeyStore.getInstance(keystoreType);
11380        try (final FileInputStream inputStream =
11381                  new FileInputStream(JVM_DEFAULT_CACERTS_FILE))
11382        {
11383          ks.load(inputStream, null);
11384          jvmDefaultTrustStore = ks;
11385          jvmDefaultTrustStoreRef.set(jvmDefaultTrustStore);
11386          break;
11387        }
11388        catch (final Exception e)
11389        {
11390          Debug.debugException(e);
11391        }
11392      }
11393    }
11394
11395    if (jvmDefaultTrustStore != null)
11396    {
11397      issuerCertificate = getIssuerCertificate(certificate,
11398           jvmDefaultTrustStore);
11399    }
11400
11401    if (issuerCertificate == null)
11402    {
11403      missingIssuerRef.set(issuerDN);
11404    }
11405
11406    return issuerCertificate;
11407  }
11408
11409
11410
11411  /**
11412   * Attempts to retrieve the issuer certificate for the provided certificate
11413   * from the given keystore.
11414   *
11415   * @param  certificate  The certificate for which to retrieve the issuer
11416   *                      certificate.
11417   * @param  keystore     The keystore in which to look for the issuer
11418   *                      certificate.
11419   *
11420   * @return  The issuer certificate for the provided certificate, or
11421   *          {@code null} if the issuer certificate could not be retrieved.
11422   *
11423   * @throws  Exception   If a problem is encountered while trying to retrieve
11424   *                      the issuer certificate.
11425   */
11426  private static X509Certificate getIssuerCertificate(
11427                                      final X509Certificate certificate,
11428                                      final KeyStore keystore)
11429          throws Exception
11430  {
11431    final Enumeration<String> aliases = keystore.aliases();
11432    while (aliases.hasMoreElements())
11433    {
11434      final String alias = aliases.nextElement();
11435
11436      Certificate[] certs = null;
11437      if (hasCertificateAlias(keystore, alias))
11438      {
11439        final Certificate c = keystore.getCertificate(alias);
11440        if (c == null)
11441        {
11442          continue;
11443        }
11444
11445        certs = new Certificate[] { c };
11446      }
11447      else if (hasKeyAlias(keystore, alias))
11448      {
11449        certs = keystore.getCertificateChain(alias);
11450      }
11451
11452      if (certs != null)
11453      {
11454        for (final Certificate c : certs)
11455        {
11456          final X509Certificate xc = new X509Certificate(c.getEncoded());
11457          if (xc.isIssuerFor(certificate))
11458          {
11459            return xc;
11460          }
11461        }
11462      }
11463    }
11464
11465    return null;
11466  }
11467
11468
11469
11470  /**
11471   * Retrieves the authority key identifier value for the provided certificate,
11472   * if present.
11473   *
11474   * @param  c  The certificate for which to retrieve the authority key
11475   *            identifier.
11476   *
11477   * @return  The authority key identifier value for the provided certificate,
11478   *          or {@code null} if the certificate does not have an authority
11479   *          key identifier.
11480   */
11481  private static byte[] getAuthorityKeyIdentifier(final X509Certificate c)
11482  {
11483    for (final X509CertificateExtension extension : c.getExtensions())
11484    {
11485      if (extension instanceof AuthorityKeyIdentifierExtension)
11486      {
11487        final AuthorityKeyIdentifierExtension e =
11488             (AuthorityKeyIdentifierExtension) extension;
11489        if (e.getKeyIdentifier() != null)
11490        {
11491          return e.getKeyIdentifier().getValue();
11492        }
11493      }
11494    }
11495
11496    return null;
11497  }
11498
11499
11500
11501  /**
11502   * Writes the provided keystore to the specified file.  If the keystore file
11503   * already exists, a new temporary file will be created, the old file renamed
11504   * out of the way, the new file renamed into place, and the old file deleted.
11505   * If the keystore file does not exist, then it will simply be created in the
11506   * correct place.
11507   *
11508   * @param  keystore          The keystore to be written.
11509   * @param  keystorePath      The path to the keystore file to be written.
11510   * @param  keystorePassword  The password to use for the keystore.
11511   *
11512   * @throws  LDAPException  If a problem is encountered while writing the
11513   *                         keystore.
11514   */
11515  static void writeKeystore(final KeyStore keystore, final File keystorePath,
11516                            final char[] keystorePassword)
11517          throws LDAPException
11518  {
11519    File copyOfExistingKeystore = null;
11520    final String timestamp =
11521         StaticUtils.encodeGeneralizedTime(System.currentTimeMillis());
11522    if (keystorePath.exists())
11523    {
11524      copyOfExistingKeystore = new File(keystorePath.getAbsolutePath() +
11525           ".backup-" + timestamp);
11526      try
11527      {
11528        Files.copy(keystorePath.toPath(), copyOfExistingKeystore.toPath());
11529      }
11530      catch (final Exception e)
11531      {
11532        Debug.debugException(e);
11533        throw new LDAPException(ResultCode.LOCAL_ERROR,
11534             ERR_MANAGE_CERTS_WRITE_KS_ERROR_COPYING_EXISTING_KS.get(
11535                  keystorePath.getAbsolutePath(),
11536                  copyOfExistingKeystore.getAbsolutePath(),
11537                  StaticUtils.getExceptionMessage(e)),
11538             e);
11539      }
11540    }
11541
11542    try (final FileOutputStream outputStream =
11543              new FileOutputStream(keystorePath))
11544    {
11545      keystore.store(outputStream, keystorePassword);
11546    }
11547    catch (final Exception e)
11548    {
11549      Debug.debugException(e);
11550      if (copyOfExistingKeystore == null)
11551      {
11552        throw new LDAPException(ResultCode.LOCAL_ERROR,
11553             ERR_MANAGE_CERTS_WRITE_KS_ERROR_WRITING_NEW_KS.get(
11554                  keystorePath.getAbsolutePath(),
11555                  StaticUtils.getExceptionMessage(e)),
11556             e);
11557      }
11558      else
11559      {
11560        throw new LDAPException(ResultCode.LOCAL_ERROR,
11561             ERR_MANAGE_CERTS_WRITE_KS_ERROR_OVERWRITING_KS.get(
11562                  keystorePath.getAbsolutePath(),
11563                  StaticUtils.getExceptionMessage(e),
11564                  copyOfExistingKeystore.getAbsolutePath()),
11565             e);
11566      }
11567    }
11568
11569    if (copyOfExistingKeystore != null)
11570    {
11571      try
11572      {
11573        Files.delete(copyOfExistingKeystore.toPath());
11574      }
11575      catch (final Exception e)
11576      {
11577        Debug.debugException(e);
11578        throw new LDAPException(ResultCode.LOCAL_ERROR,
11579             ERR_MANAGE_CERTS_WRITE_KS_ERROR_DELETING_KS_BACKUP.get(
11580                  copyOfExistingKeystore.getAbsolutePath(),
11581                  keystorePath.getAbsolutePath(),
11582                  StaticUtils.getExceptionMessage(e)),
11583             e);
11584      }
11585    }
11586  }
11587
11588
11589
11590  /**
11591   * Indicates whether the provided keystore has a certificate entry with the
11592   * specified alias.
11593   *
11594   * @param  keystore  The keystore to examine.
11595   * @param  alias     The alias for which to make the determination.
11596   *
11597   * @return  {@code true} if the keystore has a certificate entry with the
11598   *          specified alias, or {@code false} if the alias doesn't exist or
11599   *          is associated with some other type of entry (like a key).
11600   */
11601  private static boolean hasCertificateAlias(final KeyStore keystore,
11602                                             final String alias)
11603  {
11604    try
11605    {
11606      return keystore.isCertificateEntry(alias);
11607    }
11608    catch (final Exception e)
11609    {
11610      // This should never happen.  If it does, then we'll assume the alias
11611      // doesn't exist or isn't associated with a certificate.
11612      Debug.debugException(e);
11613      return false;
11614    }
11615  }
11616
11617
11618
11619  /**
11620   * Indicates whether the provided keystore has a key entry with the specified
11621   * alias.
11622   *
11623   * @param  keystore  The keystore to examine.
11624   * @param  alias     The alias for which to make the determination.
11625   *
11626   * @return  {@code true} if the keystore has a key entry with the specified
11627   *          alias, or {@code false} if the alias doesn't exist or is
11628   *          associated with some other type of entry (like a certificate).
11629   */
11630  private static boolean hasKeyAlias(final KeyStore keystore,
11631                                     final String alias)
11632  {
11633    try
11634    {
11635      return keystore.isKeyEntry(alias);
11636    }
11637    catch (final Exception e)
11638    {
11639      // This should never happen.  If it does, then we'll assume the alias
11640      // doesn't exist or isn't associated with a key.
11641      Debug.debugException(e);
11642      return false;
11643    }
11644  }
11645
11646
11647
11648  /**
11649   * Adds arguments for each of the provided extensions to the given list.
11650   *
11651   * @param  keytoolArguments   The list to which the extension arguments should
11652   *                            be added.
11653   * @param  basicConstraints   The basic constraints extension to include.  It
11654   *                            may be {@code null} if this extension should not
11655   *                            be included.
11656   * @param  keyUsage           The key usage extension to include.  It may be
11657   *                            {@code null} if this extension should not be
11658   *                            included.
11659   * @param  extendedKeyUsage   The extended key usage extension to include.  It
11660   *                            may be {@code null} if this extension should not
11661   *                            be included.
11662   * @param  sanValues          The list of subject alternative name values to
11663   *                            include.  It must not be {@code null} but may be
11664   *                            empty.
11665   * @param  ianValues          The list of issuer alternative name values to
11666   *                            include.  It must not be {@code null} but may be
11667   *                            empty.
11668   * @param  genericExtensions  The list of generic extensions to include.  It
11669   *                            must not be {@code null} but may be empty.
11670   */
11671  private static void addExtensionArguments(final List<String> keytoolArguments,
11672               final BasicConstraintsExtension basicConstraints,
11673               final KeyUsageExtension keyUsage,
11674               final ExtendedKeyUsageExtension extendedKeyUsage,
11675               final Set<String> sanValues,
11676               final Set<String> ianValues,
11677               final List<X509CertificateExtension> genericExtensions)
11678  {
11679    if (basicConstraints != null)
11680    {
11681      final StringBuilder basicConstraintsValue = new StringBuilder();
11682      basicConstraintsValue.append("ca:");
11683      basicConstraintsValue.append(basicConstraints.isCA());
11684
11685      if (basicConstraints.getPathLengthConstraint() != null)
11686      {
11687        basicConstraintsValue.append(",pathlen:");
11688        basicConstraintsValue.append(
11689             basicConstraints.getPathLengthConstraint());
11690      }
11691
11692      keytoolArguments.add("-ext");
11693      keytoolArguments.add("BasicConstraints=" + basicConstraintsValue);
11694    }
11695
11696    if (keyUsage != null)
11697    {
11698      final StringBuilder keyUsageValue = new StringBuilder();
11699      if (keyUsage.isDigitalSignatureBitSet())
11700      {
11701        commaAppend(keyUsageValue, "digitalSignature");
11702      }
11703
11704      if (keyUsage.isNonRepudiationBitSet())
11705      {
11706        commaAppend(keyUsageValue, "nonRepudiation");
11707      }
11708
11709      if (keyUsage.isKeyEnciphermentBitSet())
11710      {
11711        commaAppend(keyUsageValue, "keyEncipherment");
11712      }
11713
11714      if (keyUsage.isDataEnciphermentBitSet())
11715      {
11716        commaAppend(keyUsageValue, "dataEncipherment");
11717      }
11718
11719      if (keyUsage.isKeyAgreementBitSet())
11720      {
11721        commaAppend(keyUsageValue, "keyAgreement");
11722      }
11723
11724      if (keyUsage.isKeyCertSignBitSet())
11725      {
11726        commaAppend(keyUsageValue, "keyCertSign");
11727      }
11728
11729      if (keyUsage.isCRLSignBitSet())
11730      {
11731        commaAppend(keyUsageValue, "cRLSign");
11732      }
11733
11734      if (keyUsage.isEncipherOnlyBitSet())
11735      {
11736        commaAppend(keyUsageValue, "encipherOnly");
11737      }
11738
11739      if (keyUsage.isEncipherOnlyBitSet())
11740      {
11741        commaAppend(keyUsageValue, "decipherOnly");
11742      }
11743
11744      keytoolArguments.add("-ext");
11745      keytoolArguments.add("KeyUsage=" + keyUsageValue);
11746    }
11747
11748    if (extendedKeyUsage != null)
11749    {
11750      final StringBuilder extendedKeyUsageValue = new StringBuilder();
11751      for (final OID oid : extendedKeyUsage.getKeyPurposeIDs())
11752      {
11753        final ExtendedKeyUsageID id = ExtendedKeyUsageID.forOID(oid);
11754        if (id == null)
11755        {
11756          commaAppend(extendedKeyUsageValue, oid.toString());
11757        }
11758        else
11759        {
11760          switch (id)
11761          {
11762            case TLS_SERVER_AUTHENTICATION:
11763              commaAppend(extendedKeyUsageValue, "serverAuth");
11764              break;
11765            case TLS_CLIENT_AUTHENTICATION:
11766              commaAppend(extendedKeyUsageValue, "clientAuth");
11767              break;
11768            case CODE_SIGNING:
11769              commaAppend(extendedKeyUsageValue, "codeSigning");
11770              break;
11771            case EMAIL_PROTECTION:
11772              commaAppend(extendedKeyUsageValue, "emailProtection");
11773              break;
11774            case TIME_STAMPING:
11775              commaAppend(extendedKeyUsageValue, "timeStamping");
11776              break;
11777            case OCSP_SIGNING:
11778              commaAppend(extendedKeyUsageValue, "OCSPSigning");
11779              break;
11780            default:
11781              // This should never happen.
11782              commaAppend(extendedKeyUsageValue, id.getOID().toString());
11783              break;
11784          }
11785        }
11786      }
11787
11788      keytoolArguments.add("-ext");
11789      keytoolArguments.add("ExtendedKeyUsage=" + extendedKeyUsageValue);
11790    }
11791
11792    if (! sanValues.isEmpty())
11793    {
11794      final StringBuilder subjectAltNameValue = new StringBuilder();
11795      for (final String sanValue : sanValues)
11796      {
11797        commaAppend(subjectAltNameValue, sanValue);
11798      }
11799
11800      keytoolArguments.add("-ext");
11801      keytoolArguments.add("SAN=" + subjectAltNameValue);
11802    }
11803
11804    if (! ianValues.isEmpty())
11805    {
11806      final StringBuilder issuerAltNameValue = new StringBuilder();
11807      for (final String ianValue : ianValues)
11808      {
11809        commaAppend(issuerAltNameValue, ianValue);
11810      }
11811
11812      keytoolArguments.add("-ext");
11813      keytoolArguments.add("IAN=" + issuerAltNameValue);
11814    }
11815
11816    for (final X509CertificateExtension e : genericExtensions)
11817    {
11818      keytoolArguments.add("-ext");
11819      if (e.isCritical())
11820      {
11821        keytoolArguments.add(e.getOID().toString() + ":critical=" +
11822             toColonDelimitedHex(e.getValue()));
11823      }
11824      else
11825      {
11826        keytoolArguments.add(e.getOID().toString() + '=' +
11827             toColonDelimitedHex(e.getValue()));
11828      }
11829    }
11830  }
11831
11832
11833
11834  /**
11835   * Appends the provided value to the given buffer.  If the buffer is not
11836   * empty, the new value will be preceded by a comma.  There will not be any
11837   * spaces on either side of the comma.
11838   *
11839   * @param  buffer  The buffer to which the value should be appended.
11840   * @param  value   The value to append to the buffer.
11841   */
11842  private static void commaAppend(final StringBuilder buffer,
11843                                  final String value)
11844  {
11845    if (buffer.length() > 0)
11846    {
11847      buffer.append(',');
11848    }
11849
11850    buffer.append(value);
11851  }
11852
11853
11854
11855  /**
11856   * Retrieves a set of information that may be used to generate example usage
11857   * information.  Each element in the returned map should consist of a map
11858   * between an example set of arguments and a string that describes the
11859   * behavior of the tool when invoked with that set of arguments.
11860   *
11861   * @return  A set of information that may be used to generate example usage
11862   *          information.  It may be {@code null} or empty if no example usage
11863   *          information is available.
11864   */
11865  @Override()
11866  public LinkedHashMap<String[],String> getExampleUsages()
11867  {
11868    final String keystorePath = getPlatformSpecificPath("config", "keystore");
11869    final String keystorePWPath =
11870         getPlatformSpecificPath("config", "keystore.pin");
11871    final String privateKeyPWPath =
11872         getPlatformSpecificPath("config", "server-cert-private-key.pin");
11873    final String exportCertOutputFile =
11874         getPlatformSpecificPath("server-cert.crt");
11875    final String exportKeyOutputFile =
11876         getPlatformSpecificPath("server-cert.private-key");
11877    final String genCSROutputFile = getPlatformSpecificPath("server-cert.csr");
11878    final String truststorePath =
11879         getPlatformSpecificPath("config", "truststore");
11880    final String truststorePWPath =
11881         getPlatformSpecificPath("config", "truststore.pin");
11882
11883    final LinkedHashMap<String[],String> examples =
11884         new LinkedHashMap<>(StaticUtils.computeMapCapacity(20));
11885
11886    examples.put(
11887         new String[]
11888         {
11889           "list-certificates",
11890           "--keystore", keystorePath,
11891           "--keystore-password-file", keystorePWPath,
11892           "--verbose",
11893           "--display-keytool-command"
11894         },
11895         INFO_MANAGE_CERTS_EXAMPLE_LIST_1.get(keystorePath));
11896
11897    examples.put(
11898         new String[]
11899         {
11900           "export-certificate",
11901           "--keystore", keystorePath,
11902           "--keystore-password-file", keystorePWPath,
11903           "--alias", "server-cert",
11904           "--output-file", exportCertOutputFile,
11905           "--output-format", "PEM",
11906           "--verbose",
11907           "--display-keytool-command"
11908         },
11909         INFO_MANAGE_CERTS_EXAMPLE_EXPORT_CERT_1.get(keystorePath,
11910              exportCertOutputFile));
11911
11912    examples.put(
11913         new String[]
11914         {
11915           "export-private-key",
11916           "--keystore", keystorePath,
11917           "--keystore-password-file", keystorePWPath,
11918           "--private-key-password-file", privateKeyPWPath,
11919           "--alias", "server-cert",
11920           "--output-file", exportKeyOutputFile,
11921           "--output-format", "PEM",
11922           "--verbose",
11923           "--display-keytool-command"
11924         },
11925         INFO_MANAGE_CERTS_EXAMPLE_EXPORT_KEY_1.get(keystorePath,
11926              exportKeyOutputFile));
11927
11928    examples.put(
11929         new String[]
11930         {
11931           "import-certificate",
11932           "--keystore", keystorePath,
11933           "--keystore-type", "JKS",
11934           "--keystore-password-file", keystorePWPath,
11935           "--alias", "server-cert",
11936           "--certificate-file", exportCertOutputFile,
11937           "--private-key-file", exportKeyOutputFile,
11938           "--display-keytool-command"
11939         },
11940         INFO_MANAGE_CERTS_EXAMPLE_IMPORT_1.get(exportCertOutputFile,
11941              exportKeyOutputFile, keystorePath));
11942
11943    examples.put(
11944         new String[]
11945         {
11946           "delete-certificate",
11947           "--keystore", keystorePath,
11948           "--keystore-password-file", keystorePWPath,
11949           "--alias", "server-cert"
11950         },
11951         INFO_MANAGE_CERTS_EXAMPLE_DELETE_1.get(keystorePath));
11952
11953    examples.put(
11954         new String[]
11955         {
11956           "generate-self-signed-certificate",
11957           "--keystore", keystorePath,
11958           "--keystore-type", "PKCS12",
11959           "--keystore-password-file", keystorePWPath,
11960           "--alias", "ca-cert",
11961           "--subject-dn", "CN=Example Authority,O=Example Corporation,C=US",
11962           "--days-valid", "7300",
11963           "--validity-start-time", "20170101000000",
11964           "--key-algorithm", "RSA",
11965           "--key-size-bits", "4096",
11966           "--signature-algorithm", "SHA256withRSA",
11967           "--basic-constraints-is-ca", "true",
11968           "--key-usage", "key-cert-sign",
11969           "--key-usage", "crl-sign",
11970           "--display-keytool-command"
11971         },
11972         INFO_MANAGE_CERTS_EXAMPLE_GEN_CERT_1.get(keystorePath));
11973
11974    examples.put(
11975         new String[]
11976         {
11977           "generate-certificate-signing-request",
11978           "--keystore", keystorePath,
11979           "--keystore-type", "PKCS12",
11980           "--keystore-password-file", keystorePWPath,
11981           "--output-file", genCSROutputFile,
11982           "--alias", "server-cert",
11983           "--subject-dn", "CN=ldap.example.com,O=Example Corporation,C=US",
11984           "--key-algorithm", "EC",
11985           "--key-size-bits", "256",
11986           "--signature-algorithm", "SHA256withECDSA",
11987           "--subject-alternative-name-dns", "ldap1.example.com",
11988           "--subject-alternative-name-dns", "ldap2.example.com",
11989           "--extended-key-usage", "server-auth",
11990           "--extended-key-usage", "client-auth",
11991           "--display-keytool-command"
11992         },
11993         INFO_MANAGE_CERTS_EXAMPLE_GEN_CSR_1.get(keystorePath,
11994              genCSROutputFile));
11995
11996    examples.put(
11997         new String[]
11998         {
11999           "generate-certificate-signing-request",
12000           "--keystore", keystorePath,
12001           "--keystore-password-file", keystorePWPath,
12002           "--alias", "server-cert",
12003           "--use-existing-key-pair",
12004           "--inherit-extensions",
12005           "--display-keytool-command"
12006         },
12007         INFO_MANAGE_CERTS_EXAMPLE_GEN_CSR_2.get(keystorePath));
12008
12009    examples.put(
12010         new String[]
12011         {
12012           "sign-certificate-signing-request",
12013           "--keystore", keystorePath,
12014           "--keystore-password-file", keystorePWPath,
12015           "--request-input-file", genCSROutputFile,
12016           "--certificate-output-file", exportCertOutputFile,
12017           "--alias", "ca-cert",
12018           "--days-valid", "730",
12019           "--include-requested-extensions",
12020           "--display-keytool-command"
12021         },
12022         INFO_MANAGE_CERTS_EXAMPLE_SIGN_CERT_1.get(keystorePath,
12023              genCSROutputFile, exportCertOutputFile));
12024
12025    examples.put(
12026         new String[]
12027         {
12028           "change-certificate-alias",
12029           "--keystore", keystorePath,
12030           "--keystore-password-file", keystorePWPath,
12031           "--current-alias", "server-cert",
12032           "--new-alias", "server-certificate",
12033           "--display-keytool-command"
12034         },
12035         INFO_MANAGE_CERTS_EXAMPLE_CHANGE_ALIAS_1.get(keystorePath,
12036              genCSROutputFile, exportCertOutputFile));
12037
12038    examples.put(
12039         new String[]
12040         {
12041           "change-keystore-password",
12042           "--keystore", getPlatformSpecificPath("config", "keystore"),
12043           "--current-keystore-password-file",
12044                getPlatformSpecificPath("config", "current.pin"),
12045           "--new-keystore-password-file",
12046                getPlatformSpecificPath("config", "new.pin"),
12047           "--display-keytool-command"
12048         },
12049         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_EXAMPLE_1.get(
12050              getPlatformSpecificPath("config", "keystore"),
12051              getPlatformSpecificPath("config", "current.pin"),
12052              getPlatformSpecificPath("config", "new.pin")));
12053
12054    examples.put(
12055         new String[]
12056         {
12057           "trust-server-certificate",
12058           "--hostname", "ldap.example.com",
12059           "--port", "636",
12060           "--keystore", truststorePath,
12061           "--keystore-password-file", truststorePWPath,
12062           "--alias", "ldap.example.com:636"
12063         },
12064         INFO_MANAGE_CERTS_EXAMPLE_TRUST_SERVER_1.get(truststorePath));
12065
12066    examples.put(
12067         new String[]
12068         {
12069           "check-certificate-usability",
12070           "--keystore", keystorePath,
12071           "--keystore-password-file", keystorePWPath,
12072           "--alias", "server-cert"
12073         },
12074         INFO_MANAGE_CERTS_EXAMPLE_CHECK_USABILITY_1.get(keystorePath));
12075
12076    examples.put(
12077         new String[]
12078         {
12079           "display-certificate-file",
12080           "--certificate-file", exportCertOutputFile,
12081           "--verbose",
12082           "--display-keytool-command"
12083         },
12084         INFO_MANAGE_CERTS_EXAMPLE_DISPLAY_CERT_1.get(keystorePath));
12085
12086    examples.put(
12087         new String[]
12088         {
12089           "display-certificate-signing-request-file",
12090           "--certificate-signing-request-file", genCSROutputFile,
12091           "--display-keytool-command"
12092         },
12093         INFO_MANAGE_CERTS_EXAMPLE_DISPLAY_CSR_1.get(keystorePath));
12094
12095    examples.put(
12096         new String[]
12097         {
12098           "--help-subcommands"
12099         },
12100         INFO_MANAGE_CERTS_EXAMPLE_HELP_SUBCOMMANDS_1.get(keystorePath));
12101
12102    return examples;
12103  }
12104}