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