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