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