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