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