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