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