001/*
002 * Copyright 2017-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2017-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2017-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util.ssl.cert;
037
038
039
040import java.io.BufferedInputStream;
041import java.io.BufferedReader;
042import java.io.ByteArrayInputStream;
043import java.io.File;
044import java.io.FileInputStream;
045import java.io.FileOutputStream;
046import java.io.InputStream;
047import java.io.InputStreamReader;
048import java.io.IOException;
049import java.io.OutputStream;
050import java.io.PrintStream;
051import java.nio.file.Files;
052import java.net.InetAddress;
053import java.security.Key;
054import java.security.KeyPair;
055import java.security.KeyStore;
056import java.security.PrivateKey;
057import java.security.Provider;
058import java.security.PublicKey;
059import java.security.UnrecoverableKeyException;
060import java.security.cert.Certificate;
061import java.text.SimpleDateFormat;
062import java.util.ArrayList;
063import java.util.Arrays;
064import java.util.Collections;
065import java.util.Date;
066import java.util.Enumeration;
067import java.util.Iterator;
068import java.util.LinkedHashMap;
069import java.util.LinkedHashSet;
070import java.util.List;
071import java.util.Map;
072import java.util.Set;
073import java.util.concurrent.LinkedBlockingQueue;
074import java.util.concurrent.TimeUnit;
075import java.util.concurrent.atomic.AtomicReference;
076
077import com.unboundid.asn1.ASN1BitString;
078import com.unboundid.asn1.ASN1Element;
079import com.unboundid.ldap.sdk.DN;
080import com.unboundid.ldap.sdk.LDAPConnectionOptions;
081import com.unboundid.ldap.sdk.LDAPException;
082import com.unboundid.ldap.sdk.ResultCode;
083import com.unboundid.ldap.sdk.Version;
084import com.unboundid.util.Base64;
085import com.unboundid.util.BouncyCastleFIPSHelper;
086import com.unboundid.util.ByteStringBuffer;
087import com.unboundid.util.CommandLineTool;
088import com.unboundid.util.CryptoHelper;
089import com.unboundid.util.Debug;
090import com.unboundid.util.NotNull;
091import com.unboundid.util.Nullable;
092import com.unboundid.util.OID;
093import com.unboundid.util.ObjectPair;
094import com.unboundid.util.PasswordReader;
095import com.unboundid.util.StaticUtils;
096import com.unboundid.util.ThreadSafety;
097import com.unboundid.util.ThreadSafetyLevel;
098import com.unboundid.util.Validator;
099import com.unboundid.util.args.ArgumentException;
100import com.unboundid.util.args.ArgumentParser;
101import com.unboundid.util.args.BooleanArgument;
102import com.unboundid.util.args.BooleanValueArgument;
103import com.unboundid.util.args.DNArgument;
104import com.unboundid.util.args.FileArgument;
105import com.unboundid.util.args.IA5StringArgumentValueValidator;
106import com.unboundid.util.args.IPAddressArgumentValueValidator;
107import com.unboundid.util.args.IntegerArgument;
108import com.unboundid.util.args.OIDArgumentValueValidator;
109import com.unboundid.util.args.StringArgument;
110import com.unboundid.util.args.TimestampArgument;
111import com.unboundid.util.args.SubCommand;
112import com.unboundid.util.ssl.JVMDefaultTrustManager;
113import com.unboundid.util.ssl.PKCS11KeyManager;
114
115import static com.unboundid.util.ssl.cert.CertMessages.*;
116
117
118
119/**
120 * This class provides a tool that can be used to manage X.509 certificates for
121 * use in TLS communication.
122 */
123@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
124public final class ManageCertificates
125       extends CommandLineTool
126{
127  /**
128   * The path to the keystore with the JVM's set of default trusted issuer
129   * certificates.
130   */
131  @Nullable private static final File JVM_DEFAULT_CACERTS_FILE;
132  static
133  {
134    File caCertsFile;
135    try
136    {
137      caCertsFile = JVMDefaultTrustManager.getInstance().getCACertsFile();
138    }
139    catch (final Exception e)
140    {
141      Debug.debugException(e);
142      caCertsFile = null;
143    }
144
145    JVM_DEFAULT_CACERTS_FILE = caCertsFile;
146  }
147
148
149
150  /**
151   * The name of the keystore type that should be used for the Bouncy Castle
152   * FIPS 140-2-compliant keystore.
153   */
154  @NotNull private static final String BCFKS_KEYSTORE_TYPE =
155       BouncyCastleFIPSHelper.FIPS_KEY_STORE_TYPE;
156
157
158
159  /**
160   * The name of the BCFKS keystore type, formatted in all lowercase.
161   */
162  @NotNull private static final String BCFKS_KEYSTORE_TYPE_LC =
163       BCFKS_KEYSTORE_TYPE.toLowerCase();
164
165
166
167  /**
168   * The name of a system property that can be used to specify the default
169   * keystore type for new keystores.
170   */
171  @NotNull private static final String PROPERTY_DEFAULT_KEYSTORE_TYPE =
172       ManageCertificates.class.getName() + ".defaultKeystoreType";
173
174
175
176  /**
177   * The default keystore type that will be used for new keystores when the
178   * type is not specified.
179   */
180  @NotNull private static final String DEFAULT_KEYSTORE_TYPE;
181  static
182  {
183    final String propertyValue =
184         StaticUtils.getSystemProperty(PROPERTY_DEFAULT_KEYSTORE_TYPE);
185    if (CryptoHelper.usingFIPSMode() ||
186         ((propertyValue != null) && propertyValue.equalsIgnoreCase(
187              BCFKS_KEYSTORE_TYPE)))
188    {
189      DEFAULT_KEYSTORE_TYPE = BCFKS_KEYSTORE_TYPE;
190    }
191    else if ((propertyValue != null) &&
192         (propertyValue.equalsIgnoreCase("PKCS12") ||
193              propertyValue.equalsIgnoreCase("PKCS#12") ||
194              propertyValue.equalsIgnoreCase("PKCS #12") ||
195              propertyValue.equalsIgnoreCase("PKCS 12")))
196    {
197      DEFAULT_KEYSTORE_TYPE = CryptoHelper.KEY_STORE_TYPE_PKCS_12;
198    }
199    else
200    {
201      DEFAULT_KEYSTORE_TYPE = CryptoHelper.KEY_STORE_TYPE_JKS;
202    }
203  }
204
205
206
207  /**
208   * The column at which to wrap long lines of output.
209   */
210  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
211
212
213
214  /**
215   * The set of values that will be allowed for the keystore type argument.
216   */
217  @NotNull private static final Set<String> ALLOWED_KEYSTORE_TYPE_VALUES =
218       StaticUtils.setOf("jks",
219            "pkcs11", "pkcs 11", "pkcs#11", "pkcs #11",
220            "pkcs12", "pkcs 12", "pkcs#12", "pkcs #12",
221            BCFKS_KEYSTORE_TYPE_LC);
222
223
224
225  // The global argument parser used by this tool.
226  @Nullable private volatile ArgumentParser globalParser = null;
227
228  // The argument parser for the selected subcommand.
229  @Nullable private volatile ArgumentParser subCommandParser = null;
230
231  // The input stream to use for standard input.
232  @NotNull private final InputStream in;
233
234
235
236  /**
237   * Invokes this tool with the default standard output and standard error and
238   * the provided set of arguments.
239   *
240   * @param  args  The command-line arguments provided to this program.
241   */
242  public static void main(@NotNull final String... args)
243  {
244    final ResultCode resultCode = main(System.in, System.out, System.err, args);
245    if (resultCode != ResultCode.SUCCESS)
246    {
247      System.exit(Math.max(1, Math.min(resultCode.intValue(), 255)));
248    }
249  }
250
251
252
253  /**
254   * Invokes this tool with the provided output and error streams and set of
255   * arguments.
256   *
257   * @param  in    The input stream to use for standard input.  It may be
258   *               {@code null} if no input stream should be available.
259   * @param  out   The output stream to use for standard output.  It may be
260   *               {@code null} if standard output should be suppressed.
261   * @param  err   The output stream to use for standard error.  It may be
262   *               {@code null} if standard error should be suppressed.
263   * @param  args  The command-line arguments provided to this program.
264   *
265   * @return  The result code obtained from tool processing.
266   */
267  @NotNull()
268  public static ResultCode main(@Nullable final InputStream in,
269                                @Nullable final OutputStream out,
270                                @Nullable final OutputStream err,
271                                @NotNull final String... args)
272  {
273    final ManageCertificates manageCertificates =
274         new ManageCertificates(in, out, err);
275    return manageCertificates.runTool(args);
276  }
277
278
279
280  /**
281   * Creates a new instance of this tool with the provided output and error
282   * streams.  Standard input will bot be available.
283   *
284   * @param  out  The output stream to use for standard output.  It may be
285   *              {@code null} if standard output should be suppressed.
286   * @param  err  The output stream to use for standard error.  It may be
287   *              {@code null} if standard error should be suppressed.
288   */
289  public ManageCertificates(@Nullable final OutputStream out,
290                            @Nullable final OutputStream err)
291  {
292    this(null, out, err);
293  }
294
295
296
297  /**
298   * Creates a new instance of this tool with the provided output and error
299   * streams.
300   *
301   * @param  in   The input stream to use for standard input.  It may be
302   *              {@code null} if no input stream should be available.
303   * @param  out  The output stream to use for standard output.  It may be
304   *              {@code null} if standard output should be suppressed.
305   * @param  err  The output stream to use for standard error.  It may be
306   *              {@code null} if standard error should be suppressed.
307   */
308  public ManageCertificates(@Nullable final InputStream in,
309                            @Nullable final OutputStream out,
310                            @Nullable final OutputStream err)
311  {
312    super(out, err);
313
314    if (in == null)
315    {
316      this.in = new ByteArrayInputStream(StaticUtils.NO_BYTES);
317    }
318    else
319    {
320      this.in = in;
321    }
322  }
323
324
325
326  /**
327   * Retrieves the name of this tool.  It should be the name of the command used
328   * to invoke this tool.
329   *
330   * @return  The name for this tool.
331   */
332  @Override()
333  @NotNull()
334  public String getToolName()
335  {
336    return "manage-certificates";
337  }
338
339
340
341  /**
342   * Retrieves a human-readable description for this tool.
343   *
344   * @return  A human-readable description for this tool.
345   */
346  @Override()
347  @NotNull()
348  public String getToolDescription()
349  {
350    return INFO_MANAGE_CERTS_TOOL_DESC.get();
351  }
352
353
354
355  /**
356   * Retrieves a version string for this tool, if available.
357   *
358   * @return  A version string for this tool, or {@code null} if none is
359   *          available.
360   */
361  @Override()
362  @NotNull()
363  public String getToolVersion()
364  {
365    return Version.NUMERIC_VERSION_STRING;
366  }
367
368
369
370  /**
371   * Indicates whether this tool should provide support for an interactive mode,
372   * in which the tool offers a mode in which the arguments can be provided in
373   * a text-driven menu rather than requiring them to be given on the command
374   * line.  If interactive mode is supported, it may be invoked using the
375   * "--interactive" argument.  Alternately, if interactive mode is supported
376   * and {@link #defaultsToInteractiveMode()} returns {@code true}, then
377   * interactive mode may be invoked by simply launching the tool without any
378   * arguments.
379   *
380   * @return  {@code true} if this tool supports interactive mode, or
381   *          {@code false} if not.
382   */
383  @Override()
384  public boolean supportsInteractiveMode()
385  {
386    return true;
387  }
388
389
390
391  /**
392   * Indicates whether this tool defaults to launching in interactive mode if
393   * the tool is invoked without any command-line arguments.  This will only be
394   * used if {@link #supportsInteractiveMode()} returns {@code true}.
395   *
396   * @return  {@code true} if this tool defaults to using interactive mode if
397   *          launched without any command-line arguments, or {@code false} if
398   *          not.
399   */
400  @Override()
401  public boolean defaultsToInteractiveMode()
402  {
403    return true;
404  }
405
406
407
408  /**
409   * Indicates whether this tool supports the use of a properties file for
410   * specifying default values for arguments that aren't specified on the
411   * command line.
412   *
413   * @return  {@code true} if this tool supports the use of a properties file
414   *          for specifying default values for arguments that aren't specified
415   *          on the command line, or {@code false} if not.
416   */
417  @Override()
418  public boolean supportsPropertiesFile()
419  {
420    return true;
421  }
422
423
424
425  /**
426   * Indicates whether this tool should provide arguments for redirecting output
427   * to a file.  If this method returns {@code true}, then the tool will offer
428   * an "--outputFile" argument that will specify the path to a file to which
429   * all standard output and standard error content will be written, and it will
430   * also offer a "--teeToStandardOut" argument that can only be used if the
431   * "--outputFile" argument is present and will cause all output to be written
432   * to both the specified output file and to standard output.
433   *
434   * @return  {@code true} if this tool should provide arguments for redirecting
435   *          output to a file, or {@code false} if not.
436   */
437  @Override()
438  protected boolean supportsOutputFile()
439  {
440    return false;
441  }
442
443
444
445  /**
446   * {@inheritDoc}
447   */
448  @Override()
449  protected boolean supportsDebugLogging()
450  {
451    return true;
452  }
453
454
455
456  /**
457   * Indicates whether to log messages about the launch and completion of this
458   * tool into the invocation log of Ping Identity server products that may
459   * include it.  This method is not needed for tools that are not expected to
460   * be part of the Ping Identity server products suite.  Further, this value
461   * may be overridden by settings in the server's
462   * tool-invocation-logging.properties file.
463   * <BR><BR>
464   * This method should generally return {@code true} for tools that may alter
465   * the server configuration, data, or other state information, and
466   * {@code false} for tools that do not make any changes.
467   *
468   * @return  {@code true} if Ping Identity server products should include
469   *          messages about the launch and completion of this tool in tool
470   *          invocation log files by default, or {@code false} if not.
471   */
472  @Override()
473  protected boolean logToolInvocationByDefault()
474  {
475    return true;
476  }
477
478
479
480  /**
481   * Adds the command-line arguments supported for use with this tool to the
482   * provided argument parser.  The tool may need to retain references to the
483   * arguments (and/or the argument parser, if trailing arguments are allowed)
484   * to it in order to obtain their values for use in later processing.
485   *
486   * @param  parser  The argument parser to which the arguments are to be added.
487   *
488   * @throws  ArgumentException  If a problem occurs while adding any of the
489   *                             tool-specific arguments to the provided
490   *                             argument parser.
491   */
492  @Override()
493  public void addToolArguments(@NotNull final ArgumentParser parser)
494         throws ArgumentException
495  {
496    globalParser = parser;
497
498
499    // Define the "list-certificates" subcommand and all of its arguments.
500    final ArgumentParser listCertsParser = new ArgumentParser(
501         "list-certificates", INFO_MANAGE_CERTS_SC_LIST_CERTS_DESC.get());
502
503    final FileArgument listCertsKeystore = new FileArgument(null, "keystore",
504         (JVM_DEFAULT_CACERTS_FILE == null), 1, null,
505         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_DESC.get(), true, true,  true,
506         false);
507    listCertsKeystore.addLongIdentifier("keystore-path", true);
508    listCertsKeystore.addLongIdentifier("keystorePath", true);
509    listCertsKeystore.addLongIdentifier("keystore-file", true);
510    listCertsKeystore.addLongIdentifier("keystoreFile", true);
511    listCertsParser.addArgument(listCertsKeystore);
512
513    if (JVM_DEFAULT_CACERTS_FILE != null)
514    {
515      final BooleanArgument listCertsUseJVMDefault = new BooleanArgument(null,
516           "use-jvm-default-trust-store", 1,
517           INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_JVM_DEFAULT_DESC.get(
518                JVM_DEFAULT_CACERTS_FILE.getAbsolutePath()));
519      listCertsUseJVMDefault.addLongIdentifier("useJVMDefaultTrustStore", true);
520      listCertsUseJVMDefault.addLongIdentifier("jvm-default", true);
521      listCertsUseJVMDefault.addLongIdentifier("jvmDefault", true);
522      listCertsParser.addArgument(listCertsUseJVMDefault);
523
524      listCertsParser.addRequiredArgumentSet(listCertsUseJVMDefault,
525           listCertsKeystore);
526      listCertsParser.addExclusiveArgumentSet(listCertsUseJVMDefault,
527           listCertsKeystore);
528    }
529
530    final StringArgument listCertsKeystorePassword = new StringArgument(null,
531         "keystore-password", false, 1,
532         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
533         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_PW_DESC.get());
534    listCertsKeystorePassword.addLongIdentifier("keystorePassword", true);
535    listCertsKeystorePassword.addLongIdentifier("keystore-passphrase", true);
536    listCertsKeystorePassword.addLongIdentifier("keystorePassphrase", true);
537    listCertsKeystorePassword.addLongIdentifier("keystore-pin", true);
538    listCertsKeystorePassword.addLongIdentifier("keystorePIN", true);
539    listCertsKeystorePassword.addLongIdentifier("storepass", true);
540    listCertsKeystorePassword.setSensitive(true);
541    listCertsParser.addArgument(listCertsKeystorePassword);
542
543    final FileArgument listCertsKeystorePasswordFile = new FileArgument(null,
544         "keystore-password-file", false, 1, null,
545         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_PW_FILE_DESC.get(), true, true,
546         true, false);
547    listCertsKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
548         true);
549    listCertsKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
550         true);
551    listCertsKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
552         true);
553    listCertsKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
554         true);
555    listCertsKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
556    listCertsParser.addArgument(listCertsKeystorePasswordFile);
557
558    final BooleanArgument listCertsPromptForKeystorePassword =
559         new BooleanArgument(null, "prompt-for-keystore-password",
560        INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_PROMPT_FOR_KS_PW_DESC.get());
561    listCertsPromptForKeystorePassword.addLongIdentifier(
562         "promptForKeystorePassword", true);
563    listCertsPromptForKeystorePassword.addLongIdentifier(
564         "prompt-for-keystore-passphrase", true);
565    listCertsPromptForKeystorePassword.addLongIdentifier(
566         "promptForKeystorePassphrase", true);
567    listCertsPromptForKeystorePassword.addLongIdentifier(
568         "prompt-for-keystore-pin", true);
569    listCertsPromptForKeystorePassword.addLongIdentifier(
570         "promptForKeystorePIN", true);
571    listCertsParser.addArgument(listCertsPromptForKeystorePassword);
572
573    final StringArgument listCertsKeystoreType = new StringArgument(null,
574         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
575         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_KS_TYPE_DESC.get(),
576         ALLOWED_KEYSTORE_TYPE_VALUES);
577    listCertsKeystoreType.addLongIdentifier("key-store-type", true);
578    listCertsKeystoreType.addLongIdentifier("keystoreType", true);
579    listCertsKeystoreType.addLongIdentifier("keystore-format", true);
580    listCertsKeystoreType.addLongIdentifier("key-store-format", true);
581    listCertsKeystoreType.addLongIdentifier("keystoreFormat", true);
582    listCertsKeystoreType.addLongIdentifier("storetype", true);
583    listCertsParser.addArgument(listCertsKeystoreType);
584
585    final StringArgument listCertsAlias = new StringArgument(null, "alias",
586         false, 0, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
587         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_ALIAS_DESC.get());
588    listCertsAlias.addLongIdentifier("nickname", true);
589    listCertsParser.addArgument(listCertsAlias);
590
591    final BooleanArgument listCertsDisplayPEM = new BooleanArgument(null,
592         "display-pem-certificate", 1,
593         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_DISPLAY_PEM_DESC.get());
594    listCertsDisplayPEM.addLongIdentifier("displayPEMCertificate", true);
595    listCertsDisplayPEM.addLongIdentifier("display-pem", true);
596    listCertsDisplayPEM.addLongIdentifier("displayPEM", true);
597    listCertsDisplayPEM.addLongIdentifier("show-pem-certificate", true);
598    listCertsDisplayPEM.addLongIdentifier("showPEMCertificate", true);
599    listCertsDisplayPEM.addLongIdentifier("show-pem", true);
600    listCertsDisplayPEM.addLongIdentifier("showPEM", true);
601    listCertsDisplayPEM.addLongIdentifier("pem", true);
602    listCertsDisplayPEM.addLongIdentifier("rfc", true);
603    listCertsParser.addArgument(listCertsDisplayPEM);
604
605    final BooleanArgument listCertsVerbose = new BooleanArgument(null,
606         "verbose", 1, INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_VERBOSE_DESC.get());
607    listCertsParser.addArgument(listCertsVerbose);
608
609    final BooleanArgument listCertsDisplayCommand = new BooleanArgument(null,
610         "display-keytool-command", 1,
611         INFO_MANAGE_CERTS_SC_LIST_CERTS_ARG_DISPLAY_COMMAND_DESC.get());
612    listCertsDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
613    listCertsDisplayCommand.addLongIdentifier("show-keytool-command", true);
614    listCertsDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
615    listCertsParser.addArgument(listCertsDisplayCommand);
616
617    listCertsParser.addExclusiveArgumentSet(listCertsKeystorePassword,
618         listCertsKeystorePasswordFile, listCertsPromptForKeystorePassword);
619
620    final LinkedHashMap<String[],String> listCertsExamples =
621         new LinkedHashMap<>(StaticUtils.computeMapCapacity(3));
622    listCertsExamples.put(
623         new String[]
624         {
625           "list-certificates",
626           "--keystore", getPlatformSpecificPath("config", "keystore")
627         },
628         INFO_MANAGE_CERTS_SC_LIST_CERTS_EXAMPLE_1.get(
629              getPlatformSpecificPath("config", "keystore")));
630    listCertsExamples.put(
631         new String[]
632         {
633           "list-certificates",
634           "--keystore", getPlatformSpecificPath("config", "keystore.p12"),
635           "--keystore-password-file",
636                getPlatformSpecificPath("config", "keystore.pin"),
637           "--alias", "server-cert",
638           "--verbose",
639           "--display-pem-certificate",
640           "--display-keytool-command"
641         },
642         INFO_MANAGE_CERTS_SC_LIST_CERTS_EXAMPLE_2.get(
643              getPlatformSpecificPath("config", "keystore.p12"),
644              getPlatformSpecificPath("config", "keystore.pin")));
645    if (JVM_DEFAULT_CACERTS_FILE != null)
646    {
647      listCertsExamples.put(
648           new String[]
649           {
650             "list-certificates",
651             "--use-jvm-default-trust-store"
652           },
653           INFO_MANAGE_CERTS_SC_LIST_CERTS_EXAMPLE_3.get());
654    }
655
656    final SubCommand listCertsSubCommand = new SubCommand("list-certificates",
657         INFO_MANAGE_CERTS_SC_LIST_CERTS_DESC.get(), listCertsParser,
658         listCertsExamples);
659    listCertsSubCommand.addName("listCertificates", true);
660    listCertsSubCommand.addName("list-certs", true);
661    listCertsSubCommand.addName("listCerts", true);
662    listCertsSubCommand.addName("list", true);
663
664    parser.addSubCommand(listCertsSubCommand);
665
666
667    // Define the "export-certificate" subcommand and all of its arguments.
668    final ArgumentParser exportCertParser = new ArgumentParser(
669         "export-certificate", INFO_MANAGE_CERTS_SC_EXPORT_CERT_DESC.get());
670
671    final FileArgument exportCertKeystore = new FileArgument(null, "keystore",
672         (JVM_DEFAULT_CACERTS_FILE == null), 1, null,
673         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_DESC.get(), true, true,  true,
674         false);
675    exportCertKeystore.addLongIdentifier("keystore-path", true);
676    exportCertKeystore.addLongIdentifier("keystorePath", true);
677    exportCertKeystore.addLongIdentifier("keystore-file", true);
678    exportCertKeystore.addLongIdentifier("keystoreFile", true);
679    exportCertParser.addArgument(exportCertKeystore);
680
681    if (JVM_DEFAULT_CACERTS_FILE != null)
682    {
683      final BooleanArgument exportCertUseJVMDefault = new BooleanArgument(null,
684           "use-jvm-default-trust-store", 1,
685           INFO_MANAGE_CERTS_SC_EXPORT_CERTS_ARG_JVM_DEFAULT_DESC.get(
686                JVM_DEFAULT_CACERTS_FILE.getAbsolutePath()));
687      exportCertUseJVMDefault.addLongIdentifier("useJVMDefaultTrustStore",
688           true);
689      exportCertUseJVMDefault.addLongIdentifier("jvm-default", true);
690      exportCertUseJVMDefault.addLongIdentifier("jvmDefault", true);
691      exportCertParser.addArgument(exportCertUseJVMDefault);
692
693      exportCertParser.addRequiredArgumentSet(exportCertUseJVMDefault,
694           exportCertKeystore);
695      exportCertParser.addExclusiveArgumentSet(exportCertUseJVMDefault,
696           exportCertKeystore);
697    }
698
699    final StringArgument exportCertKeystorePassword = new StringArgument(null,
700         "keystore-password", false, 1,
701         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
702         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_PW_DESC.get());
703    exportCertKeystorePassword.addLongIdentifier("keystorePassword", true);
704    exportCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
705    exportCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
706    exportCertKeystorePassword.addLongIdentifier("keystore-pin", true);
707    exportCertKeystorePassword.addLongIdentifier("keystorePIN", true);
708    exportCertKeystorePassword.addLongIdentifier("storepass", true);
709    exportCertKeystorePassword.setSensitive(true);
710    exportCertParser.addArgument(exportCertKeystorePassword);
711
712    final FileArgument exportCertKeystorePasswordFile = new FileArgument(null,
713         "keystore-password-file", false, 1, null,
714         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
715         true, false);
716    exportCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
717         true);
718    exportCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
719         true);
720    exportCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
721         true);
722    exportCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
723         true);
724    exportCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
725    exportCertParser.addArgument(exportCertKeystorePasswordFile);
726
727    final BooleanArgument exportCertPromptForKeystorePassword =
728         new BooleanArgument(null, "prompt-for-keystore-password",
729        INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
730    exportCertPromptForKeystorePassword.addLongIdentifier(
731         "promptForKeystorePassword", true);
732    exportCertPromptForKeystorePassword.addLongIdentifier(
733         "prompt-for-keystore-passphrase", true);
734    exportCertPromptForKeystorePassword.addLongIdentifier(
735         "promptForKeystorePassphrase", true);
736    exportCertPromptForKeystorePassword.addLongIdentifier(
737         "prompt-for-keystore-pin", true);
738    exportCertPromptForKeystorePassword.addLongIdentifier(
739         "promptForKeystorePIN", true);
740    exportCertParser.addArgument(exportCertPromptForKeystorePassword);
741
742    final StringArgument exportCertKeystoreType = new StringArgument(null,
743         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
744         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_KS_TYPE_DESC.get(),
745         ALLOWED_KEYSTORE_TYPE_VALUES);
746    exportCertKeystoreType.addLongIdentifier("key-store-type", true);
747    exportCertKeystoreType.addLongIdentifier("keystoreType", true);
748    exportCertKeystoreType.addLongIdentifier("keystore-format", true);
749    exportCertKeystoreType.addLongIdentifier("key-store-format", true);
750    exportCertKeystoreType.addLongIdentifier("keystoreFormat", true);
751    exportCertKeystoreType.addLongIdentifier("storetype", true);
752    exportCertParser.addArgument(exportCertKeystoreType);
753
754    final StringArgument exportCertAlias = new StringArgument(null, "alias",
755         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
756         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_ALIAS_DESC.get());
757    exportCertAlias.addLongIdentifier("nickname", true);
758    exportCertParser.addArgument(exportCertAlias);
759
760    final BooleanArgument exportCertChain = new BooleanArgument(null,
761         "export-certificate-chain", 1,
762         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_CHAIN_DESC.get());
763    exportCertChain.addLongIdentifier("exportCertificateChain", true);
764    exportCertChain.addLongIdentifier("export-chain", true);
765    exportCertChain.addLongIdentifier("exportChain", true);
766    exportCertChain.addLongIdentifier("certificate-chain", true);
767    exportCertChain.addLongIdentifier("certificateChain", true);
768    exportCertChain.addLongIdentifier("chain", true);
769    exportCertParser.addArgument(exportCertChain);
770
771    final Set<String> exportCertOutputFormatAllowedValues = StaticUtils.setOf(
772         "PEM", "text", "txt", "RFC", "DER", "binary", "bin");
773    final StringArgument exportCertOutputFormat = new StringArgument(null,
774         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
775         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_FORMAT_DESC.get(),
776         exportCertOutputFormatAllowedValues, "PEM");
777    exportCertOutputFormat.addLongIdentifier("outputFormat", true);
778    exportCertParser.addArgument(exportCertOutputFormat);
779
780    final FileArgument exportCertOutputFile = new FileArgument(null,
781         "output-file", false, 1, null,
782         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_FILE_DESC.get(), false, true,
783         true, false);
784    exportCertOutputFile.addLongIdentifier("outputFile", true);
785    exportCertOutputFile.addLongIdentifier("export-file", true);
786    exportCertOutputFile.addLongIdentifier("exportFile", true);
787    exportCertOutputFile.addLongIdentifier("certificate-file", true);
788    exportCertOutputFile.addLongIdentifier("certificateFile", true);
789    exportCertOutputFile.addLongIdentifier("file", true);
790    exportCertOutputFile.addLongIdentifier("filename", true);
791    exportCertParser.addArgument(exportCertOutputFile);
792
793    final BooleanArgument exportCertSeparateFile = new BooleanArgument(null,
794         "separate-file-per-certificate", 1,
795         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_SEPARATE_FILE_DESC.get());
796    exportCertSeparateFile.addLongIdentifier("separateFilePerCertificate",
797         true);
798    exportCertSeparateFile.addLongIdentifier("separate-files", true);
799    exportCertSeparateFile.addLongIdentifier("separateFiles", true);
800    exportCertParser.addArgument(exportCertSeparateFile);
801
802    final BooleanArgument exportCertDisplayCommand = new BooleanArgument(null,
803         "display-keytool-command", 1,
804         INFO_MANAGE_CERTS_SC_EXPORT_CERT_ARG_DISPLAY_COMMAND_DESC.get());
805    exportCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
806    exportCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
807    exportCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
808    exportCertParser.addArgument(exportCertDisplayCommand);
809
810    exportCertParser.addExclusiveArgumentSet(exportCertKeystorePassword,
811         exportCertKeystorePasswordFile, exportCertPromptForKeystorePassword);
812    exportCertParser.addDependentArgumentSet(exportCertSeparateFile,
813         exportCertChain);
814    exportCertParser.addDependentArgumentSet(exportCertSeparateFile,
815         exportCertOutputFile);
816
817    final LinkedHashMap<String[],String> exportCertExamples =
818         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
819    exportCertExamples.put(
820         new String[]
821         {
822           "export-certificate",
823           "--keystore", getPlatformSpecificPath("config", "keystore"),
824           "--alias", "server-cert"
825         },
826         INFO_MANAGE_CERTS_SC_EXPORT_CERT_EXAMPLE_1.get());
827    exportCertExamples.put(
828         new String[]
829         {
830           "export-certificate",
831           "--keystore", getPlatformSpecificPath("config", "keystore.p12"),
832           "--keystore-password-file",
833                getPlatformSpecificPath("config", "keystore.pin"),
834           "--alias", "server-cert",
835           "--export-certificate-chain",
836           "--output-format", "DER",
837           "--output-file", "certificate-chain.der",
838           "--display-keytool-command"
839         },
840         INFO_MANAGE_CERTS_SC_EXPORT_CERT_EXAMPLE_2.get());
841
842    final SubCommand exportCertSubCommand = new SubCommand("export-certificate",
843         INFO_MANAGE_CERTS_SC_EXPORT_CERT_DESC.get(), exportCertParser,
844         exportCertExamples);
845    exportCertSubCommand.addName("exportCertificate", true);
846    exportCertSubCommand.addName("export-cert", true);
847    exportCertSubCommand.addName("exportCert", true);
848    exportCertSubCommand.addName("export", true);
849
850    parser.addSubCommand(exportCertSubCommand);
851
852
853    // Define the "export-private-key" subcommand and all of its arguments.
854    final ArgumentParser exportKeyParser = new ArgumentParser(
855         "export-private-key", INFO_MANAGE_CERTS_SC_EXPORT_KEY_DESC.get());
856
857    final FileArgument exportKeyKeystore = new FileArgument(null, "keystore",
858         true, 1, null, INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_DESC.get(),
859         true, true,  true, false);
860    exportKeyKeystore.addLongIdentifier("keystore-path", true);
861    exportKeyKeystore.addLongIdentifier("keystorePath", true);
862    exportKeyKeystore.addLongIdentifier("keystore-file", true);
863    exportKeyKeystore.addLongIdentifier("keystoreFile", true);
864    exportKeyParser.addArgument(exportKeyKeystore);
865
866    final StringArgument exportKeyKeystorePassword = new StringArgument(null,
867         "keystore-password", false, 1,
868         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
869         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_PW_DESC.get());
870    exportKeyKeystorePassword.addLongIdentifier("keystorePassword", true);
871    exportKeyKeystorePassword.addLongIdentifier("keystore-passphrase", true);
872    exportKeyKeystorePassword.addLongIdentifier("keystorePassphrase", true);
873    exportKeyKeystorePassword.addLongIdentifier("keystore-pin", true);
874    exportKeyKeystorePassword.addLongIdentifier("keystorePIN", true);
875    exportKeyKeystorePassword.addLongIdentifier("storepass", true);
876    exportKeyKeystorePassword.setSensitive(true);
877    exportKeyParser.addArgument(exportKeyKeystorePassword);
878
879    final FileArgument exportKeyKeystorePasswordFile = new FileArgument(null,
880         "keystore-password-file", false, 1, null,
881         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_PW_FILE_DESC.get(), true, true,
882         true, false);
883    exportKeyKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
884         true);
885    exportKeyKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
886         true);
887    exportKeyKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
888         true);
889    exportKeyKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
890         true);
891    exportKeyKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
892    exportKeyParser.addArgument(exportKeyKeystorePasswordFile);
893
894    final BooleanArgument exportKeyPromptForKeystorePassword =
895         new BooleanArgument(null, "prompt-for-keystore-password",
896        INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PROMPT_FOR_KS_PW_DESC.get());
897    exportKeyPromptForKeystorePassword.addLongIdentifier(
898         "promptForKeystorePassword", true);
899    exportKeyPromptForKeystorePassword.addLongIdentifier(
900         "prompt-for-keystore-passphrase", true);
901    exportKeyPromptForKeystorePassword.addLongIdentifier(
902         "promptForKeystorePassphrase", true);
903    exportKeyPromptForKeystorePassword.addLongIdentifier(
904         "prompt-for-keystore-pin", true);
905    exportKeyPromptForKeystorePassword.addLongIdentifier(
906         "promptForKeystorePIN", true);
907    exportKeyParser.addArgument(exportKeyPromptForKeystorePassword);
908
909    final StringArgument exportKeyPKPassword = new StringArgument(null,
910         "private-key-password", false, 1,
911         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
912         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PK_PW_DESC.get());
913    exportKeyPKPassword.addLongIdentifier("privateKeyPassword", true);
914    exportKeyPKPassword.addLongIdentifier("private-key-passphrase", true);
915    exportKeyPKPassword.addLongIdentifier("privateKeyPassphrase", true);
916    exportKeyPKPassword.addLongIdentifier("private-key-pin", true);
917    exportKeyPKPassword.addLongIdentifier("privateKeyPIN", true);
918    exportKeyPKPassword.addLongIdentifier("key-password", true);
919    exportKeyPKPassword.addLongIdentifier("keyPassword", true);
920    exportKeyPKPassword.addLongIdentifier("key-passphrase", true);
921    exportKeyPKPassword.addLongIdentifier("keyPassphrase", true);
922    exportKeyPKPassword.addLongIdentifier("key-pin", true);
923    exportKeyPKPassword.addLongIdentifier("keyPIN", true);
924    exportKeyPKPassword.addLongIdentifier("keypass", true);
925    exportKeyPKPassword.setSensitive(true);
926    exportKeyParser.addArgument(exportKeyPKPassword);
927
928    final FileArgument exportKeyPKPasswordFile = new FileArgument(null,
929         "private-key-password-file", false, 1, null,
930         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PK_PW_FILE_DESC.get(), true, true,
931         true, false);
932    exportKeyPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
933    exportKeyPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
934         true);
935    exportKeyPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
936         true);
937    exportKeyPKPasswordFile.addLongIdentifier("private-key-pin-file",
938         true);
939    exportKeyPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
940    exportKeyPKPasswordFile.addLongIdentifier("key-password-file", true);
941    exportKeyPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
942    exportKeyPKPasswordFile.addLongIdentifier("key-passphrase-file",
943         true);
944    exportKeyPKPasswordFile.addLongIdentifier("keyPassphraseFile",
945         true);
946    exportKeyPKPasswordFile.addLongIdentifier("key-pin-file",
947         true);
948    exportKeyPKPasswordFile.addLongIdentifier("keyPINFile", true);
949    exportKeyParser.addArgument(exportKeyPKPasswordFile);
950
951    final BooleanArgument exportKeyPromptForPKPassword =
952         new BooleanArgument(null, "prompt-for-private-key-password",
953        INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PROMPT_FOR_PK_PW_DESC.get());
954    exportKeyPromptForPKPassword.addLongIdentifier(
955         "promptForPrivateKeyPassword", true);
956    exportKeyPromptForPKPassword.addLongIdentifier(
957         "prompt-for-private-key-passphrase", true);
958    exportKeyPromptForPKPassword.addLongIdentifier(
959         "promptForPrivateKeyPassphrase", true);
960    exportKeyPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
961         true);
962    exportKeyPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
963         true);
964    exportKeyPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
965         true);
966    exportKeyPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
967         true);
968    exportKeyPromptForPKPassword.addLongIdentifier(
969         "prompt-for-key-passphrase", true);
970    exportKeyPromptForPKPassword.addLongIdentifier(
971         "promptForKeyPassphrase", true);
972    exportKeyPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
973    exportKeyPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
974    exportKeyParser.addArgument(exportKeyPromptForPKPassword);
975
976    final StringArgument exportKeyKeystoreType = new StringArgument(null,
977         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
978         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_KS_TYPE_DESC.get(),
979         ALLOWED_KEYSTORE_TYPE_VALUES);
980    exportKeyKeystoreType.addLongIdentifier("key-store-type", true);
981    exportKeyKeystoreType.addLongIdentifier("keystoreType", true);
982    exportKeyKeystoreType.addLongIdentifier("keystore-format", true);
983    exportKeyKeystoreType.addLongIdentifier("key-store-format", true);
984    exportKeyKeystoreType.addLongIdentifier("keystoreFormat", true);
985    exportKeyKeystoreType.addLongIdentifier("storetype", true);
986    exportKeyParser.addArgument(exportKeyKeystoreType);
987
988    final StringArgument exportKeyAlias = new StringArgument(null, "alias",
989         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
990         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_ALIAS_DESC.get());
991    exportKeyAlias.addLongIdentifier("nickname", true);
992    exportKeyParser.addArgument(exportKeyAlias);
993
994    final Set<String> exportKeyOutputFormatAllowedValues = StaticUtils.setOf(
995         "PEM", "text", "txt", "RFC", "DER", "binary", "bin");
996    final StringArgument exportKeyOutputFormat = new StringArgument(null,
997         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
998         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_FORMAT_DESC.get(),
999         exportKeyOutputFormatAllowedValues, "PEM");
1000    exportKeyOutputFormat.addLongIdentifier("outputFormat", true);
1001    exportKeyParser.addArgument(exportKeyOutputFormat);
1002
1003    final FileArgument exportKeyOutputFile = new FileArgument(null,
1004         "output-file", false, 1, null,
1005         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_FILE_DESC.get(), false, true,
1006         true, false);
1007    exportKeyOutputFile.addLongIdentifier("outputFile", true);
1008    exportKeyOutputFile.addLongIdentifier("export-file", true);
1009    exportKeyOutputFile.addLongIdentifier("exportFile", true);
1010    exportKeyOutputFile.addLongIdentifier("private-key-file", true);
1011    exportKeyOutputFile.addLongIdentifier("privateKeyFile", true);
1012    exportKeyOutputFile.addLongIdentifier("key-file", true);
1013    exportKeyOutputFile.addLongIdentifier("keyFile", true);
1014    exportKeyOutputFile.addLongIdentifier("file", true);
1015    exportKeyOutputFile.addLongIdentifier("filename", true);
1016    exportKeyParser.addArgument(exportKeyOutputFile);
1017
1018    final StringArgument exportKeyEncryptionPassword = new StringArgument(null,
1019         "encryption-password", false, 1,
1020         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1021         INFO_MANAGE_CERTS_SC_EXPIRT_KEY_ARG_ENC_PW_DESC.get());
1022    exportKeyEncryptionPassword.addLongIdentifier("encryptionPassword", true);
1023    exportKeyParser.addArgument(exportKeyEncryptionPassword);
1024
1025    final FileArgument exportKeyEncryptionPasswordFile = new FileArgument(null,
1026       "encryption-password-file", false, 1, null,
1027         INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_ENC_PW_FILE_DESC.get(), true, true,
1028         true, false);
1029    exportKeyEncryptionPasswordFile.addLongIdentifier("encryptionPasswordFile",
1030         true);
1031    exportKeyParser.addArgument(exportKeyEncryptionPasswordFile);
1032
1033    final BooleanArgument exportKeyPromptForEncryptionPassword =
1034         new BooleanArgument(null, "prompt-for-encryption-password", 1,
1035              INFO_MANAGE_CERTS_SC_EXPORT_KEY_ARG_PROMPT_FOR_ENC_PW.get());
1036    exportKeyPromptForEncryptionPassword.addLongIdentifier(
1037         "promptForEncryptionPassword", true);
1038    exportKeyParser.addArgument(exportKeyPromptForEncryptionPassword);
1039
1040    exportKeyParser.addRequiredArgumentSet(exportKeyKeystorePassword,
1041         exportKeyKeystorePasswordFile, exportKeyPromptForKeystorePassword);
1042    exportKeyParser.addExclusiveArgumentSet(exportKeyKeystorePassword,
1043         exportKeyKeystorePasswordFile, exportKeyPromptForKeystorePassword);
1044    exportKeyParser.addExclusiveArgumentSet(exportKeyPKPassword,
1045         exportKeyPKPasswordFile, exportKeyPromptForPKPassword);
1046    exportKeyParser.addExclusiveArgumentSet(exportKeyEncryptionPassword,
1047         exportKeyEncryptionPasswordFile, exportKeyPromptForEncryptionPassword);
1048
1049    final LinkedHashMap<String[],String> exportKeyExamples =
1050         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
1051    exportKeyExamples.put(
1052         new String[]
1053         {
1054           "export-private-key",
1055           "--keystore", getPlatformSpecificPath("config", "keystore"),
1056           "--keystore-password-file",
1057                getPlatformSpecificPath("config", "keystore.pin"),
1058           "--alias", "server-cert"
1059         },
1060         INFO_MANAGE_CERTS_SC_EXPORT_KEY_EXAMPLE_1.get());
1061    exportKeyExamples.put(
1062         new String[]
1063         {
1064           "export-private-key",
1065           "--keystore", getPlatformSpecificPath("config", "keystore.p12"),
1066           "--keystore-password-file",
1067                getPlatformSpecificPath("config", "keystore.pin"),
1068           "--private-key-password-file",
1069                getPlatformSpecificPath("config", "server-cert-key.pin"),
1070           "--alias", "server-cert",
1071           "--output-format", "DER",
1072           "--output-file", "server-cert-key.der"
1073         },
1074         INFO_MANAGE_CERTS_SC_EXPORT_KEY_EXAMPLE_2.get());
1075
1076    final SubCommand exportKeySubCommand = new SubCommand("export-private-key",
1077         INFO_MANAGE_CERTS_SC_EXPORT_CERT_DESC.get(), exportKeyParser,
1078         exportKeyExamples);
1079    exportKeySubCommand.addName("exportPrivateKey", true);
1080    exportKeySubCommand.addName("export-key", true);
1081    exportKeySubCommand.addName("exportKey", true);
1082
1083    parser.addSubCommand(exportKeySubCommand);
1084
1085
1086    // Define the "import-certificate" subcommand and all of its arguments.
1087    final ArgumentParser importCertParser = new ArgumentParser(
1088         "import-certificate", INFO_MANAGE_CERTS_SC_IMPORT_CERT_DESC.get());
1089
1090    final FileArgument importCertKeystore = new FileArgument(null, "keystore",
1091         true, 1, null, INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_DESC.get(),
1092         false, true,  true, false);
1093    importCertKeystore.addLongIdentifier("keystore-path", true);
1094    importCertKeystore.addLongIdentifier("keystorePath", true);
1095    importCertKeystore.addLongIdentifier("keystore-file", true);
1096    importCertKeystore.addLongIdentifier("keystoreFile", true);
1097    importCertParser.addArgument(importCertKeystore);
1098
1099    final StringArgument importCertKeystorePassword = new StringArgument(null,
1100         "keystore-password", false, 1,
1101         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1102         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_PW_DESC.get());
1103    importCertKeystorePassword.addLongIdentifier("keystorePassword", true);
1104    importCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
1105    importCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
1106    importCertKeystorePassword.addLongIdentifier("keystore-pin", true);
1107    importCertKeystorePassword.addLongIdentifier("keystorePIN", true);
1108    importCertKeystorePassword.addLongIdentifier("storepass", true);
1109    importCertKeystorePassword.setSensitive(true);
1110    importCertParser.addArgument(importCertKeystorePassword);
1111
1112    final FileArgument importCertKeystorePasswordFile = new FileArgument(null,
1113         "keystore-password-file", false, 1, null,
1114         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
1115         true, false);
1116    importCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
1117         true);
1118    importCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
1119         true);
1120    importCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
1121         true);
1122    importCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
1123         true);
1124    importCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
1125    importCertParser.addArgument(importCertKeystorePasswordFile);
1126
1127    final BooleanArgument importCertPromptForKeystorePassword =
1128         new BooleanArgument(null, "prompt-for-keystore-password",
1129        INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
1130    importCertPromptForKeystorePassword.addLongIdentifier(
1131         "promptForKeystorePassword", true);
1132    importCertPromptForKeystorePassword.addLongIdentifier(
1133         "prompt-for-keystore-passphrase", true);
1134    importCertPromptForKeystorePassword.addLongIdentifier(
1135         "promptForKeystorePassphrase", true);
1136    importCertPromptForKeystorePassword.addLongIdentifier(
1137         "prompt-for-keystore-pin", true);
1138    importCertPromptForKeystorePassword.addLongIdentifier(
1139         "promptForKeystorePIN", true);
1140    importCertParser.addArgument(importCertPromptForKeystorePassword);
1141
1142    final StringArgument importCertKeystoreType = new StringArgument(null,
1143         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
1144         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KS_TYPE_DESC.get(),
1145         ALLOWED_KEYSTORE_TYPE_VALUES);
1146    importCertKeystoreType.addLongIdentifier("key-store-type", true);
1147    importCertKeystoreType.addLongIdentifier("keystoreType", true);
1148    importCertKeystoreType.addLongIdentifier("keystore-format", true);
1149    importCertKeystoreType.addLongIdentifier("key-store-format", true);
1150    importCertKeystoreType.addLongIdentifier("keystoreFormat", true);
1151    importCertKeystoreType.addLongIdentifier("storetype", true);
1152    importCertParser.addArgument(importCertKeystoreType);
1153
1154    final StringArgument importCertAlias = new StringArgument(null, "alias",
1155         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
1156         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_ALIAS_DESC.get());
1157    importCertAlias.addLongIdentifier("nickname", true);
1158    importCertParser.addArgument(importCertAlias);
1159
1160    final FileArgument importCertCertificateFile = new FileArgument(null,
1161         "certificate-file", true, 0, null,
1162         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_CERT_FILE_DESC.get(), true, true,
1163         true, false);
1164    importCertCertificateFile.addLongIdentifier("certificateFile", true);
1165    importCertCertificateFile.addLongIdentifier("certificate-chain-file", true);
1166    importCertCertificateFile.addLongIdentifier("certificateChainFile", true);
1167    importCertCertificateFile.addLongIdentifier("input-file", true);
1168    importCertCertificateFile.addLongIdentifier("inputFile", true);
1169    importCertCertificateFile.addLongIdentifier("import-file", true);
1170    importCertCertificateFile.addLongIdentifier("importFile", true);
1171    importCertCertificateFile.addLongIdentifier("file", true);
1172    importCertCertificateFile.addLongIdentifier("filename", true);
1173    importCertParser.addArgument(importCertCertificateFile);
1174
1175    final FileArgument importCertPKFile = new FileArgument(null,
1176         "private-key-file", false, 1, null,
1177         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_KEY_FILE_DESC.get(), true, true,
1178         true, false);
1179    importCertPKFile.addLongIdentifier("privateKeyFile", true);
1180    importCertPKFile.addLongIdentifier("key-file", true);
1181    importCertPKFile.addLongIdentifier("keyFile", true);
1182    importCertParser.addArgument(importCertPKFile);
1183
1184    final StringArgument importCertPKPassword = new StringArgument(null,
1185         "private-key-password", false, 1,
1186         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1187         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PK_PW_DESC.get());
1188    importCertPKPassword.addLongIdentifier("privateKeyPassword", true);
1189    importCertPKPassword.addLongIdentifier("private-key-passphrase", true);
1190    importCertPKPassword.addLongIdentifier("privateKeyPassphrase", true);
1191    importCertPKPassword.addLongIdentifier("private-key-pin", true);
1192    importCertPKPassword.addLongIdentifier("privateKeyPIN", true);
1193    importCertPKPassword.addLongIdentifier("key-password", true);
1194    importCertPKPassword.addLongIdentifier("keyPassword", true);
1195    importCertPKPassword.addLongIdentifier("key-passphrase", true);
1196    importCertPKPassword.addLongIdentifier("keyPassphrase", true);
1197    importCertPKPassword.addLongIdentifier("key-pin", true);
1198    importCertPKPassword.addLongIdentifier("keyPIN", true);
1199    importCertPKPassword.addLongIdentifier("keypass", true);
1200    importCertPKPassword.setSensitive(true);
1201    importCertParser.addArgument(importCertPKPassword);
1202
1203    final FileArgument importCertPKPasswordFile = new FileArgument(null,
1204         "private-key-password-file", false, 1, null,
1205         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PK_PW_FILE_DESC.get(), true, true,
1206         true, false);
1207    importCertPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
1208    importCertPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
1209         true);
1210    importCertPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
1211         true);
1212    importCertPKPasswordFile.addLongIdentifier("private-key-pin-file",
1213         true);
1214    importCertPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
1215    importCertPKPasswordFile.addLongIdentifier("key-password-file", true);
1216    importCertPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
1217    importCertPKPasswordFile.addLongIdentifier("key-passphrase-file",
1218         true);
1219    importCertPKPasswordFile.addLongIdentifier("keyPassphraseFile",
1220         true);
1221    importCertPKPasswordFile.addLongIdentifier("key-pin-file",
1222         true);
1223    importCertPKPasswordFile.addLongIdentifier("keyPINFile", true);
1224    importCertParser.addArgument(importCertPKPasswordFile);
1225
1226    final BooleanArgument importCertPromptForPKPassword =
1227         new BooleanArgument(null, "prompt-for-private-key-password",
1228        INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PROMPT_FOR_PK_PW_DESC.get());
1229    importCertPromptForPKPassword.addLongIdentifier(
1230         "promptForPrivateKeyPassword", true);
1231    importCertPromptForPKPassword.addLongIdentifier(
1232         "prompt-for-private-key-passphrase", true);
1233    importCertPromptForPKPassword.addLongIdentifier(
1234         "promptForPrivateKeyPassphrase", true);
1235    importCertPromptForPKPassword.addLongIdentifier(
1236         "prompt-for-private-key-pin", true);
1237    importCertPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
1238         true);
1239    importCertPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
1240         true);
1241    importCertPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
1242         true);
1243    importCertPromptForPKPassword.addLongIdentifier(
1244         "prompt-for-key-passphrase", true);
1245    importCertPromptForPKPassword.addLongIdentifier(
1246         "promptForKeyPassphrase", true);
1247    importCertPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
1248    importCertPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
1249    importCertParser.addArgument(importCertPromptForPKPassword);
1250
1251    final StringArgument importCertEncryptionPassword = new StringArgument(null,
1252         "encryption-password", false, 1,
1253         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1254         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_ENC_PW_DESC.get());
1255    importCertEncryptionPassword.addLongIdentifier("encryptionPassword", true);
1256    importCertEncryptionPassword.setSensitive(true);
1257    importCertParser.addArgument(importCertEncryptionPassword);
1258
1259    final FileArgument importCertEncryptionPasswordFile = new FileArgument(null,
1260         "encryption-password-file", false, 1, null,
1261         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_ENC_PW_FILE_DESC.get(), true,
1262         true, true, false);
1263    importCertEncryptionPasswordFile.addLongIdentifier("encryptionPasswordFile",
1264         true);
1265    importCertParser.addArgument(importCertEncryptionPasswordFile);
1266
1267    final BooleanArgument importCertPromptForEncryptionPassword =
1268         new BooleanArgument(null, "prompt-for-encryption-password",
1269        INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_PROMPT_FOR_ENC_PW_DESC.get());
1270    importCertPromptForEncryptionPassword.addLongIdentifier(
1271         "promptForEncryptionPassword", true);
1272    importCertParser.addArgument(importCertPromptForEncryptionPassword);
1273
1274    final BooleanArgument importCertNoPrompt = new BooleanArgument(null,
1275         "no-prompt", 1,
1276         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_NO_PROMPT_DESC.get());
1277    importCertNoPrompt.addLongIdentifier("noPrompt", true);
1278    importCertParser.addArgument(importCertNoPrompt);
1279
1280    final BooleanArgument importCertDisplayCommand = new BooleanArgument(null,
1281         "display-keytool-command", 1,
1282         INFO_MANAGE_CERTS_SC_IMPORT_CERT_ARG_DISPLAY_COMMAND_DESC.get());
1283    importCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
1284    importCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
1285    importCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
1286    importCertParser.addArgument(importCertDisplayCommand);
1287
1288    importCertParser.addRequiredArgumentSet(importCertKeystorePassword,
1289         importCertKeystorePasswordFile, importCertPromptForKeystorePassword);
1290    importCertParser.addExclusiveArgumentSet(importCertKeystorePassword,
1291         importCertKeystorePasswordFile, importCertPromptForKeystorePassword);
1292    importCertParser.addExclusiveArgumentSet(importCertPKPassword,
1293         importCertPKPasswordFile, importCertPromptForPKPassword);
1294    importCertParser.addExclusiveArgumentSet(importCertEncryptionPassword,
1295         importCertEncryptionPasswordFile,
1296         importCertPromptForEncryptionPassword);
1297
1298    final LinkedHashMap<String[],String> importCertExamples =
1299         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
1300    importCertExamples.put(
1301         new String[]
1302         {
1303           "import-certificate",
1304           "--keystore", getPlatformSpecificPath("config", "keystore"),
1305           "--keystore-password-file",
1306                getPlatformSpecificPath("config", "keystore.pin"),
1307           "--alias", "server-cert",
1308           "--certificate-file", "server-cert.crt"
1309         },
1310         INFO_MANAGE_CERTS_SC_IMPORT_CERT_EXAMPLE_1.get("server-cert.crt"));
1311    importCertExamples.put(
1312         new String[]
1313         {
1314           "import-certificate",
1315           "--keystore", getPlatformSpecificPath("config", "keystore"),
1316           "--keystore-password-file",
1317                getPlatformSpecificPath("config", "keystore.pin"),
1318           "--alias", "server-cert",
1319           "--certificate-file", "server-cert.crt",
1320           "--certificate-file", "server-cert-issuer.crt",
1321           "--private-key-file", "server-cert.key",
1322           "--display-keytool-command"
1323         },
1324         INFO_MANAGE_CERTS_SC_IMPORT_CERT_EXAMPLE_2.get());
1325
1326    final SubCommand importCertSubCommand = new SubCommand("import-certificate",
1327         INFO_MANAGE_CERTS_SC_IMPORT_CERT_DESC.get(), importCertParser,
1328         importCertExamples);
1329    importCertSubCommand.addName("importCertificate", true);
1330    importCertSubCommand.addName("import-certificates", true);
1331    importCertSubCommand.addName("importCertificates", true);
1332    importCertSubCommand.addName("import-cert", true);
1333    importCertSubCommand.addName("importCert", true);
1334    importCertSubCommand.addName("import-certs", true);
1335    importCertSubCommand.addName("importCerts", true);
1336    importCertSubCommand.addName("import-certificate-chain", true);
1337    importCertSubCommand.addName("importCertificateChain", true);
1338    importCertSubCommand.addName("import-chain", true);
1339    importCertSubCommand.addName("importChain", true);
1340    importCertSubCommand.addName("import", true);
1341
1342    parser.addSubCommand(importCertSubCommand);
1343
1344
1345    // Define the "delete-certificate" subcommand and all of its arguments.
1346    final ArgumentParser deleteCertParser = new ArgumentParser(
1347         "delete-certificate", INFO_MANAGE_CERTS_SC_DELETE_CERT_DESC.get());
1348
1349    final FileArgument deleteCertKeystore = new FileArgument(null, "keystore",
1350         true, 1, null, INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_DESC.get(),
1351         true, true,  true, false);
1352    deleteCertKeystore.addLongIdentifier("keystore-path", true);
1353    deleteCertKeystore.addLongIdentifier("keystorePath", true);
1354    deleteCertKeystore.addLongIdentifier("keystore-file", true);
1355    deleteCertKeystore.addLongIdentifier("keystoreFile", true);
1356    deleteCertParser.addArgument(deleteCertKeystore);
1357
1358    final StringArgument deleteCertKeystorePassword = new StringArgument(null,
1359         "keystore-password", false, 1,
1360         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1361         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_PW_DESC.get());
1362    deleteCertKeystorePassword.addLongIdentifier("keystorePassword", true);
1363    deleteCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
1364    deleteCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
1365    deleteCertKeystorePassword.addLongIdentifier("keystore-pin", true);
1366    deleteCertKeystorePassword.addLongIdentifier("keystorePIN", true);
1367    deleteCertKeystorePassword.addLongIdentifier("storepass", true);
1368    deleteCertKeystorePassword.setSensitive(true);
1369    deleteCertParser.addArgument(deleteCertKeystorePassword);
1370
1371    final FileArgument deleteCertKeystorePasswordFile = new FileArgument(null,
1372         "keystore-password-file", false, 1, null,
1373         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
1374         true, false);
1375    deleteCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
1376         true);
1377    deleteCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
1378         true);
1379    deleteCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
1380         true);
1381    deleteCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
1382         true);
1383    deleteCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
1384    deleteCertParser.addArgument(deleteCertKeystorePasswordFile);
1385
1386    final BooleanArgument deleteCertPromptForKeystorePassword =
1387         new BooleanArgument(null, "prompt-for-keystore-password",
1388        INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
1389    deleteCertPromptForKeystorePassword.addLongIdentifier(
1390         "promptForKeystorePassword", true);
1391    deleteCertPromptForKeystorePassword.addLongIdentifier(
1392         "prompt-for-keystore-passphrase", true);
1393    deleteCertPromptForKeystorePassword.addLongIdentifier(
1394         "promptForKeystorePassphrase", true);
1395    deleteCertPromptForKeystorePassword.addLongIdentifier(
1396         "prompt-for-keystore-pin", true);
1397    deleteCertPromptForKeystorePassword.addLongIdentifier(
1398         "promptForKeystorePIN", true);
1399    deleteCertParser.addArgument(deleteCertPromptForKeystorePassword);
1400
1401    final StringArgument deleteCertKeystoreType = new StringArgument(null,
1402         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
1403         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_KS_TYPE_DESC.get(),
1404         ALLOWED_KEYSTORE_TYPE_VALUES);
1405    deleteCertKeystoreType.addLongIdentifier("key-store-type", true);
1406    deleteCertKeystoreType.addLongIdentifier("keystoreType", true);
1407    deleteCertKeystoreType.addLongIdentifier("keystore-format", true);
1408    deleteCertKeystoreType.addLongIdentifier("key-store-format", true);
1409    deleteCertKeystoreType.addLongIdentifier("keystoreFormat", true);
1410    deleteCertKeystoreType.addLongIdentifier("storetype", true);
1411    deleteCertParser.addArgument(deleteCertKeystoreType);
1412
1413    final StringArgument deleteCertAlias = new StringArgument(null, "alias",
1414         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
1415         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_ALIAS_DESC.get());
1416    deleteCertAlias.addLongIdentifier("nickname", true);
1417    deleteCertParser.addArgument(deleteCertAlias);
1418
1419    final BooleanArgument deleteCertNoPrompt = new BooleanArgument(null,
1420         "no-prompt", 1,
1421         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_NO_PROMPT_DESC.get());
1422    deleteCertNoPrompt.addLongIdentifier("noPrompt", true);
1423    deleteCertParser.addArgument(deleteCertNoPrompt);
1424
1425    final BooleanArgument deleteCertDisplayCommand = new BooleanArgument(null,
1426         "display-keytool-command", 1,
1427         INFO_MANAGE_CERTS_SC_DELETE_CERT_ARG_DISPLAY_COMMAND_DESC.get());
1428    deleteCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
1429    deleteCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
1430    deleteCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
1431    deleteCertParser.addArgument(deleteCertDisplayCommand);
1432
1433    deleteCertParser.addExclusiveArgumentSet(deleteCertKeystorePassword,
1434         deleteCertKeystorePasswordFile, deleteCertPromptForKeystorePassword);
1435    deleteCertParser.addRequiredArgumentSet(deleteCertKeystorePassword,
1436         deleteCertKeystorePasswordFile, deleteCertPromptForKeystorePassword);
1437
1438    final LinkedHashMap<String[],String> deleteCertExamples =
1439         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
1440    deleteCertExamples.put(
1441         new String[]
1442         {
1443           "delete-certificate",
1444           "--keystore", getPlatformSpecificPath("config", "keystore"),
1445           "--alias", "server-cert"
1446         },
1447         INFO_MANAGE_CERTS_SC_DELETE_CERT_EXAMPLE_1.get(
1448              getPlatformSpecificPath("config", "keystore")));
1449
1450    final SubCommand deleteCertSubCommand = new SubCommand("delete-certificate",
1451         INFO_MANAGE_CERTS_SC_DELETE_CERT_DESC.get(), deleteCertParser,
1452         deleteCertExamples);
1453    deleteCertSubCommand.addName("deleteCertificate", true);
1454    deleteCertSubCommand.addName("remove-certificate", true);
1455    deleteCertSubCommand.addName("removeCertificate", true);
1456    deleteCertSubCommand.addName("delete", true);
1457    deleteCertSubCommand.addName("remove", true);
1458
1459    parser.addSubCommand(deleteCertSubCommand);
1460
1461
1462    // Define the "generate-self-signed-certificate" subcommand and all of its
1463    // arguments.
1464    final ArgumentParser genCertParser = new ArgumentParser(
1465         "generate-self-signed-certificate",
1466         INFO_MANAGE_CERTS_SC_GEN_CERT_DESC.get());
1467
1468    final FileArgument genCertKeystore = new FileArgument(null, "keystore",
1469         true, 1, null, INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_DESC.get(), false,
1470         true,  true, false);
1471    genCertKeystore.addLongIdentifier("keystore-path", true);
1472    genCertKeystore.addLongIdentifier("keystorePath", true);
1473    genCertKeystore.addLongIdentifier("keystore-file", true);
1474    genCertKeystore.addLongIdentifier("keystoreFile", true);
1475    genCertParser.addArgument(genCertKeystore);
1476
1477    final StringArgument genCertKeystorePassword = new StringArgument(null,
1478         "keystore-password", false, 1,
1479         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1480         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_PW_DESC.get());
1481    genCertKeystorePassword.addLongIdentifier("keystorePassword", true);
1482    genCertKeystorePassword.addLongIdentifier("keystore-passphrase", true);
1483    genCertKeystorePassword.addLongIdentifier("keystorePassphrase", true);
1484    genCertKeystorePassword.addLongIdentifier("keystore-pin", true);
1485    genCertKeystorePassword.addLongIdentifier("keystorePIN", true);
1486    genCertKeystorePassword.addLongIdentifier("storepass", true);
1487    genCertKeystorePassword.setSensitive(true);
1488    genCertParser.addArgument(genCertKeystorePassword);
1489
1490    final FileArgument genCertKeystorePasswordFile = new FileArgument(null,
1491         "keystore-password-file", false, 1, null,
1492         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_PW_FILE_DESC.get(), true, true,
1493         true, false);
1494    genCertKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
1495         true);
1496    genCertKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
1497         true);
1498    genCertKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
1499         true);
1500    genCertKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
1501         true);
1502    genCertKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
1503    genCertParser.addArgument(genCertKeystorePasswordFile);
1504
1505    final BooleanArgument genCertPromptForKeystorePassword =
1506         new BooleanArgument(null, "prompt-for-keystore-password",
1507        INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PROMPT_FOR_KS_PW_DESC.get());
1508    genCertPromptForKeystorePassword.addLongIdentifier(
1509         "promptForKeystorePassword", true);
1510    genCertPromptForKeystorePassword.addLongIdentifier(
1511         "prompt-for-keystore-passphrase", true);
1512    genCertPromptForKeystorePassword.addLongIdentifier(
1513         "promptForKeystorePassphrase", true);
1514    genCertPromptForKeystorePassword.addLongIdentifier(
1515         "prompt-for-keystore-pin", true);
1516    genCertPromptForKeystorePassword.addLongIdentifier(
1517         "promptForKeystorePIN", true);
1518    genCertParser.addArgument(genCertPromptForKeystorePassword);
1519
1520    final StringArgument genCertPKPassword = new StringArgument(null,
1521         "private-key-password", false, 1,
1522         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
1523         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PK_PW_DESC.get());
1524    genCertPKPassword.addLongIdentifier("privateKeyPassword", true);
1525    genCertPKPassword.addLongIdentifier("private-key-passphrase", true);
1526    genCertPKPassword.addLongIdentifier("privateKeyPassphrase", true);
1527    genCertPKPassword.addLongIdentifier("private-key-pin", true);
1528    genCertPKPassword.addLongIdentifier("privateKeyPIN", true);
1529    genCertPKPassword.addLongIdentifier("key-password", true);
1530    genCertPKPassword.addLongIdentifier("keyPassword", true);
1531    genCertPKPassword.addLongIdentifier("key-passphrase", true);
1532    genCertPKPassword.addLongIdentifier("keyPassphrase", true);
1533    genCertPKPassword.addLongIdentifier("key-pin", true);
1534    genCertPKPassword.addLongIdentifier("keyPIN", true);
1535    genCertPKPassword.addLongIdentifier("keypass", true);
1536    genCertPKPassword.setSensitive(true);
1537    genCertParser.addArgument(genCertPKPassword);
1538
1539    final FileArgument genCertPKPasswordFile = new FileArgument(null,
1540         "private-key-password-file", false, 1, null,
1541         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PK_PW_FILE_DESC.get(), true, true,
1542         true, false);
1543    genCertPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
1544    genCertPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
1545         true);
1546    genCertPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
1547         true);
1548    genCertPKPasswordFile.addLongIdentifier("private-key-pin-file",
1549         true);
1550    genCertPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
1551    genCertPKPasswordFile.addLongIdentifier("key-password-file", true);
1552    genCertPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
1553    genCertPKPasswordFile.addLongIdentifier("key-passphrase-file",
1554         true);
1555    genCertPKPasswordFile.addLongIdentifier("keyPassphraseFile",
1556         true);
1557    genCertPKPasswordFile.addLongIdentifier("key-pin-file",
1558         true);
1559    genCertPKPasswordFile.addLongIdentifier("keyPINFile", true);
1560    genCertParser.addArgument(genCertPKPasswordFile);
1561
1562    final BooleanArgument genCertPromptForPKPassword =
1563         new BooleanArgument(null, "prompt-for-private-key-password",
1564        INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_PROMPT_FOR_PK_PW_DESC.get());
1565    genCertPromptForPKPassword.addLongIdentifier(
1566         "promptForPrivateKeyPassword", true);
1567    genCertPromptForPKPassword.addLongIdentifier(
1568         "prompt-for-private-key-passphrase", true);
1569    genCertPromptForPKPassword.addLongIdentifier(
1570         "promptForPrivateKeyPassphrase", true);
1571    genCertPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
1572         true);
1573    genCertPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
1574         true);
1575    genCertPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
1576         true);
1577    genCertPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
1578         true);
1579    genCertPromptForPKPassword.addLongIdentifier(
1580         "prompt-for-key-passphrase", true);
1581    genCertPromptForPKPassword.addLongIdentifier(
1582         "promptForKeyPassphrase", true);
1583    genCertPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
1584    genCertPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
1585    genCertParser.addArgument(genCertPromptForPKPassword);
1586
1587    final StringArgument genCertKeystoreType = new StringArgument(null,
1588         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
1589         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KS_TYPE_DESC.get(),
1590         ALLOWED_KEYSTORE_TYPE_VALUES);
1591    genCertKeystoreType.addLongIdentifier("key-store-type", true);
1592    genCertKeystoreType.addLongIdentifier("keystoreType", true);
1593    genCertKeystoreType.addLongIdentifier("keystore-format", true);
1594    genCertKeystoreType.addLongIdentifier("key-store-format", true);
1595    genCertKeystoreType.addLongIdentifier("keystoreFormat", true);
1596    genCertKeystoreType.addLongIdentifier("storetype", true);
1597    genCertParser.addArgument(genCertKeystoreType);
1598
1599    final StringArgument genCertAlias = new StringArgument(null, "alias",
1600         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
1601         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_ALIAS_DESC.get());
1602    genCertAlias.addLongIdentifier("nickname", true);
1603    genCertParser.addArgument(genCertAlias);
1604
1605    final BooleanArgument genCertUseExistingKeyPair = new BooleanArgument(null,
1606         "use-existing-key-pair", 1,
1607         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_USE_EXISTING_KP_DESC.get());
1608    genCertUseExistingKeyPair.addLongIdentifier("use-existing-keypair", true);
1609    genCertUseExistingKeyPair.addLongIdentifier("useExistingKeypair", true);
1610    genCertUseExistingKeyPair.addLongIdentifier("replace-existing-certificate",
1611         true);
1612    genCertUseExistingKeyPair.addLongIdentifier("replaceExistingCertificate",
1613         true);
1614    genCertUseExistingKeyPair.addLongIdentifier("replace-certificate", true);
1615    genCertUseExistingKeyPair.addLongIdentifier("replaceCertificate", true);
1616    genCertUseExistingKeyPair.addLongIdentifier("replace-existing", true);
1617    genCertUseExistingKeyPair.addLongIdentifier("replaceExisting", true);
1618    genCertUseExistingKeyPair.addLongIdentifier("replace", true);
1619    genCertParser.addArgument(genCertUseExistingKeyPair);
1620
1621    final DNArgument genCertSubjectDN = new DNArgument(null, "subject-dn",
1622         false, 1, null,
1623         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SUBJECT_DN_DESC.get());
1624    genCertSubjectDN.addLongIdentifier("subjectDN", true);
1625    genCertSubjectDN.addLongIdentifier("subject", true);
1626    genCertSubjectDN.addLongIdentifier("dname", true);
1627    genCertParser.addArgument(genCertSubjectDN);
1628
1629    final IntegerArgument genCertDaysValid = new IntegerArgument(null,
1630         "days-valid", false, 1, null,
1631         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_DAYS_VALID_DESC.get(), 1,
1632         Integer.MAX_VALUE);
1633    genCertDaysValid.addLongIdentifier("daysValid", true);
1634    genCertDaysValid.addLongIdentifier("validity", true);
1635    genCertParser.addArgument(genCertDaysValid);
1636
1637    final TimestampArgument genCertNotBefore = new TimestampArgument(null,
1638         "validity-start-time", false, 1,
1639         INFO_MANAGE_CERTS_PLACEHOLDER_TIMESTAMP.get(),
1640         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_VALIDITY_START_TIME_DESC.get(
1641              "20180102123456"));
1642    genCertNotBefore.addLongIdentifier("validityStartTime", true);
1643    genCertNotBefore.addLongIdentifier("not-before", true);
1644    genCertNotBefore.addLongIdentifier("notBefore", true);
1645    genCertParser.addArgument(genCertNotBefore);
1646
1647    final StringArgument genCertKeyAlgorithm = new StringArgument(null,
1648         "key-algorithm", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1649         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KEY_ALGORITHM_DESC.get());
1650    genCertKeyAlgorithm.addLongIdentifier("keyAlgorithm", true);
1651    genCertKeyAlgorithm.addLongIdentifier("key-alg", true);
1652    genCertKeyAlgorithm.addLongIdentifier("keyAlg", true);
1653    genCertParser.addArgument(genCertKeyAlgorithm);
1654
1655    final IntegerArgument genCertKeySizeBits = new IntegerArgument(null,
1656         "key-size-bits", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_BITS.get(),
1657         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KEY_SIZE_BITS_DESC.get(), 1,
1658         Integer.MAX_VALUE);
1659    genCertKeySizeBits.addLongIdentifier("keySizeBits", true);
1660    genCertKeySizeBits.addLongIdentifier("key-length-bits", true);
1661    genCertKeySizeBits.addLongIdentifier("keyLengthBits", true);
1662    genCertKeySizeBits.addLongIdentifier("key-size", true);
1663    genCertKeySizeBits.addLongIdentifier("keySize", true);
1664    genCertKeySizeBits.addLongIdentifier("key-length", true);
1665    genCertKeySizeBits.addLongIdentifier("keyLength", true);
1666    genCertParser.addArgument(genCertKeySizeBits);
1667
1668    final StringArgument genCertSignatureAlgorithm = new StringArgument(null,
1669         "signature-algorithm", false, 1,
1670         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1671         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SIG_ALG_DESC.get());
1672    genCertSignatureAlgorithm.addLongIdentifier("signatureAlgorithm", true);
1673    genCertSignatureAlgorithm.addLongIdentifier("signature-alg", true);
1674    genCertSignatureAlgorithm.addLongIdentifier("signatureAlg", true);
1675    genCertSignatureAlgorithm.addLongIdentifier("sig-alg", true);
1676    genCertSignatureAlgorithm.addLongIdentifier("sigAlg", true);
1677    genCertParser.addArgument(genCertSignatureAlgorithm);
1678
1679    final BooleanArgument genCertInheritExtensions = new BooleanArgument(null,
1680         "inherit-extensions", 1,
1681         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_INHERIT_EXT_DESC.get());
1682    genCertInheritExtensions.addLongIdentifier("inheritExtensions", true);
1683    genCertParser.addArgument(genCertInheritExtensions);
1684
1685    final StringArgument genCertSubjectAltDNS = new StringArgument(null,
1686         "subject-alternative-name-dns", false, 0,
1687         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1688         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_DNS_DESC.get());
1689    genCertSubjectAltDNS.addLongIdentifier("subjectAlternativeNameDNS", true);
1690    genCertSubjectAltDNS.addLongIdentifier("subject-alt-name-dns", true);
1691    genCertSubjectAltDNS.addLongIdentifier("subjectAltNameDNS", true);
1692    genCertSubjectAltDNS.addLongIdentifier("subject-alternative-dns", true);
1693    genCertSubjectAltDNS.addLongIdentifier("subjectAlternativeDNS", true);
1694    genCertSubjectAltDNS.addLongIdentifier("subject-alt-dns", true);
1695    genCertSubjectAltDNS.addLongIdentifier("subjectAltDNS", true);
1696    genCertSubjectAltDNS.addLongIdentifier("san-dns", true);
1697    genCertSubjectAltDNS.addLongIdentifier("sanDNS", true);
1698    genCertSubjectAltDNS.addValueValidator(
1699         new IA5StringArgumentValueValidator(false));
1700    genCertParser.addArgument(genCertSubjectAltDNS);
1701
1702    final StringArgument genCertSubjectAltIP = new StringArgument(null,
1703         "subject-alternative-name-ip-address", false, 0,
1704         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1705         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_IP_DESC.get());
1706    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeNameIPAddress",
1707         true);
1708    genCertSubjectAltIP.addLongIdentifier("subject-alternative-name-ip", true);
1709    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeNameIP", true);
1710    genCertSubjectAltIP.addLongIdentifier("subject-alt-name-ip-address", true);
1711    genCertSubjectAltIP.addLongIdentifier("subjectAltNameIPAddress", true);
1712    genCertSubjectAltIP.addLongIdentifier("subject-alt-name-ip", true);
1713    genCertSubjectAltIP.addLongIdentifier("subjectAltNameIP", true);
1714    genCertSubjectAltIP.addLongIdentifier("subject-alternative-ip-address",
1715         true);
1716    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeIPAddress", true);
1717    genCertSubjectAltIP.addLongIdentifier("subject-alternative-ip", true);
1718    genCertSubjectAltIP.addLongIdentifier("subjectAlternativeIP", true);
1719    genCertSubjectAltIP.addLongIdentifier("subject-alt-ip-address", true);
1720    genCertSubjectAltIP.addLongIdentifier("subjectAltIPAddress", true);
1721    genCertSubjectAltIP.addLongIdentifier("subject-alt-ip", true);
1722    genCertSubjectAltIP.addLongIdentifier("subjectAltIP", true);
1723    genCertSubjectAltIP.addLongIdentifier("san-ip-address", true);
1724    genCertSubjectAltIP.addLongIdentifier("sanIPAddress", true);
1725    genCertSubjectAltIP.addLongIdentifier("san-ip", true);
1726    genCertSubjectAltIP.addLongIdentifier("sanIP", true);
1727    genCertSubjectAltIP.addValueValidator(
1728         new IPAddressArgumentValueValidator(true, true));
1729    genCertParser.addArgument(genCertSubjectAltIP);
1730
1731    final StringArgument genCertSubjectAltEmail = new StringArgument(null,
1732         "subject-alternative-name-email-address", false, 0,
1733         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
1734         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_EMAIL_DESC.get());
1735    genCertSubjectAltEmail.addLongIdentifier(
1736         "subjectAlternativeNameEmailAddress", true);
1737    genCertSubjectAltEmail.addLongIdentifier("subject-alternative-name-email",
1738         true);
1739    genCertSubjectAltEmail.addLongIdentifier("subjectAlternativeNameEmail",
1740         true);
1741    genCertSubjectAltEmail.addLongIdentifier("subject-alt-name-email-address",
1742         true);
1743    genCertSubjectAltEmail.addLongIdentifier("subjectAltNameEmailAddress",
1744         true);
1745    genCertSubjectAltEmail.addLongIdentifier("subject-alt-name-email", true);
1746    genCertSubjectAltEmail.addLongIdentifier("subjectAltNameEmail", true);
1747    genCertSubjectAltEmail.addLongIdentifier(
1748         "subject-alternative-email-address", true);
1749    genCertSubjectAltEmail.addLongIdentifier("subjectAlternativeEmailAddress",
1750         true);
1751    genCertSubjectAltEmail.addLongIdentifier("subject-alternative-email", true);
1752    genCertSubjectAltEmail.addLongIdentifier("subjectAlternativeEmail", true);
1753    genCertSubjectAltEmail.addLongIdentifier("subject-alt-email-address", true);
1754    genCertSubjectAltEmail.addLongIdentifier("subjectAltEmailAddress", true);
1755    genCertSubjectAltEmail.addLongIdentifier("subject-alt-email", true);
1756    genCertSubjectAltEmail.addLongIdentifier("subjectAltEmail", true);
1757    genCertSubjectAltEmail.addLongIdentifier("san-email-address", true);
1758    genCertSubjectAltEmail.addLongIdentifier("sanEmailAddress", true);
1759    genCertSubjectAltEmail.addLongIdentifier("san-email", true);
1760    genCertSubjectAltEmail.addLongIdentifier("sanEmail", true);
1761    genCertSubjectAltEmail.addValueValidator(
1762         new IA5StringArgumentValueValidator(false));
1763    genCertParser.addArgument(genCertSubjectAltEmail);
1764
1765    final StringArgument genCertSubjectAltURI = new StringArgument(null,
1766         "subject-alternative-name-uri", false, 0,
1767         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
1768         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_URI_DESC.get());
1769    genCertSubjectAltURI.addLongIdentifier("subjectAlternativeNameURI", true);
1770    genCertSubjectAltURI.addLongIdentifier("subject-alt-name-uri", true);
1771    genCertSubjectAltURI.addLongIdentifier("subjectAltNameURI", true);
1772    genCertSubjectAltURI.addLongIdentifier("subject-alternative-uri", true);
1773    genCertSubjectAltURI.addLongIdentifier("subjectAlternativeURI", true);
1774    genCertSubjectAltURI.addLongIdentifier("subject-alt-uri", true);
1775    genCertSubjectAltURI.addLongIdentifier("subjectAltURI", true);
1776    genCertSubjectAltURI.addLongIdentifier("san-uri", true);
1777    genCertSubjectAltURI.addLongIdentifier("sanURI", true);
1778    genCertParser.addArgument(genCertSubjectAltURI);
1779
1780    final StringArgument genCertSubjectAltOID = new StringArgument(null,
1781         "subject-alternative-name-oid", false, 0,
1782         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
1783         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_SAN_OID_DESC.get());
1784    genCertSubjectAltOID.addLongIdentifier("subjectAlternativeNameOID", true);
1785    genCertSubjectAltOID.addLongIdentifier("subject-alt-name-oid", true);
1786    genCertSubjectAltOID.addLongIdentifier("subjectAltNameOID", true);
1787    genCertSubjectAltOID.addLongIdentifier("subject-alternative-oid", true);
1788    genCertSubjectAltOID.addLongIdentifier("subjectAlternativeOID", true);
1789    genCertSubjectAltOID.addLongIdentifier("subject-alt-oid", true);
1790    genCertSubjectAltOID.addLongIdentifier("subjectAltOID", true);
1791    genCertSubjectAltOID.addLongIdentifier("san-oid", true);
1792    genCertSubjectAltOID.addLongIdentifier("sanOID", true);
1793    genCertSubjectAltOID.addValueValidator(new OIDArgumentValueValidator(true));
1794    genCertParser.addArgument(genCertSubjectAltOID);
1795
1796    final BooleanValueArgument genCertBasicConstraintsIsCA =
1797         new BooleanValueArgument(null, "basic-constraints-is-ca", false, null,
1798              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_IS_CA_DESC.get());
1799    genCertBasicConstraintsIsCA.addLongIdentifier("basicConstraintsIsCA", true);
1800    genCertBasicConstraintsIsCA.addLongIdentifier("bc-is-ca", true);
1801    genCertBasicConstraintsIsCA.addLongIdentifier("bcIsCA", true);
1802    genCertParser.addArgument(genCertBasicConstraintsIsCA);
1803
1804    final IntegerArgument genCertBasicConstraintsPathLength =
1805         new IntegerArgument(null, "basic-constraints-maximum-path-length",
1806              false, 1, null,
1807              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_PATH_LENGTH_DESC.get(), 0,
1808              Integer.MAX_VALUE);
1809    genCertBasicConstraintsPathLength.addLongIdentifier(
1810         "basicConstraintsMaximumPathLength", true);
1811    genCertBasicConstraintsPathLength.addLongIdentifier(
1812         "basic-constraints-max-path-length", true);
1813    genCertBasicConstraintsPathLength.addLongIdentifier(
1814         "basicConstraintsMaxPathLength", true);
1815    genCertBasicConstraintsPathLength.addLongIdentifier(
1816         "basic-constraints-path-length", true);
1817    genCertBasicConstraintsPathLength.addLongIdentifier(
1818         "basicConstraintsPathLength", true);
1819    genCertBasicConstraintsPathLength.addLongIdentifier(
1820         "bc-maximum-path-length", true);
1821    genCertBasicConstraintsPathLength.addLongIdentifier("bcMaximumPathLength",
1822         true);
1823    genCertBasicConstraintsPathLength.addLongIdentifier("bc-max-path-length",
1824         true);
1825    genCertBasicConstraintsPathLength.addLongIdentifier("bcMaxPathLength",
1826         true);
1827    genCertBasicConstraintsPathLength.addLongIdentifier("bc-path-length", true);
1828    genCertBasicConstraintsPathLength.addLongIdentifier("bcPathLength", true);
1829    genCertParser.addArgument(genCertBasicConstraintsPathLength);
1830
1831    final StringArgument genCertKeyUsage = new StringArgument(null, "key-usage",
1832         false, 0, null, INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_KU_DESC.get());
1833    genCertKeyUsage.addLongIdentifier("keyUsage", true);
1834    genCertParser.addArgument(genCertKeyUsage);
1835
1836    final StringArgument genCertExtendedKeyUsage = new StringArgument(null,
1837         "extended-key-usage", false, 0, null,
1838         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_EKU_DESC.get());
1839    genCertExtendedKeyUsage.addLongIdentifier("extendedKeyUsage", true);
1840    genCertParser.addArgument(genCertExtendedKeyUsage);
1841
1842    final StringArgument genCertExtension = new StringArgument(null,
1843         "extension", false, 0, null,
1844         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_EXT_DESC.get());
1845    genCertExtension.addLongIdentifier("ext", true);
1846    genCertParser.addArgument(genCertExtension);
1847
1848    final FileArgument genCertOutputFile = new FileArgument(null, "output-file",
1849         false, 1, null,
1850         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_OUTPUT_FILE_DESC.get(), false, true,
1851         true, false);
1852    genCertOutputFile.addLongIdentifier("outputFile", true);
1853    genCertOutputFile.addLongIdentifier("filename", true);
1854    genCertOutputFile.addLongIdentifier("file", true);
1855    genCertParser.addArgument(genCertOutputFile);
1856
1857    final Set<String> genCertOutputFormatAllowedValues = StaticUtils.setOf(
1858         "PEM", "text", "txt", "RFC", "DER", "binary", "bin");
1859    final StringArgument genCertOutputFormat = new StringArgument(null,
1860         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
1861         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_FORMAT_DESC.get(),
1862         genCertOutputFormatAllowedValues, "PEM");
1863    genCertOutputFormat.addLongIdentifier("outputFormat", true);
1864    genCertParser.addArgument(genCertOutputFormat);
1865
1866    final BooleanArgument genCertDisplayCommand = new BooleanArgument(null,
1867         "display-keytool-command", 1,
1868         INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_DISPLAY_COMMAND_DESC.get());
1869    genCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
1870    genCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
1871    genCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
1872    genCertParser.addArgument(genCertDisplayCommand);
1873
1874    genCertParser.addRequiredArgumentSet(genCertKeystorePassword,
1875         genCertKeystorePasswordFile, genCertPromptForKeystorePassword);
1876    genCertParser.addExclusiveArgumentSet(genCertKeystorePassword,
1877         genCertKeystorePasswordFile, genCertPromptForKeystorePassword);
1878    genCertParser.addExclusiveArgumentSet(genCertPKPassword,
1879         genCertPKPasswordFile, genCertPromptForPKPassword);
1880    genCertParser.addExclusiveArgumentSet(genCertUseExistingKeyPair,
1881         genCertKeyAlgorithm);
1882    genCertParser.addExclusiveArgumentSet(genCertUseExistingKeyPair,
1883         genCertKeySizeBits);
1884    genCertParser.addExclusiveArgumentSet(genCertUseExistingKeyPair,
1885         genCertSignatureAlgorithm);
1886    genCertParser.addDependentArgumentSet(genCertBasicConstraintsPathLength,
1887         genCertBasicConstraintsIsCA);
1888    genCertParser.addDependentArgumentSet(genCertOutputFormat,
1889         genCertOutputFile);
1890
1891    final LinkedHashMap<String[],String> genCertExamples =
1892         new LinkedHashMap<>(StaticUtils.computeMapCapacity(4));
1893    genCertExamples.put(
1894         new String[]
1895         {
1896           "generate-self-signed-certificate",
1897           "--keystore", getPlatformSpecificPath("config", "keystore"),
1898           "--keystore-password-file",
1899                getPlatformSpecificPath("config", "keystore.pin"),
1900           "--alias", "server-cert",
1901           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US"
1902         },
1903         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_1.get());
1904    genCertExamples.put(
1905         new String[]
1906         {
1907           "generate-self-signed-certificate",
1908           "--keystore", getPlatformSpecificPath("config", "keystore"),
1909           "--keystore-password-file",
1910                getPlatformSpecificPath("config", "keystore.pin"),
1911           "--alias", "server-cert",
1912           "--use-existing-key-pair",
1913           "--inherit-extensions"
1914         },
1915         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_2.get());
1916    genCertExamples.put(
1917         new String[]
1918         {
1919           "generate-self-signed-certificate",
1920           "--keystore", getPlatformSpecificPath("config", "keystore"),
1921           "--keystore-password-file",
1922                getPlatformSpecificPath("config", "keystore.pin"),
1923           "--alias", "server-cert",
1924           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US",
1925           "--days-valid", "3650",
1926           "--validity-start-time", "20170101000000",
1927           "--key-algorithm", "RSA",
1928           "--key-size-bits", "4096",
1929           "--signature-algorithm", "SHA256withRSA",
1930           "--subject-alternative-name-dns", "ldap1.example.com",
1931           "--subject-alternative-name-dns", "ldap2.example.com",
1932           "--subject-alternative-name-ip-address", "1.2.3.4",
1933           "--subject-alternative-name-ip-address", "1.2.3.5",
1934           "--extended-key-usage", "server-auth",
1935           "--extended-key-usage", "client-auth",
1936           "--display-keytool-command"
1937         },
1938         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_3.get());
1939    genCertExamples.put(
1940         new String[]
1941         {
1942           "generate-self-signed-certificate",
1943           "--keystore", getPlatformSpecificPath("config", "keystore"),
1944           "--keystore-password-file",
1945                getPlatformSpecificPath("config", "keystore.pin"),
1946           "--alias", "ca-cert",
1947           "--subject-dn",
1948                "CN=Example Certification Authority,O=Example Corp,C=US",
1949           "--days-valid", "7300",
1950           "--validity-start-time", "20170101000000",
1951           "--key-algorithm", "EC",
1952           "--key-size-bits", "256",
1953           "--signature-algorithm", "SHA256withECDSA",
1954           "--basic-constraints-is-ca", "true",
1955           "--key-usage", "key-cert-sign",
1956           "--key-usage", "crl-sign",
1957           "--display-keytool-command"
1958         },
1959         INFO_MANAGE_CERTS_SC_GEN_CERT_EXAMPLE_4.get());
1960
1961    final SubCommand genCertSubCommand = new SubCommand(
1962         "generate-self-signed-certificate",
1963         INFO_MANAGE_CERTS_SC_GEN_CERT_DESC.get(), genCertParser,
1964         genCertExamples);
1965    genCertSubCommand.addName("generateSelfSignedCertificate", true);
1966    genCertSubCommand.addName("generate-certificate", true);
1967    genCertSubCommand.addName("generateCertificate", true);
1968    genCertSubCommand.addName("self-signed-certificate", true);
1969    genCertSubCommand.addName("selfSignedCertificate", true);
1970    genCertSubCommand.addName("selfcert", true);
1971
1972    parser.addSubCommand(genCertSubCommand);
1973
1974
1975    // Define the "generate-certificate-signing-request" subcommand and all of
1976    // its arguments.
1977    final ArgumentParser genCSRParser = new ArgumentParser(
1978         "generate-certificate-signing-request",
1979         INFO_MANAGE_CERTS_SC_GEN_CSR_DESC.get());
1980
1981    final Set<String> genCSROutputFormatAllowedValues = StaticUtils.setOf(
1982         "PEM", "text", "txt", "RFC", "DER", "binary", "bin");
1983    final StringArgument genCSROutputFormat = new StringArgument(null,
1984         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
1985         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_FORMAT_DESC.get(),
1986         genCSROutputFormatAllowedValues, "PEM");
1987    genCSROutputFormat.addLongIdentifier("outputFormat", true);
1988    genCSRParser.addArgument(genCSROutputFormat);
1989
1990    final FileArgument genCSROutputFile = new FileArgument(null, "output-file",
1991         false, 1, null,
1992         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_OUTPUT_FILE_DESC.get(), false, true,
1993         true, false);
1994    genCSROutputFile.addLongIdentifier("outputFile", true);
1995    genCSROutputFile.addLongIdentifier("filename", true);
1996    genCSROutputFile.addLongIdentifier("file", true);
1997    genCSRParser.addArgument(genCSROutputFile);
1998
1999    final FileArgument genCSRKeystore = new FileArgument(null, "keystore",
2000         true, 1, null, INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_DESC.get(), false,
2001         true,  true, false);
2002    genCSRKeystore.addLongIdentifier("keystore-path", true);
2003    genCSRKeystore.addLongIdentifier("keystorePath", true);
2004    genCSRKeystore.addLongIdentifier("keystore-file", true);
2005    genCSRKeystore.addLongIdentifier("keystoreFile", true);
2006    genCSRParser.addArgument(genCSRKeystore);
2007
2008    final StringArgument genCSRKeystorePassword = new StringArgument(null,
2009         "keystore-password", false, 1,
2010         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2011         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_PW_DESC.get());
2012    genCSRKeystorePassword.addLongIdentifier("keystorePassword", true);
2013    genCSRKeystorePassword.addLongIdentifier("keystore-passphrase", true);
2014    genCSRKeystorePassword.addLongIdentifier("keystorePassphrase", true);
2015    genCSRKeystorePassword.addLongIdentifier("keystore-pin", true);
2016    genCSRKeystorePassword.addLongIdentifier("keystorePIN", true);
2017    genCSRKeystorePassword.addLongIdentifier("storepass", true);
2018    genCSRKeystorePassword.setSensitive(true);
2019    genCSRParser.addArgument(genCSRKeystorePassword);
2020
2021    final FileArgument genCSRKeystorePasswordFile = new FileArgument(null,
2022         "keystore-password-file", false, 1, null,
2023         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_PW_FILE_DESC.get(), true, true,
2024         true, false);
2025    genCSRKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
2026         true);
2027    genCSRKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
2028         true);
2029    genCSRKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
2030         true);
2031    genCSRKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
2032         true);
2033    genCSRKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
2034    genCSRParser.addArgument(genCSRKeystorePasswordFile);
2035
2036    final BooleanArgument genCSRPromptForKeystorePassword =
2037         new BooleanArgument(null, "prompt-for-keystore-password",
2038        INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PROMPT_FOR_KS_PW_DESC.get());
2039    genCSRPromptForKeystorePassword.addLongIdentifier(
2040         "promptForKeystorePassword", true);
2041    genCSRPromptForKeystorePassword.addLongIdentifier(
2042         "prompt-for-keystore-passphrase", true);
2043    genCSRPromptForKeystorePassword.addLongIdentifier(
2044         "promptForKeystorePassphrase", true);
2045    genCSRPromptForKeystorePassword.addLongIdentifier(
2046         "prompt-for-keystore-pin", true);
2047    genCSRPromptForKeystorePassword.addLongIdentifier(
2048         "promptForKeystorePIN", true);
2049    genCSRParser.addArgument(genCSRPromptForKeystorePassword);
2050
2051    final StringArgument genCSRPKPassword = new StringArgument(null,
2052         "private-key-password", false, 1,
2053         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2054         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PK_PW_DESC.get());
2055    genCSRPKPassword.addLongIdentifier("privateKeyPassword", true);
2056    genCSRPKPassword.addLongIdentifier("private-key-passphrase", true);
2057    genCSRPKPassword.addLongIdentifier("privateKeyPassphrase", true);
2058    genCSRPKPassword.addLongIdentifier("private-key-pin", true);
2059    genCSRPKPassword.addLongIdentifier("privateKeyPIN", true);
2060    genCSRPKPassword.addLongIdentifier("key-password", true);
2061    genCSRPKPassword.addLongIdentifier("keyPassword", true);
2062    genCSRPKPassword.addLongIdentifier("key-passphrase", true);
2063    genCSRPKPassword.addLongIdentifier("keyPassphrase", true);
2064    genCSRPKPassword.addLongIdentifier("key-pin", true);
2065    genCSRPKPassword.addLongIdentifier("keyPIN", true);
2066    genCSRPKPassword.addLongIdentifier("keypass", true);
2067    genCSRPKPassword.setSensitive(true);
2068    genCSRParser.addArgument(genCSRPKPassword);
2069
2070    final FileArgument genCSRPKPasswordFile = new FileArgument(null,
2071         "private-key-password-file", false, 1, null,
2072         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PK_PW_FILE_DESC.get(), true, true,
2073         true, false);
2074    genCSRPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
2075    genCSRPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
2076         true);
2077    genCSRPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
2078         true);
2079    genCSRPKPasswordFile.addLongIdentifier("private-key-pin-file",
2080         true);
2081    genCSRPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
2082    genCSRPKPasswordFile.addLongIdentifier("key-password-file", true);
2083    genCSRPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
2084    genCSRPKPasswordFile.addLongIdentifier("key-passphrase-file",
2085         true);
2086    genCSRPKPasswordFile.addLongIdentifier("keyPassphraseFile",
2087         true);
2088    genCSRPKPasswordFile.addLongIdentifier("key-pin-file",
2089         true);
2090    genCSRPKPasswordFile.addLongIdentifier("keyPINFile", true);
2091    genCSRParser.addArgument(genCSRPKPasswordFile);
2092
2093    final BooleanArgument genCSRPromptForPKPassword =
2094         new BooleanArgument(null, "prompt-for-private-key-password",
2095        INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_PROMPT_FOR_PK_PW_DESC.get());
2096    genCSRPromptForPKPassword.addLongIdentifier(
2097         "promptForPrivateKeyPassword", true);
2098    genCSRPromptForPKPassword.addLongIdentifier(
2099         "prompt-for-private-key-passphrase", true);
2100    genCSRPromptForPKPassword.addLongIdentifier(
2101         "promptForPrivateKeyPassphrase", true);
2102    genCSRPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
2103         true);
2104    genCSRPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
2105         true);
2106    genCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
2107         true);
2108    genCSRPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
2109         true);
2110    genCSRPromptForPKPassword.addLongIdentifier(
2111         "prompt-for-key-passphrase", true);
2112    genCSRPromptForPKPassword.addLongIdentifier(
2113         "promptForKeyPassphrase", true);
2114    genCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
2115    genCSRPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
2116    genCSRParser.addArgument(genCSRPromptForPKPassword);
2117
2118    final StringArgument genCSRKeystoreType = new StringArgument(null,
2119         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
2120         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KS_TYPE_DESC.get(),
2121         ALLOWED_KEYSTORE_TYPE_VALUES);
2122    genCSRKeystoreType.addLongIdentifier("key-store-type", true);
2123    genCSRKeystoreType.addLongIdentifier("keystoreType", true);
2124    genCSRKeystoreType.addLongIdentifier("keystore-format", true);
2125    genCSRKeystoreType.addLongIdentifier("key-store-format", true);
2126    genCSRKeystoreType.addLongIdentifier("keystoreFormat", true);
2127    genCSRKeystoreType.addLongIdentifier("storetype", true);
2128    genCSRParser.addArgument(genCSRKeystoreType);
2129
2130    final StringArgument genCSRAlias = new StringArgument(null, "alias",
2131         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
2132         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_ALIAS_DESC.get());
2133    genCSRAlias.addLongIdentifier("nickname", true);
2134    genCSRParser.addArgument(genCSRAlias);
2135
2136    final BooleanArgument genCSRUseExistingKeyPair = new BooleanArgument(null,
2137         "use-existing-key-pair", 1,
2138         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_USE_EXISTING_KP_DESC.get());
2139    genCSRUseExistingKeyPair.addLongIdentifier("use-existing-keypair", true);
2140    genCSRUseExistingKeyPair.addLongIdentifier("useExistingKeyPair", true);
2141    genCSRUseExistingKeyPair.addLongIdentifier("replace-existing-certificate",
2142         true);
2143    genCSRUseExistingKeyPair.addLongIdentifier("replaceExistingCertificate",
2144         true);
2145    genCSRUseExistingKeyPair.addLongIdentifier("replace-certificate", true);
2146    genCSRUseExistingKeyPair.addLongIdentifier("replaceCertificate", true);
2147    genCSRUseExistingKeyPair.addLongIdentifier("replace-existing", true);
2148    genCSRUseExistingKeyPair.addLongIdentifier("replaceExisting", true);
2149    genCSRUseExistingKeyPair.addLongIdentifier("replace", true);
2150    genCSRParser.addArgument(genCSRUseExistingKeyPair);
2151
2152    final DNArgument genCSRSubjectDN = new DNArgument(null, "subject-dn",
2153         false, 1, null,
2154         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SUBJECT_DN_DESC.get());
2155    genCSRSubjectDN.addLongIdentifier("subjectDN", true);
2156    genCSRSubjectDN.addLongIdentifier("subject", true);
2157    genCSRSubjectDN.addLongIdentifier("dname", true);
2158    genCSRParser.addArgument(genCSRSubjectDN);
2159
2160    final StringArgument genCSRKeyAlgorithm = new StringArgument(null,
2161         "key-algorithm", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2162         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KEY_ALGORITHM_DESC.get());
2163    genCSRKeyAlgorithm.addLongIdentifier("keyAlgorithm", true);
2164    genCSRKeyAlgorithm.addLongIdentifier("key-alg", true);
2165    genCSRKeyAlgorithm.addLongIdentifier("keyAlg", true);
2166    genCSRParser.addArgument(genCSRKeyAlgorithm);
2167
2168    final IntegerArgument genCSRKeySizeBits = new IntegerArgument(null,
2169         "key-size-bits", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_BITS.get(),
2170         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KEY_SIZE_BITS_DESC.get(), 1,
2171         Integer.MAX_VALUE);
2172    genCSRKeySizeBits.addLongIdentifier("keySizeBits", true);
2173    genCSRKeySizeBits.addLongIdentifier("key-length-bits", true);
2174    genCSRKeySizeBits.addLongIdentifier("keyLengthBits", true);
2175    genCSRKeySizeBits.addLongIdentifier("key-size", true);
2176    genCSRKeySizeBits.addLongIdentifier("keySize", true);
2177    genCSRKeySizeBits.addLongIdentifier("key-length", true);
2178    genCSRKeySizeBits.addLongIdentifier("keyLength", true);
2179    genCSRParser.addArgument(genCSRKeySizeBits);
2180
2181    final StringArgument genCSRSignatureAlgorithm = new StringArgument(null,
2182         "signature-algorithm", false, 1,
2183         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2184         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SIG_ALG_DESC.get());
2185    genCSRSignatureAlgorithm.addLongIdentifier("signatureAlgorithm", true);
2186    genCSRSignatureAlgorithm.addLongIdentifier("signature-alg", true);
2187    genCSRSignatureAlgorithm.addLongIdentifier("signatureAlg", true);
2188    genCSRSignatureAlgorithm.addLongIdentifier("sig-alg", true);
2189    genCSRSignatureAlgorithm.addLongIdentifier("sigAlg", true);
2190    genCSRParser.addArgument(genCSRSignatureAlgorithm);
2191
2192    final BooleanArgument genCSRInheritExtensions = new BooleanArgument(null,
2193         "inherit-extensions", 1,
2194         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_INHERIT_EXT_DESC.get());
2195    genCSRInheritExtensions.addLongIdentifier("inheritExtensions", true);
2196    genCSRParser.addArgument(genCSRInheritExtensions);
2197
2198    final StringArgument genCSRSubjectAltDNS = new StringArgument(null,
2199         "subject-alternative-name-dns", false, 0,
2200         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2201         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_DNS_DESC.get());
2202    genCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeNameDNS", true);
2203    genCSRSubjectAltDNS.addLongIdentifier("subject-alt-name-dns", true);
2204    genCSRSubjectAltDNS.addLongIdentifier("subjectAltNameDNS", true);
2205    genCSRSubjectAltDNS.addLongIdentifier("subject-alternative-dns", true);
2206    genCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeDNS", true);
2207    genCSRSubjectAltDNS.addLongIdentifier("subject-alt-dns", true);
2208    genCSRSubjectAltDNS.addLongIdentifier("subjectAltDNS", true);
2209    genCSRSubjectAltDNS.addLongIdentifier("san-dns", true);
2210    genCSRSubjectAltDNS.addLongIdentifier("sanDNS", true);
2211    genCSRSubjectAltDNS.addValueValidator(
2212         new IA5StringArgumentValueValidator(false));
2213    genCSRParser.addArgument(genCSRSubjectAltDNS);
2214
2215    final StringArgument genCSRSubjectAltIP = new StringArgument(null,
2216         "subject-alternative-name-ip-address", false, 0,
2217         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2218         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_IP_DESC.get());
2219    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIPAddress",
2220         true);
2221    genCSRSubjectAltIP.addLongIdentifier("subject-alternative-name-ip", true);
2222    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIP", true);
2223    genCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip-address", true);
2224    genCSRSubjectAltIP.addLongIdentifier("subjectAltNameIPAddress", true);
2225    genCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip", true);
2226    genCSRSubjectAltIP.addLongIdentifier("subjectAltNameIP", true);
2227    genCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip-address",
2228         true);
2229    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIPAddress", true);
2230    genCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip", true);
2231    genCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIP", true);
2232    genCSRSubjectAltIP.addLongIdentifier("subject-alt-ip-address", true);
2233    genCSRSubjectAltIP.addLongIdentifier("subjectAltIPAddress", true);
2234    genCSRSubjectAltIP.addLongIdentifier("subject-alt-ip", true);
2235    genCSRSubjectAltIP.addLongIdentifier("subjectAltIP", true);
2236    genCSRSubjectAltIP.addLongIdentifier("san-ip-address", true);
2237    genCSRSubjectAltIP.addLongIdentifier("sanIPAddress", true);
2238    genCSRSubjectAltIP.addLongIdentifier("san-ip", true);
2239    genCSRSubjectAltIP.addLongIdentifier("sanIP", true);
2240    genCSRSubjectAltIP.addValueValidator(
2241         new IPAddressArgumentValueValidator(true, true));
2242    genCSRParser.addArgument(genCSRSubjectAltIP);
2243
2244    final StringArgument genCSRSubjectAltEmail = new StringArgument(null,
2245         "subject-alternative-name-email-address", false, 0,
2246         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2247         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_EMAIL_DESC.get());
2248    genCSRSubjectAltEmail.addLongIdentifier(
2249         "subjectAlternativeNameEmailAddress", true);
2250    genCSRSubjectAltEmail.addLongIdentifier("subject-alternative-name-email",
2251         true);
2252    genCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeNameEmail",
2253         true);
2254    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email-address",
2255         true);
2256    genCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmailAddress",
2257         true);
2258    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email", true);
2259    genCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmail", true);
2260    genCSRSubjectAltEmail.addLongIdentifier(
2261         "subject-alternative-email-address", true);
2262    genCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmailAddress",
2263         true);
2264    genCSRSubjectAltEmail.addLongIdentifier("subject-alternative-email", true);
2265    genCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmail", true);
2266    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-email-address", true);
2267    genCSRSubjectAltEmail.addLongIdentifier("subjectAltEmailAddress", true);
2268    genCSRSubjectAltEmail.addLongIdentifier("subject-alt-email", true);
2269    genCSRSubjectAltEmail.addLongIdentifier("subjectAltEmail", true);
2270    genCSRSubjectAltEmail.addLongIdentifier("san-email-address", true);
2271    genCSRSubjectAltEmail.addLongIdentifier("sanEmailAddress", true);
2272    genCSRSubjectAltEmail.addLongIdentifier("san-email", true);
2273    genCSRSubjectAltEmail.addLongIdentifier("sanEmail", true);
2274    genCSRSubjectAltEmail.addValueValidator(
2275         new IA5StringArgumentValueValidator(false));
2276    genCSRParser.addArgument(genCSRSubjectAltEmail);
2277
2278    final StringArgument genCSRSubjectAltURI = new StringArgument(null,
2279         "subject-alternative-name-uri", false, 0,
2280         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
2281         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_URI_DESC.get());
2282    genCSRSubjectAltURI.addLongIdentifier("subjectAlternativeNameURI", true);
2283    genCSRSubjectAltURI.addLongIdentifier("subject-alt-name-uri", true);
2284    genCSRSubjectAltURI.addLongIdentifier("subjectAltNameURI", true);
2285    genCSRSubjectAltURI.addLongIdentifier("subject-alternative-uri", true);
2286    genCSRSubjectAltURI.addLongIdentifier("subjectAlternativeURI", true);
2287    genCSRSubjectAltURI.addLongIdentifier("subject-alt-uri", true);
2288    genCSRSubjectAltURI.addLongIdentifier("subjectAltURI", true);
2289    genCSRSubjectAltURI.addLongIdentifier("san-uri", true);
2290    genCSRSubjectAltURI.addLongIdentifier("sanURI", true);
2291    genCSRParser.addArgument(genCSRSubjectAltURI);
2292
2293    final StringArgument genCSRSubjectAltOID = new StringArgument(null,
2294         "subject-alternative-name-oid", false, 0,
2295         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
2296         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_SAN_OID_DESC.get());
2297    genCSRSubjectAltOID.addLongIdentifier("subjectAlternativeNameOID", true);
2298    genCSRSubjectAltOID.addLongIdentifier("subject-alt-name-oid", true);
2299    genCSRSubjectAltOID.addLongIdentifier("subjectAltNameOID", true);
2300    genCSRSubjectAltOID.addLongIdentifier("subject-alternative-oid", true);
2301    genCSRSubjectAltOID.addLongIdentifier("subjectAlternativeOID", true);
2302    genCSRSubjectAltOID.addLongIdentifier("subject-alt-oid", true);
2303    genCSRSubjectAltOID.addLongIdentifier("subjectAltOID", true);
2304    genCSRSubjectAltOID.addLongIdentifier("san-oid", true);
2305    genCSRSubjectAltOID.addLongIdentifier("sanOID", true);
2306    genCSRSubjectAltOID.addValueValidator(new OIDArgumentValueValidator(true));
2307    genCSRParser.addArgument(genCSRSubjectAltOID);
2308
2309    final BooleanValueArgument genCSRBasicConstraintsIsCA =
2310         new BooleanValueArgument(null, "basic-constraints-is-ca", false, null,
2311              INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_BC_IS_CA_DESC.get());
2312    genCSRBasicConstraintsIsCA.addLongIdentifier("basicConstraintsIsCA", true);
2313    genCSRBasicConstraintsIsCA.addLongIdentifier("bc-is-ca", true);
2314    genCSRBasicConstraintsIsCA.addLongIdentifier("bcIsCA", true);
2315    genCSRParser.addArgument(genCSRBasicConstraintsIsCA);
2316
2317    final IntegerArgument genCSRBasicConstraintsPathLength =
2318         new IntegerArgument(null, "basic-constraints-maximum-path-length",
2319              false, 1, null,
2320              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_PATH_LENGTH_DESC.get(), 0,
2321              Integer.MAX_VALUE);
2322    genCSRBasicConstraintsPathLength.addLongIdentifier(
2323         "basicConstraintsMaximumPathLength", true);
2324    genCSRBasicConstraintsPathLength.addLongIdentifier(
2325         "basic-constraints-max-path-length", true);
2326    genCSRBasicConstraintsPathLength.addLongIdentifier(
2327         "basicConstraintsMaxPathLength", true);
2328    genCSRBasicConstraintsPathLength.addLongIdentifier(
2329         "basic-constraints-path-length", true);
2330    genCSRBasicConstraintsPathLength.addLongIdentifier(
2331         "basicConstraintsPathLength", true);
2332    genCSRBasicConstraintsPathLength.addLongIdentifier(
2333         "bc-maximum-path-length", true);
2334    genCSRBasicConstraintsPathLength.addLongIdentifier("bcMaximumPathLength",
2335         true);
2336    genCSRBasicConstraintsPathLength.addLongIdentifier("bc-max-path-length",
2337         true);
2338    genCSRBasicConstraintsPathLength.addLongIdentifier("bcMaxPathLength",
2339         true);
2340    genCSRBasicConstraintsPathLength.addLongIdentifier("bc-path-length", true);
2341    genCSRBasicConstraintsPathLength.addLongIdentifier("bcPathLength", true);
2342    genCSRParser.addArgument(genCSRBasicConstraintsPathLength);
2343
2344    final StringArgument genCSRKeyUsage = new StringArgument(null, "key-usage",
2345         false, 0, null, INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_KU_DESC.get());
2346    genCSRKeyUsage.addLongIdentifier("keyUsage", true);
2347    genCSRParser.addArgument(genCSRKeyUsage);
2348
2349    final StringArgument genCSRExtendedKeyUsage = new StringArgument(null,
2350         "extended-key-usage", false, 0, null,
2351         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_EKU_DESC.get());
2352    genCSRExtendedKeyUsage.addLongIdentifier("extendedKeyUsage", true);
2353    genCSRParser.addArgument(genCSRExtendedKeyUsage);
2354
2355    final StringArgument genCSRExtension = new StringArgument(null,
2356         "extension", false, 0, null,
2357         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_EXT_DESC.get());
2358    genCSRExtension.addLongIdentifier("ext", true);
2359    genCSRParser.addArgument(genCSRExtension);
2360
2361    final BooleanArgument genCSRDisplayCommand = new BooleanArgument(null,
2362         "display-keytool-command", 1,
2363         INFO_MANAGE_CERTS_SC_GEN_CSR_ARG_DISPLAY_COMMAND_DESC.get());
2364    genCSRDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
2365    genCSRDisplayCommand.addLongIdentifier("show-keytool-command", true);
2366    genCSRDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
2367    genCSRParser.addArgument(genCSRDisplayCommand);
2368
2369    genCSRParser.addRequiredArgumentSet(genCSRKeystorePassword,
2370         genCSRKeystorePasswordFile, genCSRPromptForKeystorePassword);
2371    genCSRParser.addExclusiveArgumentSet(genCSRKeystorePassword,
2372         genCSRKeystorePasswordFile, genCSRPromptForKeystorePassword);
2373    genCSRParser.addExclusiveArgumentSet(genCSRPKPassword,
2374         genCSRPKPasswordFile, genCSRPromptForPKPassword);
2375    genCSRParser.addExclusiveArgumentSet(genCSRUseExistingKeyPair,
2376         genCSRKeyAlgorithm);
2377    genCSRParser.addExclusiveArgumentSet(genCSRUseExistingKeyPair,
2378         genCSRKeySizeBits);
2379    genCSRParser.addExclusiveArgumentSet(genCSRUseExistingKeyPair,
2380         genCSRSignatureAlgorithm);
2381    genCSRParser.addDependentArgumentSet(genCSRBasicConstraintsPathLength,
2382         genCSRBasicConstraintsIsCA);
2383
2384    final LinkedHashMap<String[],String> genCSRExamples =
2385         new LinkedHashMap<>(StaticUtils.computeMapCapacity(3));
2386    genCSRExamples.put(
2387         new String[]
2388         {
2389           "generate-certificate-signing-request",
2390           "--keystore", getPlatformSpecificPath("config", "keystore"),
2391           "--keystore-password-file",
2392                getPlatformSpecificPath("config", "keystore.pin"),
2393           "--alias", "server-cert",
2394           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US"
2395         },
2396         INFO_MANAGE_CERTS_SC_GEN_CSR_EXAMPLE_1.get());
2397    genCSRExamples.put(
2398         new String[]
2399         {
2400           "generate-certificate-signing-request",
2401           "--keystore", getPlatformSpecificPath("config", "keystore"),
2402           "--keystore-password-file",
2403                getPlatformSpecificPath("config", "keystore.pin"),
2404           "--alias", "server-cert",
2405           "--use-existing-key-pair",
2406           "--inherit-extensions",
2407           "--output-file", "server-cert.csr"
2408         },
2409         INFO_MANAGE_CERTS_SC_GEN_CSR_EXAMPLE_2.get());
2410    genCSRExamples.put(
2411         new String[]
2412         {
2413           "generate-certificate-signing-request",
2414           "--keystore", getPlatformSpecificPath("config", "keystore"),
2415           "--keystore-password-file",
2416                getPlatformSpecificPath("config", "keystore.pin"),
2417           "--alias", "server-cert",
2418           "--subject-dn", "CN=ldap.example.com,O=Example Corp,C=US",
2419           "--key-algorithm", "EC",
2420           "--key-size-bits", "256",
2421           "--signature-algorithm", "SHA256withECDSA",
2422           "--subject-alternative-name-dns", "ldap1.example.com",
2423           "--subject-alternative-name-dns", "ldap2.example.com",
2424           "--subject-alternative-name-ip-address", "1.2.3.4",
2425           "--subject-alternative-name-ip-address", "1.2.3.5",
2426           "--extended-key-usage", "server-auth",
2427           "--extended-key-usage", "client-auth",
2428           "--output-file", "server-cert.csr",
2429           "--display-keytool-command"
2430         },
2431         INFO_MANAGE_CERTS_SC_GEN_CSR_EXAMPLE_3.get());
2432
2433    final SubCommand genCSRSubCommand = new SubCommand(
2434         "generate-certificate-signing-request",
2435         INFO_MANAGE_CERTS_SC_GEN_CSR_DESC.get(), genCSRParser,
2436         genCSRExamples);
2437    genCSRSubCommand.addName("generateCertificateSigningRequest", true);
2438    genCSRSubCommand.addName("generate-certificate-request", true);
2439    genCSRSubCommand.addName("generateCertificateRequest", true);
2440    genCSRSubCommand.addName("generate-csr", true);
2441    genCSRSubCommand.addName("generateCSR", true);
2442    genCSRSubCommand.addName("certificate-signing-request", true);
2443    genCSRSubCommand.addName("certificateSigningRequest", true);
2444    genCSRSubCommand.addName("csr", true);
2445    genCSRSubCommand.addName("certreq", true);
2446
2447    parser.addSubCommand(genCSRSubCommand);
2448
2449
2450    // Define the "sign-certificate-signing-request" subcommand and all of its
2451    // arguments.
2452    final ArgumentParser signCSRParser = new ArgumentParser(
2453         "sign-certificate-signing-request",
2454         INFO_MANAGE_CERTS_SC_SIGN_CSR_DESC.get());
2455
2456    final FileArgument signCSRInputFile = new FileArgument(null,
2457         "request-input-file", true, 1, null,
2458         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_INPUT_FILE_DESC.get(), true, true,
2459         true, false);
2460    signCSRInputFile.addLongIdentifier("requestInputFile", true);
2461    signCSRInputFile.addLongIdentifier("certificate-signing-request", true);
2462    signCSRInputFile.addLongIdentifier("certificateSigningRequest", true);
2463    signCSRInputFile.addLongIdentifier("input-file", false);
2464    signCSRInputFile.addLongIdentifier("inputFile", true);
2465    signCSRInputFile.addLongIdentifier("csr", true);
2466    signCSRParser.addArgument(signCSRInputFile);
2467
2468    final FileArgument signCSROutputFile = new FileArgument(null,
2469         "certificate-output-file", false, 1, null,
2470         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_OUTPUT_FILE_DESC.get(), false, true,
2471         true, false);
2472    signCSROutputFile.addLongIdentifier("certificateOutputFile", true);
2473    signCSROutputFile.addLongIdentifier("output-file", false);
2474    signCSROutputFile.addLongIdentifier("outputFile", true);
2475    signCSROutputFile.addLongIdentifier("certificate-file", true);
2476    signCSROutputFile.addLongIdentifier("certificateFile", true);
2477    signCSRParser.addArgument(signCSROutputFile);
2478
2479    final Set<String> signCSROutputFormatAllowedValues = StaticUtils.setOf(
2480         "PEM", "text", "txt", "RFC", "DER", "binary", "bin");
2481    final StringArgument signCSROutputFormat = new StringArgument(null,
2482         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
2483         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_FORMAT_DESC.get(),
2484         signCSROutputFormatAllowedValues, "PEM");
2485    signCSROutputFormat.addLongIdentifier("outputFormat", true);
2486    signCSRParser.addArgument(signCSROutputFormat);
2487
2488    final FileArgument signCSRKeystore = new FileArgument(null, "keystore",
2489         true, 1, null, INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_DESC.get(), true,
2490         true,  true, false);
2491    signCSRKeystore.addLongIdentifier("keystore-path", true);
2492    signCSRKeystore.addLongIdentifier("keystorePath", true);
2493    signCSRKeystore.addLongIdentifier("keystore-file", true);
2494    signCSRKeystore.addLongIdentifier("keystoreFile", true);
2495    signCSRParser.addArgument(signCSRKeystore);
2496
2497    final StringArgument signCSRKeystorePassword = new StringArgument(null,
2498         "keystore-password", false, 1,
2499         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2500         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_PW_DESC.get());
2501    signCSRKeystorePassword.addLongIdentifier("keystorePassword", true);
2502    signCSRKeystorePassword.addLongIdentifier("keystore-passphrase", true);
2503    signCSRKeystorePassword.addLongIdentifier("keystorePassphrase", true);
2504    signCSRKeystorePassword.addLongIdentifier("keystore-pin", true);
2505    signCSRKeystorePassword.addLongIdentifier("keystorePIN", true);
2506    signCSRKeystorePassword.addLongIdentifier("storepass", true);
2507    signCSRKeystorePassword.setSensitive(true);
2508    signCSRParser.addArgument(signCSRKeystorePassword);
2509
2510    final FileArgument signCSRKeystorePasswordFile = new FileArgument(null,
2511         "keystore-password-file", false, 1, null,
2512         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_PW_FILE_DESC.get(), true, true,
2513         true, false);
2514    signCSRKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
2515         true);
2516    signCSRKeystorePasswordFile.addLongIdentifier("keystore-passphrase-file",
2517         true);
2518    signCSRKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
2519         true);
2520    signCSRKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
2521         true);
2522    signCSRKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
2523    signCSRParser.addArgument(signCSRKeystorePasswordFile);
2524
2525    final BooleanArgument signCSRPromptForKeystorePassword =
2526         new BooleanArgument(null, "prompt-for-keystore-password",
2527        INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PROMPT_FOR_KS_PW_DESC.get());
2528    signCSRPromptForKeystorePassword.addLongIdentifier(
2529         "promptForKeystorePassword", true);
2530    signCSRPromptForKeystorePassword.addLongIdentifier(
2531         "prompt-for-keystore-passphrase", true);
2532    signCSRPromptForKeystorePassword.addLongIdentifier(
2533         "promptForKeystorePassphrase", true);
2534    signCSRPromptForKeystorePassword.addLongIdentifier(
2535         "prompt-for-keystore-pin", true);
2536    signCSRPromptForKeystorePassword.addLongIdentifier(
2537         "promptForKeystorePIN", true);
2538    signCSRParser.addArgument(signCSRPromptForKeystorePassword);
2539
2540    final StringArgument signCSRPKPassword = new StringArgument(null,
2541         "private-key-password", false, 1,
2542         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
2543         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PK_PW_DESC.get());
2544    signCSRPKPassword.addLongIdentifier("privateKeyPassword", true);
2545    signCSRPKPassword.addLongIdentifier("private-key-passphrase", true);
2546    signCSRPKPassword.addLongIdentifier("privateKeyPassphrase", true);
2547    signCSRPKPassword.addLongIdentifier("private-key-pin", true);
2548    signCSRPKPassword.addLongIdentifier("privateKeyPIN", true);
2549    signCSRPKPassword.addLongIdentifier("key-password", true);
2550    signCSRPKPassword.addLongIdentifier("keyPassword", true);
2551    signCSRPKPassword.addLongIdentifier("key-passphrase", true);
2552    signCSRPKPassword.addLongIdentifier("keyPassphrase", true);
2553    signCSRPKPassword.addLongIdentifier("key-pin", true);
2554    signCSRPKPassword.addLongIdentifier("keyPIN", true);
2555    signCSRPKPassword.addLongIdentifier("keypass", true);
2556    signCSRPKPassword.setSensitive(true);
2557    signCSRParser.addArgument(signCSRPKPassword);
2558
2559    final FileArgument signCSRPKPasswordFile = new FileArgument(null,
2560         "private-key-password-file", false, 1, null,
2561         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PK_PW_FILE_DESC.get(), true, true,
2562         true, false);
2563    signCSRPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
2564    signCSRPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
2565         true);
2566    signCSRPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
2567         true);
2568    signCSRPKPasswordFile.addLongIdentifier("private-key-pin-file",
2569         true);
2570    signCSRPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
2571    signCSRPKPasswordFile.addLongIdentifier("key-password-file", true);
2572    signCSRPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
2573    signCSRPKPasswordFile.addLongIdentifier("key-passphrase-file",
2574         true);
2575    signCSRPKPasswordFile.addLongIdentifier("keyPassphraseFile",
2576         true);
2577    signCSRPKPasswordFile.addLongIdentifier("key-pin-file",
2578         true);
2579    signCSRPKPasswordFile.addLongIdentifier("keyPINFile", true);
2580    signCSRParser.addArgument(signCSRPKPasswordFile);
2581
2582    final BooleanArgument signCSRPromptForPKPassword =
2583         new BooleanArgument(null, "prompt-for-private-key-password",
2584        INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_PROMPT_FOR_PK_PW_DESC.get());
2585    signCSRPromptForPKPassword.addLongIdentifier(
2586         "promptForPrivateKeyPassword", true);
2587    signCSRPromptForPKPassword.addLongIdentifier(
2588         "prompt-for-private-key-passphrase", true);
2589    signCSRPromptForPKPassword.addLongIdentifier(
2590         "promptForPrivateKeyPassphrase", true);
2591    signCSRPromptForPKPassword.addLongIdentifier("prompt-for-private-key-pin",
2592         true);
2593    signCSRPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
2594         true);
2595    signCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
2596         true);
2597    signCSRPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
2598         true);
2599    signCSRPromptForPKPassword.addLongIdentifier(
2600         "prompt-for-key-passphrase", true);
2601    signCSRPromptForPKPassword.addLongIdentifier(
2602         "promptForKeyPassphrase", true);
2603    signCSRPromptForPKPassword.addLongIdentifier("prompt-for-key-pin", true);
2604    signCSRPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
2605    signCSRParser.addArgument(signCSRPromptForPKPassword);
2606
2607    final StringArgument signCSRKeystoreType = new StringArgument(null,
2608         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
2609         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KS_TYPE_DESC.get(),
2610         ALLOWED_KEYSTORE_TYPE_VALUES);
2611    signCSRKeystoreType.addLongIdentifier("key-store-type", true);
2612    signCSRKeystoreType.addLongIdentifier("keystoreType", true);
2613    signCSRKeystoreType.addLongIdentifier("keystore-format", true);
2614    signCSRKeystoreType.addLongIdentifier("key-store-format", true);
2615    signCSRKeystoreType.addLongIdentifier("keystoreFormat", true);
2616    signCSRKeystoreType.addLongIdentifier("storetype", true);
2617    signCSRParser.addArgument(signCSRKeystoreType);
2618
2619    final StringArgument signCSRAlias = new StringArgument(null,
2620         "signing-certificate-alias",
2621         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
2622         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_ALIAS_DESC.get());
2623    signCSRAlias.addLongIdentifier("signingCertificateAlias", true);
2624    signCSRAlias.addLongIdentifier("signing-certificate-nickname", true);
2625    signCSRAlias.addLongIdentifier("signingCertificateNickname", true);
2626    signCSRAlias.addLongIdentifier("alias", true);
2627    signCSRAlias.addLongIdentifier("nickname", true);
2628    signCSRParser.addArgument(signCSRAlias);
2629
2630    final DNArgument signCSRSubjectDN = new DNArgument(null, "subject-dn",
2631         false, 1, null,
2632         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SUBJECT_DN_DESC.get());
2633    signCSRSubjectDN.addLongIdentifier("subjectDN", true);
2634    signCSRSubjectDN.addLongIdentifier("subject", true);
2635    signCSRSubjectDN.addLongIdentifier("dname", true);
2636    signCSRParser.addArgument(signCSRSubjectDN);
2637
2638    final IntegerArgument signCSRDaysValid = new IntegerArgument(null,
2639         "days-valid", false, 1, null,
2640         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_DAYS_VALID_DESC.get(), 1,
2641         Integer.MAX_VALUE);
2642    signCSRDaysValid.addLongIdentifier("daysValid", true);
2643    signCSRDaysValid.addLongIdentifier("validity", true);
2644    signCSRParser.addArgument(signCSRDaysValid);
2645
2646    final TimestampArgument signCSRNotBefore = new TimestampArgument(null,
2647         "validity-start-time", false, 1,
2648         INFO_MANAGE_CERTS_PLACEHOLDER_TIMESTAMP.get(),
2649         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_VALIDITY_START_TIME_DESC.get(
2650              "20180102123456"));
2651    signCSRNotBefore.addLongIdentifier("validityStartTime", true);
2652    signCSRNotBefore.addLongIdentifier("not-before", true);
2653    signCSRNotBefore.addLongIdentifier("notBefore", true);
2654    signCSRParser.addArgument(signCSRNotBefore);
2655
2656    final StringArgument signCSRSignatureAlgorithm = new StringArgument(null,
2657         "signature-algorithm", false, 1,
2658         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2659         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SIG_ALG_DESC.get());
2660    signCSRSignatureAlgorithm.addLongIdentifier("signatureAlgorithm", true);
2661    signCSRSignatureAlgorithm.addLongIdentifier("signature-alg", true);
2662    signCSRSignatureAlgorithm.addLongIdentifier("signatureAlg", true);
2663    signCSRSignatureAlgorithm.addLongIdentifier("sig-alg", true);
2664    signCSRSignatureAlgorithm.addLongIdentifier("sigAlg", true);
2665    signCSRParser.addArgument(signCSRSignatureAlgorithm);
2666
2667    final BooleanArgument signCSRIncludeExtensions = new BooleanArgument(null,
2668         "include-requested-extensions", 1,
2669         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_INCLUDE_EXT_DESC.get());
2670    signCSRIncludeExtensions.addLongIdentifier("includeRequestedExtensions",
2671         true);
2672    signCSRParser.addArgument(signCSRIncludeExtensions);
2673
2674    final StringArgument signCSRSubjectAltDNS = new StringArgument(null,
2675         "subject-alternative-name-dns", false, 0,
2676         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2677         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_DNS_DESC.get());
2678    signCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeNameDNS", true);
2679    signCSRSubjectAltDNS.addLongIdentifier("subject-alt-name-dns", true);
2680    signCSRSubjectAltDNS.addLongIdentifier("subjectAltNameDNS", true);
2681    signCSRSubjectAltDNS.addLongIdentifier("subject-alternative-dns", true);
2682    signCSRSubjectAltDNS.addLongIdentifier("subjectAlternativeDNS", true);
2683    signCSRSubjectAltDNS.addLongIdentifier("subject-alt-dns", true);
2684    signCSRSubjectAltDNS.addLongIdentifier("subjectAltDNS", true);
2685    signCSRSubjectAltDNS.addLongIdentifier("san-dns", true);
2686    signCSRSubjectAltDNS.addLongIdentifier("sanDNS", true);
2687    signCSRSubjectAltDNS.addValueValidator(
2688         new IA5StringArgumentValueValidator(false));
2689    signCSRParser.addArgument(signCSRSubjectAltDNS);
2690
2691    final StringArgument signCSRSubjectAltIP = new StringArgument(null,
2692         "subject-alternative-name-ip-address", false, 0,
2693         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2694         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_IP_DESC.get());
2695    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIPAddress",
2696         true);
2697    signCSRSubjectAltIP.addLongIdentifier("subject-alternative-name-ip", true);
2698    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeNameIP", true);
2699    signCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip-address", true);
2700    signCSRSubjectAltIP.addLongIdentifier("subjectAltNameIPAddress", true);
2701    signCSRSubjectAltIP.addLongIdentifier("subject-alt-name-ip", true);
2702    signCSRSubjectAltIP.addLongIdentifier("subjectAltNameIP", true);
2703    signCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip-address",
2704         true);
2705    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIPAddress", true);
2706    signCSRSubjectAltIP.addLongIdentifier("subject-alternative-ip", true);
2707    signCSRSubjectAltIP.addLongIdentifier("subjectAlternativeIP", true);
2708    signCSRSubjectAltIP.addLongIdentifier("subject-alt-ip-address", true);
2709    signCSRSubjectAltIP.addLongIdentifier("subjectAltIPAddress", true);
2710    signCSRSubjectAltIP.addLongIdentifier("subject-alt-ip", true);
2711    signCSRSubjectAltIP.addLongIdentifier("subjectAltIP", true);
2712    signCSRSubjectAltIP.addLongIdentifier("san-ip-address", true);
2713    signCSRSubjectAltIP.addLongIdentifier("sanIPAddress", true);
2714    signCSRSubjectAltIP.addLongIdentifier("san-ip", true);
2715    signCSRSubjectAltIP.addLongIdentifier("sanIP", true);
2716    signCSRSubjectAltIP.addValueValidator(
2717         new IPAddressArgumentValueValidator(true, true));
2718    signCSRParser.addArgument(signCSRSubjectAltIP);
2719
2720    final StringArgument signCSRSubjectAltEmail = new StringArgument(null,
2721         "subject-alternative-name-email-address", false, 0,
2722         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2723         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_EMAIL_DESC.get());
2724    signCSRSubjectAltEmail.addLongIdentifier(
2725         "subjectAlternativeNameEmailAddress", true);
2726    signCSRSubjectAltEmail.addLongIdentifier("subject-alternative-name-email",
2727         true);
2728    signCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeNameEmail",
2729         true);
2730    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email-address",
2731         true);
2732    signCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmailAddress",
2733         true);
2734    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-name-email", true);
2735    signCSRSubjectAltEmail.addLongIdentifier("subjectAltNameEmail", true);
2736    signCSRSubjectAltEmail.addLongIdentifier(
2737         "subject-alternative-email-address", true);
2738    signCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmailAddress",
2739         true);
2740    signCSRSubjectAltEmail.addLongIdentifier("subject-alternative-email", true);
2741    signCSRSubjectAltEmail.addLongIdentifier("subjectAlternativeEmail", true);
2742    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-email-address", true);
2743    signCSRSubjectAltEmail.addLongIdentifier("subjectAltEmailAddress", true);
2744    signCSRSubjectAltEmail.addLongIdentifier("subject-alt-email", true);
2745    signCSRSubjectAltEmail.addLongIdentifier("subjectAltEmail", true);
2746    signCSRSubjectAltEmail.addLongIdentifier("san-email-address", true);
2747    signCSRSubjectAltEmail.addLongIdentifier("sanEmailAddress", true);
2748    signCSRSubjectAltEmail.addLongIdentifier("san-email", true);
2749    signCSRSubjectAltEmail.addLongIdentifier("sanEmail", true);
2750    signCSRSubjectAltEmail.addValueValidator(
2751         new IA5StringArgumentValueValidator(false));
2752    signCSRParser.addArgument(signCSRSubjectAltEmail);
2753
2754    final StringArgument signCSRSubjectAltURI = new StringArgument(null,
2755         "subject-alternative-name-uri", false, 0,
2756         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
2757         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_URI_DESC.get());
2758    signCSRSubjectAltURI.addLongIdentifier("subjectAlternativeNameURI", true);
2759    signCSRSubjectAltURI.addLongIdentifier("subject-alt-name-uri", true);
2760    signCSRSubjectAltURI.addLongIdentifier("subjectAltNameURI", true);
2761    signCSRSubjectAltURI.addLongIdentifier("subject-alternative-uri", true);
2762    signCSRSubjectAltURI.addLongIdentifier("subjectAlternativeURI", true);
2763    signCSRSubjectAltURI.addLongIdentifier("subject-alt-uri", true);
2764    signCSRSubjectAltURI.addLongIdentifier("subjectAltURI", true);
2765    signCSRSubjectAltURI.addLongIdentifier("san-uri", true);
2766    signCSRSubjectAltURI.addLongIdentifier("sanURI", true);
2767    signCSRParser.addArgument(signCSRSubjectAltURI);
2768
2769    final StringArgument signCSRSubjectAltOID = new StringArgument(null,
2770         "subject-alternative-name-oid", false, 0,
2771         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
2772         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_SAN_OID_DESC.get());
2773    signCSRSubjectAltOID.addLongIdentifier("subjectAlternativeNameOID", true);
2774    signCSRSubjectAltOID.addLongIdentifier("subject-alt-name-oid", true);
2775    signCSRSubjectAltOID.addLongIdentifier("subjectAltNameOID", true);
2776    signCSRSubjectAltOID.addLongIdentifier("subject-alternative-oid", true);
2777    signCSRSubjectAltOID.addLongIdentifier("subjectAlternativeOID", true);
2778    signCSRSubjectAltOID.addLongIdentifier("subject-alt-oid", true);
2779    signCSRSubjectAltOID.addLongIdentifier("subjectAltOID", true);
2780    signCSRSubjectAltOID.addLongIdentifier("san-oid", true);
2781    signCSRSubjectAltOID.addLongIdentifier("sanOID", true);
2782    signCSRSubjectAltOID.addValueValidator(new OIDArgumentValueValidator(true));
2783    signCSRParser.addArgument(signCSRSubjectAltOID);
2784
2785    final StringArgument signCSRIssuerAltDNS = new StringArgument(null,
2786         "issuer-alternative-name-dns", false, 0,
2787         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2788         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_DNS_DESC.get());
2789    signCSRIssuerAltDNS.addLongIdentifier("issuerAlternativeNameDNS", true);
2790    signCSRIssuerAltDNS.addLongIdentifier("issuer-alt-name-dns", true);
2791    signCSRIssuerAltDNS.addLongIdentifier("issuerAltNameDNS", true);
2792    signCSRIssuerAltDNS.addLongIdentifier("issuer-alternative-dns", true);
2793    signCSRIssuerAltDNS.addLongIdentifier("issuerAlternativeDNS", true);
2794    signCSRIssuerAltDNS.addLongIdentifier("issuer-alt-dns", true);
2795    signCSRIssuerAltDNS.addLongIdentifier("issuerAltDNS", true);
2796    signCSRIssuerAltDNS.addLongIdentifier("ian-dns", true);
2797    signCSRIssuerAltDNS.addLongIdentifier("ianDNS", true);
2798    signCSRIssuerAltDNS.addValueValidator(
2799         new IA5StringArgumentValueValidator(false));
2800    signCSRParser.addArgument(signCSRIssuerAltDNS);
2801
2802    final StringArgument signCSRIssuerAltIP = new StringArgument(null,
2803         "issuer-alternative-name-ip-address", false, 0,
2804         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2805         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_IP_DESC.get());
2806    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeNameIPAddress",
2807         true);
2808    signCSRIssuerAltIP.addLongIdentifier("issuer-alternative-name-ip", true);
2809    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeNameIP", true);
2810    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-name-ip-address", true);
2811    signCSRIssuerAltIP.addLongIdentifier("issuerAltNameIPAddress", true);
2812    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-name-ip", true);
2813    signCSRIssuerAltIP.addLongIdentifier("issuerAltNameIP", true);
2814    signCSRIssuerAltIP.addLongIdentifier("issuer-alternative-ip-address",
2815         true);
2816    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeIPAddress", true);
2817    signCSRIssuerAltIP.addLongIdentifier("issuer-alternative-ip", true);
2818    signCSRIssuerAltIP.addLongIdentifier("issuerAlternativeIP", true);
2819    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-ip-address", true);
2820    signCSRIssuerAltIP.addLongIdentifier("issuerAltIPAddress", true);
2821    signCSRIssuerAltIP.addLongIdentifier("issuer-alt-ip", true);
2822    signCSRIssuerAltIP.addLongIdentifier("issuerAltIP", true);
2823    signCSRIssuerAltIP.addLongIdentifier("ian-ip-address", true);
2824    signCSRIssuerAltIP.addLongIdentifier("ianIPAddress", true);
2825    signCSRIssuerAltIP.addLongIdentifier("ian-ip", true);
2826    signCSRIssuerAltIP.addLongIdentifier("ianIP", true);
2827    signCSRIssuerAltIP.addValueValidator(
2828         new IPAddressArgumentValueValidator(true, true));
2829    signCSRParser.addArgument(signCSRIssuerAltIP);
2830
2831    final StringArgument signCSRIssuerAltEmail = new StringArgument(null,
2832         "issuer-alternative-name-email-address", false, 0,
2833         INFO_MANAGE_CERTS_PLACEHOLDER_NAME.get(),
2834         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_EMAIL_DESC.get());
2835    signCSRIssuerAltEmail.addLongIdentifier(
2836         "issuerAlternativeNameEmailAddress", true);
2837    signCSRIssuerAltEmail.addLongIdentifier("issuer-alternative-name-email",
2838         true);
2839    signCSRIssuerAltEmail.addLongIdentifier("issuerAlternativeNameEmail",
2840         true);
2841    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-name-email-address",
2842         true);
2843    signCSRIssuerAltEmail.addLongIdentifier("issuerAltNameEmailAddress",
2844         true);
2845    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-name-email", true);
2846    signCSRIssuerAltEmail.addLongIdentifier("issuerAltNameEmail", true);
2847    signCSRIssuerAltEmail.addLongIdentifier(
2848         "issuer-alternative-email-address", true);
2849    signCSRIssuerAltEmail.addLongIdentifier("issuerAlternativeEmailAddress",
2850         true);
2851    signCSRIssuerAltEmail.addLongIdentifier("issuer-alternative-email", true);
2852    signCSRIssuerAltEmail.addLongIdentifier("issuerAlternativeEmail", true);
2853    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-email-address", true);
2854    signCSRIssuerAltEmail.addLongIdentifier("issuerAltEmailAddress", true);
2855    signCSRIssuerAltEmail.addLongIdentifier("issuer-alt-email", true);
2856    signCSRIssuerAltEmail.addLongIdentifier("issuerAltEmail", true);
2857    signCSRIssuerAltEmail.addLongIdentifier("ian-email-address", true);
2858    signCSRIssuerAltEmail.addLongIdentifier("ianEmailAddress", true);
2859    signCSRIssuerAltEmail.addLongIdentifier("ian-email", true);
2860    signCSRIssuerAltEmail.addLongIdentifier("ianEmail", true);
2861    signCSRIssuerAltEmail.addValueValidator(
2862         new IA5StringArgumentValueValidator(false));
2863    signCSRParser.addArgument(signCSRIssuerAltEmail);
2864
2865    final StringArgument signCSRIssuerAltURI = new StringArgument(null,
2866         "issuer-alternative-name-uri", false, 0,
2867         INFO_MANAGE_CERTS_PLACEHOLDER_URI.get(),
2868         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_URI_DESC.get());
2869    signCSRIssuerAltURI.addLongIdentifier("issuerAlternativeNameURI", true);
2870    signCSRIssuerAltURI.addLongIdentifier("issuer-alt-name-uri", true);
2871    signCSRIssuerAltURI.addLongIdentifier("issuerAltNameURI", true);
2872    signCSRIssuerAltURI.addLongIdentifier("issuer-alternative-uri", true);
2873    signCSRIssuerAltURI.addLongIdentifier("issuerAlternativeURI", true);
2874    signCSRIssuerAltURI.addLongIdentifier("issuer-alt-uri", true);
2875    signCSRIssuerAltURI.addLongIdentifier("issuerAltURI", true);
2876    signCSRIssuerAltURI.addLongIdentifier("ian-uri", true);
2877    signCSRIssuerAltURI.addLongIdentifier("ianURI", true);
2878    signCSRParser.addArgument(signCSRIssuerAltURI);
2879
2880    final StringArgument signCSRIssuerAltOID = new StringArgument(null,
2881         "issuer-alternative-name-oid", false, 0,
2882         INFO_MANAGE_CERTS_PLACEHOLDER_OID.get(),
2883         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_IAN_OID_DESC.get());
2884    signCSRIssuerAltOID.addLongIdentifier("issuerAlternativeNameOID", true);
2885    signCSRIssuerAltOID.addLongIdentifier("issuer-alt-name-oid", true);
2886    signCSRIssuerAltOID.addLongIdentifier("issuerAltNameOID", true);
2887    signCSRIssuerAltOID.addLongIdentifier("issuer-alternative-oid", true);
2888    signCSRIssuerAltOID.addLongIdentifier("issuerAlternativeOID", true);
2889    signCSRIssuerAltOID.addLongIdentifier("issuer-alt-oid", true);
2890    signCSRIssuerAltOID.addLongIdentifier("issuerAltOID", true);
2891    signCSRIssuerAltOID.addLongIdentifier("ian-oid", true);
2892    signCSRIssuerAltOID.addLongIdentifier("ianOID", true);
2893    signCSRIssuerAltOID.addValueValidator(new OIDArgumentValueValidator(true));
2894    signCSRParser.addArgument(signCSRIssuerAltOID);
2895
2896    final BooleanValueArgument signCSRBasicConstraintsIsCA =
2897         new BooleanValueArgument(null, "basic-constraints-is-ca", false, null,
2898              INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_BC_IS_CA_DESC.get());
2899    signCSRBasicConstraintsIsCA.addLongIdentifier("basicConstraintsIsCA", true);
2900    signCSRBasicConstraintsIsCA.addLongIdentifier("bc-is-ca", true);
2901    signCSRBasicConstraintsIsCA.addLongIdentifier("bcIsCA", true);
2902    signCSRParser.addArgument(signCSRBasicConstraintsIsCA);
2903
2904    final IntegerArgument signCSRBasicConstraintsPathLength =
2905         new IntegerArgument(null, "basic-constraints-maximum-path-length",
2906              false, 1, null,
2907              INFO_MANAGE_CERTS_SC_GEN_CERT_ARG_BC_PATH_LENGTH_DESC.get(), 0,
2908              Integer.MAX_VALUE);
2909    signCSRBasicConstraintsPathLength.addLongIdentifier(
2910         "basicConstraintsMaximumPathLength", true);
2911    signCSRBasicConstraintsPathLength.addLongIdentifier(
2912         "basic-constraints-max-path-length", true);
2913    signCSRBasicConstraintsPathLength.addLongIdentifier(
2914         "basicConstraintsMaxPathLength", true);
2915    signCSRBasicConstraintsPathLength.addLongIdentifier(
2916         "basic-constraints-path-length", true);
2917    signCSRBasicConstraintsPathLength.addLongIdentifier(
2918         "basicConstraintsPathLength", true);
2919    signCSRBasicConstraintsPathLength.addLongIdentifier(
2920         "bc-maximum-path-length", true);
2921    signCSRBasicConstraintsPathLength.addLongIdentifier("bcMaximumPathLength",
2922         true);
2923    signCSRBasicConstraintsPathLength.addLongIdentifier("bc-max-path-length",
2924         true);
2925    signCSRBasicConstraintsPathLength.addLongIdentifier("bcMaxPathLength",
2926         true);
2927    signCSRBasicConstraintsPathLength.addLongIdentifier("bc-path-length", true);
2928    signCSRBasicConstraintsPathLength.addLongIdentifier("bcPathLength", true);
2929    signCSRParser.addArgument(signCSRBasicConstraintsPathLength);
2930
2931    final StringArgument signCSRKeyUsage = new StringArgument(null, "key-usage",
2932         false, 0, null, INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_KU_DESC.get());
2933    signCSRKeyUsage.addLongIdentifier("keyUsage", true);
2934    signCSRParser.addArgument(signCSRKeyUsage);
2935
2936    final StringArgument signCSRExtendedKeyUsage = new StringArgument(null,
2937         "extended-key-usage", false, 0, null,
2938         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_EKU_DESC.get());
2939    signCSRExtendedKeyUsage.addLongIdentifier("extendedKeyUsage", true);
2940    signCSRParser.addArgument(signCSRExtendedKeyUsage);
2941
2942    final StringArgument signCSRExtension = new StringArgument(null,
2943         "extension", false, 0, null,
2944         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_EXT_DESC.get());
2945    signCSRExtension.addLongIdentifier("ext", true);
2946    signCSRParser.addArgument(signCSRExtension);
2947
2948    final BooleanArgument signCSRNoPrompt = new BooleanArgument(null,
2949         "no-prompt", 1,
2950         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_NO_PROMPT_DESC.get());
2951    signCSRNoPrompt.addLongIdentifier("noPrompt", true);
2952    signCSRParser.addArgument(signCSRNoPrompt);
2953
2954    final BooleanArgument signCSRDisplayCommand = new BooleanArgument(null,
2955         "display-keytool-command", 1,
2956         INFO_MANAGE_CERTS_SC_SIGN_CSR_ARG_DISPLAY_COMMAND_DESC.get());
2957    signCSRDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
2958    signCSRDisplayCommand.addLongIdentifier("show-keytool-command", true);
2959    signCSRDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
2960    signCSRParser.addArgument(signCSRDisplayCommand);
2961
2962    signCSRParser.addRequiredArgumentSet(signCSRKeystorePassword,
2963         signCSRKeystorePasswordFile, signCSRPromptForKeystorePassword);
2964    signCSRParser.addExclusiveArgumentSet(signCSRKeystorePassword,
2965         signCSRKeystorePasswordFile, signCSRPromptForKeystorePassword);
2966    signCSRParser.addExclusiveArgumentSet(signCSRPKPassword,
2967         signCSRPKPasswordFile, signCSRPromptForPKPassword);
2968    signCSRParser.addDependentArgumentSet(signCSRBasicConstraintsPathLength,
2969         signCSRBasicConstraintsIsCA);
2970
2971    final LinkedHashMap<String[],String> signCSRExamples =
2972         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
2973    signCSRExamples.put(
2974         new String[]
2975         {
2976           "sign-certificate-signing-request",
2977           "--request-input-file", "server-cert.csr",
2978           "--keystore", getPlatformSpecificPath("config", "keystore"),
2979           "--keystore-password-file",
2980                getPlatformSpecificPath("config", "keystore.pin"),
2981           "--signing-certificate-alias", "ca-cert",
2982           "--include-requested-extensions"
2983         },
2984         INFO_MANAGE_CERTS_SC_SIGN_CSR_EXAMPLE_1.get(
2985              getPlatformSpecificPath("config", "keystore")));
2986    signCSRExamples.put(
2987         new String[]
2988         {
2989           "sign-certificate-signing-request",
2990           "--request-input-file", "server-cert.csr",
2991           "--certificate-output-file", "server-cert.der",
2992           "--output-format", "DER",
2993           "--keystore", getPlatformSpecificPath("config", "keystore"),
2994           "--keystore-password-file",
2995                getPlatformSpecificPath("config", "keystore.pin"),
2996           "--signing-certificate-alias", "ca-cert",
2997           "--days-valid", "730",
2998           "--validity-start-time", "20170101000000",
2999           "--include-requested-extensions",
3000           "--issuer-alternative-name-email-address", "ca@example.com",
3001         },
3002         INFO_MANAGE_CERTS_SC_SIGN_CSR_EXAMPLE_2.get(
3003              getPlatformSpecificPath("config", "keystore")));
3004
3005    final SubCommand signCSRSubCommand = new SubCommand(
3006         "sign-certificate-signing-request",
3007         INFO_MANAGE_CERTS_SC_SIGN_CSR_DESC.get(), signCSRParser,
3008         signCSRExamples);
3009    signCSRSubCommand.addName("signCertificateSigningRequest", true);
3010    signCSRSubCommand.addName("sign-certificate-request", true);
3011    signCSRSubCommand.addName("signCertificateRequest", true);
3012    signCSRSubCommand.addName("sign-certificate", true);
3013    signCSRSubCommand.addName("signCertificate", true);
3014    signCSRSubCommand.addName("sign-csr", true);
3015    signCSRSubCommand.addName("signCSR", true);
3016    signCSRSubCommand.addName("sign", true);
3017    signCSRSubCommand.addName("gencert", true);
3018
3019    parser.addSubCommand(signCSRSubCommand);
3020
3021
3022    // Define the "change-certificate-alias" subcommand and all of its
3023    // arguments.
3024    final ArgumentParser changeAliasParser = new ArgumentParser(
3025         "change-certificate-alias",
3026         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_DESC.get());
3027
3028    final FileArgument changeAliasKeystore = new FileArgument(null, "keystore",
3029         true, 1, null, INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_DESC.get(),
3030         true, true,  true, false);
3031    changeAliasKeystore.addLongIdentifier("keystore-path", true);
3032    changeAliasKeystore.addLongIdentifier("keystorePath", true);
3033    changeAliasKeystore.addLongIdentifier("keystore-file", true);
3034    changeAliasKeystore.addLongIdentifier("keystoreFile", true);
3035    changeAliasParser.addArgument(changeAliasKeystore);
3036
3037    final StringArgument changeAliasKeystorePassword = new StringArgument(null,
3038         "keystore-password", false, 1,
3039         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3040         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_PW_DESC.get());
3041    changeAliasKeystorePassword.addLongIdentifier("keystorePassword", true);
3042    changeAliasKeystorePassword.addLongIdentifier("keystore-passphrase", true);
3043    changeAliasKeystorePassword.addLongIdentifier("keystorePassphrase", true);
3044    changeAliasKeystorePassword.addLongIdentifier("keystore-pin", true);
3045    changeAliasKeystorePassword.addLongIdentifier("keystorePIN", true);
3046    changeAliasKeystorePassword.addLongIdentifier("storepass", true);
3047    changeAliasKeystorePassword.setSensitive(true);
3048    changeAliasParser.addArgument(changeAliasKeystorePassword);
3049
3050    final FileArgument changeAliasKeystorePasswordFile = new FileArgument(null,
3051         "keystore-password-file", false, 1, null,
3052         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_PW_FILE_DESC.get(), true,
3053         true, true, false);
3054    changeAliasKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
3055         true);
3056    changeAliasKeystorePasswordFile.addLongIdentifier(
3057         "keystore-passphrase-file", true);
3058    changeAliasKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
3059         true);
3060    changeAliasKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
3061         true);
3062    changeAliasKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
3063    changeAliasParser.addArgument(changeAliasKeystorePasswordFile);
3064
3065    final BooleanArgument changeAliasPromptForKeystorePassword =
3066         new BooleanArgument(null, "prompt-for-keystore-password",
3067        INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PROMPT_FOR_KS_PW_DESC.get());
3068    changeAliasPromptForKeystorePassword.addLongIdentifier(
3069         "promptForKeystorePassword", true);
3070    changeAliasPromptForKeystorePassword.addLongIdentifier(
3071         "prompt-for-keystore-passphrase", true);
3072    changeAliasPromptForKeystorePassword.addLongIdentifier(
3073         "promptForKeystorePassphrase", true);
3074    changeAliasPromptForKeystorePassword.addLongIdentifier(
3075         "prompt-for-keystore-pin", true);
3076    changeAliasPromptForKeystorePassword.addLongIdentifier(
3077         "promptForKeystorePIN", true);
3078    changeAliasParser.addArgument(changeAliasPromptForKeystorePassword);
3079
3080    final StringArgument changeAliasPKPassword = new StringArgument(null,
3081         "private-key-password", false, 1,
3082         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3083         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PK_PW_DESC.get());
3084    changeAliasPKPassword.addLongIdentifier("privateKeyPassword", true);
3085    changeAliasPKPassword.addLongIdentifier("private-key-passphrase", true);
3086    changeAliasPKPassword.addLongIdentifier("privateKeyPassphrase", true);
3087    changeAliasPKPassword.addLongIdentifier("private-key-pin", true);
3088    changeAliasPKPassword.addLongIdentifier("privateKeyPIN", true);
3089    changeAliasPKPassword.addLongIdentifier("key-password", true);
3090    changeAliasPKPassword.addLongIdentifier("keyPassword", true);
3091    changeAliasPKPassword.addLongIdentifier("key-passphrase", true);
3092    changeAliasPKPassword.addLongIdentifier("keyPassphrase", true);
3093    changeAliasPKPassword.addLongIdentifier("key-pin", true);
3094    changeAliasPKPassword.addLongIdentifier("keyPIN", true);
3095    changeAliasPKPassword.addLongIdentifier("keypass", true);
3096    changeAliasPKPassword.setSensitive(true);
3097    changeAliasParser.addArgument(changeAliasPKPassword);
3098
3099    final FileArgument changeAliasPKPasswordFile = new FileArgument(null,
3100         "private-key-password-file", false, 1, null,
3101         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PK_PW_FILE_DESC.get(), true,
3102         true, true, false);
3103    changeAliasPKPasswordFile.addLongIdentifier("privateKeyPasswordFile", true);
3104    changeAliasPKPasswordFile.addLongIdentifier("private-key-passphrase-file",
3105         true);
3106    changeAliasPKPasswordFile.addLongIdentifier("privateKeyPassphraseFile",
3107         true);
3108    changeAliasPKPasswordFile.addLongIdentifier("private-key-pin-file",
3109         true);
3110    changeAliasPKPasswordFile.addLongIdentifier("privateKeyPINFile", true);
3111    changeAliasPKPasswordFile.addLongIdentifier("key-password-file", true);
3112    changeAliasPKPasswordFile.addLongIdentifier("keyPasswordFile", true);
3113    changeAliasPKPasswordFile.addLongIdentifier("key-passphrase-file",
3114         true);
3115    changeAliasPKPasswordFile.addLongIdentifier("keyPassphraseFile",
3116         true);
3117    changeAliasPKPasswordFile.addLongIdentifier("key-pin-file",
3118         true);
3119    changeAliasPKPasswordFile.addLongIdentifier("keyPINFile", true);
3120    changeAliasParser.addArgument(changeAliasPKPasswordFile);
3121
3122    final BooleanArgument changeAliasPromptForPKPassword =
3123         new BooleanArgument(null, "prompt-for-private-key-password",
3124        INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_PROMPT_FOR_PK_PW_DESC.get());
3125    changeAliasPromptForPKPassword.addLongIdentifier(
3126         "promptForPrivateKeyPassword", true);
3127    changeAliasPromptForPKPassword.addLongIdentifier(
3128         "prompt-for-private-key-passphrase", true);
3129    changeAliasPromptForPKPassword.addLongIdentifier(
3130         "promptForPrivateKeyPassphrase", true);
3131    changeAliasPromptForPKPassword.addLongIdentifier(
3132         "prompt-for-private-key-pin", true);
3133    changeAliasPromptForPKPassword.addLongIdentifier("promptForPrivateKeyPIN",
3134         true);
3135    changeAliasPromptForPKPassword.addLongIdentifier("prompt-for-key-password",
3136         true);
3137    changeAliasPromptForPKPassword.addLongIdentifier("promptForKeyPassword",
3138         true);
3139    changeAliasPromptForPKPassword.addLongIdentifier(
3140         "prompt-for-key-passphrase", true);
3141    changeAliasPromptForPKPassword.addLongIdentifier(
3142         "promptForKeyPassphrase", true);
3143    changeAliasPromptForPKPassword.addLongIdentifier("prompt-for-key-pin",
3144         true);
3145    changeAliasPromptForPKPassword.addLongIdentifier("promptForKeyPIN", true);
3146    changeAliasParser.addArgument(changeAliasPromptForPKPassword);
3147
3148    final StringArgument changeAliasKeystoreType = new StringArgument(null,
3149         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
3150         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_KS_TYPE_DESC.get(),
3151         ALLOWED_KEYSTORE_TYPE_VALUES);
3152    changeAliasKeystoreType.addLongIdentifier("key-store-type", true);
3153    changeAliasKeystoreType.addLongIdentifier("keystoreType", true);
3154    changeAliasKeystoreType.addLongIdentifier("keystore-format", true);
3155    changeAliasKeystoreType.addLongIdentifier("key-store-format", true);
3156    changeAliasKeystoreType.addLongIdentifier("keystoreFormat", true);
3157    changeAliasKeystoreType.addLongIdentifier("storetype", true);
3158    changeAliasParser.addArgument(changeAliasKeystoreType);
3159
3160    final StringArgument changeAliasCurrentAlias = new StringArgument(null,
3161         "current-alias", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3162         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_CURRENT_ALIAS_DESC.get());
3163    changeAliasCurrentAlias.addLongIdentifier("currentAlias", true);
3164    changeAliasCurrentAlias.addLongIdentifier("old-alias", true);
3165    changeAliasCurrentAlias.addLongIdentifier("oldAlias", true);
3166    changeAliasCurrentAlias.addLongIdentifier("source-alias", true);
3167    changeAliasCurrentAlias.addLongIdentifier("sourceAlias", true);
3168    changeAliasCurrentAlias.addLongIdentifier("alias", true);
3169    changeAliasCurrentAlias.addLongIdentifier("current-nickname", true);
3170    changeAliasCurrentAlias.addLongIdentifier("currentNickname", true);
3171    changeAliasCurrentAlias.addLongIdentifier("old-nickname", true);
3172    changeAliasCurrentAlias.addLongIdentifier("oldNickname", true);
3173    changeAliasCurrentAlias.addLongIdentifier("source-nickname", true);
3174    changeAliasCurrentAlias.addLongIdentifier("sourceNickname", true);
3175    changeAliasCurrentAlias.addLongIdentifier("nickname", true);
3176    changeAliasCurrentAlias.addLongIdentifier("from", false);
3177    changeAliasParser.addArgument(changeAliasCurrentAlias);
3178
3179    final StringArgument changeAliasNewAlias = new StringArgument(null,
3180         "new-alias", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3181         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_NEW_ALIAS_DESC.get());
3182    changeAliasNewAlias.addLongIdentifier("newAlias", true);
3183    changeAliasNewAlias.addLongIdentifier("destination-alias", true);
3184    changeAliasNewAlias.addLongIdentifier("destinationAlias", true);
3185    changeAliasNewAlias.addLongIdentifier("new-nickname", true);
3186    changeAliasNewAlias.addLongIdentifier("newNickname", true);
3187    changeAliasNewAlias.addLongIdentifier("destination-nickname", true);
3188    changeAliasNewAlias.addLongIdentifier("destinationNickname", true);
3189    changeAliasNewAlias.addLongIdentifier("to", false);
3190    changeAliasParser.addArgument(changeAliasNewAlias);
3191
3192    final BooleanArgument changeAliasDisplayCommand = new BooleanArgument(null,
3193         "display-keytool-command", 1,
3194         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_ARG_DISPLAY_COMMAND_DESC.get());
3195    changeAliasDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3196    changeAliasDisplayCommand.addLongIdentifier("show-keytool-command", true);
3197    changeAliasDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3198    changeAliasParser.addArgument(changeAliasDisplayCommand);
3199
3200    changeAliasParser.addRequiredArgumentSet(changeAliasKeystorePassword,
3201         changeAliasKeystorePasswordFile, changeAliasPromptForKeystorePassword);
3202    changeAliasParser.addExclusiveArgumentSet(changeAliasKeystorePassword,
3203         changeAliasKeystorePasswordFile, changeAliasPromptForKeystorePassword);
3204    changeAliasParser.addExclusiveArgumentSet(changeAliasPKPassword,
3205         changeAliasPKPasswordFile, changeAliasPromptForPKPassword);
3206
3207    final LinkedHashMap<String[],String> changeAliasExamples =
3208         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
3209    changeAliasExamples.put(
3210         new String[]
3211         {
3212           "change-certificate-alias",
3213           "--keystore", getPlatformSpecificPath("config", "keystore"),
3214           "--keystore-password-file",
3215                getPlatformSpecificPath("config", "keystore.pin"),
3216           "--current-alias", "server-cert",
3217           "--new-alias", "server-certificate",
3218           "--display-keytool-command"
3219         },
3220         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_EXAMPLE_1.get());
3221
3222    final SubCommand changeAliasSubCommand = new SubCommand(
3223         "change-certificate-alias",
3224         INFO_MANAGE_CERTS_SC_CHANGE_ALIAS_DESC.get(), changeAliasParser,
3225         changeAliasExamples);
3226    changeAliasSubCommand.addName("changeCertificateAlias", true);
3227    changeAliasSubCommand.addName("change-alias", true);
3228    changeAliasSubCommand.addName("changeAlias", true);
3229    changeAliasSubCommand.addName("rename-certificate", true);
3230    changeAliasSubCommand.addName("renameCertificate", true);
3231    changeAliasSubCommand.addName("rename", true);
3232
3233    parser.addSubCommand(changeAliasSubCommand);
3234
3235
3236    // Define the "change-keystore-password" subcommand and all of its
3237    // arguments.
3238    final ArgumentParser changeKSPWParser = new ArgumentParser(
3239         "change-keystore-password",
3240         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_DESC.get());
3241
3242    final FileArgument changeKSPWKeystore = new FileArgument(null, "keystore",
3243         true, 1, null, INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_KS_DESC.get(),
3244         true, true,  true, false);
3245    changeKSPWKeystore.addLongIdentifier("keystore-path", true);
3246    changeKSPWKeystore.addLongIdentifier("keystorePath", true);
3247    changeKSPWKeystore.addLongIdentifier("keystore-file", true);
3248    changeKSPWKeystore.addLongIdentifier("keystoreFile", true);
3249    changeKSPWParser.addArgument(changeKSPWKeystore);
3250
3251    final StringArgument changeKSPWCurrentPassword = new StringArgument(null,
3252         "current-keystore-password", false, 1,
3253         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3254         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_CURRENT_PW_DESC.get());
3255    changeKSPWCurrentPassword.addLongIdentifier("currentKeystorePassword",
3256         true);
3257    changeKSPWCurrentPassword.addLongIdentifier("current-keystore-passphrase",
3258         true);
3259    changeKSPWCurrentPassword.addLongIdentifier("currentKeystorePassphrase",
3260         true);
3261    changeKSPWCurrentPassword.addLongIdentifier("current-keystore-pin", true);
3262    changeKSPWCurrentPassword.addLongIdentifier("currentKeystorePIN", true);
3263    changeKSPWCurrentPassword.addLongIdentifier("storepass", true);
3264    changeKSPWCurrentPassword.setSensitive(true);
3265    changeKSPWParser.addArgument(changeKSPWCurrentPassword);
3266
3267    final FileArgument changeKSPWCurrentPasswordFile = new FileArgument(null,
3268         "current-keystore-password-file", false, 1, null,
3269         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_CURRENT_PW_FILE_DESC.get(), true,
3270         true, true, false);
3271    changeKSPWCurrentPasswordFile.addLongIdentifier(
3272         "currentKeystorePasswordFile", true);
3273    changeKSPWCurrentPasswordFile.addLongIdentifier(
3274         "current-keystore-passphrase-file", true);
3275    changeKSPWCurrentPasswordFile.addLongIdentifier(
3276         "currentKeystorePassphraseFile", true);
3277    changeKSPWCurrentPasswordFile.addLongIdentifier("current-keystore-pin-file",
3278         true);
3279    changeKSPWCurrentPasswordFile.addLongIdentifier("currentKeystorePINFile",
3280         true);
3281    changeKSPWParser.addArgument(changeKSPWCurrentPasswordFile);
3282
3283    final BooleanArgument changeKSPWPromptForCurrentPassword =
3284         new BooleanArgument(null, "prompt-for-current-keystore-password",
3285        INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_PROMPT_FOR_CURRENT_PW_DESC.get());
3286    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3287         "promptForCurrentKeystorePassword", true);
3288    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3289         "prompt-for-current-keystore-passphrase", true);
3290    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3291         "promptForCurrentKeystorePassphrase", true);
3292    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3293         "prompt-for-current-keystore-pin", true);
3294    changeKSPWPromptForCurrentPassword.addLongIdentifier(
3295         "promptForCurrentKeystorePIN", true);
3296    changeKSPWParser.addArgument(changeKSPWPromptForCurrentPassword);
3297
3298    final StringArgument changeKSPWNewPassword = new StringArgument(null,
3299         "new-keystore-password", false, 1,
3300         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3301         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_NEW_PW_DESC.get());
3302    changeKSPWNewPassword.addLongIdentifier("newKeystorePassword",
3303         true);
3304    changeKSPWNewPassword.addLongIdentifier("new-keystore-passphrase",
3305         true);
3306    changeKSPWNewPassword.addLongIdentifier("newKeystorePassphrase",
3307         true);
3308    changeKSPWNewPassword.addLongIdentifier("new-keystore-pin", true);
3309    changeKSPWNewPassword.addLongIdentifier("newKeystorePIN", true);
3310    changeKSPWNewPassword.addLongIdentifier("new", true);
3311    changeKSPWNewPassword.setSensitive(true);
3312    changeKSPWParser.addArgument(changeKSPWNewPassword);
3313
3314    final FileArgument changeKSPWNewPasswordFile = new FileArgument(null,
3315         "new-keystore-password-file", false, 1, null,
3316         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_NEW_PW_FILE_DESC.get(), true,
3317         true, true, false);
3318    changeKSPWNewPasswordFile.addLongIdentifier("newKeystorePasswordFile",
3319         true);
3320    changeKSPWNewPasswordFile.addLongIdentifier("new-keystore-passphrase-file",
3321         true);
3322    changeKSPWNewPasswordFile.addLongIdentifier("newKeystorePassphraseFile",
3323         true);
3324    changeKSPWNewPasswordFile.addLongIdentifier("new-keystore-pin-file", true);
3325    changeKSPWNewPasswordFile.addLongIdentifier("newKeystorePINFile", true);
3326    changeKSPWParser.addArgument(changeKSPWNewPasswordFile);
3327
3328    final BooleanArgument changeKSPWPromptForNewPassword =
3329         new BooleanArgument(null, "prompt-for-new-keystore-password",
3330        INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_PROMPT_FOR_NEW_PW_DESC.get());
3331    changeKSPWPromptForNewPassword.addLongIdentifier(
3332         "promptForNewKeystorePassword", true);
3333    changeKSPWPromptForNewPassword.addLongIdentifier(
3334         "prompt-for-new-keystore-passphrase", true);
3335    changeKSPWPromptForNewPassword.addLongIdentifier(
3336         "promptForNewKeystorePassphrase", true);
3337    changeKSPWPromptForNewPassword.addLongIdentifier(
3338         "prompt-for-new-keystore-pin", true);
3339    changeKSPWPromptForNewPassword.addLongIdentifier(
3340         "promptForNewKeystorePIN", true);
3341    changeKSPWParser.addArgument(changeKSPWPromptForNewPassword);
3342
3343    final BooleanArgument changeKSPWDisplayCommand = new BooleanArgument(null,
3344         "display-keytool-command", 1,
3345         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_ARG_DISPLAY_COMMAND_DESC.get());
3346    changeKSPWDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3347    changeKSPWDisplayCommand.addLongIdentifier("show-keytool-command", true);
3348    changeKSPWDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3349    changeKSPWParser.addArgument(changeKSPWDisplayCommand);
3350
3351    changeKSPWParser.addRequiredArgumentSet(changeKSPWCurrentPassword,
3352         changeKSPWCurrentPasswordFile, changeKSPWPromptForCurrentPassword);
3353    changeKSPWParser.addExclusiveArgumentSet(changeKSPWCurrentPassword,
3354         changeKSPWCurrentPasswordFile, changeKSPWPromptForCurrentPassword);
3355    changeKSPWParser.addRequiredArgumentSet(changeKSPWNewPassword,
3356         changeKSPWNewPasswordFile, changeKSPWPromptForNewPassword);
3357    changeKSPWParser.addExclusiveArgumentSet(changeKSPWNewPassword,
3358         changeKSPWNewPasswordFile, changeKSPWPromptForNewPassword);
3359
3360    final LinkedHashMap<String[],String> changeKSPWExamples =
3361         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
3362    changeKSPWExamples.put(
3363         new String[]
3364         {
3365           "change-keystore-password",
3366           "--keystore", getPlatformSpecificPath("config", "keystore"),
3367           "--current-keystore-password-file",
3368                getPlatformSpecificPath("config", "current.pin"),
3369           "--new-keystore-password-file",
3370                getPlatformSpecificPath("config", "new.pin"),
3371           "--display-keytool-command"
3372         },
3373         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_EXAMPLE_1.get(
3374              getPlatformSpecificPath("config", "keystore"),
3375              getPlatformSpecificPath("config", "current.pin"),
3376              getPlatformSpecificPath("config", "new.pin")));
3377
3378    final SubCommand changeKSPWSubCommand = new SubCommand(
3379         "change-keystore-password",
3380         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_DESC.get(), changeKSPWParser,
3381         changeKSPWExamples);
3382    changeKSPWSubCommand.addName("changeKeystorePassword", true);
3383    changeKSPWSubCommand.addName("change-keystore-passphrase", true);
3384    changeKSPWSubCommand.addName("changeKeystorePassphrase", true);
3385    changeKSPWSubCommand.addName("change-keystore-pin", true);
3386    changeKSPWSubCommand.addName("changeKeystorePIN", true);
3387    changeKSPWSubCommand.addName("storepasswd", true);
3388
3389    parser.addSubCommand(changeKSPWSubCommand);
3390
3391
3392    // Define the "change-private-key-password" subcommand and all of its
3393    // arguments.
3394    final ArgumentParser changePKPWParser = new ArgumentParser(
3395         "change-private-key-password",
3396         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_DESC.get());
3397
3398    final FileArgument changePKPWKeystore = new FileArgument(null, "keystore",
3399         true, 1, null, INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_DESC.get(),
3400         true, true,  true, false);
3401    changePKPWKeystore.addLongIdentifier("keystore-path", true);
3402    changePKPWKeystore.addLongIdentifier("keystorePath", true);
3403    changePKPWKeystore.addLongIdentifier("keystore-file", true);
3404    changePKPWKeystore.addLongIdentifier("keystoreFile", true);
3405    changePKPWParser.addArgument(changePKPWKeystore);
3406
3407    final StringArgument changePKPWKeystorePassword = new StringArgument(null,
3408         "keystore-password", false, 1,
3409         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3410         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_PW_DESC.get());
3411    changePKPWKeystorePassword.addLongIdentifier("keystorePassword", true);
3412    changePKPWKeystorePassword.addLongIdentifier("keystore-passphrase", true);
3413    changePKPWKeystorePassword.addLongIdentifier("keystorePassphrase", true);
3414    changePKPWKeystorePassword.addLongIdentifier("keystore-pin", true);
3415    changePKPWKeystorePassword.addLongIdentifier("keystorePIN", true);
3416    changePKPWKeystorePassword.addLongIdentifier("storepass", true);
3417    changePKPWKeystorePassword.setSensitive(true);
3418    changePKPWParser.addArgument(changePKPWKeystorePassword);
3419
3420    final FileArgument changePKPWKeystorePasswordFile = new FileArgument(null,
3421         "keystore-password-file", false, 1, null,
3422         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_PW_FILE_DESC.get(), true,
3423         true, true, false);
3424    changePKPWKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
3425         true);
3426    changePKPWKeystorePasswordFile.addLongIdentifier(
3427         "keystore-passphrase-file", true);
3428    changePKPWKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
3429         true);
3430    changePKPWKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
3431         true);
3432    changePKPWKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
3433    changePKPWParser.addArgument(changePKPWKeystorePasswordFile);
3434
3435    final BooleanArgument changePKPWPromptForKeystorePassword =
3436         new BooleanArgument(null, "prompt-for-keystore-password",
3437        INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_PROMPT_FOR_KS_PW_DESC.get());
3438    changePKPWPromptForKeystorePassword.addLongIdentifier(
3439         "promptForKeystorePassword", true);
3440    changePKPWPromptForKeystorePassword.addLongIdentifier(
3441         "prompt-for-keystore-passphrase", true);
3442    changePKPWPromptForKeystorePassword.addLongIdentifier(
3443         "promptForKeystorePassphrase", true);
3444    changePKPWPromptForKeystorePassword.addLongIdentifier(
3445         "prompt-for-keystore-pin", true);
3446    changePKPWPromptForKeystorePassword.addLongIdentifier(
3447         "promptForKeystorePIN", true);
3448    changePKPWParser.addArgument(changePKPWPromptForKeystorePassword);
3449
3450    final StringArgument changePKPWKeystoreType = new StringArgument(null,
3451         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
3452         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_KS_TYPE_DESC.get(),
3453         ALLOWED_KEYSTORE_TYPE_VALUES);
3454    changePKPWKeystoreType.addLongIdentifier("key-store-type", true);
3455    changePKPWKeystoreType.addLongIdentifier("keystoreType", true);
3456    changePKPWKeystoreType.addLongIdentifier("keystore-format", true);
3457    changePKPWKeystoreType.addLongIdentifier("key-store-format", true);
3458    changePKPWKeystoreType.addLongIdentifier("keystoreFormat", true);
3459    changePKPWKeystoreType.addLongIdentifier("storetype", true);
3460    changePKPWParser.addArgument(changePKPWKeystoreType);
3461
3462    final StringArgument changePKPWAlias = new StringArgument(null, "alias",
3463         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3464         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_ALIAS_DESC.get());
3465    changePKPWAlias.addLongIdentifier("nickname", true);
3466    changePKPWParser.addArgument(changePKPWAlias);
3467
3468    final StringArgument changePKPWCurrentPassword = new StringArgument(null,
3469         "current-private-key-password", false, 1,
3470         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3471         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_CURRENT_PW_DESC.get());
3472    changePKPWCurrentPassword.addLongIdentifier("currentPrivateKeyPassword",
3473         true);
3474    changePKPWCurrentPassword.addLongIdentifier(
3475         "current-private-key-passphrase", true);
3476    changePKPWCurrentPassword.addLongIdentifier("currentPrivateKeyPassphrase",
3477         true);
3478    changePKPWCurrentPassword.addLongIdentifier("current-private-key-pin",
3479         true);
3480    changePKPWCurrentPassword.addLongIdentifier("currentPrivateKeyPIN", true);
3481    changePKPWCurrentPassword.addLongIdentifier("keypass", true);
3482    changePKPWCurrentPassword.setSensitive(true);
3483    changePKPWParser.addArgument(changePKPWCurrentPassword);
3484
3485    final FileArgument changePKPWCurrentPasswordFile = new FileArgument(null,
3486         "current-private-key-password-file", false, 1, null,
3487         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_CURRENT_PW_FILE_DESC.get(), true,
3488         true, true, false);
3489    changePKPWCurrentPasswordFile.addLongIdentifier(
3490         "currentPrivateKeyPasswordFile", true);
3491    changePKPWCurrentPasswordFile.addLongIdentifier(
3492         "current-private-key-passphrase-file", true);
3493    changePKPWCurrentPasswordFile.addLongIdentifier(
3494         "currentPrivateKeyPassphraseFile", true);
3495    changePKPWCurrentPasswordFile.addLongIdentifier(
3496         "current-private-key-pin-file", true);
3497    changePKPWCurrentPasswordFile.addLongIdentifier("currentPrivateKeyPINFile",
3498         true);
3499    changePKPWParser.addArgument(changePKPWCurrentPasswordFile);
3500
3501    final BooleanArgument changePKPWPromptForCurrentPassword =
3502         new BooleanArgument(null, "prompt-for-current-private-key-password",
3503        INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_PROMPT_FOR_CURRENT_PW_DESC.get());
3504    changePKPWPromptForCurrentPassword.addLongIdentifier(
3505         "promptForCurrentPrivateKeyPassword", true);
3506    changePKPWPromptForCurrentPassword.addLongIdentifier(
3507         "prompt-for-current-private-key-passphrase", true);
3508    changePKPWPromptForCurrentPassword.addLongIdentifier(
3509         "promptForCurrentPrivateKeyPassphrase", true);
3510    changePKPWPromptForCurrentPassword.addLongIdentifier(
3511         "prompt-for-current-private-key-pin", true);
3512    changePKPWPromptForCurrentPassword.addLongIdentifier(
3513         "promptForCurrentPrivateKeyPIN", true);
3514    changePKPWParser.addArgument(changePKPWPromptForCurrentPassword);
3515
3516    final StringArgument changePKPWNewPassword = new StringArgument(null,
3517         "new-private-key-password", false, 1,
3518         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3519         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_NEW_PW_DESC.get());
3520    changePKPWNewPassword.addLongIdentifier("newPrivateKeyPassword",
3521         true);
3522    changePKPWNewPassword.addLongIdentifier("new-private-key-passphrase", true);
3523    changePKPWNewPassword.addLongIdentifier("newPrivateKeyPassphrase", true);
3524    changePKPWNewPassword.addLongIdentifier("new-private-key-pin", true);
3525    changePKPWNewPassword.addLongIdentifier("newPrivateKeyPIN", true);
3526    changePKPWNewPassword.addLongIdentifier("new", true);
3527    changePKPWNewPassword.setSensitive(true);
3528    changePKPWParser.addArgument(changePKPWNewPassword);
3529
3530    final FileArgument changePKPWNewPasswordFile = new FileArgument(null,
3531         "new-private-key-password-file", false, 1, null,
3532         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_NEW_PW_FILE_DESC.get(), true,
3533         true, true, false);
3534    changePKPWNewPasswordFile.addLongIdentifier("newPrivateKeyPasswordFile",
3535         true);
3536    changePKPWNewPasswordFile.addLongIdentifier(
3537         "new-private-key-passphrase-file", true);
3538    changePKPWNewPasswordFile.addLongIdentifier("newPrivateKeyPassphraseFile",
3539         true);
3540    changePKPWNewPasswordFile.addLongIdentifier("new-private-key-pin-file",
3541         true);
3542    changePKPWNewPasswordFile.addLongIdentifier("newPrivateKeyPINFile", true);
3543    changePKPWParser.addArgument(changePKPWNewPasswordFile);
3544
3545    final BooleanArgument changePKPWPromptForNewPassword =
3546         new BooleanArgument(null, "prompt-for-new-private-key-password",
3547        INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_PROMPT_FOR_NEW_PW_DESC.get());
3548    changePKPWPromptForNewPassword.addLongIdentifier(
3549         "promptForNewPrivateKeyPassword", true);
3550    changePKPWPromptForNewPassword.addLongIdentifier(
3551         "prompt-for-new-private-key-passphrase", true);
3552    changePKPWPromptForNewPassword.addLongIdentifier(
3553         "promptForNewPrivateKeyPassphrase", true);
3554    changePKPWPromptForNewPassword.addLongIdentifier(
3555         "prompt-for-new-private-key-pin", true);
3556    changePKPWPromptForNewPassword.addLongIdentifier(
3557         "promptForNewPrivateKeyPIN", true);
3558    changePKPWParser.addArgument(changePKPWPromptForNewPassword);
3559
3560    final BooleanArgument changePKPWDisplayCommand = new BooleanArgument(null,
3561         "display-keytool-command", 1,
3562         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_ARG_DISPLAY_COMMAND_DESC.get());
3563    changePKPWDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
3564    changePKPWDisplayCommand.addLongIdentifier("show-keytool-command", true);
3565    changePKPWDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
3566    changePKPWParser.addArgument(changePKPWDisplayCommand);
3567
3568    changePKPWParser.addRequiredArgumentSet(changePKPWKeystorePassword,
3569         changePKPWKeystorePasswordFile, changePKPWPromptForKeystorePassword);
3570    changePKPWParser.addExclusiveArgumentSet(changePKPWKeystorePassword,
3571         changePKPWKeystorePasswordFile, changePKPWPromptForKeystorePassword);
3572    changePKPWParser.addRequiredArgumentSet(changePKPWCurrentPassword,
3573         changePKPWCurrentPasswordFile, changePKPWPromptForCurrentPassword);
3574    changePKPWParser.addExclusiveArgumentSet(changePKPWCurrentPassword,
3575         changePKPWCurrentPasswordFile, changePKPWPromptForCurrentPassword);
3576    changePKPWParser.addRequiredArgumentSet(changePKPWNewPassword,
3577         changePKPWNewPasswordFile, changePKPWPromptForNewPassword);
3578    changePKPWParser.addExclusiveArgumentSet(changePKPWNewPassword,
3579         changePKPWNewPasswordFile, changePKPWPromptForNewPassword);
3580
3581    final LinkedHashMap<String[],String> changePKPWExamples =
3582         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
3583    changePKPWExamples.put(
3584         new String[]
3585         {
3586           "change-private-key-password",
3587           "--keystore", getPlatformSpecificPath("config", "keystore"),
3588           "--keystore-password-file",
3589                getPlatformSpecificPath("config", "keystore.pin"),
3590           "--alias", "server-cert",
3591           "--current-private-key-password-file",
3592                getPlatformSpecificPath("config", "current.pin"),
3593           "--new-private-key-password-file",
3594                getPlatformSpecificPath("config", "new.pin"),
3595           "--display-keytool-command"
3596         },
3597         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_EXAMPLE_1.get(
3598              getPlatformSpecificPath("config", "keystore"),
3599              getPlatformSpecificPath("config", "current.pin"),
3600              getPlatformSpecificPath("config", "new.pin")));
3601
3602    final SubCommand changePKPWSubCommand = new SubCommand(
3603         "change-private-key-password",
3604         INFO_MANAGE_CERTS_SC_CHANGE_PK_PW_DESC.get(), changePKPWParser,
3605         changePKPWExamples);
3606    changePKPWSubCommand.addName("changePrivateKeyPassword", true);
3607    changePKPWSubCommand.addName("change-private-key-passphrase", true);
3608    changePKPWSubCommand.addName("changePrivateKeyPassphrase", true);
3609    changePKPWSubCommand.addName("change-private-key-pin", true);
3610    changePKPWSubCommand.addName("changePrivateKeyPIN", true);
3611    changePKPWSubCommand.addName("change-key-password", true);
3612    changePKPWSubCommand.addName("changeKeyPassword", true);
3613    changePKPWSubCommand.addName("change-key-passphrase", true);
3614    changePKPWSubCommand.addName("changeKeyPassphrase", true);
3615    changePKPWSubCommand.addName("change-key-pin", true);
3616    changePKPWSubCommand.addName("changeKeyPIN", true);
3617    changePKPWSubCommand.addName("keypasswd", true);
3618
3619    parser.addSubCommand(changePKPWSubCommand);
3620
3621
3622    // Define the "copy-keystore" subcommand and all of its arguments.
3623    final ArgumentParser copyKSParser = new ArgumentParser("copy-keystore",
3624         INFO_MANAGE_CERTS_SC_COPY_KS_DESC.get());
3625
3626    final FileArgument copyKSSourceKeystore = new FileArgument(null,
3627         "source-keystore", true, 1, null,
3628         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_SRC_KS_DESC.get(), true, true, true,
3629         false);
3630    copyKSSourceKeystore.addLongIdentifier("sourceKeystore", true);
3631    copyKSSourceKeystore.addLongIdentifier("source-keystore-path", true);
3632    copyKSSourceKeystore.addLongIdentifier("sourceKeystorePath", true);
3633    copyKSSourceKeystore.addLongIdentifier("source-keystore-file", true);
3634    copyKSSourceKeystore.addLongIdentifier("sourceKeystoreFile", true);
3635    copyKSParser.addArgument(copyKSSourceKeystore);
3636
3637    final StringArgument copyKSSourceKeystorePassword = new StringArgument(null,
3638         "source-keystore-password", false, 1,
3639         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3640         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_SRC_KS_PW_DESC.get());
3641    copyKSSourceKeystorePassword.addLongIdentifier("sourceKeystorePassword",
3642         true);
3643    copyKSSourceKeystorePassword.addLongIdentifier("source-keystore-passphrase",
3644         true);
3645    copyKSSourceKeystorePassword.addLongIdentifier("sourceKeystorePassphrase",
3646         true);
3647    copyKSSourceKeystorePassword.addLongIdentifier("source-keystore-pin", true);
3648    copyKSSourceKeystorePassword.addLongIdentifier("sourceKeystorePIN", true);
3649    copyKSParser.addArgument(copyKSSourceKeystorePassword);
3650
3651    final FileArgument copyKSSourceKeystorePasswordFile = new FileArgument(null,
3652         "source-keystore-password-file", false, 1, null,
3653         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_SRC_KS_PW_FILE_DESC.get(), true, true,
3654         true, false);
3655    copyKSSourceKeystorePasswordFile.addLongIdentifier(
3656         "sourceKeystorePasswordFile", true);
3657    copyKSSourceKeystorePasswordFile.addLongIdentifier(
3658         "source-keystore-passphrase-file", true);
3659    copyKSSourceKeystorePasswordFile.addLongIdentifier(
3660         "sourceKeystorePassphraseFile", true);
3661    copyKSSourceKeystorePasswordFile.addLongIdentifier(
3662         "source-keystore-pin-file", true);
3663    copyKSSourceKeystorePasswordFile.addLongIdentifier(
3664         "sourceKeystorePINFile", true);
3665    copyKSParser.addArgument(copyKSSourceKeystorePasswordFile);
3666
3667    final BooleanArgument copyKSPromptForSourceKeystorePassword =
3668         new BooleanArgument(null, "prompt-for-source-keystore-password", 1,
3669              INFO_MANAGE_CERTS_SC_COPY_KS_ARG_PROMPT_FOR_SRC_KS_PW.get());
3670    copyKSPromptForSourceKeystorePassword.addLongIdentifier(
3671         "promptForSourceKeystorePassword", true);
3672    copyKSPromptForSourceKeystorePassword.addLongIdentifier(
3673         "prompt-for-source-keystore-passphrase", true);
3674    copyKSPromptForSourceKeystorePassword.addLongIdentifier(
3675         "promptForSourceKeystorePassphrase", true);
3676    copyKSPromptForSourceKeystorePassword.addLongIdentifier(
3677         "prompt-for-source-keystore-pin", true);
3678    copyKSPromptForSourceKeystorePassword.addLongIdentifier(
3679         "promptForSourceKeystorePIN", true);
3680    copyKSParser.addArgument(copyKSPromptForSourceKeystorePassword);
3681
3682    final StringArgument copyKSSourcePKPassword = new StringArgument(null,
3683         "source-private-key-password", false, 1,
3684         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3685         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_SRC_PK_PW_DESC.get());
3686    copyKSSourcePKPassword.addLongIdentifier("sourcePrivateKeyPassword", true);
3687    copyKSSourcePKPassword.addLongIdentifier("source-private-key-passphrase",
3688         true);
3689    copyKSSourcePKPassword.addLongIdentifier("sourcePrivateKeyPassphrase",
3690         true);
3691    copyKSSourcePKPassword.addLongIdentifier("source-private-key-pin", true);
3692    copyKSSourcePKPassword.addLongIdentifier("sourcePrivateKeyPIN", true);
3693    copyKSParser.addArgument(copyKSSourcePKPassword);
3694
3695    final FileArgument copyKSSourcePKPasswordFile = new FileArgument(null,
3696         "source-private-key-password-file", false, 1, null,
3697         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_SRC_PK_PW_FILE_DESC.get(), true, true,
3698         true, false);
3699    copyKSSourcePKPasswordFile.addLongIdentifier(
3700         "sourcePrivateKeyPasswordFile", true);
3701    copyKSSourcePKPasswordFile.addLongIdentifier(
3702         "source-private-key-passphrase-file", true);
3703    copyKSSourcePKPasswordFile.addLongIdentifier(
3704         "sourcePrivateKeyPassphraseFile", true);
3705    copyKSSourcePKPasswordFile.addLongIdentifier(
3706         "source-private-key-pin-file", true);
3707    copyKSSourcePKPasswordFile.addLongIdentifier(
3708         "sourcePrivateKeyPINFile", true);
3709    copyKSParser.addArgument(copyKSSourcePKPasswordFile);
3710
3711    final BooleanArgument copyKSPromptForSourcePKPassword =
3712         new BooleanArgument(null, "prompt-for-source-private-key-password", 1,
3713              INFO_MANAGE_CERTS_SC_COPY_KS_ARG_PROMPT_FOR_SRC_PK_PW.get());
3714    copyKSPromptForSourcePKPassword.addLongIdentifier(
3715         "promptForSourcePrivateKeyPassword", true);
3716    copyKSPromptForSourcePKPassword.addLongIdentifier(
3717         "prompt-for-source-private-key-passphrase", true);
3718    copyKSPromptForSourcePKPassword.addLongIdentifier(
3719         "promptForSourcePrivateKeyPassphrase", true);
3720    copyKSPromptForSourcePKPassword.addLongIdentifier(
3721         "prompt-for-source-private-key-pin", true);
3722    copyKSPromptForSourcePKPassword.addLongIdentifier(
3723         "promptForSourcePrivateKeyPIN", true);
3724    copyKSParser.addArgument(copyKSPromptForSourcePKPassword);
3725
3726    final StringArgument copyKSSourceKeystoreType = new StringArgument(null,
3727         "source-keystore-type", false, 1,
3728         INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
3729         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_SRC_KS_TYPE.get(),
3730         ALLOWED_KEYSTORE_TYPE_VALUES);
3731    copyKSSourceKeystoreType.addLongIdentifier("source-key-store-type", true);
3732    copyKSSourceKeystoreType.addLongIdentifier("sourceKeystoreType", true);
3733    copyKSSourceKeystoreType.addLongIdentifier("source-keystore-format", true);
3734    copyKSSourceKeystoreType.addLongIdentifier("source-key-store-format", true);
3735    copyKSSourceKeystoreType.addLongIdentifier("sourceKeystoreFormat", true);
3736    copyKSParser.addArgument(copyKSSourceKeystoreType);
3737
3738    final FileArgument copyKSDestKeystore = new FileArgument(null,
3739         "destination-keystore", true, 1, null,
3740         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_DST_KS_DESC.get(), false, true, true,
3741         false);
3742    copyKSDestKeystore.addLongIdentifier("destinationKeystore", true);
3743    copyKSDestKeystore.addLongIdentifier("destination-keystore-path", true);
3744    copyKSDestKeystore.addLongIdentifier("destinationKeystorePath", true);
3745    copyKSDestKeystore.addLongIdentifier("destination-keystore-file", true);
3746    copyKSDestKeystore.addLongIdentifier("destinationKeystoreFile", true);
3747    copyKSDestKeystore.addLongIdentifier("target-keystore", true);
3748    copyKSDestKeystore.addLongIdentifier("targetKeystore", true);
3749    copyKSDestKeystore.addLongIdentifier("target-keystore-path", true);
3750    copyKSDestKeystore.addLongIdentifier("targetKeystorePath", true);
3751    copyKSDestKeystore.addLongIdentifier("target-keystore-file", true);
3752    copyKSDestKeystore.addLongIdentifier("targetKeystoreFile", true);
3753    copyKSParser.addArgument(copyKSDestKeystore);
3754
3755    final StringArgument copyKSDestKeystorePassword = new StringArgument(null,
3756         "destination-keystore-password", false, 1,
3757         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3758         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_DST_KS_PW_DESC.get());
3759    copyKSDestKeystorePassword.addLongIdentifier("destinationKeystorePassword",
3760         true);
3761    copyKSDestKeystorePassword.addLongIdentifier(
3762         "destination-keystore-passphrase", true);
3763    copyKSDestKeystorePassword.addLongIdentifier(
3764         "destinationKeystorePassphrase", true);
3765    copyKSDestKeystorePassword.addLongIdentifier("destination-keystore-pin",
3766         true);
3767    copyKSDestKeystorePassword.addLongIdentifier("destinationKeystorePIN",
3768         true);
3769    copyKSDestKeystorePassword.addLongIdentifier("target-keystore-password",
3770         true);
3771    copyKSDestKeystorePassword.addLongIdentifier("targetKeystorePassword",
3772         true);
3773    copyKSDestKeystorePassword.addLongIdentifier("target-keystore-passphrase",
3774         true);
3775    copyKSDestKeystorePassword.addLongIdentifier("targetKeystorePassphrase",
3776         true);
3777    copyKSDestKeystorePassword.addLongIdentifier("target-keystore-pin", true);
3778    copyKSDestKeystorePassword.addLongIdentifier("targetKeystorePIN", true);
3779    copyKSParser.addArgument(copyKSDestKeystorePassword);
3780
3781    final FileArgument copyKSDestKeystorePasswordFile = new FileArgument(null,
3782         "destination-keystore-password-file", false, 1, null,
3783         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_DST_KS_PW_FILE_DESC.get(), true, true,
3784         true, false);
3785    copyKSDestKeystorePasswordFile.addLongIdentifier(
3786         "destinationKeystorePasswordFile", true);
3787    copyKSDestKeystorePasswordFile.addLongIdentifier(
3788         "destination-keystore-passphrase-file", true);
3789    copyKSDestKeystorePasswordFile.addLongIdentifier(
3790         "destinationKeystorePassphraseFile", true);
3791    copyKSDestKeystorePasswordFile.addLongIdentifier(
3792         "destination-keystore-pin-file", true);
3793    copyKSDestKeystorePasswordFile.addLongIdentifier(
3794         "destinationKeystorePINFile", true);
3795    copyKSDestKeystorePasswordFile.addLongIdentifier(
3796         "target-keystore-password-file", true);
3797    copyKSDestKeystorePasswordFile.addLongIdentifier(
3798         "targetKeystorePasswordFile", true);
3799    copyKSDestKeystorePasswordFile.addLongIdentifier(
3800         "target-keystore-passphrase-file", true);
3801    copyKSDestKeystorePasswordFile.addLongIdentifier(
3802         "targetKeystorePassphraseFile", true);
3803    copyKSDestKeystorePasswordFile.addLongIdentifier("target-keystore-pin-file",
3804         true);
3805    copyKSDestKeystorePasswordFile.addLongIdentifier("targetKeystorePINFile",
3806         true);
3807    copyKSParser.addArgument(copyKSDestKeystorePasswordFile);
3808
3809    final BooleanArgument copyKSPromptForDestKeystorePassword =
3810         new BooleanArgument(null, "prompt-for-destination-keystore-password",
3811              1, INFO_MANAGE_CERTS_SC_COPY_KS_ARG_PROMPT_FOR_DST_KS_PW.get());
3812    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3813         "promptForDestinationKeystorePassword", true);
3814    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3815         "prompt-for-Destination-keystore-passphrase", true);
3816    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3817         "promptForDestinationKeystorePassphrase", true);
3818    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3819         "prompt-for-Destination-keystore-pin", true);
3820    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3821         "promptForDestinationKeystorePIN", true);
3822    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3823         "prompt-for-target-keystore-password", true);
3824    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3825         "promptForTargetKeystorePassword", true);
3826    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3827         "prompt-for-Target-keystore-passphrase", true);
3828    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3829         "promptForTargetKeystorePassphrase", true);
3830    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3831         "prompt-for-Target-keystore-pin", true);
3832    copyKSPromptForDestKeystorePassword.addLongIdentifier(
3833         "promptForTargetKeystorePIN", true);
3834    copyKSParser.addArgument(copyKSPromptForDestKeystorePassword);
3835
3836    final StringArgument copyKSDestPKPassword = new StringArgument(null,
3837         "destination-private-key-password", false, 1,
3838         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
3839         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_DST_PK_PW_DESC.get());
3840    copyKSDestPKPassword.addLongIdentifier("destinationPrivateKeyPassword",
3841         true);
3842    copyKSDestPKPassword.addLongIdentifier("destination-private-key-passphrase",
3843         true);
3844    copyKSDestPKPassword.addLongIdentifier("destinationPrivateKeyPassphrase",
3845         true);
3846    copyKSDestPKPassword.addLongIdentifier("destination-private-key-pin", true);
3847    copyKSDestPKPassword.addLongIdentifier("destinationPrivateKeyPIN", true);
3848    copyKSDestPKPassword.addLongIdentifier("target-private-key-password",
3849         true);
3850    copyKSDestPKPassword.addLongIdentifier("targetPrivateKeyPassword",
3851         true);
3852    copyKSDestPKPassword.addLongIdentifier("target-private-key-passphrase",
3853         true);
3854    copyKSDestPKPassword.addLongIdentifier("targetPrivateKeyPassphrase",
3855         true);
3856    copyKSDestPKPassword.addLongIdentifier("target-private-key-pin", true);
3857    copyKSDestPKPassword.addLongIdentifier("targetPrivateKeyPIN", true);
3858    copyKSParser.addArgument(copyKSDestPKPassword);
3859
3860    final FileArgument copyKSDestPKPasswordFile = new FileArgument(null,
3861         "destination-private-key-password-file", false, 1, null,
3862         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_DST_PK_PW_FILE_DESC.get(), true, true,
3863         true, false);
3864    copyKSDestPKPasswordFile.addLongIdentifier(
3865         "destinationPrivateKeyPasswordFile", true);
3866    copyKSDestPKPasswordFile.addLongIdentifier(
3867         "destination-private-key-passphrase-file", true);
3868    copyKSDestPKPasswordFile.addLongIdentifier(
3869         "destinationPrivateKeyPassphraseFile", true);
3870    copyKSDestPKPasswordFile.addLongIdentifier(
3871         "destination-private-key-pin-file", true);
3872    copyKSDestPKPasswordFile.addLongIdentifier(
3873         "destinationPrivateKeyPINFile", true);
3874    copyKSDestPKPasswordFile.addLongIdentifier(
3875         "target-private-key-password-file", true);
3876    copyKSDestPKPasswordFile.addLongIdentifier(
3877         "targetPrivateKeyPasswordFile", true);
3878    copyKSDestPKPasswordFile.addLongIdentifier(
3879         "target-private-key-passphrase-file", true);
3880    copyKSDestPKPasswordFile.addLongIdentifier(
3881         "targetPrivateKeyPassphraseFile", true);
3882    copyKSDestPKPasswordFile.addLongIdentifier(
3883         "target-private-key-pin-file", true);
3884    copyKSDestPKPasswordFile.addLongIdentifier(
3885         "targetPrivateKeyPINFile", true);
3886    copyKSParser.addArgument(copyKSDestPKPasswordFile);
3887
3888    final BooleanArgument copyKSPromptForDestPKPassword =
3889         new BooleanArgument(null,
3890              "prompt-for-destination-private-key-password", 1,
3891              INFO_MANAGE_CERTS_SC_COPY_KS_ARG_PROMPT_FOR_DST_PK_PW.get());
3892    copyKSPromptForDestPKPassword.addLongIdentifier(
3893         "promptForDestinationPrivateKeyPassword", true);
3894    copyKSPromptForDestPKPassword.addLongIdentifier(
3895         "prompt-for-Destination-private-key-passphrase", true);
3896    copyKSPromptForDestPKPassword.addLongIdentifier(
3897         "promptForDestinationPrivateKeyPassphrase", true);
3898    copyKSPromptForDestPKPassword.addLongIdentifier(
3899         "prompt-for-Destination-private-key-pin", true);
3900    copyKSPromptForDestPKPassword.addLongIdentifier(
3901         "promptForDestinationPrivateKeyPIN", true);
3902    copyKSPromptForDestPKPassword.addLongIdentifier(
3903         "prompt-for-target-private-key-password", true);
3904    copyKSPromptForDestPKPassword.addLongIdentifier(
3905         "promptForTargetPrivateKeyPassword", true);
3906    copyKSPromptForDestPKPassword.addLongIdentifier(
3907         "prompt-for-Target-private-key-passphrase", true);
3908    copyKSPromptForDestPKPassword.addLongIdentifier(
3909         "promptForTargetPrivateKeyPassphrase", true);
3910    copyKSPromptForDestPKPassword.addLongIdentifier(
3911         "prompt-for-Target-private-key-pin", true);
3912    copyKSPromptForDestPKPassword.addLongIdentifier(
3913         "promptForTargetPrivateKeyPIN", true);
3914    copyKSParser.addArgument(copyKSPromptForDestPKPassword);
3915
3916    final StringArgument copyKSDestKeystoreType = new StringArgument(null,
3917         "destination-keystore-type", false, 1,
3918         INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
3919         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_DST_KS_TYPE.get(),
3920         ALLOWED_KEYSTORE_TYPE_VALUES);
3921    copyKSDestKeystoreType.addLongIdentifier("destination-key-store-type",
3922         true);
3923    copyKSDestKeystoreType.addLongIdentifier("destinationKeystoreType", true);
3924    copyKSDestKeystoreType.addLongIdentifier("destination-keystore-format",
3925         true);
3926    copyKSDestKeystoreType.addLongIdentifier("destination-key-store-format",
3927         true);
3928    copyKSDestKeystoreType.addLongIdentifier("destinationKeystoreFormat", true);
3929    copyKSDestKeystoreType.addLongIdentifier("target-key-store-type", true);
3930    copyKSDestKeystoreType.addLongIdentifier("targetKeystoreType", true);
3931    copyKSDestKeystoreType.addLongIdentifier("target-keystore-format", true);
3932    copyKSDestKeystoreType.addLongIdentifier("target-key-store-format", true);
3933    copyKSDestKeystoreType.addLongIdentifier("targetKeystoreFormat", true);
3934    copyKSParser.addArgument(copyKSDestKeystoreType);
3935
3936    final StringArgument copyKSAlias = new StringArgument(null, "alias", false,
3937         0, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
3938         INFO_MANAGE_CERTS_SC_COPY_KS_ARG_ALIAS.get());
3939    copyKSAlias.addLongIdentifier("nickname", true);
3940    copyKSParser.addArgument(copyKSAlias);
3941
3942    copyKSParser.addRequiredArgumentSet(copyKSSourceKeystorePassword,
3943         copyKSSourceKeystorePasswordFile,
3944         copyKSPromptForSourceKeystorePassword);
3945    copyKSParser.addExclusiveArgumentSet(copyKSSourceKeystorePassword,
3946         copyKSSourceKeystorePasswordFile,
3947         copyKSPromptForSourceKeystorePassword);
3948    copyKSParser.addExclusiveArgumentSet(copyKSSourcePKPassword,
3949         copyKSSourcePKPasswordFile, copyKSPromptForDestPKPassword);
3950    copyKSParser.addExclusiveArgumentSet(copyKSDestKeystorePassword,
3951         copyKSDestKeystorePasswordFile, copyKSPromptForDestKeystorePassword);
3952    copyKSParser.addExclusiveArgumentSet(copyKSDestPKPassword,
3953         copyKSDestPKPasswordFile, copyKSPromptForDestPKPassword);
3954
3955    final LinkedHashMap<String[],String> copyKeyStoreExamples =
3956         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
3957    copyKeyStoreExamples.put(
3958         new String[]
3959         {
3960           "copy-keystore",
3961           "--source-keystore",
3962                getPlatformSpecificPath("config", "keystore.jks"),
3963           "--source-keystore-password-file",
3964                getPlatformSpecificPath("config", "keystore.pin"),
3965           "--source-keystore-type", "JKS",
3966           "--destination-keystore",
3967                getPlatformSpecificPath("config", "keystore.p12"),
3968           "--destination-keystore-password-file",
3969                getPlatformSpecificPath("config", "keystore.pin"),
3970           "--destination-keystore-type", "PKCS12"
3971         },
3972         INFO_MANAGE_CERTS_SC_COPY_KS_EXAMPLE_1.get("keystore.jks",
3973              "keystore.p12"));
3974
3975    final SubCommand copyKeyStoreSubCommand = new SubCommand("copy-keystore",
3976         INFO_MANAGE_CERTS_SC_COPY_KS_DESC.get(), copyKSParser,
3977         copyKeyStoreExamples);
3978    copyKeyStoreSubCommand.addName("copy-key-store", true);
3979    copyKeyStoreSubCommand.addName("copyKeyStore", true);
3980    copyKeyStoreSubCommand.addName("import-keystore", true);
3981    copyKeyStoreSubCommand.addName("import-key-store", true);
3982    copyKeyStoreSubCommand.addName("importKeyStore", true);
3983    copyKeyStoreSubCommand.addName("convert-keystore", true);
3984    copyKeyStoreSubCommand.addName("convert-key-store", true);
3985    copyKeyStoreSubCommand.addName("convertKeyStore", true);
3986
3987    parser.addSubCommand(copyKeyStoreSubCommand);
3988
3989    // Define the "retrieve-server-certificate" subcommand and all of its
3990    // arguments.
3991    final ArgumentParser retrieveCertParser = new ArgumentParser(
3992         "retrieve-server-certificate",
3993         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_DESC.get());
3994
3995    final StringArgument retrieveCertHostname = new StringArgument('h',
3996         "hostname", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_HOST.get(),
3997         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_HOSTNAME_DESC.get());
3998    retrieveCertHostname.addLongIdentifier("server-address", true);
3999    retrieveCertHostname.addLongIdentifier("serverAddress", true);
4000    retrieveCertHostname.addLongIdentifier("address", true);
4001    retrieveCertParser.addArgument(retrieveCertHostname);
4002
4003    final IntegerArgument retrieveCertPort = new IntegerArgument('p',
4004         "port", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_PORT.get(),
4005         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_PORT_DESC.get(), 1, 65_535);
4006    retrieveCertPort.addLongIdentifier("server-port", true);
4007    retrieveCertPort.addLongIdentifier("serverPort", true);
4008    retrieveCertParser.addArgument(retrieveCertPort);
4009
4010    final BooleanArgument retrieveCertUseStartTLS = new BooleanArgument('q',
4011         "use-ldap-start-tls", 1,
4012         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_USE_START_TLS_DESC.get());
4013    retrieveCertUseStartTLS.addLongIdentifier("use-ldap-starttls", true);
4014    retrieveCertUseStartTLS.addLongIdentifier("useLDAPStartTLS", true);
4015    retrieveCertUseStartTLS.addLongIdentifier("use-start-tls", true);
4016    retrieveCertUseStartTLS.addLongIdentifier("use-starttls", true);
4017    retrieveCertUseStartTLS.addLongIdentifier("useStartTLS", true);
4018    retrieveCertParser.addArgument(retrieveCertUseStartTLS);
4019
4020    final FileArgument retrieveCertOutputFile = new FileArgument(null,
4021         "output-file", false, 1, null,
4022         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_FILE_DESC.get(), false, true,
4023         true, false);
4024    retrieveCertOutputFile.addLongIdentifier("outputFile", true);
4025    retrieveCertOutputFile.addLongIdentifier("export-file", true);
4026    retrieveCertOutputFile.addLongIdentifier("exportFile", true);
4027    retrieveCertOutputFile.addLongIdentifier("certificate-file", true);
4028    retrieveCertOutputFile.addLongIdentifier("certificateFile", true);
4029    retrieveCertOutputFile.addLongIdentifier("file", true);
4030    retrieveCertOutputFile.addLongIdentifier("filename", true);
4031    retrieveCertParser.addArgument(retrieveCertOutputFile);
4032
4033    final Set<String> retrieveCertOutputFormatAllowedValues = StaticUtils.setOf(
4034         "PEM", "text", "txt", "RFC", "DER", "binary", "bin");
4035    final StringArgument retrieveCertOutputFormat = new StringArgument(null,
4036         "output-format", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_FORMAT.get(),
4037         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_FORMAT_DESC.get(),
4038         retrieveCertOutputFormatAllowedValues, "PEM");
4039    retrieveCertOutputFormat.addLongIdentifier("outputFormat", true);
4040    retrieveCertParser.addArgument(retrieveCertOutputFormat);
4041
4042    final BooleanArgument retrieveCertOnlyPeer = new BooleanArgument(null,
4043         "only-peer-certificate", 1,
4044         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_ONLY_PEER_DESC.get());
4045    retrieveCertOnlyPeer.addLongIdentifier("onlyPeerCertificate", true);
4046    retrieveCertOnlyPeer.addLongIdentifier("only-peer", true);
4047    retrieveCertOnlyPeer.addLongIdentifier("onlyPeer", true);
4048    retrieveCertOnlyPeer.addLongIdentifier("peer-certificate-only", true);
4049    retrieveCertOnlyPeer.addLongIdentifier("peerCertificateOnly", true);
4050    retrieveCertOnlyPeer.addLongIdentifier("peer-only", true);
4051    retrieveCertOnlyPeer.addLongIdentifier("peerOnly", true);
4052    retrieveCertParser.addArgument(retrieveCertOnlyPeer);
4053
4054    final BooleanArgument retrieveCertEnableSSLDebugging = new BooleanArgument(
4055         null, "enableSSLDebugging", 1,
4056         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_ENABLE_SSL_DEBUGGING_DESC.
4057              get());
4058    retrieveCertEnableSSLDebugging.addLongIdentifier("enableTLSDebugging",
4059         true);
4060    retrieveCertEnableSSLDebugging.addLongIdentifier("enableStartTLSDebugging",
4061         true);
4062    retrieveCertEnableSSLDebugging.addLongIdentifier("enable-ssl-debugging",
4063         true);
4064    retrieveCertEnableSSLDebugging.addLongIdentifier("enable-tls-debugging",
4065         true);
4066    retrieveCertEnableSSLDebugging.addLongIdentifier(
4067         "enable-starttls-debugging", true);
4068    retrieveCertEnableSSLDebugging.addLongIdentifier(
4069         "enable-start-tls-debugging", true);
4070    retrieveCertParser.addArgument(retrieveCertEnableSSLDebugging);
4071    addEnableSSLDebuggingArgument(retrieveCertEnableSSLDebugging);
4072
4073    final BooleanArgument retrieveCertVerbose = new BooleanArgument(null,
4074         "verbose", 1,
4075         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_ARG_VERBOSE_DESC.get());
4076    retrieveCertParser.addArgument(retrieveCertVerbose);
4077
4078    retrieveCertParser.addDependentArgumentSet(retrieveCertOutputFormat,
4079         retrieveCertOutputFile);
4080
4081    final LinkedHashMap<String[],String> retrieveCertExamples =
4082         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
4083    retrieveCertExamples.put(
4084         new String[]
4085         {
4086           "retrieve-server-certificate",
4087           "--hostname", "ds.example.com",
4088           "--port", "636"
4089         },
4090         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_EXAMPLE_1.get(
4091              getPlatformSpecificPath("config", "truststore")));
4092    retrieveCertExamples.put(
4093         new String[]
4094         {
4095           "retrieve-server-certificate",
4096           "--hostname", "ds.example.com",
4097           "--port", "389",
4098           "--use-ldap-start-tls",
4099           "--only-peer-certificate",
4100           "--output-file", "ds-cert.pem",
4101           "--output-format", "PEM",
4102           "--verbose"
4103         },
4104         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_EXAMPLE_2.get(
4105              getPlatformSpecificPath("config", "truststore")));
4106
4107    final SubCommand retrieveCertSubCommand = new SubCommand(
4108         "retrieve-server-certificate",
4109         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_DESC.get(), retrieveCertParser,
4110         retrieveCertExamples);
4111    retrieveCertSubCommand.addName("retrieveServerCertificate", true);
4112    retrieveCertSubCommand.addName("retrieve-certificate", true);
4113    retrieveCertSubCommand.addName("retrieveCertificate", true);
4114    retrieveCertSubCommand.addName("get-server-certificate", true);
4115    retrieveCertSubCommand.addName("getServerCertificate", true);
4116    retrieveCertSubCommand.addName("get-certificate", true);
4117    retrieveCertSubCommand.addName("getCertificate", true);
4118    retrieveCertSubCommand.addName("display-server-certificate", true);
4119    retrieveCertSubCommand.addName("displayServerCertificate", true);
4120
4121    parser.addSubCommand(retrieveCertSubCommand);
4122
4123
4124    // Define the "trust-server-certificate" subcommand and all of its
4125    // arguments.
4126    final ArgumentParser trustServerParser = new ArgumentParser(
4127         "trust-server-certificate",
4128         INFO_MANAGE_CERTS_SC_TRUST_SERVER_DESC.get());
4129
4130    final StringArgument trustServerHostname = new StringArgument('h',
4131         "hostname", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_HOST.get(),
4132         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_HOSTNAME_DESC.get());
4133    trustServerHostname.addLongIdentifier("server-address", true);
4134    trustServerHostname.addLongIdentifier("serverAddress", true);
4135    trustServerHostname.addLongIdentifier("address", true);
4136    trustServerParser.addArgument(trustServerHostname);
4137
4138    final IntegerArgument trustServerPort = new IntegerArgument('p',
4139         "port", true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_PORT.get(),
4140         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_PORT_DESC.get(), 1, 65_535);
4141    trustServerPort.addLongIdentifier("server-port", true);
4142    trustServerPort.addLongIdentifier("serverPort", true);
4143    trustServerParser.addArgument(trustServerPort);
4144
4145    final BooleanArgument trustServerUseStartTLS = new BooleanArgument('q',
4146         "use-ldap-start-tls", 1,
4147         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_USE_START_TLS_DESC.get());
4148    trustServerUseStartTLS.addLongIdentifier("use-ldap-starttls", true);
4149    trustServerUseStartTLS.addLongIdentifier("useLDAPStartTLS", true);
4150    trustServerUseStartTLS.addLongIdentifier("use-start-tls", true);
4151    trustServerUseStartTLS.addLongIdentifier("use-starttls", true);
4152    trustServerUseStartTLS.addLongIdentifier("useStartTLS", true);
4153    trustServerParser.addArgument(trustServerUseStartTLS);
4154
4155    final FileArgument trustServerKeystore = new FileArgument(null, "keystore",
4156         true, 1, null, INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_DESC.get(),
4157         false, true,  true, false);
4158    trustServerKeystore.addLongIdentifier("keystore-path", true);
4159    trustServerKeystore.addLongIdentifier("keystorePath", true);
4160    trustServerKeystore.addLongIdentifier("keystore-file", true);
4161    trustServerKeystore.addLongIdentifier("keystoreFile", true);
4162    trustServerParser.addArgument(trustServerKeystore);
4163
4164    final StringArgument trustServerKeystorePassword = new StringArgument(null,
4165         "keystore-password", false, 1,
4166         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
4167         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_PW_DESC.get());
4168    trustServerKeystorePassword.addLongIdentifier("keystorePassword", true);
4169    trustServerKeystorePassword.addLongIdentifier("keystore-passphrase", true);
4170    trustServerKeystorePassword.addLongIdentifier("keystorePassphrase", true);
4171    trustServerKeystorePassword.addLongIdentifier("keystore-pin", true);
4172    trustServerKeystorePassword.addLongIdentifier("keystorePIN", true);
4173    trustServerKeystorePassword.addLongIdentifier("storepass", true);
4174    trustServerKeystorePassword.setSensitive(true);
4175    trustServerParser.addArgument(trustServerKeystorePassword);
4176
4177    final FileArgument trustServerKeystorePasswordFile = new FileArgument(null,
4178         "keystore-password-file", false, 1, null,
4179         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_PW_FILE_DESC.get(), true,
4180         true, true, false);
4181    trustServerKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
4182         true);
4183    trustServerKeystorePasswordFile.addLongIdentifier(
4184         "keystore-passphrase-file", true);
4185    trustServerKeystorePasswordFile.addLongIdentifier("keystorePassphraseFile",
4186         true);
4187    trustServerKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
4188         true);
4189    trustServerKeystorePasswordFile.addLongIdentifier("keystorePINFile", true);
4190    trustServerParser.addArgument(trustServerKeystorePasswordFile);
4191
4192    final BooleanArgument trustServerPromptForKeystorePassword =
4193         new BooleanArgument(null, "prompt-for-keystore-password",
4194        INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_PROMPT_FOR_KS_PW_DESC.get());
4195    trustServerPromptForKeystorePassword.addLongIdentifier(
4196         "promptForKeystorePassword", true);
4197    trustServerPromptForKeystorePassword.addLongIdentifier(
4198         "prompt-for-keystore-passphrase", true);
4199    trustServerPromptForKeystorePassword.addLongIdentifier(
4200         "promptForKeystorePassphrase", true);
4201    trustServerPromptForKeystorePassword.addLongIdentifier(
4202         "prompt-for-keystore-pin", true);
4203    trustServerPromptForKeystorePassword.addLongIdentifier(
4204         "promptForKeystorePIN", true);
4205    trustServerParser.addArgument(trustServerPromptForKeystorePassword);
4206
4207    final StringArgument trustServerKeystoreType = new StringArgument(null,
4208         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
4209         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_KS_TYPE_DESC.get(),
4210         ALLOWED_KEYSTORE_TYPE_VALUES);
4211    trustServerKeystoreType.addLongIdentifier("key-store-type", true);
4212    trustServerKeystoreType.addLongIdentifier("keystoreType", true);
4213    trustServerKeystoreType.addLongIdentifier("keystore-format", true);
4214    trustServerKeystoreType.addLongIdentifier("key-store-format", true);
4215    trustServerKeystoreType.addLongIdentifier("keystoreFormat", true);
4216    trustServerKeystoreType.addLongIdentifier("storetype", true);
4217    trustServerParser.addArgument(trustServerKeystoreType);
4218
4219    final StringArgument trustServerAlias = new StringArgument(null,
4220         "alias", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
4221         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_ALIAS_DESC.get());
4222    trustServerAlias.addLongIdentifier("nickname", true);
4223    trustServerParser.addArgument(trustServerAlias);
4224
4225    final BooleanArgument trustServerIssuersOnly = new BooleanArgument(null,
4226         "issuers-only", 1,
4227         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_ISSUERS_ONLY_DESC.get());
4228    trustServerIssuersOnly.addLongIdentifier("issuersOnly", true);
4229    trustServerIssuersOnly.addLongIdentifier("issuer-certificates-only", true);
4230    trustServerIssuersOnly.addLongIdentifier("issuerCertificatesOnly", true);
4231    trustServerIssuersOnly.addLongIdentifier("only-issuers", true);
4232    trustServerIssuersOnly.addLongIdentifier("onlyIssuers", true);
4233    trustServerIssuersOnly.addLongIdentifier("only-issuer-certificates", true);
4234    trustServerIssuersOnly.addLongIdentifier("onlyIssuerCertificates", true);
4235    trustServerParser.addArgument(trustServerIssuersOnly);
4236
4237    final BooleanArgument trustServerEnableSSLDebugging = new BooleanArgument(
4238         null, "enableSSLDebugging", 1,
4239         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_ENABLE_SSL_DEBUGGING_DESC.get());
4240    trustServerEnableSSLDebugging.addLongIdentifier("enableTLSDebugging", true);
4241    trustServerEnableSSLDebugging.addLongIdentifier("enableStartTLSDebugging",
4242         true);
4243    trustServerEnableSSLDebugging.addLongIdentifier("enable-ssl-debugging",
4244         true);
4245    trustServerEnableSSLDebugging.addLongIdentifier("enable-tls-debugging",
4246         true);
4247    trustServerEnableSSLDebugging.addLongIdentifier("enable-starttls-debugging",
4248         true);
4249    trustServerEnableSSLDebugging.addLongIdentifier(
4250         "enable-start-tls-debugging", true);
4251    trustServerParser.addArgument(trustServerEnableSSLDebugging);
4252    addEnableSSLDebuggingArgument(trustServerEnableSSLDebugging);
4253
4254    final BooleanArgument trustServerVerbose = new BooleanArgument(null,
4255         "verbose", 1,
4256         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_VERBOSE_DESC.get());
4257    trustServerParser.addArgument(trustServerVerbose);
4258
4259    final BooleanArgument trustServerNoPrompt = new BooleanArgument(null,
4260         "no-prompt", 1,
4261         INFO_MANAGE_CERTS_SC_TRUST_SERVER_ARG_NO_PROMPT_DESC.get());
4262    trustServerNoPrompt.addLongIdentifier("noPrompt", true);
4263    trustServerParser.addArgument(trustServerNoPrompt);
4264
4265    trustServerParser.addRequiredArgumentSet(trustServerKeystorePassword,
4266         trustServerKeystorePasswordFile, trustServerPromptForKeystorePassword);
4267    trustServerParser.addExclusiveArgumentSet(trustServerKeystorePassword,
4268         trustServerKeystorePasswordFile, trustServerPromptForKeystorePassword);
4269
4270    final LinkedHashMap<String[],String> trustServerExamples =
4271         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
4272    trustServerExamples.put(
4273         new String[]
4274         {
4275           "trust-server-certificate",
4276           "--hostname", "ds.example.com",
4277           "--port", "636",
4278           "--keystore", getPlatformSpecificPath("config", "truststore"),
4279           "--keystore-password-file",
4280                getPlatformSpecificPath("config", "truststore.pin"),
4281           "--verbose"
4282         },
4283         INFO_MANAGE_CERTS_SC_TRUST_SERVER_EXAMPLE_1.get(
4284              getPlatformSpecificPath("config", "truststore")));
4285    trustServerExamples.put(
4286         new String[]
4287         {
4288           "trust-server-certificate",
4289           "--hostname", "ds.example.com",
4290           "--port", "389",
4291           "--use-ldap-start-tls",
4292           "--keystore", getPlatformSpecificPath("config", "truststore"),
4293           "--keystore-password-file",
4294                getPlatformSpecificPath("config", "truststore.pin"),
4295           "--issuers-only",
4296           "--alias", "ds-start-tls-cert",
4297           "--no-prompt"
4298         },
4299         INFO_MANAGE_CERTS_SC_TRUST_SERVER_EXAMPLE_2.get(
4300              getPlatformSpecificPath("config", "truststore")));
4301
4302    final SubCommand trustServerSubCommand = new SubCommand(
4303         "trust-server-certificate",
4304         INFO_MANAGE_CERTS_SC_TRUST_SERVER_DESC.get(), trustServerParser,
4305         trustServerExamples);
4306    trustServerSubCommand.addName("trustServerCertificate", true);
4307    trustServerSubCommand.addName("trust-server", true);
4308    trustServerSubCommand.addName("trustServer", true);
4309
4310    parser.addSubCommand(trustServerSubCommand);
4311
4312
4313    // Define the "check-certificate-usability" subcommand and all of its
4314    // arguments.
4315    final ArgumentParser checkUsabilityParser = new ArgumentParser(
4316         "check-certificate-usability",
4317         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_DESC.get());
4318
4319    final FileArgument checkUsabilityKeystore = new FileArgument(null,
4320         "keystore", true, 1, null,
4321         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_DESC.get(),
4322         true, true,  true, false);
4323    checkUsabilityKeystore.addLongIdentifier("keystore-path", true);
4324    checkUsabilityKeystore.addLongIdentifier("keystorePath", true);
4325    checkUsabilityKeystore.addLongIdentifier("keystore-file", true);
4326    checkUsabilityKeystore.addLongIdentifier("keystoreFile", true);
4327    checkUsabilityParser.addArgument(checkUsabilityKeystore);
4328
4329    final StringArgument checkUsabilityKeystorePassword = new StringArgument(
4330         null, "keystore-password", false, 1,
4331         INFO_MANAGE_CERTS_PLACEHOLDER_PASSWORD.get(),
4332         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_PW_DESC.get());
4333    checkUsabilityKeystorePassword.addLongIdentifier("keystorePassword", true);
4334    checkUsabilityKeystorePassword.addLongIdentifier("keystore-passphrase",
4335         true);
4336    checkUsabilityKeystorePassword.addLongIdentifier("keystorePassphrase",
4337         true);
4338    checkUsabilityKeystorePassword.addLongIdentifier("keystore-pin", true);
4339    checkUsabilityKeystorePassword.addLongIdentifier("keystorePIN", true);
4340    checkUsabilityKeystorePassword.addLongIdentifier("storepass", true);
4341    checkUsabilityKeystorePassword.setSensitive(true);
4342    checkUsabilityParser.addArgument(checkUsabilityKeystorePassword);
4343
4344    final FileArgument checkUsabilityKeystorePasswordFile = new FileArgument(
4345         null, "keystore-password-file", false, 1, null,
4346         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_PW_FILE_DESC.get(), true,
4347         true, true, false);
4348    checkUsabilityKeystorePasswordFile.addLongIdentifier("keystorePasswordFile",
4349         true);
4350    checkUsabilityKeystorePasswordFile.addLongIdentifier(
4351         "keystore-passphrase-file", true);
4352    checkUsabilityKeystorePasswordFile.addLongIdentifier(
4353         "keystorePassphraseFile", true);
4354    checkUsabilityKeystorePasswordFile.addLongIdentifier("keystore-pin-file",
4355         true);
4356    checkUsabilityKeystorePasswordFile.addLongIdentifier("keystorePINFile",
4357         true);
4358    checkUsabilityParser.addArgument(checkUsabilityKeystorePasswordFile);
4359
4360    final BooleanArgument checkUsabilityPromptForKeystorePassword =
4361         new BooleanArgument(null, "prompt-for-keystore-password",
4362        INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_PROMPT_FOR_KS_PW_DESC.get());
4363    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
4364         "promptForKeystorePassword", true);
4365    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
4366         "prompt-for-keystore-passphrase", true);
4367    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
4368         "promptForKeystorePassphrase", true);
4369    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
4370         "prompt-for-keystore-pin", true);
4371    checkUsabilityPromptForKeystorePassword.addLongIdentifier(
4372         "promptForKeystorePIN", true);
4373    checkUsabilityParser.addArgument(checkUsabilityPromptForKeystorePassword);
4374
4375    final StringArgument checkUsabilityKeystoreType = new StringArgument(null,
4376         "keystore-type", false, 1, INFO_MANAGE_CERTS_PLACEHOLDER_TYPE.get(),
4377         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_KS_TYPE_DESC.get(),
4378         ALLOWED_KEYSTORE_TYPE_VALUES);
4379    checkUsabilityKeystoreType.addLongIdentifier("key-store-type", true);
4380    checkUsabilityKeystoreType.addLongIdentifier("keystoreType", true);
4381    checkUsabilityKeystoreType.addLongIdentifier("keystore-format", true);
4382    checkUsabilityKeystoreType.addLongIdentifier("key-store-format", true);
4383    checkUsabilityKeystoreType.addLongIdentifier("keystoreFormat", true);
4384    checkUsabilityKeystoreType.addLongIdentifier("storetype", true);
4385    checkUsabilityParser.addArgument(checkUsabilityKeystoreType);
4386
4387    final StringArgument checkUsabilityAlias = new StringArgument(null, "alias",
4388         true, 1, INFO_MANAGE_CERTS_PLACEHOLDER_ALIAS.get(),
4389         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_ARG_ALIAS_DESC.get());
4390    checkUsabilityAlias.addLongIdentifier("nickname", true);
4391    checkUsabilityParser.addArgument(checkUsabilityAlias);
4392
4393    final BooleanArgument checkUsabilityIgnoreSHA1Signature =
4394         new BooleanArgument(null,
4395              "allow-sha-1-signature-for-issuer-certificates", 1,
4396              INFO_MANAGE_CERTS_SC_CHECK_USABILITY_IGNORE_SHA1_WARNING_DESC.
4397                   get());
4398    checkUsabilityIgnoreSHA1Signature.addLongIdentifier(
4399         "allow-sha1-signature-for-issuer-certificates", true);
4400    checkUsabilityIgnoreSHA1Signature.addLongIdentifier(
4401         "allowSHA1SignatureForIssuerCertificates", true);
4402    checkUsabilityParser.addArgument(checkUsabilityIgnoreSHA1Signature);
4403
4404    checkUsabilityParser.addRequiredArgumentSet(checkUsabilityKeystorePassword,
4405         checkUsabilityKeystorePasswordFile,
4406         checkUsabilityPromptForKeystorePassword);
4407    checkUsabilityParser.addExclusiveArgumentSet(checkUsabilityKeystorePassword,
4408         checkUsabilityKeystorePasswordFile,
4409         checkUsabilityPromptForKeystorePassword);
4410
4411    final LinkedHashMap<String[],String> checkUsabilityExamples =
4412         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
4413    checkUsabilityExamples.put(
4414         new String[]
4415         {
4416           "check-certificate-usability",
4417           "--keystore", getPlatformSpecificPath("config", "keystore"),
4418           "--keystore-password-file",
4419                getPlatformSpecificPath("config", "keystore.pin"),
4420           "--alias", "server-cert"
4421         },
4422         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_EXAMPLE_1.get(
4423              getPlatformSpecificPath("config", "keystore")));
4424
4425    final SubCommand checkUsabilitySubCommand = new SubCommand(
4426         "check-certificate-usability",
4427         INFO_MANAGE_CERTS_SC_CHECK_USABILITY_DESC.get(), checkUsabilityParser,
4428         checkUsabilityExamples);
4429    checkUsabilitySubCommand.addName("checkCertificateUsability", true);
4430    checkUsabilitySubCommand.addName("check-usability", true);
4431    checkUsabilitySubCommand.addName("checkUsability", true);
4432
4433    parser.addSubCommand(checkUsabilitySubCommand);
4434
4435
4436    // Define the "display-certificate-file" subcommand and all of its
4437    // arguments.
4438    final ArgumentParser displayCertParser = new ArgumentParser(
4439         "display-certificate-file",
4440         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_DESC.get());
4441
4442    final FileArgument displayCertFile = new FileArgument(null,
4443         "certificate-file", true, 1, null,
4444         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_ARG_FILE_DESC.get(), true, true,
4445         true, false);
4446    displayCertFile.addLongIdentifier("certificateFile", true);
4447    displayCertFile.addLongIdentifier("input-file", true);
4448    displayCertFile.addLongIdentifier("inputFile", true);
4449    displayCertFile.addLongIdentifier("file", true);
4450    displayCertFile.addLongIdentifier("filename", true);
4451    displayCertParser.addArgument(displayCertFile);
4452
4453    final BooleanArgument displayCertVerbose = new BooleanArgument(null,
4454         "verbose", 1,
4455         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_ARG_VERBOSE_DESC.get());
4456    displayCertParser.addArgument(displayCertVerbose);
4457
4458    final BooleanArgument displayCertDisplayCommand = new BooleanArgument(null,
4459         "display-keytool-command", 1,
4460         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_ARG_DISPLAY_COMMAND_DESC.get());
4461    displayCertDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
4462    displayCertDisplayCommand.addLongIdentifier("show-keytool-command", true);
4463    displayCertDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
4464    displayCertParser.addArgument(displayCertDisplayCommand);
4465
4466    final LinkedHashMap<String[],String> displayCertExamples =
4467         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
4468    displayCertExamples.put(
4469         new String[]
4470         {
4471           "display-certificate-file",
4472           "--certificate-file", "certificate.pem",
4473         },
4474         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_EXAMPLE_1.get("certificate.pem"));
4475    displayCertExamples.put(
4476         new String[]
4477         {
4478           "display-certificate-file",
4479           "--certificate-file", "certificate.pem",
4480           "--verbose",
4481           "--display-keytool-command"
4482         },
4483         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_EXAMPLE_2.get("certificate.pem"));
4484
4485    final SubCommand displayCertSubCommand = new SubCommand(
4486         "display-certificate-file",
4487         INFO_MANAGE_CERTS_SC_DISPLAY_CERT_DESC.get(), displayCertParser,
4488         displayCertExamples);
4489    displayCertSubCommand.addName("displayCertificateFile", true);
4490    displayCertSubCommand.addName("display-certificate", true);
4491    displayCertSubCommand.addName("displayCertificate", true);
4492    displayCertSubCommand.addName("display-certificates", true);
4493    displayCertSubCommand.addName("displayCertificates", true);
4494    displayCertSubCommand.addName("show-certificate", true);
4495    displayCertSubCommand.addName("showCertificate", true);
4496    displayCertSubCommand.addName("show-certificate-file", true);
4497    displayCertSubCommand.addName("showCertificateFile", true);
4498    displayCertSubCommand.addName("show-certificates", true);
4499    displayCertSubCommand.addName("showCertificates", true);
4500    displayCertSubCommand.addName("print-certificate-file", true);
4501    displayCertSubCommand.addName("printCertificateFile", true);
4502    displayCertSubCommand.addName("print-certificate", true);
4503    displayCertSubCommand.addName("printCertificate", true);
4504    displayCertSubCommand.addName("print-certificates", true);
4505    displayCertSubCommand.addName("printCertificates", true);
4506    displayCertSubCommand.addName("printcert", true);
4507
4508    parser.addSubCommand(displayCertSubCommand);
4509
4510
4511    // Define the "display-certificate-signing-request-file" subcommand and all
4512    // of its arguments.
4513    final ArgumentParser displayCSRParser = new ArgumentParser(
4514         "display-certificate-signing-request-file",
4515         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_DESC.get());
4516
4517    final FileArgument displayCSRFile = new FileArgument(null,
4518         "certificate-signing-request-file", true, 1, null,
4519         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_ARG_FILE_DESC.get(), true, true,
4520         true, false);
4521    displayCSRFile.addLongIdentifier("certificateSigningRequestFile", true);
4522    displayCSRFile.addLongIdentifier("request-file", true);
4523    displayCSRFile.addLongIdentifier("requestFile", true);
4524    displayCSRFile.addLongIdentifier("input-file", true);
4525    displayCSRFile.addLongIdentifier("inputFile", true);
4526    displayCSRFile.addLongIdentifier("file", true);
4527    displayCSRFile.addLongIdentifier("filename", true);
4528    displayCSRParser.addArgument(displayCSRFile);
4529
4530    final BooleanArgument displayCSRVerbose = new BooleanArgument(null,
4531         "verbose", 1,
4532         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_ARG_VERBOSE_DESC.get());
4533    displayCSRParser.addArgument(displayCSRVerbose);
4534
4535    final BooleanArgument displayCSRDisplayCommand = new BooleanArgument(null,
4536         "display-keytool-command", 1,
4537         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_ARG_DISPLAY_COMMAND_DESC.get());
4538    displayCSRDisplayCommand.addLongIdentifier("displayKeytoolCommand", true);
4539    displayCSRDisplayCommand.addLongIdentifier("show-keytool-command", true);
4540    displayCSRDisplayCommand.addLongIdentifier("showKeytoolCommand", true);
4541    displayCSRParser.addArgument(displayCSRDisplayCommand);
4542
4543    final LinkedHashMap<String[],String> displayCSRExamples =
4544         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
4545    displayCSRExamples.put(
4546         new String[]
4547         {
4548           "display-certificate-signing-request-file",
4549           "--certificate-signing-request-file", "server-cert.csr",
4550           "--display-keytool-command"
4551         },
4552         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_EXAMPLE_1.get("server-cert.csr"));
4553
4554    final SubCommand displayCSRSubCommand = new SubCommand(
4555         "display-certificate-signing-request-file",
4556         INFO_MANAGE_CERTS_SC_DISPLAY_CSR_DESC.get(), displayCSRParser,
4557         displayCSRExamples);
4558    displayCSRSubCommand.addName("displayCertificateSigningRequestFile", true);
4559    displayCSRSubCommand.addName("display-certificate-signing-request", true);
4560    displayCSRSubCommand.addName("displayCertificateSigningRequest", true);
4561    displayCSRSubCommand.addName("display-certificate-request-file", true);
4562    displayCSRSubCommand.addName("displayCertificateRequestFile", true);
4563    displayCSRSubCommand.addName("display-certificate-request", true);
4564    displayCSRSubCommand.addName("displayCertificateRequest", true);
4565    displayCSRSubCommand.addName("display-csr-file", true);
4566    displayCSRSubCommand.addName("displayCSRFile", true);
4567    displayCSRSubCommand.addName("display-csr", true);
4568    displayCSRSubCommand.addName("displayCSR", true);
4569    displayCSRSubCommand.addName("show-certificate-signing-request-file", true);
4570    displayCSRSubCommand.addName("showCertificateSigningRequestFile", true);
4571    displayCSRSubCommand.addName("show-certificate-signing-request", true);
4572    displayCSRSubCommand.addName("showCertificateSigningRequest", true);
4573    displayCSRSubCommand.addName("show-certificate-request-file", true);
4574    displayCSRSubCommand.addName("showCertificateRequestFile", true);
4575    displayCSRSubCommand.addName("show-certificate-request", true);
4576    displayCSRSubCommand.addName("showCertificateRequest", true);
4577    displayCSRSubCommand.addName("show-csr-file", true);
4578    displayCSRSubCommand.addName("showCSRFile", true);
4579    displayCSRSubCommand.addName("show-csr", true);
4580    displayCSRSubCommand.addName("showCSR", true);
4581    displayCSRSubCommand.addName("print-certificate-signing-request-file",
4582         true);
4583    displayCSRSubCommand.addName("printCertificateSigningRequestFile", true);
4584    displayCSRSubCommand.addName("print-certificate-signing-request", true);
4585    displayCSRSubCommand.addName("printCertificateSigningRequest", true);
4586    displayCSRSubCommand.addName("print-certificate-request-file", true);
4587    displayCSRSubCommand.addName("printCertificateRequestFile", true);
4588    displayCSRSubCommand.addName("print-certificate-request", true);
4589    displayCSRSubCommand.addName("printCertificateRequest", true);
4590    displayCSRSubCommand.addName("print-csr-file", true);
4591    displayCSRSubCommand.addName("printCSRFile", true);
4592    displayCSRSubCommand.addName("print-csr", true);
4593    displayCSRSubCommand.addName("printCSR", true);
4594    displayCSRSubCommand.addName("printcertreq", true);
4595
4596    parser.addSubCommand(displayCSRSubCommand);
4597  }
4598
4599
4600
4601  /**
4602   * Constructs a platform-specific relative path from the provided elements.
4603   *
4604   * @param  pathElements  The elements of the path to construct.  It must not
4605   *                       be {@code null} or empty.
4606   *
4607   * @return  The constructed path.
4608   */
4609  @NotNull()
4610  private static String getPlatformSpecificPath(
4611                             @NotNull final String... pathElements)
4612  {
4613    final StringBuilder buffer = new StringBuilder();
4614    for (int i=0; i < pathElements.length; i++)
4615    {
4616      if (i > 0)
4617      {
4618        buffer.append(File.separatorChar);
4619      }
4620
4621      buffer.append(pathElements[i]);
4622    }
4623
4624    return buffer.toString();
4625  }
4626
4627
4628
4629  /**
4630   * Performs the core set of processing for this tool.
4631   *
4632   * @return  A result code that indicates whether the processing completed
4633   *          successfully.
4634   */
4635  @Override()
4636  @NotNull()
4637  public ResultCode doToolProcessing()
4638  {
4639    final SubCommand selectedSubCommand = globalParser.getSelectedSubCommand();
4640    if (selectedSubCommand == null)
4641    {
4642      // This should never happen.
4643      wrapErr(0, WRAP_COLUMN, ERR_MANAGE_CERTS_NO_SUBCOMMAND.get());
4644      return ResultCode.PARAM_ERROR;
4645    }
4646
4647    subCommandParser = selectedSubCommand.getArgumentParser();
4648
4649    if (selectedSubCommand.hasName("list-certificates"))
4650    {
4651      return doListCertificates();
4652    }
4653    else if (selectedSubCommand.hasName("export-certificate"))
4654    {
4655      return doExportCertificate();
4656    }
4657    else if (selectedSubCommand.hasName("export-private-key"))
4658    {
4659      return doExportPrivateKey();
4660    }
4661    else if (selectedSubCommand.hasName("import-certificate"))
4662    {
4663      return doImportCertificate();
4664    }
4665    else if (selectedSubCommand.hasName("delete-certificate"))
4666    {
4667      return doDeleteCertificate();
4668    }
4669    else if (selectedSubCommand.hasName("generate-self-signed-certificate"))
4670    {
4671      return doGenerateOrSignCertificateOrCSR();
4672    }
4673    else if (selectedSubCommand.hasName("generate-certificate-signing-request"))
4674    {
4675      return doGenerateOrSignCertificateOrCSR();
4676    }
4677    else if (selectedSubCommand.hasName("sign-certificate-signing-request"))
4678    {
4679      return doGenerateOrSignCertificateOrCSR();
4680    }
4681    else if (selectedSubCommand.hasName("change-certificate-alias"))
4682    {
4683      return doChangeCertificateAlias();
4684    }
4685    else if (selectedSubCommand.hasName("change-keystore-password"))
4686    {
4687      return doChangeKeystorePassword();
4688    }
4689    else if (selectedSubCommand.hasName("change-private-key-password"))
4690    {
4691      return doChangePrivateKeyPassword();
4692    }
4693    else if (selectedSubCommand.hasName("copy-keystore"))
4694    {
4695      return doCopyKeystore();
4696    }
4697    else if (selectedSubCommand.hasName("retrieve-server-certificate"))
4698    {
4699      return doRetrieveServerCertificate();
4700    }
4701    else if (selectedSubCommand.hasName("trust-server-certificate"))
4702    {
4703      return doTrustServerCertificate();
4704    }
4705    else if (selectedSubCommand.hasName("check-certificate-usability"))
4706    {
4707      return doCheckCertificateUsability();
4708    }
4709    else if (selectedSubCommand.hasName("display-certificate-file"))
4710    {
4711      return doDisplayCertificateFile();
4712    }
4713    else if (selectedSubCommand.hasName(
4714         "display-certificate-signing-request-file"))
4715    {
4716      return doDisplayCertificateSigningRequestFile();
4717    }
4718    else
4719    {
4720      // This should never happen.
4721      wrapErr(0, WRAP_COLUMN,
4722           ERR_MANAGE_CERTS_UNKNOWN_SUBCOMMAND.get(
4723                selectedSubCommand.getPrimaryName()));
4724      return ResultCode.PARAM_ERROR;
4725    }
4726  }
4727
4728
4729
4730  /**
4731   * Performs the necessary processing for the list-certificates subcommand.
4732   *
4733   * @return  A result code that indicates whether the processing completed
4734   *          successfully.
4735   */
4736  @NotNull()
4737  private ResultCode doListCertificates()
4738  {
4739    // Get the values of a number of configured arguments.
4740    final BooleanArgument displayPEMArgument =
4741         subCommandParser.getBooleanArgument("display-pem-certificate");
4742    final boolean displayPEM =
4743         ((displayPEMArgument != null) && displayPEMArgument.isPresent());
4744
4745    final BooleanArgument verboseArgument =
4746         subCommandParser.getBooleanArgument("verbose");
4747    final boolean verbose =
4748         ((verboseArgument != null) && verboseArgument.isPresent());
4749
4750    final Map<String,String> missingAliases;
4751    final Set<String> aliases;
4752    final StringArgument aliasArgument =
4753         subCommandParser.getStringArgument("alias");
4754    if ((aliasArgument == null) || (! aliasArgument.isPresent()))
4755    {
4756      aliases = Collections.emptySet();
4757      missingAliases = Collections.emptyMap();
4758    }
4759    else
4760    {
4761      final List<String> values = aliasArgument.getValues();
4762      aliases = new LinkedHashSet<>(StaticUtils.computeMapCapacity(
4763           values.size()));
4764      missingAliases =
4765           new LinkedHashMap<>(StaticUtils.computeMapCapacity(values.size()));
4766      for (final String alias : values)
4767      {
4768        final String lowerAlias = StaticUtils.toLowerCase(alias);
4769        aliases.add(StaticUtils.toLowerCase(lowerAlias));
4770        missingAliases.put(lowerAlias, alias);
4771      }
4772    }
4773
4774    final String keystoreType;
4775    final File keystorePath = getKeystorePath();
4776    try
4777    {
4778      keystoreType = inferKeystoreType(keystorePath);
4779    }
4780    catch (final LDAPException le)
4781    {
4782      Debug.debugException(le);
4783      wrapErr(0, WRAP_COLUMN, le.getMessage());
4784      return le.getResultCode();
4785    }
4786
4787    final char[] keystorePassword;
4788    try
4789    {
4790      keystorePassword = getKeystorePassword(keystorePath);
4791    }
4792    catch (final LDAPException le)
4793    {
4794      Debug.debugException(le);
4795      wrapErr(0, WRAP_COLUMN, le.getMessage());
4796      return le.getResultCode();
4797    }
4798
4799    final BooleanArgument displayKeytoolCommandArgument =
4800         subCommandParser.getBooleanArgument("display-keytool-command");
4801    if ((displayKeytoolCommandArgument != null) &&
4802        displayKeytoolCommandArgument.isPresent())
4803    {
4804      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
4805      keytoolArgs.add("-list");
4806
4807      keytoolArgs.add("-keystore");
4808      keytoolArgs.add(keystorePath.getAbsolutePath());
4809      keytoolArgs.add("-storetype");
4810      keytoolArgs.add(keystoreType);
4811
4812      if (keystorePassword != null)
4813      {
4814        keytoolArgs.add("-storepass");
4815        keytoolArgs.add("*****REDACTED*****");
4816      }
4817
4818      for (final String alias : missingAliases.values())
4819      {
4820        keytoolArgs.add("-alias");
4821        keytoolArgs.add(alias);
4822      }
4823
4824      if (displayPEM)
4825      {
4826        keytoolArgs.add("-rfc");
4827      }
4828
4829      if (verbose)
4830      {
4831        keytoolArgs.add("-v");
4832      }
4833
4834      displayKeytoolCommand(keytoolArgs);
4835    }
4836
4837
4838    // Get the keystore.
4839    final KeyStore keystore;
4840    try
4841    {
4842      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
4843    }
4844    catch (final LDAPException le)
4845    {
4846      Debug.debugException(le);
4847      wrapErr(0, WRAP_COLUMN, le.getMessage());
4848      return le.getResultCode();
4849    }
4850
4851
4852    // Display a message with the keystore type.
4853    wrapOut(0, WRAP_COLUMN,
4854         INFO_MANAGE_CERTS_LIST_KEYSTORE_TYPE.get(keystoreType));
4855
4856
4857    // Iterate through the keystore and display the appropriate certificates.
4858    final Enumeration<String> aliasEnumeration;
4859    try
4860    {
4861      aliasEnumeration = keystore.aliases();
4862    }
4863    catch (final Exception e)
4864    {
4865      Debug.debugException(e);
4866      err();
4867      wrapErr(0, WRAP_COLUMN,
4868           ERR_MANAGE_CERTS_LIST_CERTS_CANNOT_GET_ALIASES.get(
4869                keystorePath.getAbsolutePath()));
4870      e.printStackTrace(getErr());
4871      return ResultCode.LOCAL_ERROR;
4872    }
4873
4874    int listedCount = 0;
4875    ResultCode resultCode = ResultCode.SUCCESS;
4876    while (aliasEnumeration.hasMoreElements())
4877    {
4878      final String alias = aliasEnumeration.nextElement();
4879      final String lowerAlias = StaticUtils.toLowerCase(alias);
4880      if ((!aliases.isEmpty()) && (missingAliases.remove(lowerAlias) == null))
4881      {
4882        // We don't care about this alias.
4883        continue;
4884      }
4885
4886      final X509Certificate[] certificateChain;
4887      try
4888      {
4889        // NOTE:  Keystore entries that have private keys may have a certificate
4890        // chain associated with them (the end certificate plus all of the
4891        // issuer certificates).  In that case all of those certificates in the
4892        // chain will be stored under the same alias, and the only way we can
4893        // access them is to call the getCertificateChain method.  However, if
4894        // the keystore only has a certificate for the alias but no private key,
4895        // then the entry will not have a chain, and the call to
4896        // getCertificateChain will return null for that alias.  We want to be
4897        // able to handle both of these cases, so we will first try
4898        // getCertificateChain to see if we can get a complete chain, but if
4899        // that returns null, then use getCertificate to see if we can get a
4900        // single certificate.  That call to getCertificate can also return null
4901        // because the entry with this alias might be some other type of entry,
4902        // like a secret key entry.
4903        Certificate[] chain = keystore.getCertificateChain(alias);
4904        if ((chain == null) || (chain.length == 0))
4905        {
4906          final Certificate cert = keystore.getCertificate(alias);
4907          if (cert == null)
4908          {
4909            continue;
4910          }
4911          else
4912          {
4913            chain = new Certificate[] { cert };
4914          }
4915        }
4916
4917        certificateChain = new X509Certificate[chain.length];
4918        for (int i = 0; i < chain.length; i++)
4919        {
4920          certificateChain[i] = new X509Certificate(chain[i].getEncoded());
4921        }
4922      }
4923      catch (final Exception e)
4924      {
4925        Debug.debugException(e);
4926        err();
4927        wrapErr(0, WRAP_COLUMN,
4928             ERR_MANAGE_CERTS_LIST_CERTS_ERROR_GETTING_CERT.get(alias,
4929                  StaticUtils.getExceptionMessage(e)));
4930        resultCode = ResultCode.LOCAL_ERROR;
4931        continue;
4932      }
4933
4934      listedCount++;
4935      for (int i = 0; i < certificateChain.length; i++)
4936      {
4937        out();
4938        if (certificateChain.length == 1)
4939        {
4940          out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_ALIAS_WITHOUT_CHAIN.get(
4941               alias));
4942        }
4943        else
4944        {
4945          out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_ALIAS_WITH_CHAIN.get(alias,
4946               (i + 1), certificateChain.length));
4947        }
4948
4949        printCertificate(certificateChain[i], "", verbose);
4950
4951        if (i == 0)
4952        {
4953          if (hasKeyAlias(keystore, alias))
4954          {
4955            out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_HAS_PK_YES.get());
4956          }
4957          else
4958          {
4959            out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_HAS_PK_NO.get());
4960          }
4961        }
4962
4963        CertException signatureVerificationException = null;
4964        if (certificateChain[i].isSelfSigned())
4965        {
4966          try
4967          {
4968            certificateChain[i].verifySignature(null);
4969          }
4970          catch (final CertException ce)
4971          {
4972            Debug.debugException(ce);
4973            signatureVerificationException = ce;
4974          }
4975        }
4976        else
4977        {
4978          X509Certificate issuerCertificate = null;
4979          try
4980          {
4981            final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
4982                 new AtomicReference<>();
4983            final AtomicReference<DN> missingIssuerRef =
4984                 new AtomicReference<>();
4985            issuerCertificate = getIssuerCertificate(certificateChain[i],
4986                 keystore, jvmDefaultTrustStoreRef, missingIssuerRef);
4987          }
4988          catch (final Exception e)
4989          {
4990            Debug.debugException(e);
4991          }
4992
4993          if (issuerCertificate == null)
4994          {
4995            signatureVerificationException = new CertException(
4996                 ERR_MANAGE_CERTS_LIST_CERTS_VERIFY_SIGNATURE_NO_ISSUER.get(
4997                      certificateChain[i].getIssuerDN()));
4998          }
4999          else
5000          {
5001            try
5002            {
5003              certificateChain[i].verifySignature(issuerCertificate);
5004            }
5005            catch (final CertException ce)
5006            {
5007              Debug.debugException(ce);
5008              signatureVerificationException = ce;
5009            }
5010          }
5011        }
5012
5013        if (signatureVerificationException == null)
5014        {
5015          wrapOut(0, WRAP_COLUMN,
5016               INFO_MANAGE_CERTS_LIST_CERTS_SIGNATURE_VALID.get());
5017        }
5018        else
5019        {
5020          wrapErr(0, WRAP_COLUMN,
5021               signatureVerificationException.getMessage());
5022        }
5023
5024        if (displayPEM)
5025        {
5026          out(INFO_MANAGE_CERTS_LIST_CERTS_LABEL_PEM.get());
5027          writePEMCertificate(getOut(),
5028               certificateChain[i].getX509CertificateBytes());
5029        }
5030      }
5031    }
5032
5033    if (! missingAliases.isEmpty())
5034    {
5035      err();
5036      for (final String missingAlias : missingAliases.values())
5037      {
5038        wrapErr(0, WRAP_COLUMN,
5039             WARN_MANAGE_CERTS_LIST_CERTS_ALIAS_NOT_IN_KS.get(missingAlias,
5040                  keystorePath.getAbsolutePath()));
5041        resultCode = ResultCode.PARAM_ERROR;
5042      }
5043    }
5044    else if (listedCount == 0)
5045    {
5046      out();
5047      if (keystorePassword == null)
5048      {
5049        wrapOut(0, WRAP_COLUMN,
5050             INFO_MANAGE_CERTS_LIST_CERTS_NO_CERTS_OR_KEYS_WITHOUT_PW.get());
5051      }
5052      else
5053      {
5054        wrapOut(0, WRAP_COLUMN,
5055             INFO_MANAGE_CERTS_LIST_CERTS_NO_CERTS_OR_KEYS_WITH_PW.get());
5056      }
5057    }
5058
5059    return resultCode;
5060  }
5061
5062
5063
5064  /**
5065   * Performs the necessary processing for the export-certificate subcommand.
5066   *
5067   * @return  A result code that indicates whether the processing completed
5068   *          successfully.
5069   */
5070  @NotNull()
5071  private ResultCode doExportCertificate()
5072  {
5073    // Get the values of a number of configured arguments.
5074    final StringArgument aliasArgument =
5075         subCommandParser.getStringArgument("alias");
5076    final String alias = aliasArgument.getValue();
5077
5078    final BooleanArgument exportChainArgument =
5079         subCommandParser.getBooleanArgument("export-certificate-chain");
5080    final boolean exportChain =
5081         ((exportChainArgument != null) && exportChainArgument.isPresent());
5082
5083    final BooleanArgument separateFilePerCertificateArgument =
5084         subCommandParser.getBooleanArgument("separate-file-per-certificate");
5085    final boolean separateFilePerCertificate =
5086         ((separateFilePerCertificateArgument != null) &&
5087          separateFilePerCertificateArgument.isPresent());
5088
5089    boolean exportPEM = true;
5090    final StringArgument outputFormatArgument =
5091         subCommandParser.getStringArgument("output-format");
5092    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
5093    {
5094      final String format = outputFormatArgument.getValue().toLowerCase();
5095      if (format.equals("der") || format.equals("binary") ||
5096          format.equals("bin"))
5097      {
5098        exportPEM = false;
5099      }
5100    }
5101
5102    File outputFile = null;
5103    final FileArgument outputFileArgument =
5104         subCommandParser.getFileArgument("output-file");
5105    if ((outputFileArgument != null) && outputFileArgument.isPresent())
5106    {
5107      outputFile = outputFileArgument.getValue();
5108    }
5109
5110    if ((outputFile == null) && (! exportPEM))
5111    {
5112      wrapErr(0, WRAP_COLUMN,
5113           ERR_MANAGE_CERTS_EXPORT_CERT_NO_FILE_WITH_DER.get());
5114      return ResultCode.PARAM_ERROR;
5115    }
5116
5117    final String keystoreType;
5118    final File keystorePath = getKeystorePath();
5119    try
5120    {
5121      keystoreType = inferKeystoreType(keystorePath);
5122    }
5123    catch (final LDAPException le)
5124    {
5125      Debug.debugException(le);
5126      wrapErr(0, WRAP_COLUMN, le.getMessage());
5127      return le.getResultCode();
5128    }
5129
5130    final char[] keystorePassword;
5131    try
5132    {
5133      keystorePassword = getKeystorePassword(keystorePath);
5134    }
5135    catch (final LDAPException le)
5136    {
5137      Debug.debugException(le);
5138      wrapErr(0, WRAP_COLUMN, le.getMessage());
5139      return le.getResultCode();
5140    }
5141
5142    final BooleanArgument displayKeytoolCommandArgument =
5143         subCommandParser.getBooleanArgument("display-keytool-command");
5144    if ((displayKeytoolCommandArgument != null) &&
5145        displayKeytoolCommandArgument.isPresent())
5146    {
5147      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
5148      keytoolArgs.add("-list");
5149
5150      keytoolArgs.add("-keystore");
5151      keytoolArgs.add(keystorePath.getAbsolutePath());
5152      keytoolArgs.add("-storetype");
5153      keytoolArgs.add(keystoreType);
5154
5155      if (keystorePassword != null)
5156      {
5157        keytoolArgs.add("-storepass");
5158        keytoolArgs.add("*****REDACTED*****");
5159      }
5160
5161      keytoolArgs.add("-alias");
5162      keytoolArgs.add(alias);
5163
5164      if (exportPEM)
5165      {
5166        keytoolArgs.add("-rfc");
5167      }
5168
5169      if (outputFile != null)
5170      {
5171        keytoolArgs.add("-file");
5172        keytoolArgs.add(outputFile.getAbsolutePath());
5173      }
5174
5175      displayKeytoolCommand(keytoolArgs);
5176    }
5177
5178
5179    // Get the keystore.
5180    final KeyStore keystore;
5181    try
5182    {
5183      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
5184    }
5185    catch (final LDAPException le)
5186    {
5187      Debug.debugException(le);
5188      wrapErr(0, WRAP_COLUMN, le.getMessage());
5189      return le.getResultCode();
5190    }
5191
5192
5193    // Get the certificates to export.  If the --export-certificate-chain
5194    // argument was provided, this can be multiple certificates.  Otherwise, it
5195    // there will only be one.
5196    DN missingIssuerDN = null;
5197    final X509Certificate[] certificatesToExport;
5198    if (exportChain)
5199    {
5200      try
5201      {
5202        final AtomicReference<DN> missingIssuerRef = new AtomicReference<>();
5203        certificatesToExport =
5204             getCertificateChain(alias, keystore, missingIssuerRef);
5205        missingIssuerDN = missingIssuerRef.get();
5206      }
5207      catch (final LDAPException le)
5208      {
5209        Debug.debugException(le);
5210        wrapErr(0, WRAP_COLUMN, le.getMessage());
5211        return le.getResultCode();
5212      }
5213    }
5214    else
5215    {
5216      try
5217      {
5218        final Certificate cert = keystore.getCertificate(alias);
5219        if (cert == null)
5220        {
5221          certificatesToExport = new X509Certificate[0];
5222        }
5223        else
5224        {
5225          certificatesToExport = new X509Certificate[]
5226          {
5227            new X509Certificate(cert.getEncoded())
5228          };
5229        }
5230      }
5231      catch (final Exception e)
5232      {
5233        Debug.debugException(e);
5234        wrapErr(0, WRAP_COLUMN,
5235             ERR_MANAGE_CERTS_EXPORT_CERT_ERROR_GETTING_CERT.get(alias,
5236                  keystorePath.getAbsolutePath()));
5237        e.printStackTrace(getErr());
5238        return ResultCode.LOCAL_ERROR;
5239      }
5240    }
5241
5242    if (certificatesToExport.length == 0)
5243    {
5244      wrapErr(0, WRAP_COLUMN,
5245           ERR_MANAGE_CERTS_EXPORT_CERT_NO_CERT_WITH_ALIAS.get(alias,
5246                keystorePath.getAbsolutePath()));
5247      return ResultCode.PARAM_ERROR;
5248    }
5249
5250
5251    // Get a PrintStream to use for the output.
5252    int fileCounter = 1;
5253    String filename = null;
5254    PrintStream printStream;
5255    if (outputFile == null)
5256    {
5257      printStream = getOut();
5258    }
5259    else
5260    {
5261      try
5262      {
5263        if ((certificatesToExport.length > 1) && separateFilePerCertificate)
5264        {
5265          filename = outputFile.getAbsolutePath() + '.' + fileCounter;
5266        }
5267        else
5268        {
5269          filename = outputFile.getAbsolutePath();
5270        }
5271        printStream = new PrintStream(filename);
5272      }
5273      catch (final Exception e)
5274      {
5275        Debug.debugException(e);
5276        wrapErr(0, WRAP_COLUMN,
5277             ERR_MANAGE_CERTS_EXPORT_CERT_ERROR_OPENING_OUTPUT.get(
5278                  outputFile.getAbsolutePath()));
5279        e.printStackTrace(getErr());
5280        return ResultCode.LOCAL_ERROR;
5281      }
5282    }
5283
5284    try
5285    {
5286      for (final X509Certificate certificate : certificatesToExport)
5287      {
5288        try
5289        {
5290          if (separateFilePerCertificate && (certificatesToExport.length > 1))
5291          {
5292            if (fileCounter > 1)
5293            {
5294              printStream.close();
5295              filename = outputFile.getAbsolutePath() + '.' + fileCounter;
5296              printStream = new PrintStream(filename);
5297            }
5298
5299            fileCounter++;
5300          }
5301
5302          if (exportPEM)
5303          {
5304            writePEMCertificate(printStream,
5305                 certificate.getX509CertificateBytes());
5306          }
5307          else
5308          {
5309            printStream.write(certificate.getX509CertificateBytes());
5310          }
5311        }
5312        catch (final Exception e)
5313        {
5314          Debug.debugException(e);
5315          wrapErr(0, WRAP_COLUMN,
5316               ERR_MANAGE_CERTS_EXPORT_CERT_ERROR_WRITING_CERT.get(alias,
5317                    certificate.getSubjectDN()));
5318          e.printStackTrace(getErr());
5319          return ResultCode.LOCAL_ERROR;
5320        }
5321
5322        if (outputFile != null)
5323        {
5324          out();
5325          wrapOut(0, WRAP_COLUMN,
5326               INFO_MANAGE_CERTS_EXPORT_CERT_EXPORT_SUCCESSFUL.get(filename));
5327          printCertificate(certificate, "", false);
5328        }
5329      }
5330    }
5331    finally
5332    {
5333      printStream.flush();
5334      if (outputFile != null)
5335      {
5336        printStream.close();
5337      }
5338    }
5339
5340    if (missingIssuerDN != null)
5341    {
5342      err();
5343      wrapErr(0, WRAP_COLUMN,
5344           WARN_MANAGE_CERTS_EXPORT_CERT_MISSING_CERT_IN_CHAIN.get(
5345                missingIssuerDN, keystorePath.getAbsolutePath()));
5346      return ResultCode.NO_SUCH_OBJECT;
5347    }
5348
5349    return ResultCode.SUCCESS;
5350  }
5351
5352
5353
5354  /**
5355   * Performs the necessary processing for the export-private-key subcommand.
5356   *
5357   * @return  A result code that indicates whether the processing completed
5358   *          successfully.
5359   */
5360  @NotNull()
5361  private ResultCode doExportPrivateKey()
5362  {
5363    // Get the values of a number of configured arguments.
5364    final StringArgument aliasArgument =
5365         subCommandParser.getStringArgument("alias");
5366    final String alias = aliasArgument.getValue();
5367
5368    boolean exportPEM = true;
5369    final StringArgument outputFormatArgument =
5370         subCommandParser.getStringArgument("output-format");
5371    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
5372    {
5373      final String format = outputFormatArgument.getValue().toLowerCase();
5374      if (format.equals("der") || format.equals("binary") ||
5375          format.equals("bin"))
5376      {
5377        exportPEM = false;
5378      }
5379    }
5380
5381    File outputFile = null;
5382    final FileArgument outputFileArgument =
5383         subCommandParser.getFileArgument("output-file");
5384    if ((outputFileArgument != null) && outputFileArgument.isPresent())
5385    {
5386      outputFile = outputFileArgument.getValue();
5387    }
5388
5389    if ((outputFile == null) && (! exportPEM))
5390    {
5391      wrapErr(0, WRAP_COLUMN,
5392           ERR_MANAGE_CERTS_EXPORT_KEY_NO_FILE_WITH_DER.get());
5393      return ResultCode.PARAM_ERROR;
5394    }
5395
5396    final String keystoreType;
5397    final File keystorePath = getKeystorePath();
5398    try
5399    {
5400      keystoreType = inferKeystoreType(keystorePath);
5401    }
5402    catch (final LDAPException le)
5403    {
5404      Debug.debugException(le);
5405      wrapErr(0, WRAP_COLUMN, le.getMessage());
5406      return le.getResultCode();
5407    }
5408
5409    final char[] keystorePassword;
5410    try
5411    {
5412      keystorePassword = getKeystorePassword(keystorePath);
5413    }
5414    catch (final LDAPException le)
5415    {
5416      Debug.debugException(le);
5417      wrapErr(0, WRAP_COLUMN, le.getMessage());
5418      return le.getResultCode();
5419    }
5420
5421
5422    // Get the keystore.
5423    final KeyStore keystore;
5424    try
5425    {
5426      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
5427    }
5428    catch (final LDAPException le)
5429    {
5430      Debug.debugException(le);
5431      wrapErr(0, WRAP_COLUMN, le.getMessage());
5432      return le.getResultCode();
5433    }
5434
5435
5436    // See if we need to use a private key password that is different from the
5437    // keystore password.
5438    final char[] privateKeyPassword;
5439    try
5440    {
5441      privateKeyPassword =
5442           getPrivateKeyPassword(keystore, alias, keystorePassword);
5443    }
5444    catch (final LDAPException le)
5445    {
5446      Debug.debugException(le);
5447      wrapErr(0, WRAP_COLUMN, le.getMessage());
5448      return le.getResultCode();
5449    }
5450
5451
5452    // Get the password to use to encrypt the private key when it is exported.
5453    final char[] encryptionPassword;
5454    try
5455    {
5456      encryptionPassword = getPrivateKeyEncryptionPassword(false);
5457    }
5458    catch (final LDAPException le)
5459    {
5460      Debug.debugException(le);
5461      wrapErr(0, WRAP_COLUMN, le.getMessage());
5462      return le.getResultCode();
5463    }
5464
5465
5466    // Get the private key to export.
5467    final PrivateKey privateKey;
5468    try
5469    {
5470      final Key key = keystore.getKey(alias, privateKeyPassword);
5471      if (key == null)
5472      {
5473        wrapErr(0, WRAP_COLUMN,
5474             ERR_MANAGE_CERTS_EXPORT_KEY_NO_KEY_WITH_ALIAS.get(alias,
5475                  keystorePath.getAbsolutePath()));
5476        return ResultCode.PARAM_ERROR;
5477      }
5478
5479      privateKey = (PrivateKey) key;
5480    }
5481    catch (final UnrecoverableKeyException e)
5482    {
5483      Debug.debugException(e);
5484      wrapErr(0, WRAP_COLUMN,
5485           ERR_MANAGE_CERTS_EXPORT_KEY_WRONG_KEY_PW.get(alias,
5486                keystorePath.getAbsolutePath()));
5487      return ResultCode.PARAM_ERROR;
5488    }
5489    catch (final Exception e)
5490    {
5491      Debug.debugException(e);
5492      wrapErr(0, WRAP_COLUMN,
5493           ERR_MANAGE_CERTS_EXPORT_KEY_ERROR_GETTING_KEY.get(alias,
5494                keystorePath.getAbsolutePath()));
5495      e.printStackTrace(getErr());
5496      return ResultCode.LOCAL_ERROR;
5497    }
5498
5499
5500    // Get a PrintStream to use for the output.
5501    final PrintStream printStream;
5502    if (outputFile == null)
5503    {
5504      printStream = getOut();
5505    }
5506    else
5507    {
5508      try
5509      {
5510        printStream = new PrintStream(outputFile);
5511      }
5512      catch (final Exception e)
5513      {
5514        Debug.debugException(e);
5515        wrapErr(0, WRAP_COLUMN,
5516             ERR_MANAGE_CERTS_EXPORT_KEY_ERROR_OPENING_OUTPUT.get(
5517                  outputFile.getAbsolutePath()));
5518        e.printStackTrace(getErr());
5519        return ResultCode.LOCAL_ERROR;
5520      }
5521    }
5522
5523    try
5524    {
5525      try
5526      {
5527        if (exportPEM)
5528        {
5529          writePEMPrivateKey(printStream, privateKey.getEncoded(),
5530               encryptionPassword);
5531        }
5532        else
5533        {
5534          if (encryptionPassword == null)
5535          {
5536            printStream.write(privateKey.getEncoded());
5537          }
5538          else
5539          {
5540            final byte[] encryptedPrivateKey =
5541                 PKCS8EncryptionHandler.encryptPrivateKey(
5542                      privateKey.getEncoded(), encryptionPassword,
5543                      new PKCS8EncryptionProperties());
5544            printStream.write(encryptedPrivateKey);
5545          }
5546        }
5547      }
5548      catch (final Exception e)
5549      {
5550        Debug.debugException(e);
5551        wrapErr(0, WRAP_COLUMN,
5552             ERR_MANAGE_CERTS_EXPORT_KEY_ERROR_WRITING_KEY.get(alias));
5553        e.printStackTrace(getErr());
5554        return ResultCode.LOCAL_ERROR;
5555      }
5556
5557      if (outputFile != null)
5558      {
5559        out();
5560        wrapOut(0, WRAP_COLUMN,
5561             INFO_MANAGE_CERTS_EXPORT_KEY_EXPORT_SUCCESSFUL.get());
5562      }
5563    }
5564    finally
5565    {
5566      printStream.flush();
5567      if (outputFile != null)
5568      {
5569        printStream.close();
5570      }
5571    }
5572
5573    return ResultCode.SUCCESS;
5574  }
5575
5576
5577
5578  /**
5579   * Performs the necessary processing for the import-certificate subcommand.
5580   *
5581   * @return  A result code that indicates whether the processing completed
5582   *          successfully.
5583   */
5584  @NotNull()
5585  private ResultCode doImportCertificate()
5586  {
5587    // Get the values of a number of configured arguments.
5588    final StringArgument aliasArgument =
5589         subCommandParser.getStringArgument("alias");
5590    final String alias = aliasArgument.getValue();
5591
5592    final FileArgument certificateFileArgument =
5593         subCommandParser.getFileArgument("certificate-file");
5594    final List<File> certFiles = certificateFileArgument.getValues();
5595
5596    final File privateKeyFile;
5597    final FileArgument privateKeyFileArgument =
5598         subCommandParser.getFileArgument("private-key-file");
5599    if ((privateKeyFileArgument != null) && privateKeyFileArgument.isPresent())
5600    {
5601      privateKeyFile = privateKeyFileArgument.getValue();
5602    }
5603    else
5604    {
5605      privateKeyFile = null;
5606    }
5607
5608    final BooleanArgument noPromptArgument =
5609         subCommandParser.getBooleanArgument("no-prompt");
5610    final boolean noPrompt =
5611         ((noPromptArgument != null) && noPromptArgument.isPresent());
5612
5613    final String keystoreType;
5614    final File keystorePath = getKeystorePath();
5615    final boolean isNewKeystore = (! keystorePath.exists());
5616    try
5617    {
5618      keystoreType = inferKeystoreType(keystorePath);
5619    }
5620    catch (final LDAPException le)
5621    {
5622      Debug.debugException(le);
5623      wrapErr(0, WRAP_COLUMN, le.getMessage());
5624      return le.getResultCode();
5625    }
5626
5627
5628    final char[] keystorePassword;
5629    try
5630    {
5631      keystorePassword = getKeystorePassword(keystorePath);
5632    }
5633    catch (final LDAPException le)
5634    {
5635      Debug.debugException(le);
5636      wrapErr(0, WRAP_COLUMN, le.getMessage());
5637      return le.getResultCode();
5638    }
5639
5640
5641    // Read the contents of the certificate files.
5642    final ArrayList<X509Certificate> certList = new ArrayList<>(5);
5643    for (final File certFile : certFiles)
5644    {
5645      try
5646      {
5647        final List<X509Certificate> certs = readCertificatesFromFile(certFile);
5648        if (certs.isEmpty())
5649        {
5650          wrapErr(0, WRAP_COLUMN,
5651               ERR_MANAGE_CERTS_IMPORT_CERT_NO_CERTS_IN_FILE.get(
5652                    certFile.getAbsolutePath()));
5653          return ResultCode.PARAM_ERROR;
5654        }
5655
5656        certList.addAll(certs);
5657      }
5658      catch (final LDAPException le)
5659      {
5660        Debug.debugException(le);
5661        wrapErr(0, WRAP_COLUMN, le.getMessage());
5662        return le.getResultCode();
5663      }
5664    }
5665
5666
5667    // See if there is a password to use to decrypt the private key that is
5668    // being imported.
5669    final char[] encryptionPassword;
5670    try
5671    {
5672      encryptionPassword = getPrivateKeyEncryptionPassword(true);
5673    }
5674    catch (final LDAPException le)
5675    {
5676      Debug.debugException(le);
5677      wrapErr(0, WRAP_COLUMN, le.getMessage());
5678      return le.getResultCode();
5679    }
5680
5681
5682    // If a private key file was specified, then read the private key.
5683    final PKCS8PrivateKey privateKey;
5684    if (privateKeyFile == null)
5685    {
5686      privateKey = null;
5687    }
5688    else
5689    {
5690      try
5691      {
5692        privateKey = readPrivateKeyFromFile(privateKeyFile, encryptionPassword);
5693      }
5694      catch (final LDAPException le)
5695      {
5696        Debug.debugException(le);
5697        wrapErr(0, WRAP_COLUMN, le.getMessage());
5698        return le.getResultCode();
5699      }
5700    }
5701
5702
5703    // Get the keystore.
5704    final KeyStore keystore;
5705    try
5706    {
5707      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
5708    }
5709    catch (final LDAPException le)
5710    {
5711      Debug.debugException(le);
5712      wrapErr(0, WRAP_COLUMN, le.getMessage());
5713      return le.getResultCode();
5714    }
5715
5716
5717    // If there is a private key, then see if we need to use a private key
5718    // password that is different from the keystore password.
5719    final char[] privateKeyPassword;
5720    try
5721    {
5722      privateKeyPassword =
5723           getPrivateKeyPassword(keystore, alias, keystorePassword);
5724    }
5725    catch (final LDAPException le)
5726    {
5727      Debug.debugException(le);
5728      wrapErr(0, WRAP_COLUMN, le.getMessage());
5729      return le.getResultCode();
5730    }
5731
5732
5733    // If we should display an equivalent keytool command, then do that now.
5734    final BooleanArgument displayKeytoolCommandArgument =
5735         subCommandParser.getBooleanArgument("display-keytool-command");
5736    if ((displayKeytoolCommandArgument != null) &&
5737        displayKeytoolCommandArgument.isPresent())
5738    {
5739      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
5740      keytoolArgs.add("-import");
5741
5742      keytoolArgs.add("-keystore");
5743      keytoolArgs.add(keystorePath.getAbsolutePath());
5744      keytoolArgs.add("-storetype");
5745      keytoolArgs.add(keystoreType);
5746      keytoolArgs.add("-storepass");
5747      keytoolArgs.add("*****REDACTED*****");
5748      keytoolArgs.add("-keypass");
5749      keytoolArgs.add("*****REDACTED*****");
5750      keytoolArgs.add("-alias");
5751      keytoolArgs.add(alias);
5752      keytoolArgs.add("-file");
5753      keytoolArgs.add(certFiles.get(0).getAbsolutePath());
5754      keytoolArgs.add("-trustcacerts");
5755
5756      displayKeytoolCommand(keytoolArgs);
5757    }
5758
5759
5760    // Look at all the certificates to be imported.  Make sure that every
5761    // subsequent certificate in the chain is the issuer for the previous.
5762    final Iterator<X509Certificate> certIterator = certList.iterator();
5763    X509Certificate subjectCert = certIterator.next();
5764    while (true)
5765    {
5766      if (subjectCert.isSelfSigned())
5767      {
5768        if (certIterator.hasNext())
5769        {
5770          wrapErr(0, WRAP_COLUMN,
5771               ERR_MANAGE_CERTS_IMPORT_CERT_SELF_SIGNED_NOT_LAST.get(
5772                    subjectCert.getSubjectDN()));
5773          return ResultCode.PARAM_ERROR;
5774        }
5775      }
5776
5777      if (! certIterator.hasNext())
5778      {
5779        break;
5780      }
5781
5782      final X509Certificate issuerCert = certIterator.next();
5783      final StringBuilder notIssuerReason = new StringBuilder();
5784      if (! issuerCert.isIssuerFor(subjectCert, notIssuerReason))
5785      {
5786        // In some cases, the process of signing a certificate can put two
5787        // certificates in the output file (both the signed certificate and its
5788        // issuer.  If the same certificate is in the chain twice, then we'll
5789        // silently ignore it.
5790        if (Arrays.equals(issuerCert.getX509CertificateBytes(),
5791                 subjectCert.getX509CertificateBytes()))
5792        {
5793          certIterator.remove();
5794        }
5795        else
5796        {
5797          wrapErr(0, WRAP_COLUMN,
5798               ERR_MANAGE_CERTS_IMPORT_CERT_NEXT_NOT_ISSUER_OF_PREV.get(
5799                    notIssuerReason.toString()));
5800          return ResultCode.PARAM_ERROR;
5801        }
5802      }
5803
5804      subjectCert = issuerCert;
5805    }
5806
5807
5808    // If the last certificate in the chain is not self-signed, then make sure
5809    // that we can complete the chain using other certificates in the keystore
5810    // or in the JVM's set of default trusted issuers.  If we can't complete
5811    // the chain, then that's an error, although we'll go ahead and proceed
5812    // anyway with the import if we're not also importing a private key.
5813    final ArrayList<X509Certificate> chain;
5814    if (certList.get(certList.size() - 1).isSelfSigned())
5815    {
5816      chain = certList;
5817    }
5818    else
5819    {
5820      chain = new ArrayList<>(certList.size() + 5);
5821      chain.addAll(certList);
5822
5823      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
5824           new AtomicReference<>();
5825      final AtomicReference<DN> missingIssuerRef = new AtomicReference<>();
5826
5827      X509Certificate c = certList.get(certList.size() - 1);
5828      while (! c.isSelfSigned())
5829      {
5830        final X509Certificate issuer;
5831        try
5832        {
5833          issuer = getIssuerCertificate(c, keystore, jvmDefaultTrustStoreRef,
5834               missingIssuerRef);
5835        }
5836        catch (final Exception e)
5837        {
5838          Debug.debugException(e);
5839          wrapErr(0, WRAP_COLUMN,
5840               ERR_MANAGE_CERTS_IMPORT_CERT_CANNOT_GET_ISSUER.get(
5841                    c.getIssuerDN()));
5842          e.printStackTrace(getErr());
5843          return ResultCode.LOCAL_ERROR;
5844        }
5845
5846        if (issuer == null)
5847        {
5848          final byte[] authorityKeyIdentifier = getAuthorityKeyIdentifier(c);
5849
5850          // We couldn't find the issuer certificate.  If we're importing a
5851          // private key, or if the keystore already has a key entry with the
5852          // same alias that we're going to use, then this is definitely an
5853          // error because we can only write a key entry if we have a complete
5854          // certificate chain.
5855          //
5856          // If we weren't explicitly provided with a private key, then it's
5857          // still an undesirable thing to import a certificate without having
5858          // the complete set of issuers, but we'll go ahead and let it slide
5859          // with just a warning.
5860          if ((privateKey != null) || hasKeyAlias(keystore, alias))
5861          {
5862            if (authorityKeyIdentifier == null)
5863            {
5864              err();
5865              wrapErr(0, WRAP_COLUMN,
5866                   ERR_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_NO_AKI.get(
5867                        c.getIssuerDN()));
5868            }
5869            else
5870            {
5871              err();
5872              wrapErr(0, WRAP_COLUMN,
5873                   ERR_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_WITH_AKI.get(
5874                        c.getIssuerDN(),
5875                        toColonDelimitedHex(authorityKeyIdentifier)));
5876            }
5877
5878            return ResultCode.PARAM_ERROR;
5879          }
5880          else
5881          {
5882            if (authorityKeyIdentifier == null)
5883            {
5884              err();
5885              wrapErr(0, WRAP_COLUMN,
5886                   WARN_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_NO_AKI.get(
5887                        c.getIssuerDN()));
5888            }
5889            else
5890            {
5891              err();
5892              wrapErr(0, WRAP_COLUMN,
5893                   WARN_MANAGE_CERTS_IMPORT_CERT_NO_ISSUER_WITH_AKI.get(
5894                        c.getIssuerDN(),
5895                        toColonDelimitedHex(authorityKeyIdentifier)));
5896            }
5897
5898            break;
5899          }
5900        }
5901        else
5902        {
5903          chain.add(issuer);
5904          c = issuer;
5905        }
5906      }
5907    }
5908
5909
5910    // If we're going to import a private key with a certificate chain, then
5911    // perform the necessary validation and do the import.
5912    if (privateKey != null)
5913    {
5914      // Make sure that the keystore doesn't already have a key or certificate
5915      // with the specified alias.
5916      if (hasKeyAlias(keystore, alias))
5917      {
5918        wrapErr(0, WRAP_COLUMN,
5919             ERR_MANAGE_CERTS_IMPORT_CERT_WITH_PK_KEY_ALIAS_CONFLICT.get(
5920                  alias));
5921        return ResultCode.PARAM_ERROR;
5922      }
5923      else if (hasCertificateAlias(keystore, alias))
5924      {
5925        wrapErr(0, WRAP_COLUMN,
5926             ERR_MANAGE_CERTS_IMPORT_CERT_WITH_PK_CERT_ALIAS_CONFLICT.get(
5927                  alias));
5928        return ResultCode.PARAM_ERROR;
5929      }
5930
5931
5932      // Make sure that the private key has a key algorithm of either RSA or EC,
5933      // and convert it into a Java PrivateKey object.
5934      final PrivateKey javaPrivateKey;
5935      try
5936      {
5937        javaPrivateKey = privateKey.toPrivateKey();
5938      }
5939      catch (final Exception e)
5940      {
5941        Debug.debugException(e);
5942        wrapErr(0, WRAP_COLUMN,
5943             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_KEY.get(
5944                  privateKeyFile.getAbsolutePath()));
5945        e.printStackTrace(getErr());
5946        return ResultCode.LOCAL_ERROR;
5947      }
5948
5949
5950      // Convert the certificate chain into a Java Certificate[].
5951      final Certificate[] javaCertificateChain = new Certificate[chain.size()];
5952      for (int i=0; i < javaCertificateChain.length; i++)
5953      {
5954        final X509Certificate c = chain.get(i);
5955        try
5956        {
5957          javaCertificateChain[i] = c.toCertificate();
5958        }
5959        catch (final Exception e)
5960        {
5961          Debug.debugException(e);
5962          wrapErr(0, WRAP_COLUMN,
5963               ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_CERT.get(
5964                    c.getSubjectDN()));
5965          e.printStackTrace(getErr());
5966          return ResultCode.LOCAL_ERROR;
5967        }
5968      }
5969
5970
5971      // Prompt the user to confirm the import, if appropriate.
5972      if (! noPrompt)
5973      {
5974        out();
5975        wrapOut(0, WRAP_COLUMN,
5976             INFO_MANAGE_CERTS_IMPORT_CERT_CONFIRM_IMPORT_CHAIN_NEW_KEY.get(
5977                  alias));
5978
5979        for (final X509Certificate c : chain)
5980        {
5981          out();
5982          printCertificate(c, "", false);
5983        }
5984
5985        out();
5986
5987        try
5988        {
5989          if (! promptForYesNo(
5990               INFO_MANAGE_CERTS_IMPORT_CERT_PROMPT_IMPORT_CHAIN.get()))
5991          {
5992            wrapErr(0, WRAP_COLUMN,
5993                 ERR_MANAGE_CERTS_IMPORT_CERT_CANCELED.get());
5994            return ResultCode.USER_CANCELED;
5995          }
5996        }
5997        catch (final LDAPException le)
5998        {
5999          Debug.debugException(le);
6000          err();
6001          wrapErr(0, WRAP_COLUMN, le.getMessage());
6002          return le.getResultCode();
6003        }
6004      }
6005
6006
6007      // Set the private key entry in the keystore.
6008      try
6009      {
6010        keystore.setKeyEntry(alias, javaPrivateKey, privateKeyPassword,
6011             javaCertificateChain);
6012      }
6013      catch (final Exception e)
6014      {
6015        Debug.debugException(e);
6016        wrapErr(0, WRAP_COLUMN,
6017             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_UPDATING_KS_WITH_CHAIN.get(
6018                  alias));
6019        e.printStackTrace(getErr());
6020        return ResultCode.LOCAL_ERROR;
6021      }
6022
6023
6024      // Write the updated keystore to disk.
6025      try
6026      {
6027        writeKeystore(keystore, keystorePath, keystorePassword);
6028      }
6029      catch (final LDAPException le)
6030      {
6031        Debug.debugException(le);
6032        wrapErr(0, WRAP_COLUMN, le.getMessage());
6033        return le.getResultCode();
6034      }
6035
6036      if (isNewKeystore)
6037      {
6038        out();
6039        wrapOut(0, WRAP_COLUMN,
6040             INFO_MANAGE_CERTS_IMPORT_CERT_CREATED_KEYSTORE.get(
6041                  getUserFriendlyKeystoreType(keystoreType)));
6042      }
6043
6044      out();
6045      wrapOut(0, WRAP_COLUMN,
6046           INFO_MANAGE_CERTS_IMPORT_CERT_IMPORTED_CHAIN_WITH_PK.get());
6047      return ResultCode.SUCCESS;
6048    }
6049
6050
6051    // If we've gotten here, then we were given one or more certificates but no
6052    // private key.  See if the keystore already has a certificate entry with
6053    // the specified alias.  If so, then that's always an error.
6054    if (hasCertificateAlias(keystore, alias))
6055    {
6056      wrapErr(0, WRAP_COLUMN,
6057           ERR_MANAGE_CERTS_IMPORT_CERT_WITH_CONFLICTING_CERT_ALIAS.get(alias));
6058      return ResultCode.PARAM_ERROR;
6059    }
6060
6061
6062    // See if the keystore already has a key entry with the specified alias.
6063    // If so, then it may or may not be an error.  This can happen if we
6064    // generated a certificate signing request from an existing key pair, and
6065    // now want to import the signed certificate.  If that is the case, then we
6066    // will replace the existing key entry with a new one that contains the full
6067    // new certificate chain and the existing private key, but only if the
6068    // new certificate uses the same public key as the certificate at the head
6069    // of the existing chain in that alias.
6070    if (hasKeyAlias(keystore, alias))
6071    {
6072      // Make sure that the existing key pair uses the same public key as the
6073      // new certificate we are importing.
6074      final PrivateKey existingPrivateKey;
6075      final Certificate[] existingChain;
6076      final X509Certificate existingEndCertificate;
6077      try
6078      {
6079        existingPrivateKey =
6080             (PrivateKey) keystore.getKey(alias, privateKeyPassword);
6081        existingChain = keystore.getCertificateChain(alias);
6082        existingEndCertificate =
6083             new X509Certificate(existingChain[0].getEncoded());
6084      }
6085      catch (final Exception e)
6086      {
6087        Debug.debugException(e);
6088        wrapErr(0, WRAP_COLUMN,
6089             ERR_MANAGE_CERTS_IMPORT_CERT_INTO_KEY_ALIAS_CANNOT_GET_KEY.get(
6090                  alias));
6091        e.printStackTrace(getErr());
6092        return ResultCode.LOCAL_ERROR;
6093      }
6094
6095      final boolean[] existingPublicKeyBits =
6096           existingEndCertificate.getEncodedPublicKey().getBits();
6097      final boolean[] newPublicKeyBits =
6098           chain.get(0).getEncodedPublicKey().getBits();
6099      if (! Arrays.equals(existingPublicKeyBits, newPublicKeyBits))
6100      {
6101        wrapErr(0, WRAP_COLUMN,
6102             ERR_MANAGE_CERTS_IMPORT_CERT_INTO_KEY_ALIAS_KEY_MISMATCH.get(
6103                  alias));
6104        return ResultCode.PARAM_ERROR;
6105      }
6106
6107
6108      // Prepare the new certificate chain to store in the alias.
6109      final Certificate[] newChain = new Certificate[chain.size()];
6110      for (int i=0; i < chain.size(); i++)
6111      {
6112        final X509Certificate c = chain.get(i);
6113        try
6114        {
6115          newChain[i] = c.toCertificate();
6116        }
6117        catch (final Exception e)
6118        {
6119          Debug.debugException(e);
6120          wrapErr(0, WRAP_COLUMN,
6121               ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_CERT.get(
6122                    c.getSubjectDN()));
6123          e.printStackTrace(getErr());
6124          return ResultCode.LOCAL_ERROR;
6125        }
6126      }
6127
6128
6129      // Prompt the user to confirm the import, if appropriate.
6130      if (! noPrompt)
6131      {
6132        out();
6133        wrapOut(0, WRAP_COLUMN,
6134             INFO_MANAGE_CERTS_IMPORT_CERT_CONFIRM_IMPORT_CHAIN_EXISTING_KEY.
6135                  get(alias));
6136
6137        for (final X509Certificate c : chain)
6138        {
6139          out();
6140          printCertificate(c, "", false);
6141        }
6142
6143        out();
6144
6145        try
6146        {
6147          if (! promptForYesNo(
6148               INFO_MANAGE_CERTS_IMPORT_CERT_PROMPT_IMPORT_CHAIN.get()))
6149          {
6150            wrapErr(0, WRAP_COLUMN,
6151                 ERR_MANAGE_CERTS_IMPORT_CERT_CANCELED.get());
6152            return ResultCode.USER_CANCELED;
6153          }
6154        }
6155        catch (final LDAPException le)
6156        {
6157          Debug.debugException(le);
6158          err();
6159          wrapErr(0, WRAP_COLUMN, le.getMessage());
6160          return le.getResultCode();
6161        }
6162      }
6163
6164
6165      // Set the private key entry in the keystore.
6166      try
6167      {
6168        keystore.setKeyEntry(alias, existingPrivateKey, privateKeyPassword,
6169             newChain);
6170      }
6171      catch (final Exception e)
6172      {
6173        Debug.debugException(e);
6174        wrapErr(0, WRAP_COLUMN,
6175             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_UPDATING_KS_WITH_CHAIN.get(
6176                  alias));
6177        e.printStackTrace(getErr());
6178        return ResultCode.LOCAL_ERROR;
6179      }
6180
6181
6182      // Write the updated keystore to disk.
6183      try
6184      {
6185        writeKeystore(keystore, keystorePath, keystorePassword);
6186      }
6187      catch (final LDAPException le)
6188      {
6189        Debug.debugException(le);
6190        wrapErr(0, WRAP_COLUMN, le.getMessage());
6191        return le.getResultCode();
6192      }
6193
6194      out();
6195
6196      if (isNewKeystore)
6197      {
6198        wrapOut(0, WRAP_COLUMN,
6199             INFO_MANAGE_CERTS_IMPORT_CERT_CREATED_KEYSTORE.get(
6200                  getUserFriendlyKeystoreType(keystoreType)));
6201      }
6202
6203      wrapOut(0, WRAP_COLUMN,
6204           INFO_MANAGE_CERTS_IMPORT_CERT_IMPORTED_CHAIN_WITHOUT_PK.get());
6205      return ResultCode.SUCCESS;
6206    }
6207
6208
6209    // If we've gotten here, then we know that we're just going to add
6210    // certificate entries to the keystore.  Iterate through the certificates
6211    // and add them to the keystore under the appropriate aliases, first making
6212    // sure that the alias isn't already in use.
6213    final LinkedHashMap<String,X509Certificate> certMap =
6214         new LinkedHashMap<>(StaticUtils.computeMapCapacity(certList.size()));
6215    for (int i=0; i < certList.size(); i++)
6216    {
6217      final X509Certificate x509Certificate = certList.get(i);
6218      final Certificate javaCertificate;
6219      try
6220      {
6221        javaCertificate = x509Certificate.toCertificate();
6222      }
6223      catch (final Exception e)
6224      {
6225        Debug.debugException(e);
6226        wrapErr(0, WRAP_COLUMN,
6227             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_CONVERTING_CERT.get(
6228                  x509Certificate.getSubjectDN()));
6229        e.printStackTrace(getErr());
6230        return ResultCode.LOCAL_ERROR;
6231      }
6232
6233      final String certAlias;
6234      if (i == 0)
6235      {
6236        certAlias = alias;
6237      }
6238      else if (certList.size() > 2)
6239      {
6240        certAlias = alias + "-issuer-" + i;
6241      }
6242      else
6243      {
6244        certAlias = alias + "-issuer";
6245      }
6246
6247      certMap.put(certAlias, x509Certificate);
6248
6249      if (hasKeyAlias(keystore, certAlias) ||
6250          hasCertificateAlias(keystore, certAlias))
6251      {
6252        wrapErr(0, WRAP_COLUMN,
6253             ERR_MANAGE_CERTS_IMPORT_CERT_WITH_CONFLICTING_ISSUER_ALIAS.get(
6254                  x509Certificate.getSubjectDN(), certAlias));
6255        return ResultCode.PARAM_ERROR;
6256      }
6257
6258      try
6259      {
6260        keystore.setCertificateEntry(certAlias, javaCertificate);
6261      }
6262      catch (final Exception e)
6263      {
6264        Debug.debugException(e);
6265        wrapErr(0, WRAP_COLUMN,
6266             ERR_MANAGE_CERTS_IMPORT_CERT_ERROR_UPDATING_KS_WITH_CERT.get(
6267                  x509Certificate.getSubjectDN(), alias));
6268        e.printStackTrace(getErr());
6269        return ResultCode.LOCAL_ERROR;
6270      }
6271    }
6272
6273
6274    // Prompt about whether to perform the import, if appropriate.
6275    if (! noPrompt)
6276    {
6277      out();
6278      wrapOut(0, WRAP_COLUMN,
6279           INFO_MANAGE_CERTS_IMPORT_CERT_CONFIRM_IMPORT_CHAIN_NO_KEY.
6280                get(alias));
6281
6282      for (final Map.Entry<String,X509Certificate> e : certMap.entrySet())
6283      {
6284        out();
6285        wrapOut(0, WRAP_COLUMN,
6286             INFO_MANAGE_CERTS_IMPORT_CERT_LABEL_ALIAS.get(e.getKey()));
6287        printCertificate(e.getValue(), "", false);
6288      }
6289
6290      out();
6291
6292      try
6293      {
6294        if (! promptForYesNo(
6295             INFO_MANAGE_CERTS_IMPORT_CERT_PROMPT_IMPORT_CHAIN.get()))
6296        {
6297          wrapErr(0, WRAP_COLUMN,
6298               ERR_MANAGE_CERTS_IMPORT_CERT_CANCELED.get());
6299          return ResultCode.USER_CANCELED;
6300        }
6301      }
6302      catch (final LDAPException le)
6303      {
6304        Debug.debugException(le);
6305        err();
6306        wrapErr(0, WRAP_COLUMN, le.getMessage());
6307        return le.getResultCode();
6308      }
6309    }
6310
6311
6312    // Write the updated keystore to disk.
6313    try
6314    {
6315      writeKeystore(keystore, keystorePath, keystorePassword);
6316    }
6317    catch (final LDAPException le)
6318    {
6319      Debug.debugException(le);
6320      wrapErr(0, WRAP_COLUMN, le.getMessage());
6321      return le.getResultCode();
6322    }
6323
6324    out();
6325
6326    if (isNewKeystore)
6327    {
6328      wrapOut(0, WRAP_COLUMN,
6329           INFO_MANAGE_CERTS_IMPORT_CERT_CREATED_KEYSTORE.get(
6330                getUserFriendlyKeystoreType(keystoreType)));
6331    }
6332
6333    wrapOut(0, WRAP_COLUMN,
6334         INFO_MANAGE_CERTS_IMPORT_CERT_IMPORTED_CHAIN_WITHOUT_PK.get());
6335    return ResultCode.SUCCESS;
6336  }
6337
6338
6339
6340  /**
6341   * Performs the necessary processing for the delete-certificate subcommand.
6342   *
6343   * @return  A result code that indicates whether the processing completed
6344   *          successfully.
6345   */
6346  @NotNull()
6347  private ResultCode doDeleteCertificate()
6348  {
6349    // Get the values of a number of configured arguments.
6350    final StringArgument aliasArgument =
6351         subCommandParser.getStringArgument("alias");
6352    final String alias = aliasArgument.getValue();
6353
6354    final BooleanArgument noPromptArgument =
6355         subCommandParser.getBooleanArgument("no-prompt");
6356    final boolean noPrompt =
6357         ((noPromptArgument != null) && noPromptArgument.isPresent());
6358
6359    final String keystoreType;
6360    final File keystorePath = getKeystorePath();
6361    try
6362    {
6363      keystoreType = inferKeystoreType(keystorePath);
6364    }
6365    catch (final LDAPException le)
6366    {
6367      Debug.debugException(le);
6368      wrapErr(0, WRAP_COLUMN, le.getMessage());
6369      return le.getResultCode();
6370    }
6371
6372    final char[] keystorePassword;
6373    try
6374    {
6375      keystorePassword = getKeystorePassword(keystorePath);
6376    }
6377    catch (final LDAPException le)
6378    {
6379      Debug.debugException(le);
6380      wrapErr(0, WRAP_COLUMN, le.getMessage());
6381      return le.getResultCode();
6382    }
6383
6384    final BooleanArgument displayKeytoolCommandArgument =
6385         subCommandParser.getBooleanArgument("display-keytool-command");
6386    if ((displayKeytoolCommandArgument != null) &&
6387         displayKeytoolCommandArgument.isPresent())
6388    {
6389      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
6390      keytoolArgs.add("-delete");
6391
6392      keytoolArgs.add("-keystore");
6393      keytoolArgs.add(keystorePath.getAbsolutePath());
6394      keytoolArgs.add("-storetype");
6395      keytoolArgs.add(keystoreType);
6396      keytoolArgs.add("-storepass");
6397      keytoolArgs.add("*****REDACTED*****");
6398      keytoolArgs.add("-alias");
6399      keytoolArgs.add(alias);
6400
6401      displayKeytoolCommand(keytoolArgs);
6402    }
6403
6404
6405    // Get the keystore.
6406    final KeyStore keystore;
6407    try
6408    {
6409      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
6410    }
6411    catch (final LDAPException le)
6412    {
6413      Debug.debugException(le);
6414      wrapErr(0, WRAP_COLUMN, le.getMessage());
6415      return le.getResultCode();
6416    }
6417
6418
6419    // Get the entry for the specified alias.
6420    final boolean hasPrivateKey;
6421    final ArrayList<X509Certificate> certList = new ArrayList<>(5);
6422    if (hasCertificateAlias(keystore, alias))
6423    {
6424      try
6425      {
6426        hasPrivateKey = false;
6427        certList.add(
6428             new X509Certificate(keystore.getCertificate(alias).getEncoded()));
6429      }
6430      catch (final Exception e)
6431      {
6432        Debug.debugException(e);
6433        wrapErr(0, WRAP_COLUMN,
6434             ERR_MANAGE_CERTS_DELETE_CERT_ERROR_GETTING_CERT.get(alias));
6435        e.printStackTrace(getErr());
6436        return ResultCode.LOCAL_ERROR;
6437      }
6438    }
6439    else if (hasKeyAlias(keystore, alias))
6440    {
6441      try
6442      {
6443        hasPrivateKey = true;
6444        for (final Certificate c : keystore.getCertificateChain(alias))
6445        {
6446          certList.add(new X509Certificate(c.getEncoded()));
6447        }
6448      }
6449      catch (final Exception e)
6450      {
6451        Debug.debugException(e);
6452        wrapErr(0, WRAP_COLUMN,
6453             ERR_MANAGE_CERTS_DELETE_CERT_ERROR_GETTING_CHAIN.get(alias));
6454        e.printStackTrace(getErr());
6455        return ResultCode.LOCAL_ERROR;
6456      }
6457    }
6458    else
6459    {
6460      wrapErr(0, WRAP_COLUMN,
6461           ERR_MANAGE_CERTS_DELETE_CERT_ERROR_ALIAS_NOT_CERT_OR_KEY.get(alias));
6462      return ResultCode.PARAM_ERROR;
6463    }
6464
6465
6466    // Prompt about whether to perform the delete, if appropriate.
6467    if (! noPrompt)
6468    {
6469      out();
6470      if (! hasPrivateKey)
6471      {
6472        wrapOut(0, WRAP_COLUMN,
6473             INFO_MANAGE_CERTS_DELETE_CERT_CONFIRM_DELETE_CERT.get());
6474      }
6475      else
6476      {
6477        wrapOut(0, WRAP_COLUMN,
6478             INFO_MANAGE_CERTS_DELETE_CERT_CONFIRM_DELETE_CHAIN.get());
6479      }
6480
6481      for (final X509Certificate c : certList)
6482      {
6483        out();
6484        printCertificate(c, "", false);
6485      }
6486
6487      out();
6488
6489      try
6490      {
6491        if (! promptForYesNo(
6492             INFO_MANAGE_CERTS_DELETE_CERT_PROMPT_DELETE.get()))
6493        {
6494          wrapErr(0, WRAP_COLUMN,
6495               ERR_MANAGE_CERTS_DELETE_CERT_CANCELED.get());
6496          return ResultCode.USER_CANCELED;
6497        }
6498      }
6499      catch (final LDAPException le)
6500      {
6501        Debug.debugException(le);
6502        err();
6503        wrapErr(0, WRAP_COLUMN, le.getMessage());
6504        return le.getResultCode();
6505      }
6506    }
6507
6508
6509    // Delete the entry from the keystore.
6510    try
6511    {
6512      keystore.deleteEntry(alias);
6513    }
6514    catch (final Exception e)
6515    {
6516      Debug.debugException(e);
6517      wrapErr(0, WRAP_COLUMN,
6518           ERR_MANAGE_CERTS_DELETE_CERT_DELETE_ERROR.get(alias));
6519      e.printStackTrace(getErr());
6520      return ResultCode.LOCAL_ERROR;
6521    }
6522
6523
6524    // Write the updated keystore to disk.
6525    try
6526    {
6527      writeKeystore(keystore, keystorePath, keystorePassword);
6528    }
6529    catch (final LDAPException le)
6530    {
6531      Debug.debugException(le);
6532      wrapErr(0, WRAP_COLUMN, le.getMessage());
6533      return le.getResultCode();
6534    }
6535
6536    if (certList.size() == 1)
6537    {
6538      out();
6539      wrapOut(0, WRAP_COLUMN,
6540           INFO_MANAGE_CERTS_DELETE_CERT_DELETED_CERT.get());
6541    }
6542    else
6543    {
6544      out();
6545      wrapOut(0, WRAP_COLUMN,
6546           INFO_MANAGE_CERTS_DELETE_CERT_DELETED_CHAIN.get());
6547    }
6548
6549    return ResultCode.SUCCESS;
6550  }
6551
6552
6553
6554  /**
6555   * Performs the necessary processing for the generate-self-signed-certificate,
6556   * generate-certificate-signing-request, and sign-certificate-signing-request
6557   * subcommands.
6558   *
6559   * @return  A result code that indicates whether the processing completed
6560   *          successfully.
6561   */
6562  @NotNull()
6563  private ResultCode doGenerateOrSignCertificateOrCSR()
6564  {
6565    // Figure out which subcommand we're processing.
6566    final boolean isGenerateCertificate;
6567    final boolean isGenerateCSR;
6568    final boolean isSignCSR;
6569    final SubCommand selectedSubCommand = globalParser.getSelectedSubCommand();
6570    if (selectedSubCommand.hasName("generate-self-signed-certificate"))
6571    {
6572      isGenerateCertificate = true;
6573      isGenerateCSR = false;
6574      isSignCSR = false;
6575    }
6576    else if (selectedSubCommand.hasName("generate-certificate-signing-request"))
6577    {
6578      isGenerateCertificate = false;
6579      isGenerateCSR = true;
6580      isSignCSR = false;
6581    }
6582    else
6583    {
6584      Validator.ensureTrue(
6585           selectedSubCommand.hasName("sign-certificate-signing-request"));
6586      isGenerateCertificate = false;
6587      isGenerateCSR = false;
6588      isSignCSR = true;
6589    }
6590
6591
6592    // Get the values of a number of configured arguments.
6593    final StringArgument aliasArgument =
6594         subCommandParser.getStringArgument("alias");
6595    final String alias = aliasArgument.getValue();
6596
6597    final File keystorePath = getKeystorePath();
6598    final boolean isNewKeystore = (! keystorePath.exists());
6599
6600    DN subjectDN = null;
6601    final DNArgument subjectDNArgument =
6602         subCommandParser.getDNArgument("subject-dn");
6603    if ((subjectDNArgument != null) && subjectDNArgument.isPresent())
6604    {
6605      subjectDN = subjectDNArgument.getValue();
6606    }
6607
6608    File inputFile = null;
6609    final FileArgument inputFileArgument =
6610         subCommandParser.getFileArgument("input-file");
6611    if ((inputFileArgument != null) && inputFileArgument.isPresent())
6612    {
6613      inputFile = inputFileArgument.getValue();
6614    }
6615
6616    File outputFile = null;
6617    final FileArgument outputFileArgument =
6618         subCommandParser.getFileArgument("output-file");
6619    if ((outputFileArgument != null) && outputFileArgument.isPresent())
6620    {
6621      outputFile = outputFileArgument.getValue();
6622    }
6623
6624    boolean outputPEM = true;
6625    final StringArgument outputFormatArgument =
6626         subCommandParser.getStringArgument("output-format");
6627    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
6628    {
6629      final String format = outputFormatArgument.getValue().toLowerCase();
6630      if (format.equals("der") || format.equals("binary") ||
6631          format.equals("bin"))
6632      {
6633        outputPEM = false;
6634      }
6635    }
6636
6637    if ((! outputPEM) && (outputFile == null))
6638    {
6639      wrapErr(0, WRAP_COLUMN,
6640           ERR_MANAGE_CERTS_GEN_CERT_NO_FILE_WITH_DER.get());
6641      return ResultCode.PARAM_ERROR;
6642    }
6643
6644    final BooleanArgument useExistingKeyPairArgument =
6645         subCommandParser.getBooleanArgument("use-existing-key-pair");
6646    final boolean useExistingKeyPair =
6647         ((useExistingKeyPairArgument != null) &&
6648              useExistingKeyPairArgument.isPresent());
6649    if (useExistingKeyPair && (! keystorePath.exists()))
6650    {
6651      wrapErr(0, WRAP_COLUMN,
6652           ERR_MANAGE_CERTS_GEN_CERT_USE_EXISTING_KP_WITHOUT_KS.get());
6653      return ResultCode.PARAM_ERROR;
6654    }
6655
6656    final BooleanArgument inheritExtensionsArgument =
6657         subCommandParser.getBooleanArgument("inherit-extensions");
6658    final boolean inheritExtensions =
6659         ((inheritExtensionsArgument != null) &&
6660              inheritExtensionsArgument.isPresent());
6661
6662    final BooleanArgument includeRequestedExtensionsArgument =
6663         subCommandParser.getBooleanArgument("include-requested-extensions");
6664    final boolean includeRequestedExtensions =
6665         ((includeRequestedExtensionsArgument != null) &&
6666              includeRequestedExtensionsArgument.isPresent());
6667
6668    final BooleanArgument noPromptArgument =
6669         subCommandParser.getBooleanArgument("no-prompt");
6670    final boolean noPrompt =
6671         ((noPromptArgument != null) && noPromptArgument.isPresent());
6672
6673    final BooleanArgument displayKeytoolCommandArgument =
6674         subCommandParser.getBooleanArgument("display-keytool-command");
6675    final boolean displayKeytoolCommand =
6676         ((displayKeytoolCommandArgument != null) &&
6677          displayKeytoolCommandArgument.isPresent());
6678
6679    int daysValid = 365;
6680    final IntegerArgument daysValidArgument =
6681         subCommandParser.getIntegerArgument("days-valid");
6682    if ((daysValidArgument != null) && daysValidArgument.isPresent())
6683    {
6684      daysValid = daysValidArgument.getValue();
6685    }
6686
6687    Date validityStartTime = null;
6688    final TimestampArgument validityStartTimeArgument =
6689         subCommandParser.getTimestampArgument("validity-start-time");
6690    if ((validityStartTimeArgument != null) &&
6691         validityStartTimeArgument.isPresent())
6692    {
6693      validityStartTime = validityStartTimeArgument.getValue();
6694    }
6695
6696    PublicKeyAlgorithmIdentifier keyAlgorithmIdentifier = null;
6697    String keyAlgorithmName = null;
6698    final StringArgument keyAlgorithmArgument =
6699         subCommandParser.getStringArgument("key-algorithm");
6700    if ((keyAlgorithmArgument != null) && keyAlgorithmArgument.isPresent())
6701    {
6702      final String name = keyAlgorithmArgument.getValue();
6703      keyAlgorithmIdentifier = PublicKeyAlgorithmIdentifier.forName(name);
6704      if (keyAlgorithmIdentifier == null)
6705      {
6706        wrapErr(0, WRAP_COLUMN,
6707             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_KEY_ALG.get(name));
6708        return ResultCode.PARAM_ERROR;
6709      }
6710      else
6711      {
6712        keyAlgorithmName = keyAlgorithmIdentifier.getName();
6713      }
6714    }
6715
6716    Integer keySizeBits = null;
6717    final IntegerArgument keySizeBitsArgument =
6718         subCommandParser.getIntegerArgument("key-size-bits");
6719    if ((keySizeBitsArgument != null) && keySizeBitsArgument.isPresent())
6720    {
6721      keySizeBits = keySizeBitsArgument.getValue();
6722    }
6723
6724    if ((keyAlgorithmIdentifier != null) &&
6725        (keyAlgorithmIdentifier != PublicKeyAlgorithmIdentifier.RSA) &&
6726        (keySizeBits == null))
6727    {
6728      wrapErr(0, WRAP_COLUMN,
6729           ERR_MANAGE_CERTS_GEN_CERT_NO_KEY_SIZE_FOR_NON_RSA_KEY.get());
6730      return ResultCode.PARAM_ERROR;
6731    }
6732
6733    String signatureAlgorithmName = null;
6734    SignatureAlgorithmIdentifier signatureAlgorithmIdentifier = null;
6735    final StringArgument signatureAlgorithmArgument =
6736         subCommandParser.getStringArgument("signature-algorithm");
6737    if ((signatureAlgorithmArgument != null) &&
6738        signatureAlgorithmArgument.isPresent())
6739    {
6740      final String name = signatureAlgorithmArgument.getValue();
6741      signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forName(name);
6742      if (signatureAlgorithmIdentifier == null)
6743      {
6744        wrapErr(0, WRAP_COLUMN,
6745             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG.get(name));
6746        return ResultCode.PARAM_ERROR;
6747      }
6748      else
6749      {
6750        signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
6751      }
6752    }
6753
6754    if ((keyAlgorithmIdentifier != null) &&
6755        (keyAlgorithmIdentifier != PublicKeyAlgorithmIdentifier.RSA) &&
6756        (signatureAlgorithmIdentifier == null))
6757    {
6758      wrapErr(0, WRAP_COLUMN,
6759           ERR_MANAGE_CERTS_GEN_CERT_NO_SIG_ALG_FOR_NON_RSA_KEY.get());
6760      return ResultCode.PARAM_ERROR;
6761    }
6762
6763
6764    // Build a subject alternative name extension, if appropriate.
6765    final ArrayList<X509CertificateExtension> extensionList =
6766         new ArrayList<>(10);
6767    final GeneralNamesBuilder sanBuilder = new GeneralNamesBuilder();
6768    final LinkedHashSet<String> sanValues =
6769         new LinkedHashSet<>(StaticUtils.computeMapCapacity(10));
6770    final StringArgument sanDNSArgument =
6771         subCommandParser.getStringArgument("subject-alternative-name-dns");
6772    if ((sanDNSArgument != null) && sanDNSArgument.isPresent())
6773    {
6774      for (final String value : sanDNSArgument.getValues())
6775      {
6776        sanBuilder.addDNSName(value);
6777        sanValues.add("DNS:" + value);
6778      }
6779    }
6780
6781    final StringArgument sanIPArgument = subCommandParser.getStringArgument(
6782         "subject-alternative-name-ip-address");
6783    if ((sanIPArgument != null) && sanIPArgument.isPresent())
6784    {
6785      for (final String value : sanIPArgument.getValues())
6786      {
6787        try
6788        {
6789          sanBuilder.addIPAddress(LDAPConnectionOptions.DEFAULT_NAME_RESOLVER.
6790               getByName(value));
6791          sanValues.add("IP:" + value);
6792        }
6793        catch (final Exception e)
6794        {
6795          // This should never happen.
6796          Debug.debugException(e);
6797          throw new RuntimeException(e);
6798        }
6799      }
6800    }
6801
6802    final StringArgument sanEmailArgument = subCommandParser.getStringArgument(
6803         "subject-alternative-name-email-address");
6804    if ((sanEmailArgument != null) && sanEmailArgument.isPresent())
6805    {
6806      for (final String value : sanEmailArgument.getValues())
6807      {
6808        sanBuilder.addRFC822Name(value);
6809        sanValues.add("EMAIL:" + value);
6810      }
6811    }
6812
6813    final StringArgument sanURIArgument =
6814         subCommandParser.getStringArgument("subject-alternative-name-uri");
6815    if ((sanURIArgument != null) && sanURIArgument.isPresent())
6816    {
6817      for (final String value : sanURIArgument.getValues())
6818      {
6819        sanBuilder.addUniformResourceIdentifier(value);
6820        sanValues.add("URI:" + value);
6821      }
6822    }
6823
6824    final StringArgument sanOIDArgument =
6825         subCommandParser.getStringArgument("subject-alternative-name-oid");
6826    if ((sanOIDArgument != null) && sanOIDArgument.isPresent())
6827    {
6828      for (final String value : sanOIDArgument.getValues())
6829      {
6830        sanBuilder.addRegisteredID(new OID(value));
6831        sanValues.add("OID:" + value);
6832      }
6833    }
6834
6835    if (! sanValues.isEmpty())
6836    {
6837      try
6838      {
6839        extensionList.add(
6840             new SubjectAlternativeNameExtension(false, sanBuilder.build()));
6841      }
6842      catch (final Exception e)
6843      {
6844        // This should never happen.
6845        Debug.debugException(e);
6846        throw new RuntimeException(e);
6847      }
6848    }
6849
6850    // Build a set of issuer alternative name extension values.
6851    final GeneralNamesBuilder ianBuilder = new GeneralNamesBuilder();
6852    final LinkedHashSet<String> ianValues =
6853         new LinkedHashSet<>(StaticUtils.computeMapCapacity(10));
6854    final StringArgument ianDNSArgument =
6855         subCommandParser.getStringArgument("issuer-alternative-name-dns");
6856    if ((ianDNSArgument != null) && ianDNSArgument.isPresent())
6857    {
6858      for (final String value : ianDNSArgument.getValues())
6859      {
6860        ianBuilder.addDNSName(value);
6861        ianValues.add("DNS:" + value);
6862      }
6863    }
6864
6865    final StringArgument ianIPArgument = subCommandParser.getStringArgument(
6866         "issuer-alternative-name-ip-address");
6867    if ((ianIPArgument != null) && ianIPArgument.isPresent())
6868    {
6869      for (final String value : ianIPArgument.getValues())
6870      {
6871        try
6872        {
6873          ianBuilder.addIPAddress(LDAPConnectionOptions.DEFAULT_NAME_RESOLVER.
6874               getByName(value));
6875          ianValues.add("IP:" + value);
6876        }
6877        catch (final Exception e)
6878        {
6879          // This should never happen.
6880          Debug.debugException(e);
6881          throw new RuntimeException(e);
6882        }
6883      }
6884    }
6885
6886    final StringArgument ianEmailArgument = subCommandParser.getStringArgument(
6887         "issuer-alternative-name-email-address");
6888    if ((ianEmailArgument != null) && ianEmailArgument.isPresent())
6889    {
6890      for (final String value : ianEmailArgument.getValues())
6891      {
6892        ianBuilder.addRFC822Name(value);
6893        ianValues.add("EMAIL:" + value);
6894      }
6895    }
6896
6897    final StringArgument ianURIArgument =
6898         subCommandParser.getStringArgument("issuer-alternative-name-uri");
6899    if ((ianURIArgument != null) && ianURIArgument.isPresent())
6900    {
6901      for (final String value : ianURIArgument.getValues())
6902      {
6903        ianBuilder.addUniformResourceIdentifier(value);
6904        ianValues.add("URI:" + value);
6905      }
6906    }
6907
6908    final StringArgument ianOIDArgument =
6909         subCommandParser.getStringArgument("issuer-alternative-name-oid");
6910    if ((ianOIDArgument != null) && ianOIDArgument.isPresent())
6911    {
6912      for (final String value : ianOIDArgument.getValues())
6913      {
6914        ianBuilder.addRegisteredID(new OID(value));
6915        ianValues.add("OID:" + value);
6916      }
6917    }
6918
6919    if (! ianValues.isEmpty())
6920    {
6921      try
6922      {
6923        extensionList.add(
6924             new IssuerAlternativeNameExtension(false, ianBuilder.build()));
6925      }
6926      catch (final Exception e)
6927      {
6928        // This should never happen.
6929        Debug.debugException(e);
6930        throw new RuntimeException(e);
6931      }
6932    }
6933
6934
6935    // Build a basic constraints extension, if appropriate.
6936    BasicConstraintsExtension basicConstraints = null;
6937    final BooleanValueArgument basicConstraintsIsCAArgument =
6938         subCommandParser.getBooleanValueArgument("basic-constraints-is-ca");
6939    if ((basicConstraintsIsCAArgument != null) &&
6940         basicConstraintsIsCAArgument.isPresent())
6941    {
6942      final boolean isCA = basicConstraintsIsCAArgument.getValue();
6943
6944      Integer pathLength = null;
6945      final IntegerArgument pathLengthArgument =
6946           subCommandParser.getIntegerArgument(
6947                "basic-constraints-maximum-path-length");
6948      if ((pathLengthArgument != null) && pathLengthArgument.isPresent())
6949      {
6950        if (isCA)
6951        {
6952          pathLength = pathLengthArgument.getValue();
6953        }
6954        else
6955        {
6956          wrapErr(0, WRAP_COLUMN,
6957               ERR_MANAGE_CERTS_GEN_CERT_BC_PATH_LENGTH_WITHOUT_CA.get());
6958          return ResultCode.PARAM_ERROR;
6959        }
6960      }
6961
6962      basicConstraints = new BasicConstraintsExtension(false, isCA, pathLength);
6963      extensionList.add(basicConstraints);
6964    }
6965
6966
6967    // Build a key usage extension, if appropriate.
6968    KeyUsageExtension keyUsage = null;
6969    final StringArgument keyUsageArgument =
6970         subCommandParser.getStringArgument("key-usage");
6971    if ((keyUsageArgument != null) && keyUsageArgument.isPresent())
6972    {
6973      boolean digitalSignature = false;
6974      boolean nonRepudiation = false;
6975      boolean keyEncipherment = false;
6976      boolean dataEncipherment = false;
6977      boolean keyAgreement = false;
6978      boolean keyCertSign = false;
6979      boolean crlSign = false;
6980      boolean encipherOnly = false;
6981      boolean decipherOnly = false;
6982
6983      for (final String value : keyUsageArgument.getValues())
6984      {
6985        if (value.equalsIgnoreCase("digital-signature") ||
6986             value.equalsIgnoreCase("digitalSignature"))
6987        {
6988          digitalSignature = true;
6989        }
6990        else if (value.equalsIgnoreCase("non-repudiation") ||
6991             value.equalsIgnoreCase("nonRepudiation") ||
6992             value.equalsIgnoreCase("content-commitment") ||
6993             value.equalsIgnoreCase("contentCommitment"))
6994        {
6995          nonRepudiation = true;
6996        }
6997        else if (value.equalsIgnoreCase("key-encipherment") ||
6998             value.equalsIgnoreCase("keyEncipherment"))
6999        {
7000          keyEncipherment = true;
7001        }
7002        else if (value.equalsIgnoreCase("data-encipherment") ||
7003             value.equalsIgnoreCase("dataEncipherment"))
7004        {
7005          dataEncipherment = true;
7006        }
7007        else if (value.equalsIgnoreCase("key-agreement") ||
7008             value.equalsIgnoreCase("keyAgreement"))
7009        {
7010          keyAgreement = true;
7011        }
7012        else if (value.equalsIgnoreCase("key-cert-sign") ||
7013             value.equalsIgnoreCase("keyCertSign"))
7014        {
7015          keyCertSign = true;
7016        }
7017        else if (value.equalsIgnoreCase("crl-sign") ||
7018             value.equalsIgnoreCase("crlSign"))
7019        {
7020          crlSign = true;
7021        }
7022        else if (value.equalsIgnoreCase("encipher-only") ||
7023             value.equalsIgnoreCase("encipherOnly"))
7024        {
7025          encipherOnly = true;
7026        }
7027        else if (value.equalsIgnoreCase("decipher-only") ||
7028             value.equalsIgnoreCase("decipherOnly"))
7029        {
7030          decipherOnly = true;
7031        }
7032        else
7033        {
7034          wrapErr(0, WRAP_COLUMN,
7035               ERR_MANAGE_CERTS_GEN_CERT_INVALID_KEY_USAGE.get(value));
7036          return ResultCode.PARAM_ERROR;
7037        }
7038      }
7039
7040      keyUsage = new KeyUsageExtension(false, digitalSignature, nonRepudiation,
7041           keyEncipherment, dataEncipherment, keyAgreement, keyCertSign,
7042           crlSign, encipherOnly, decipherOnly);
7043      extensionList.add(keyUsage);
7044    }
7045
7046
7047    // Build an extended key usage extension, if appropriate.
7048    ExtendedKeyUsageExtension extendedKeyUsage = null;
7049    final StringArgument extendedKeyUsageArgument =
7050         subCommandParser.getStringArgument("extended-key-usage");
7051    if ((extendedKeyUsageArgument != null) &&
7052         extendedKeyUsageArgument.isPresent())
7053    {
7054      final List<String> values = extendedKeyUsageArgument.getValues();
7055      final ArrayList<OID> keyPurposeIDs = new ArrayList<>(values.size());
7056      for (final String value : values)
7057      {
7058        if (value.equalsIgnoreCase("server-auth") ||
7059             value.equalsIgnoreCase("serverAuth") ||
7060             value.equalsIgnoreCase("server-authentication") ||
7061             value.equalsIgnoreCase("serverAuthentication") ||
7062             value.equalsIgnoreCase("tls-server-authentication") ||
7063             value.equalsIgnoreCase("tlsServerAuthentication"))
7064        {
7065          keyPurposeIDs.add(
7066               ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID());
7067        }
7068        else if (value.equalsIgnoreCase("client-auth") ||
7069             value.equalsIgnoreCase("clientAuth") ||
7070             value.equalsIgnoreCase("client-authentication") ||
7071             value.equalsIgnoreCase("clientAuthentication") ||
7072             value.equalsIgnoreCase("tls-client-authentication") ||
7073             value.equalsIgnoreCase("tlsClientAuthentication"))
7074        {
7075          keyPurposeIDs.add(
7076               ExtendedKeyUsageID.TLS_CLIENT_AUTHENTICATION.getOID());
7077        }
7078        else if (value.equalsIgnoreCase("code-signing") ||
7079             value.equalsIgnoreCase("codeSigning"))
7080        {
7081          keyPurposeIDs.add(ExtendedKeyUsageID.CODE_SIGNING.getOID());
7082        }
7083        else if (value.equalsIgnoreCase("email-protection") ||
7084             value.equalsIgnoreCase("emailProtection"))
7085        {
7086          keyPurposeIDs.add(ExtendedKeyUsageID.EMAIL_PROTECTION.getOID());
7087        }
7088        else if (value.equalsIgnoreCase("time-stamping") ||
7089             value.equalsIgnoreCase("timeStamping"))
7090        {
7091          keyPurposeIDs.add(ExtendedKeyUsageID.TIME_STAMPING.getOID());
7092        }
7093        else if (value.equalsIgnoreCase("ocsp-signing") ||
7094             value.equalsIgnoreCase("ocspSigning"))
7095        {
7096          keyPurposeIDs.add(ExtendedKeyUsageID.OCSP_SIGNING.getOID());
7097        }
7098        else if (OID.isStrictlyValidNumericOID(value))
7099        {
7100          keyPurposeIDs.add(new OID(value));
7101        }
7102        else
7103        {
7104          wrapErr(0, WRAP_COLUMN,
7105               ERR_MANAGE_CERTS_GEN_CERT_INVALID_EXTENDED_KEY_USAGE.get(value));
7106          return ResultCode.PARAM_ERROR;
7107        }
7108      }
7109
7110      try
7111      {
7112        extendedKeyUsage = new ExtendedKeyUsageExtension(false, keyPurposeIDs);
7113      }
7114      catch (final Exception e)
7115      {
7116        // This should never happen.
7117        Debug.debugException(e);
7118        wrapErr(0, WRAP_COLUMN,
7119             ERR_MANAGE_CERTS_GEN_CERT_EXTENDED_KEY_USAGE_ERROR.get());
7120        e.printStackTrace(getErr());
7121        return ResultCode.PARAM_ERROR;
7122      }
7123
7124      extensionList.add(extendedKeyUsage);
7125    }
7126
7127
7128    // Build a list of generic extensions.
7129    final ArrayList<X509CertificateExtension> genericExtensions =
7130         new ArrayList<>(5);
7131    final StringArgument extensionArgument =
7132         subCommandParser.getStringArgument("extension");
7133    if ((extensionArgument != null) && extensionArgument.isPresent())
7134    {
7135      for (final String value : extensionArgument.getValues())
7136      {
7137        try
7138        {
7139          final int firstColonPos = value.indexOf(':');
7140          final int secondColonPos = value.indexOf(':', firstColonPos + 1);
7141          final OID oid = new OID(value.substring(0, firstColonPos));
7142          if (! oid.isStrictlyValidNumericOID())
7143          {
7144            wrapErr(0, WRAP_COLUMN,
7145                 ERR_MANAGE_CERTS_GEN_CERT_EXT_MALFORMED_OID.get(value,
7146                      oid.toString()));
7147            return ResultCode.PARAM_ERROR;
7148          }
7149
7150          final boolean criticality;
7151          final String criticalityString =
7152               value.substring(firstColonPos + 1, secondColonPos);
7153          if (criticalityString.equalsIgnoreCase("true") ||
7154               criticalityString.equalsIgnoreCase("t") ||
7155               criticalityString.equalsIgnoreCase("yes") ||
7156               criticalityString.equalsIgnoreCase("y") ||
7157               criticalityString.equalsIgnoreCase("on") ||
7158               criticalityString.equalsIgnoreCase("1"))
7159          {
7160            criticality = true;
7161          }
7162          else if (criticalityString.equalsIgnoreCase("false") ||
7163               criticalityString.equalsIgnoreCase("f") ||
7164               criticalityString.equalsIgnoreCase("no") ||
7165               criticalityString.equalsIgnoreCase("n") ||
7166               criticalityString.equalsIgnoreCase("off") ||
7167               criticalityString.equalsIgnoreCase("0"))
7168          {
7169            criticality = false;
7170          }
7171          else
7172          {
7173            wrapErr(0, WRAP_COLUMN,
7174                 ERR_MANAGE_CERTS_GEN_CERT_EXT_INVALID_CRITICALITY.get(
7175                      value, criticalityString));
7176            return ResultCode.PARAM_ERROR;
7177          }
7178
7179          final byte[] valueBytes;
7180          try
7181          {
7182            valueBytes = StaticUtils.fromHex(value.substring(secondColonPos+1));
7183          }
7184          catch (final Exception e)
7185          {
7186            Debug.debugException(e);
7187            wrapErr(0, WRAP_COLUMN,
7188                 ERR_MANAGE_CERTS_GEN_CERT_EXT_INVALID_VALUE.get(value));
7189            return ResultCode.PARAM_ERROR;
7190          }
7191
7192          final X509CertificateExtension extension =
7193               new X509CertificateExtension(oid, criticality, valueBytes);
7194          genericExtensions.add(extension);
7195          extensionList.add(extension);
7196        }
7197        catch (final Exception e)
7198        {
7199          Debug.debugException(e);
7200          wrapErr(0, WRAP_COLUMN,
7201               ERR_MANAGE_CERTS_GEN_CERT_EXT_MALFORMED.get(value));
7202          return ResultCode.PARAM_ERROR;
7203        }
7204      }
7205    }
7206
7207
7208    final String keystoreType;
7209    try
7210    {
7211      keystoreType = inferKeystoreType(keystorePath);
7212    }
7213    catch (final LDAPException le)
7214    {
7215      Debug.debugException(le);
7216      wrapErr(0, WRAP_COLUMN, le.getMessage());
7217      return le.getResultCode();
7218    }
7219
7220    final char[] keystorePassword;
7221    try
7222    {
7223      keystorePassword = getKeystorePassword(keystorePath);
7224    }
7225    catch (final LDAPException le)
7226    {
7227      Debug.debugException(le);
7228      wrapErr(0, WRAP_COLUMN, le.getMessage());
7229      return le.getResultCode();
7230    }
7231
7232
7233    // Get the keystore.
7234    final KeyStore keystore;
7235    try
7236    {
7237      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
7238    }
7239    catch (final LDAPException le)
7240    {
7241      Debug.debugException(le);
7242      wrapErr(0, WRAP_COLUMN, le.getMessage());
7243      return le.getResultCode();
7244    }
7245
7246
7247    // If there is a private key, then see if we need to use a private key
7248    // password that is different from the keystore password.
7249    final char[] privateKeyPassword;
7250    try
7251    {
7252      privateKeyPassword =
7253           getPrivateKeyPassword(keystore, alias, keystorePassword);
7254    }
7255    catch (final LDAPException le)
7256    {
7257      Debug.debugException(le);
7258      wrapErr(0, WRAP_COLUMN, le.getMessage());
7259      return le.getResultCode();
7260    }
7261
7262
7263    // If we're going to replace an existing certificate in the keystore, then
7264    // perform the appropriate processing for that.
7265    if (useExistingKeyPair)
7266    {
7267      // Make sure that the keystore already has a private key entry with the
7268      // specified alias.
7269      if (! hasKeyAlias(keystore, alias))
7270      {
7271        if (hasCertificateAlias(keystore, alias))
7272        {
7273          wrapErr(0, WRAP_COLUMN,
7274               ERR_MANAGE_CERTS_GEN_CERT_USE_EXISTING_KP_ALIAS_IS_CERT.get(
7275                    alias, keystorePath.getAbsolutePath()));
7276          return ResultCode.PARAM_ERROR;
7277        }
7278        else
7279        {
7280          wrapErr(0, WRAP_COLUMN,
7281               ERR_MANAGE_CERTS_GEN_CERT_USE_EXISTING_KP_NO_SUCH_ALIAS.get(
7282                    alias, keystorePath.getAbsolutePath()));
7283          return ResultCode.PARAM_ERROR;
7284        }
7285      }
7286
7287
7288      // Get the certificate to replace, along with its key pair.
7289      final X509Certificate certToReplace;
7290      final KeyPair keyPair;
7291      try
7292      {
7293        final Certificate[] chain = keystore.getCertificateChain(alias);
7294        certToReplace = new X509Certificate(chain[0].getEncoded());
7295
7296        final PublicKey publicKey = chain[0].getPublicKey();
7297        final PrivateKey privateKey =
7298             (PrivateKey) keystore.getKey(alias, privateKeyPassword);
7299        keyPair = new KeyPair(publicKey, privateKey);
7300      }
7301      catch (final Exception e)
7302      {
7303        Debug.debugException(e);
7304        wrapErr(0, WRAP_COLUMN,
7305             ERR_MANAGE_CERTS_GEN_CERT_USE_EXISTING_KP_COULD_NOT_GET_CERT.get(
7306                  alias));
7307        e.printStackTrace(getErr());
7308        return ResultCode.LOCAL_ERROR;
7309      }
7310
7311
7312      // Assign the remaining values using information in the existing
7313      // certificate.
7314      signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forOID(
7315           certToReplace.getSignatureAlgorithmOID());
7316      if (signatureAlgorithmIdentifier == null)
7317      {
7318        wrapErr(0, WRAP_COLUMN,
7319             ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG_IN_CERT.get(
7320                  certToReplace.getSignatureAlgorithmOID()));
7321        return ResultCode.PARAM_ERROR;
7322      }
7323      else
7324      {
7325        signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
7326      }
7327
7328      if (subjectDN == null)
7329      {
7330        subjectDN = certToReplace.getSubjectDN();
7331      }
7332
7333      if (inheritExtensions)
7334      {
7335        for (final X509CertificateExtension extension :
7336             certToReplace.getExtensions())
7337        {
7338          if ((extension instanceof AuthorityKeyIdentifierExtension) ||
7339              (extension instanceof IssuerAlternativeNameExtension))
7340          {
7341            // This extension applies to the issuer.  We won't include this in
7342            // the set of inherited extensions.
7343          }
7344          else if (extension instanceof SubjectKeyIdentifierExtension)
7345          {
7346            // The generated certificate will automatically include a subject
7347            // key identifier extension, so we don't need to include it.
7348          }
7349          else if (extension instanceof BasicConstraintsExtension)
7350          {
7351            // Don't override a value already provided on the command line.
7352            if (basicConstraints == null)
7353            {
7354              basicConstraints = (BasicConstraintsExtension) extension;
7355              extensionList.add(basicConstraints);
7356            }
7357          }
7358          else if (extension instanceof ExtendedKeyUsageExtension)
7359          {
7360            // Don't override a value already provided on the command line.
7361            if (extendedKeyUsage == null)
7362            {
7363              extendedKeyUsage = (ExtendedKeyUsageExtension) extension;
7364              extensionList.add(extendedKeyUsage);
7365            }
7366          }
7367          else if (extension instanceof KeyUsageExtension)
7368          {
7369            // Don't override a value already provided on the command line.
7370            if (keyUsage == null)
7371            {
7372              keyUsage = (KeyUsageExtension) extension;
7373              extensionList.add(keyUsage);
7374            }
7375          }
7376          else if (extension instanceof SubjectAlternativeNameExtension)
7377          {
7378            // Although we could merge values, it's safer to not do that if any
7379            // subject alternative name values were provided on the command
7380            // line.
7381            if (sanValues.isEmpty())
7382            {
7383              final SubjectAlternativeNameExtension e =
7384                   (SubjectAlternativeNameExtension) extension;
7385              for (final String dnsName : e.getDNSNames())
7386              {
7387                sanValues.add("DNS:" + dnsName);
7388              }
7389
7390              for (final InetAddress ipAddress : e.getIPAddresses())
7391              {
7392                sanValues.add("IP:" + ipAddress.getHostAddress());
7393              }
7394
7395              for (final String emailAddress : e.getRFC822Names())
7396              {
7397                sanValues.add("EMAIL:" + emailAddress);
7398              }
7399
7400              for (final String uri : e.getUniformResourceIdentifiers())
7401              {
7402                sanValues.add("URI:" + uri);
7403              }
7404
7405              for (final OID oid : e.getRegisteredIDs())
7406              {
7407                sanValues.add("OID:" + oid.toString());
7408              }
7409
7410              extensionList.add(extension);
7411            }
7412          }
7413          else
7414          {
7415            genericExtensions.add(extension);
7416            extensionList.add(extension);
7417          }
7418        }
7419      }
7420
7421
7422      // Create an array with the final set of extensions to include in the
7423      // certificate or certificate signing request.
7424      final X509CertificateExtension[] extensions =
7425           new X509CertificateExtension[extensionList.size()];
7426      extensionList.toArray(extensions);
7427
7428
7429      // If we're generating a self-signed certificate or a certificate signing
7430      // request, then we should now have everything we need to do that.  Build
7431      // a keytool command that we could use to accomplish it.
7432      if (isGenerateCertificate)
7433      {
7434        if (displayKeytoolCommand)
7435        {
7436          final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7437          keytoolArguments.add("-selfcert");
7438          keytoolArguments.add("-keystore");
7439          keytoolArguments.add(keystorePath.getAbsolutePath());
7440          keytoolArguments.add("-storetype");
7441          keytoolArguments.add(keystoreType);
7442          keytoolArguments.add("-storepass");
7443          keytoolArguments.add("*****REDACTED*****");
7444          keytoolArguments.add("-keypass");
7445          keytoolArguments.add("*****REDACTED*****");
7446          keytoolArguments.add("-alias");
7447          keytoolArguments.add(alias);
7448          keytoolArguments.add("-dname");
7449          keytoolArguments.add(subjectDN.toString());
7450          keytoolArguments.add("-sigalg");
7451          keytoolArguments.add(signatureAlgorithmName);
7452          keytoolArguments.add("-validity");
7453          keytoolArguments.add(String.valueOf(daysValid));
7454
7455          if (validityStartTime != null)
7456          {
7457            keytoolArguments.add("-startdate");
7458            keytoolArguments.add(formatValidityStartTime(validityStartTime));
7459          }
7460
7461          addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
7462               extendedKeyUsage, sanValues, ianValues, genericExtensions);
7463
7464          displayKeytoolCommand(keytoolArguments);
7465        }
7466
7467
7468        // Generate the self-signed certificate.
7469        final long notBefore;
7470        if (validityStartTime == null)
7471        {
7472          notBefore = System.currentTimeMillis();
7473        }
7474        else
7475        {
7476          notBefore = validityStartTime.getTime();
7477        }
7478
7479        final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
7480
7481        final X509Certificate certificate;
7482        final Certificate[] chain;
7483        try
7484        {
7485          certificate = X509Certificate.generateSelfSignedCertificate(
7486               signatureAlgorithmIdentifier, keyPair, subjectDN, notBefore,
7487               notAfter, extensions);
7488          chain = new Certificate[] { certificate.toCertificate() };
7489        }
7490        catch (final Exception e)
7491        {
7492          Debug.debugException(e);
7493          wrapErr(0, WRAP_COLUMN,
7494               ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CERT.get());
7495          e.printStackTrace(getErr());
7496          return ResultCode.LOCAL_ERROR;
7497        }
7498
7499
7500        // Update the keystore with the new certificate.
7501        try
7502        {
7503          keystore.setKeyEntry(alias, keyPair.getPrivate(), privateKeyPassword,
7504               chain);
7505          writeKeystore(keystore, keystorePath, keystorePassword);
7506        }
7507        catch (final Exception e)
7508        {
7509          Debug.debugException(e);
7510          wrapErr(0, WRAP_COLUMN,
7511               ERR_MANAGE_CERTS_GEN_CERT_ERROR_UPDATING_KEYSTORE.get());
7512          e.printStackTrace(getErr());
7513          return ResultCode.LOCAL_ERROR;
7514        }
7515
7516
7517        // Display the certificate we just generated to the end user.
7518        out();
7519        wrapOut(0, WRAP_COLUMN,
7520             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_SELF_CERT.
7521                  get());
7522        printCertificate(certificate, "", false);
7523
7524
7525        // If we should write an output file, then do that now.
7526        if (outputFile != null)
7527        {
7528          try (PrintStream ps = new PrintStream(outputFile))
7529          {
7530            final byte[] certBytes = certificate.getX509CertificateBytes();
7531            if (outputPEM)
7532            {
7533              writePEMCertificate(ps, certBytes);
7534            }
7535            else
7536            {
7537              ps.write(certBytes);
7538            }
7539
7540            out();
7541            wrapOut(0, WRAP_COLUMN,
7542                 INFO_MANAGE_CERTS_GEN_CERT_WROTE_OUTPUT_FILE.get(
7543                      outputFile.getAbsolutePath()));
7544          }
7545          catch (final Exception e)
7546          {
7547            Debug.debugException(e);
7548            err();
7549            wrapErr(0, WRAP_COLUMN,
7550                 ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CERT.get(
7551                      outputFile.getAbsolutePath()));
7552            e.printStackTrace(getErr());
7553            return ResultCode.LOCAL_ERROR;
7554          }
7555        }
7556
7557        return ResultCode.SUCCESS;
7558      }
7559      else
7560      {
7561        // Build the keytool command used to generate the certificate signing
7562        // request.
7563        Validator.ensureTrue(isGenerateCSR);
7564        if (displayKeytoolCommand)
7565        {
7566          final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7567          keytoolArguments.add("-certreq");
7568          keytoolArguments.add("-keystore");
7569          keytoolArguments.add(keystorePath.getAbsolutePath());
7570          keytoolArguments.add("-storetype");
7571          keytoolArguments.add(keystoreType);
7572          keytoolArguments.add("-storepass");
7573          keytoolArguments.add("*****REDACTED*****");
7574          keytoolArguments.add("-keypass");
7575          keytoolArguments.add("*****REDACTED*****");
7576          keytoolArguments.add("-alias");
7577          keytoolArguments.add(alias);
7578          keytoolArguments.add("-dname");
7579          keytoolArguments.add(subjectDN.toString());
7580          keytoolArguments.add("-sigalg");
7581          keytoolArguments.add(signatureAlgorithmName);
7582
7583          addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
7584               extendedKeyUsage, sanValues, ianValues, genericExtensions);
7585
7586          if (outputFile != null)
7587          {
7588            keytoolArguments.add("-file");
7589            keytoolArguments.add(outputFile.getAbsolutePath());
7590          }
7591
7592          displayKeytoolCommand(keytoolArguments);
7593        }
7594
7595
7596        // Generate the certificate signing request.
7597        final PKCS10CertificateSigningRequest certificateSigningRequest;
7598        try
7599        {
7600          certificateSigningRequest = PKCS10CertificateSigningRequest.
7601               generateCertificateSigningRequest(signatureAlgorithmIdentifier,
7602                    keyPair, subjectDN, extensions);
7603        }
7604        catch (final Exception e)
7605        {
7606          Debug.debugException(e);
7607          wrapErr(0, WRAP_COLUMN,
7608               ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CSR.get());
7609          e.printStackTrace(getErr());
7610          return ResultCode.LOCAL_ERROR;
7611        }
7612
7613
7614        // Write the generated certificate signing request to the appropriate
7615        // location.
7616        try
7617        {
7618          final PrintStream ps;
7619          if (outputFile == null)
7620          {
7621            ps = getOut();
7622          }
7623          else
7624          {
7625            ps = new PrintStream(outputFile);
7626          }
7627
7628          if (outputPEM)
7629          {
7630            writePEMCertificateSigningRequest(ps,
7631                 certificateSigningRequest.
7632                      getPKCS10CertificateSigningRequestBytes());
7633          }
7634          else
7635          {
7636            ps.write(certificateSigningRequest.
7637                 getPKCS10CertificateSigningRequestBytes());
7638          }
7639
7640          if (outputFile != null)
7641          {
7642            ps.close();
7643          }
7644        }
7645        catch (final Exception e)
7646        {
7647          Debug.debugException(e);
7648          wrapErr(0, WRAP_COLUMN,
7649               ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CSR.get());
7650          e.printStackTrace(getErr());
7651          return ResultCode.LOCAL_ERROR;
7652        }
7653
7654
7655        // If the certificate signing request was written to an output file,
7656        // then let the user know that it was successful.  If it was written to
7657        // standard output, then we don't need to tell them because they'll be
7658        // able to see it.
7659        if (outputFile != null)
7660        {
7661          out();
7662          wrapOut(0, WRAP_COLUMN,
7663               INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_CSR.get(
7664                    outputFile.getAbsolutePath()));
7665        }
7666
7667        return ResultCode.SUCCESS;
7668      }
7669    }
7670
7671
7672    // If we've gotten here, then we know we're not replacing an existing
7673    // certificate.  Perform any remaining argument assignment and validation.
7674    if ((subjectDN == null) && (! isSignCSR))
7675    {
7676      wrapErr(0, WRAP_COLUMN,
7677           ERR_MANAGE_CERTS_GEN_CERT_NO_SUBJECT_DN_WITHOUT_USE_EXISTING_KP.
7678                get());
7679      return ResultCode.PARAM_ERROR;
7680    }
7681
7682    if (keyAlgorithmIdentifier == null)
7683    {
7684      keyAlgorithmIdentifier = PublicKeyAlgorithmIdentifier.RSA;
7685      keyAlgorithmName = keyAlgorithmIdentifier.getName();
7686    }
7687
7688    if (keySizeBits == null)
7689    {
7690      keySizeBits = 2048;
7691    }
7692
7693    if ((signatureAlgorithmIdentifier == null) && (! isSignCSR))
7694    {
7695      signatureAlgorithmIdentifier =
7696           SignatureAlgorithmIdentifier.SHA_256_WITH_RSA;
7697      signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
7698    }
7699
7700
7701    // If we're going to generate a self-signed certificate or a certificate
7702    // signing request, then we first need to generate a key pair.  Put together
7703    // the appropriate set of keytool arguments and then generate a self-signed
7704    // certificate.
7705    if (isGenerateCertificate || isGenerateCSR)
7706    {
7707      // Make sure that the specified alias is not already in use in the
7708      // keystore.
7709      if (hasKeyAlias(keystore, alias) || hasCertificateAlias(keystore, alias))
7710      {
7711        wrapErr(0, WRAP_COLUMN,
7712             ERR_MANAGE_CERTS_GEN_CERT_ALIAS_EXISTS_WITHOUT_USE_EXISTING_KP.get(
7713                  alias));
7714        return ResultCode.PARAM_ERROR;
7715      }
7716
7717
7718      if (displayKeytoolCommand)
7719      {
7720        final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7721        keytoolArguments.add("-genkeypair");
7722        keytoolArguments.add("-keystore");
7723        keytoolArguments.add(keystorePath.getAbsolutePath());
7724        keytoolArguments.add("-storetype");
7725        keytoolArguments.add(keystoreType);
7726        keytoolArguments.add("-storepass");
7727        keytoolArguments.add("*****REDACTED*****");
7728        keytoolArguments.add("-keypass");
7729        keytoolArguments.add("*****REDACTED*****");
7730        keytoolArguments.add("-alias");
7731        keytoolArguments.add(alias);
7732        keytoolArguments.add("-dname");
7733        keytoolArguments.add(subjectDN.toString());
7734        keytoolArguments.add("-keyalg");
7735        keytoolArguments.add(keyAlgorithmName);
7736        keytoolArguments.add("-keysize");
7737        keytoolArguments.add(String.valueOf(keySizeBits));
7738        keytoolArguments.add("-sigalg");
7739        keytoolArguments.add(signatureAlgorithmName);
7740        keytoolArguments.add("-validity");
7741        keytoolArguments.add(String.valueOf(daysValid));
7742
7743        if (validityStartTime != null)
7744        {
7745          keytoolArguments.add("-startdate");
7746          keytoolArguments.add(formatValidityStartTime(validityStartTime));
7747        }
7748
7749        addExtensionArguments(keytoolArguments, basicConstraints,
7750             keyUsage, extendedKeyUsage, sanValues, ianValues,
7751             genericExtensions);
7752
7753        displayKeytoolCommand(keytoolArguments);
7754      }
7755
7756
7757      // Generate the self-signed certificate.
7758      final long notBefore;
7759      if (validityStartTime == null)
7760      {
7761        notBefore = System.currentTimeMillis();
7762      }
7763      else
7764      {
7765        notBefore = validityStartTime.getTime();
7766      }
7767
7768      final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
7769
7770      final X509CertificateExtension[] extensions =
7771           new X509CertificateExtension[extensionList.size()];
7772      extensionList.toArray(extensions);
7773
7774      final Certificate[] chain;
7775      final KeyPair keyPair;
7776      final X509Certificate certificate;
7777      try
7778      {
7779        final ObjectPair<X509Certificate,KeyPair> p =
7780             X509Certificate.generateSelfSignedCertificate(
7781                  signatureAlgorithmIdentifier, keyAlgorithmIdentifier,
7782                  keySizeBits, subjectDN, notBefore, notAfter, extensions);
7783        certificate = p.getFirst();
7784        chain = new Certificate[] { certificate.toCertificate() };
7785        keyPair = p.getSecond();
7786      }
7787      catch (final Exception e)
7788      {
7789        Debug.debugException(e);
7790        wrapErr(0, WRAP_COLUMN,
7791             ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CERT.get());
7792        e.printStackTrace(getErr());
7793        return ResultCode.LOCAL_ERROR;
7794      }
7795
7796
7797      // Update the keystore with the new certificate.
7798      try
7799      {
7800        keystore.setKeyEntry(alias, keyPair.getPrivate(), privateKeyPassword,
7801             chain);
7802        writeKeystore(keystore, keystorePath, keystorePassword);
7803      }
7804      catch (final Exception e)
7805      {
7806        Debug.debugException(e);
7807        wrapErr(0, WRAP_COLUMN,
7808             ERR_MANAGE_CERTS_GEN_CERT_ERROR_UPDATING_KEYSTORE.get());
7809        e.printStackTrace(getErr());
7810        return ResultCode.LOCAL_ERROR;
7811      }
7812
7813      if (isNewKeystore)
7814      {
7815        out();
7816        wrapOut(0, WRAP_COLUMN,
7817             INFO_MANAGE_CERTS_GEN_CERT_CERT_CREATED_KEYSTORE.get(
7818                  getUserFriendlyKeystoreType(keystoreType)));
7819      }
7820
7821
7822      // If we're just generating a self-signed certificate, then display the
7823      // certificate that we generated and potentially write it to an output
7824      // file.
7825      if (isGenerateCertificate)
7826      {
7827        out();
7828        wrapOut(0, WRAP_COLUMN,
7829             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_SELF_CERT.get());
7830        printCertificate(certificate, "", false);
7831
7832
7833        // If we should write an output file, then do that now.
7834        if (outputFile != null)
7835        {
7836          try (PrintStream ps = new PrintStream(outputFile))
7837          {
7838            final byte[] certBytes = certificate.getX509CertificateBytes();
7839            if (outputPEM)
7840            {
7841              writePEMCertificate(ps, certBytes);
7842            }
7843            else
7844            {
7845              ps.write(certBytes);
7846            }
7847
7848            out();
7849            wrapOut(0, WRAP_COLUMN,
7850                 INFO_MANAGE_CERTS_GEN_CERT_WROTE_OUTPUT_FILE.get(
7851                      outputFile.getAbsolutePath()));
7852          }
7853          catch (final Exception e)
7854          {
7855            Debug.debugException(e);
7856            err();
7857            wrapErr(0, WRAP_COLUMN,
7858                 ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CERT.get(
7859                      outputFile.getAbsolutePath()));
7860            e.printStackTrace(getErr());
7861            return ResultCode.LOCAL_ERROR;
7862          }
7863        }
7864
7865        return ResultCode.SUCCESS;
7866      }
7867
7868
7869      // If we're generating a certificate signing request, then put together
7870      // the appropriate set of arguments for that.
7871      Validator.ensureTrue(isGenerateCSR);
7872      out();
7873      wrapOut(0, WRAP_COLUMN,
7874           INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_KEYPAIR.get());
7875
7876      if (displayKeytoolCommand)
7877      {
7878        final ArrayList<String> keytoolArguments = new ArrayList<>(30);
7879        keytoolArguments.add("-certreq");
7880        keytoolArguments.add("-keystore");
7881        keytoolArguments.add(keystorePath.getAbsolutePath());
7882        keytoolArguments.add("-storetype");
7883        keytoolArguments.add(keystoreType);
7884        keytoolArguments.add("-storepass");
7885        keytoolArguments.add("*****REDACTED*****");
7886        keytoolArguments.add("-keypass");
7887        keytoolArguments.add("*****REDACTED*****");
7888        keytoolArguments.add("-alias");
7889        keytoolArguments.add(alias);
7890        keytoolArguments.add("-dname");
7891        keytoolArguments.add(subjectDN.toString());
7892        keytoolArguments.add("-sigalg");
7893        keytoolArguments.add(signatureAlgorithmName);
7894
7895        addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
7896             extendedKeyUsage, sanValues, ianValues, genericExtensions);
7897
7898        if (outputFile != null)
7899        {
7900          keytoolArguments.add("-file");
7901          keytoolArguments.add(outputFile.getAbsolutePath());
7902        }
7903
7904        displayKeytoolCommand(keytoolArguments);
7905      }
7906
7907
7908      // Generate the certificate signing request.
7909      final PKCS10CertificateSigningRequest certificateSigningRequest;
7910      try
7911      {
7912        certificateSigningRequest = PKCS10CertificateSigningRequest.
7913             generateCertificateSigningRequest(signatureAlgorithmIdentifier,
7914                  keyPair, subjectDN, extensions);
7915      }
7916      catch (final Exception e)
7917      {
7918        Debug.debugException(e);
7919        wrapErr(0, WRAP_COLUMN,
7920             ERR_MANAGE_CERTS_GEN_CERT_ERROR_GENERATING_CSR.get());
7921        e.printStackTrace(getErr());
7922        return ResultCode.LOCAL_ERROR;
7923      }
7924
7925
7926      // Write the generated certificate signing request to the appropriate
7927      // location.
7928      try
7929      {
7930        final PrintStream ps;
7931        if (outputFile == null)
7932        {
7933          ps = getOut();
7934        }
7935        else
7936        {
7937          ps = new PrintStream(outputFile);
7938        }
7939
7940        if (outputPEM)
7941        {
7942          writePEMCertificateSigningRequest(ps,
7943               certificateSigningRequest.
7944                    getPKCS10CertificateSigningRequestBytes());
7945        }
7946        else
7947        {
7948          ps.write(certificateSigningRequest.
7949               getPKCS10CertificateSigningRequestBytes());
7950        }
7951
7952        if (outputFile != null)
7953        {
7954          ps.close();
7955        }
7956      }
7957      catch (final Exception e)
7958      {
7959        Debug.debugException(e);
7960        wrapErr(0, WRAP_COLUMN,
7961             ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_CSR.get());
7962        e.printStackTrace(getErr());
7963        return ResultCode.LOCAL_ERROR;
7964      }
7965
7966
7967      // If the certificate signing request was written to an output file,
7968      // then let the user know that it was successful.  If it was written to
7969      // standard output, then we don't need to tell them because they'll be
7970      // able to see it.
7971      if (outputFile != null)
7972      {
7973        out();
7974        wrapOut(0, WRAP_COLUMN,
7975             INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_GENERATED_CSR.get(
7976                  outputFile.getAbsolutePath()));
7977      }
7978
7979      return ResultCode.SUCCESS;
7980    }
7981
7982
7983    // If we've gotten here, then we should be signing a certificate signing
7984    // request.  Make sure that the keystore already has a private key entry
7985    // with the specified alias.
7986    Validator.ensureTrue(isSignCSR);
7987    if (! hasKeyAlias(keystore, alias))
7988    {
7989      if (hasCertificateAlias(keystore, alias))
7990      {
7991        wrapErr(0, WRAP_COLUMN,
7992             ERR_MANAGE_CERTS_GEN_CERT_SIGN_ALIAS_IS_CERT.get(alias,
7993                  keystorePath.getAbsolutePath()));
7994        return ResultCode.PARAM_ERROR;
7995      }
7996      else
7997      {
7998        wrapErr(0, WRAP_COLUMN,
7999             ERR_MANAGE_CERTS_GEN_CERT_SIGN_NO_SUCH_ALIAS.get(alias,
8000                  keystorePath.getAbsolutePath()));
8001        return ResultCode.PARAM_ERROR;
8002      }
8003    }
8004
8005
8006    // Get the signing certificate and its key pair.
8007    final PrivateKey issuerPrivateKey;
8008    final X509Certificate issuerCertificate;
8009    try
8010    {
8011      final Certificate[] chain = keystore.getCertificateChain(alias);
8012      issuerCertificate = new X509Certificate(chain[0].getEncoded());
8013
8014      issuerPrivateKey =
8015           (PrivateKey) keystore.getKey(alias, privateKeyPassword);
8016    }
8017    catch (final Exception e)
8018    {
8019      Debug.debugException(e);
8020      wrapErr(0, WRAP_COLUMN,
8021           ERR_MANAGE_CERTS_GEN_CERT_SIGN_CANNOT_GET_SIGNING_CERT.get(alias));
8022      e.printStackTrace(getErr());
8023      return ResultCode.LOCAL_ERROR;
8024    }
8025
8026
8027    // Make sure that we can decode the certificate signing request.
8028    final PKCS10CertificateSigningRequest csr;
8029    try
8030    {
8031      csr = readCertificateSigningRequestFromFile(inputFile);
8032    }
8033    catch (final LDAPException le)
8034    {
8035      Debug.debugException(le);
8036      wrapErr(0, WRAP_COLUMN, le.getMessage());
8037      return le.getResultCode();
8038    }
8039
8040
8041    // Make sure that we can verify the certificate signing request's signature.
8042    try
8043    {
8044      csr.verifySignature();
8045    }
8046    catch (final CertException ce)
8047    {
8048      Debug.debugException(ce);
8049      wrapErr(0, WRAP_COLUMN, ce.getMessage());
8050      return ResultCode.PARAM_ERROR;
8051    }
8052
8053
8054    // Prompt about whether to sign the request, if appropriate.
8055    if (! noPrompt)
8056    {
8057      out();
8058      wrapOut(0, WRAP_COLUMN,
8059           INFO_MANAGE_CERTS_GEN_CERT_SIGN_CONFIRM.get());
8060      out();
8061      printCertificateSigningRequest(csr, false, "");
8062      out();
8063
8064      try
8065      {
8066        if (! promptForYesNo(
8067             INFO_MANAGE_CERTS_GEN_CERT_PROMPT_SIGN.get()))
8068        {
8069          wrapErr(0, WRAP_COLUMN,
8070               ERR_MANAGE_CERTS_GEN_CERT_SIGN_CANCELED.get());
8071          return ResultCode.USER_CANCELED;
8072        }
8073      }
8074      catch (final LDAPException le)
8075      {
8076        Debug.debugException(le);
8077        err();
8078        wrapErr(0, WRAP_COLUMN, le.getMessage());
8079        return le.getResultCode();
8080      }
8081    }
8082
8083
8084    // Read the certificate signing request and see if we need to take values
8085    // from it.
8086    if ((subjectDN == null) || (signatureAlgorithmIdentifier == null) ||
8087        includeRequestedExtensions)
8088    {
8089      if (subjectDN == null)
8090      {
8091        subjectDN = csr.getSubjectDN();
8092      }
8093
8094      if (signatureAlgorithmIdentifier == null)
8095      {
8096        signatureAlgorithmIdentifier = SignatureAlgorithmIdentifier.forOID(
8097             csr.getSignatureAlgorithmOID());
8098        if (signatureAlgorithmIdentifier == null)
8099        {
8100          wrapErr(0, WRAP_COLUMN,
8101               ERR_MANAGE_CERTS_GEN_CERT_UNKNOWN_SIG_ALG_IN_CSR.get(
8102                    csr.getSignatureAlgorithmOID()));
8103          return ResultCode.PARAM_ERROR;
8104        }
8105        else
8106        {
8107          signatureAlgorithmName = signatureAlgorithmIdentifier.getJavaName();
8108        }
8109      }
8110
8111      if (includeRequestedExtensions)
8112      {
8113        for (final X509CertificateExtension extension : csr.getExtensions())
8114        {
8115          if ((extension instanceof AuthorityKeyIdentifierExtension) ||
8116              (extension instanceof IssuerAlternativeNameExtension))
8117          {
8118            // This extension applies to the issuer.  We won't include this in
8119            // the set of inherited extensions.
8120          }
8121          else if (extension instanceof SubjectKeyIdentifierExtension)
8122          {
8123            // The generated certificate will automatically include a subject
8124            // key identifier extension, so we don't need to include it.
8125          }
8126          else if (extension instanceof BasicConstraintsExtension)
8127          {
8128            // Don't override a value already provided on the command line.
8129            if (basicConstraints == null)
8130            {
8131              basicConstraints = (BasicConstraintsExtension) extension;
8132              extensionList.add(basicConstraints);
8133            }
8134          }
8135          else if (extension instanceof ExtendedKeyUsageExtension)
8136          {
8137            // Don't override a value already provided on the command line.
8138            if (extendedKeyUsage == null)
8139            {
8140              extendedKeyUsage = (ExtendedKeyUsageExtension) extension;
8141              extensionList.add(extendedKeyUsage);
8142            }
8143          }
8144          else if (extension instanceof KeyUsageExtension)
8145          {
8146            // Don't override a value already provided on the command line.
8147            if (keyUsage == null)
8148            {
8149              keyUsage = (KeyUsageExtension) extension;
8150              extensionList.add(keyUsage);
8151            }
8152          }
8153          else if (extension instanceof SubjectAlternativeNameExtension)
8154          {
8155            // Although we could merge values, it's safer to not do that if any
8156            // subject alternative name values were provided on the command
8157            // line.
8158            if (sanValues.isEmpty())
8159            {
8160              final SubjectAlternativeNameExtension e =
8161                   (SubjectAlternativeNameExtension) extension;
8162              for (final String dnsName : e.getDNSNames())
8163              {
8164                sanBuilder.addDNSName(dnsName);
8165                sanValues.add("DNS:" + dnsName);
8166              }
8167
8168              for (final InetAddress ipAddress : e.getIPAddresses())
8169              {
8170                sanBuilder.addIPAddress(ipAddress);
8171                sanValues.add("IP:" + ipAddress.getHostAddress());
8172              }
8173
8174              for (final String emailAddress : e.getRFC822Names())
8175              {
8176                sanBuilder.addRFC822Name(emailAddress);
8177                sanValues.add("EMAIL:" + emailAddress);
8178              }
8179
8180              for (final String uri : e.getUniformResourceIdentifiers())
8181              {
8182                sanBuilder.addUniformResourceIdentifier(uri);
8183                sanValues.add("URI:" + uri);
8184              }
8185
8186              for (final OID oid : e.getRegisteredIDs())
8187              {
8188                sanBuilder.addRegisteredID(oid);
8189                sanValues.add("OID:" + oid.toString());
8190              }
8191
8192              try
8193              {
8194                extensionList.add(
8195                     new SubjectAlternativeNameExtension(false,
8196                          sanBuilder.build()));
8197              }
8198              catch (final Exception ex)
8199              {
8200                // This should never happen.
8201                Debug.debugException(ex);
8202                throw new RuntimeException(ex);
8203              }
8204            }
8205          }
8206          else
8207          {
8208            genericExtensions.add(extension);
8209            extensionList.add(extension);
8210          }
8211        }
8212      }
8213    }
8214
8215
8216    // Generate the keytool arguments to use to sign the requested certificate.
8217    final ArrayList<String> keytoolArguments = new ArrayList<>(30);
8218    keytoolArguments.add("-gencert");
8219    keytoolArguments.add("-keystore");
8220    keytoolArguments.add(keystorePath.getAbsolutePath());
8221    keytoolArguments.add("-storetype");
8222    keytoolArguments.add(keystoreType);
8223    keytoolArguments.add("-storepass");
8224    keytoolArguments.add("*****REDACTED*****");
8225    keytoolArguments.add("-keypass");
8226    keytoolArguments.add("*****REDACTED*****");
8227    keytoolArguments.add("-alias");
8228    keytoolArguments.add(alias);
8229    keytoolArguments.add("-dname");
8230    keytoolArguments.add(subjectDN.toString());
8231    keytoolArguments.add("-sigalg");
8232    keytoolArguments.add(signatureAlgorithmName);
8233    keytoolArguments.add("-validity");
8234    keytoolArguments.add(String.valueOf(daysValid));
8235
8236    if (validityStartTime != null)
8237    {
8238      keytoolArguments.add("-startdate");
8239      keytoolArguments.add(formatValidityStartTime(validityStartTime));
8240    }
8241
8242    addExtensionArguments(keytoolArguments, basicConstraints, keyUsage,
8243         extendedKeyUsage, sanValues, ianValues, genericExtensions);
8244
8245    keytoolArguments.add("-infile");
8246    keytoolArguments.add(inputFile.getAbsolutePath());
8247
8248    if (outputFile != null)
8249    {
8250      keytoolArguments.add("-outfile");
8251      keytoolArguments.add(outputFile.getAbsolutePath());
8252    }
8253
8254    if (outputPEM)
8255    {
8256      keytoolArguments.add("-rfc");
8257    }
8258
8259    if (displayKeytoolCommand)
8260    {
8261      displayKeytoolCommand(keytoolArguments);
8262    }
8263
8264
8265    // Generate the signed certificate.
8266    final long notBefore;
8267    if (validityStartTime == null)
8268    {
8269      notBefore = System.currentTimeMillis();
8270    }
8271    else
8272    {
8273      notBefore = validityStartTime.getTime();
8274    }
8275
8276    final long notAfter = notBefore + TimeUnit.DAYS.toMillis(daysValid);
8277
8278    final X509CertificateExtension[] extensions =
8279         new X509CertificateExtension[extensionList.size()];
8280    extensionList.toArray(extensions);
8281
8282    final X509Certificate signedCertificate;
8283    try
8284    {
8285      signedCertificate = X509Certificate.generateIssuerSignedCertificate(
8286           signatureAlgorithmIdentifier, issuerCertificate, issuerPrivateKey,
8287           csr.getPublicKeyAlgorithmOID(),
8288           csr.getPublicKeyAlgorithmParameters(), csr.getEncodedPublicKey(),
8289           csr.getDecodedPublicKey(), subjectDN, notBefore, notAfter,
8290           extensions);
8291    }
8292    catch (final Exception e)
8293    {
8294      Debug.debugException(e);
8295      wrapErr(0, WRAP_COLUMN,
8296           ERR_MANAGE_CERTS_GEN_CERT_ERROR_SIGNING_CERT.get());
8297      e.printStackTrace(getErr());
8298      return ResultCode.LOCAL_ERROR;
8299    }
8300
8301
8302    // Write the signed certificate signing request to the appropriate location.
8303    try
8304    {
8305      final PrintStream ps;
8306      if (outputFile == null)
8307      {
8308        ps = getOut();
8309      }
8310      else
8311      {
8312        ps = new PrintStream(outputFile);
8313      }
8314
8315      if (outputPEM)
8316      {
8317        writePEMCertificate(ps, signedCertificate.getX509CertificateBytes());
8318      }
8319      else
8320      {
8321        ps.write(signedCertificate.getX509CertificateBytes());
8322      }
8323
8324      if (outputFile != null)
8325      {
8326        ps.close();
8327      }
8328    }
8329    catch (final Exception e)
8330    {
8331      Debug.debugException(e);
8332      wrapErr(0, WRAP_COLUMN,
8333           ERR_MANAGE_CERTS_GEN_CERT_ERROR_WRITING_SIGNED_CERT.get());
8334      e.printStackTrace(getErr());
8335      return ResultCode.LOCAL_ERROR;
8336    }
8337
8338
8339    // If the certificate signing request was written to an output file,
8340    // then let the user know that it was successful.  If it was written to
8341    // standard output, then we don't need to tell them because they'll be
8342    // able to see it.
8343    if (outputFile != null)
8344    {
8345      out();
8346      wrapOut(0, WRAP_COLUMN,
8347           INFO_MANAGE_CERTS_GEN_CERT_SUCCESSFULLY_SIGNED_CERT.get(
8348                outputFile.getAbsolutePath()));
8349    }
8350
8351    return ResultCode.SUCCESS;
8352  }
8353
8354
8355
8356  /**
8357   * Performs the necessary processing for the change-certificate-alias
8358   * subcommand.
8359   *
8360   * @return  A result code that indicates whether the processing completed
8361   *          successfully.
8362   */
8363  @NotNull()
8364  private ResultCode doChangeCertificateAlias()
8365  {
8366    // Get the values of a number of configured arguments.
8367    final StringArgument currentAliasArgument =
8368         subCommandParser.getStringArgument("current-alias");
8369    final String currentAlias = currentAliasArgument.getValue();
8370
8371    final StringArgument newAliasArgument =
8372         subCommandParser.getStringArgument("new-alias");
8373    final String newAlias = newAliasArgument.getValue();
8374
8375    final String keystoreType;
8376    final File keystorePath = getKeystorePath();
8377    try
8378    {
8379      keystoreType = inferKeystoreType(keystorePath);
8380    }
8381    catch (final LDAPException le)
8382    {
8383      Debug.debugException(le);
8384      wrapErr(0, WRAP_COLUMN, le.getMessage());
8385      return le.getResultCode();
8386    }
8387
8388    final char[] keystorePassword;
8389    try
8390    {
8391      keystorePassword = getKeystorePassword(keystorePath);
8392    }
8393    catch (final LDAPException le)
8394    {
8395      Debug.debugException(le);
8396      wrapErr(0, WRAP_COLUMN, le.getMessage());
8397      return le.getResultCode();
8398    }
8399
8400
8401    // Get the keystore.
8402    final KeyStore keystore;
8403    try
8404    {
8405      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
8406    }
8407    catch (final LDAPException le)
8408    {
8409      Debug.debugException(le);
8410      wrapErr(0, WRAP_COLUMN, le.getMessage());
8411      return le.getResultCode();
8412    }
8413
8414
8415    // See if we need to use a private key password that is different from the
8416    // keystore password.
8417    final char[] privateKeyPassword;
8418    try
8419    {
8420      privateKeyPassword =
8421           getPrivateKeyPassword(keystore, currentAlias, keystorePassword);
8422    }
8423    catch (final LDAPException le)
8424    {
8425      Debug.debugException(le);
8426      wrapErr(0, WRAP_COLUMN, le.getMessage());
8427      return le.getResultCode();
8428    }
8429
8430
8431    // Make sure that the keystore has an existing entry with the current alias.
8432    // It must be either a certificate entry or a private key entry.
8433    final Certificate existingCertificate;
8434    final Certificate[] existingCertificateChain;
8435    final PrivateKey existingPrivateKey;
8436    try
8437    {
8438      if (hasCertificateAlias(keystore, currentAlias))
8439      {
8440        existingCertificate = keystore.getCertificate(currentAlias);
8441        existingCertificateChain = null;
8442        existingPrivateKey = null;
8443      }
8444      else if (hasKeyAlias(keystore, currentAlias))
8445      {
8446        existingCertificateChain = keystore.getCertificateChain(currentAlias);
8447        existingPrivateKey =
8448             (PrivateKey) keystore.getKey(currentAlias, privateKeyPassword);
8449        existingCertificate = null;
8450      }
8451      else
8452      {
8453        wrapErr(0, WRAP_COLUMN,
8454             ERR_MANAGE_CERTS_CHANGE_ALIAS_NO_SUCH_ALIAS.get(currentAlias));
8455        return ResultCode.PARAM_ERROR;
8456      }
8457    }
8458    catch (final Exception e)
8459    {
8460      Debug.debugException(e);
8461      wrapErr(0, WRAP_COLUMN,
8462           ERR_MANAGE_CERTS_CHANGE_ALIAS_CANNOT_GET_EXISTING_ENTRY.get(
8463                currentAlias));
8464      e.printStackTrace(getErr());
8465      return ResultCode.LOCAL_ERROR;
8466    }
8467
8468
8469    // Make sure that the keystore does not have an entry with the new alias.
8470    if (hasCertificateAlias(keystore, newAlias) ||
8471         hasKeyAlias(keystore, newAlias))
8472    {
8473      wrapErr(0, WRAP_COLUMN,
8474           ERR_MANAGE_CERTS_CHANGE_ALIAS_NEW_ALIAS_IN_USE.get(newAlias));
8475      return ResultCode.PARAM_ERROR;
8476    }
8477
8478
8479    // Generate the keytool arguments to use to change the certificate alias.
8480    final BooleanArgument displayKeytoolCommandArgument =
8481         subCommandParser.getBooleanArgument("display-keytool-command");
8482    if ((displayKeytoolCommandArgument != null) &&
8483          displayKeytoolCommandArgument.isPresent())
8484    {
8485      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
8486      keytoolArguments.add("-changealias");
8487      keytoolArguments.add("-keystore");
8488      keytoolArguments.add(keystorePath.getAbsolutePath());
8489      keytoolArguments.add("-storetype");
8490      keytoolArguments.add(keystoreType);
8491      keytoolArguments.add("-storepass");
8492      keytoolArguments.add("*****REDACTED*****");
8493      keytoolArguments.add("-keypass");
8494      keytoolArguments.add("*****REDACTED*****");
8495      keytoolArguments.add("-alias");
8496      keytoolArguments.add(currentAlias);
8497      keytoolArguments.add("-destalias");
8498      keytoolArguments.add(newAlias);
8499
8500      displayKeytoolCommand(keytoolArguments);
8501    }
8502
8503
8504    // Update the keystore to remove the entry with the current alias and
8505    // re-write it with the new alias.
8506    try
8507    {
8508      keystore.deleteEntry(currentAlias);
8509      if (existingCertificate != null)
8510      {
8511        keystore.setCertificateEntry(newAlias, existingCertificate);
8512      }
8513      else
8514      {
8515        keystore.setKeyEntry(newAlias, existingPrivateKey,
8516             privateKeyPassword, existingCertificateChain);
8517      }
8518
8519      writeKeystore(keystore, keystorePath, keystorePassword);
8520    }
8521    catch (final Exception e)
8522    {
8523      Debug.debugException(e);
8524      wrapErr(0, WRAP_COLUMN,
8525           ERR_MANAGE_CERTS_CHANGE_ALIAS_CANNOT_UPDATE_KEYSTORE.get());
8526      e.printStackTrace(getErr());
8527      return ResultCode.LOCAL_ERROR;
8528    }
8529
8530    wrapOut(0, WRAP_COLUMN,
8531         INFO_MANAGE_CERTS_CHANGE_ALIAS_SUCCESSFUL.get(currentAlias,
8532              newAlias));
8533    return ResultCode.SUCCESS;
8534  }
8535
8536
8537
8538  /**
8539   * Performs the necessary processing for the change-keystore-password
8540   * subcommand.
8541   *
8542   * @return  A result code that indicates whether the processing completed
8543   *          successfully.
8544   */
8545  @NotNull()
8546  private ResultCode doChangeKeystorePassword()
8547  {
8548    // Get the values of a number of configured arguments.
8549    final String keystoreType;
8550    final File keystorePath = getKeystorePath();
8551    try
8552    {
8553      keystoreType = inferKeystoreType(keystorePath);
8554    }
8555    catch (final LDAPException le)
8556    {
8557      Debug.debugException(le);
8558      wrapErr(0, WRAP_COLUMN, le.getMessage());
8559      return le.getResultCode();
8560    }
8561
8562    final char[] currentKeystorePassword;
8563    try
8564    {
8565      currentKeystorePassword = getKeystorePassword(keystorePath, "current");
8566    }
8567    catch (final LDAPException le)
8568    {
8569      Debug.debugException(le);
8570      wrapErr(0, WRAP_COLUMN, le.getMessage());
8571      return le.getResultCode();
8572    }
8573
8574    final char[] newKeystorePassword;
8575    try
8576    {
8577      newKeystorePassword = getKeystorePassword(keystorePath, "new");
8578    }
8579    catch (final LDAPException le)
8580    {
8581      Debug.debugException(le);
8582      wrapErr(0, WRAP_COLUMN, le.getMessage());
8583      return le.getResultCode();
8584    }
8585
8586
8587    // Get the keystore.
8588    final KeyStore keystore;
8589    try
8590    {
8591      keystore = getKeystore(keystoreType, keystorePath,
8592           currentKeystorePassword);
8593    }
8594    catch (final LDAPException le)
8595    {
8596      Debug.debugException(le);
8597      wrapErr(0, WRAP_COLUMN, le.getMessage());
8598      return le.getResultCode();
8599    }
8600
8601
8602    // Generate the keytool arguments to use to change the keystore password.
8603    final BooleanArgument displayKeytoolCommandArgument =
8604         subCommandParser.getBooleanArgument("display-keytool-command");
8605    if ((displayKeytoolCommandArgument != null) &&
8606          displayKeytoolCommandArgument.isPresent())
8607    {
8608      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
8609      keytoolArguments.add("-storepasswd");
8610      keytoolArguments.add("-keystore");
8611      keytoolArguments.add(keystorePath.getAbsolutePath());
8612      keytoolArguments.add("-storetype");
8613      keytoolArguments.add(keystoreType);
8614      keytoolArguments.add("-storepass");
8615      keytoolArguments.add("*****REDACTED*****");
8616      keytoolArguments.add("-new");
8617      keytoolArguments.add("*****REDACTED*****");
8618
8619      displayKeytoolCommand(keytoolArguments);
8620    }
8621
8622
8623    // Rewrite the keystore with the new password.
8624    try
8625    {
8626      writeKeystore(keystore, keystorePath, newKeystorePassword);
8627    }
8628    catch (final LDAPException le)
8629    {
8630      Debug.debugException(le);
8631      wrapErr(0, WRAP_COLUMN, le.getMessage());
8632      return le.getResultCode();
8633    }
8634
8635    wrapOut(0, WRAP_COLUMN,
8636         INFO_MANAGE_CERTS_CHANGE_KS_PW_SUCCESSFUL.get(
8637              keystorePath.getAbsolutePath()));
8638    return ResultCode.SUCCESS;
8639  }
8640
8641
8642
8643  /**
8644   * Performs the necessary processing for the change-private-key-password
8645   * subcommand.
8646   *
8647   * @return  A result code that indicates whether the processing completed
8648   *          successfully.
8649   */
8650  @NotNull()
8651  private ResultCode doChangePrivateKeyPassword()
8652  {
8653    // Get the values of a number of configured arguments.
8654    final StringArgument aliasArgument =
8655         subCommandParser.getStringArgument("alias");
8656    final String alias = aliasArgument.getValue();
8657
8658    final String keystoreType;
8659    final File keystorePath = getKeystorePath();
8660    try
8661    {
8662      keystoreType = inferKeystoreType(keystorePath);
8663    }
8664    catch (final LDAPException le)
8665    {
8666      Debug.debugException(le);
8667      wrapErr(0, WRAP_COLUMN, le.getMessage());
8668      return le.getResultCode();
8669    }
8670
8671    final char[] keystorePassword;
8672    try
8673    {
8674      keystorePassword = getKeystorePassword(keystorePath);
8675    }
8676    catch (final LDAPException le)
8677    {
8678      Debug.debugException(le);
8679      wrapErr(0, WRAP_COLUMN, le.getMessage());
8680      return le.getResultCode();
8681    }
8682
8683
8684    // Get the keystore.
8685    final KeyStore keystore;
8686    try
8687    {
8688      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
8689    }
8690    catch (final LDAPException le)
8691    {
8692      Debug.debugException(le);
8693      wrapErr(0, WRAP_COLUMN, le.getMessage());
8694      return le.getResultCode();
8695    }
8696
8697
8698    // Make sure that the keystore has a key entry with the specified alias.
8699    if (hasCertificateAlias(keystore, alias))
8700    {
8701      wrapErr(0, WRAP_COLUMN,
8702           ERR_MANAGE_CERTS_CHANGE_PK_PW_ALIAS_IS_CERT.get(alias));
8703      return ResultCode.PARAM_ERROR;
8704    }
8705    else if (! hasKeyAlias(keystore, alias))
8706    {
8707      wrapErr(0, WRAP_COLUMN,
8708           ERR_MANAGE_CERTS_CHANGE_PK_PW_NO_SUCH_ALIAS.get(alias));
8709      return ResultCode.PARAM_ERROR;
8710    }
8711
8712
8713    // Get the current and new private key passwords.
8714    final char[] currentPrivateKeyPassword;
8715    try
8716    {
8717      currentPrivateKeyPassword =
8718           getPrivateKeyPassword(keystore, alias, "current", keystorePassword);
8719    }
8720    catch (final LDAPException le)
8721    {
8722      Debug.debugException(le);
8723      wrapErr(0, WRAP_COLUMN, le.getMessage());
8724      return le.getResultCode();
8725    }
8726
8727    final char[] newPrivateKeyPassword;
8728    try
8729    {
8730      newPrivateKeyPassword =
8731           getPrivateKeyPassword(keystore, alias, "new", keystorePassword);
8732    }
8733    catch (final LDAPException le)
8734    {
8735      Debug.debugException(le);
8736      wrapErr(0, WRAP_COLUMN, le.getMessage());
8737      return le.getResultCode();
8738    }
8739
8740
8741    // Generate the keytool arguments to use to change the private key.
8742    final BooleanArgument displayKeytoolCommandArgument =
8743         subCommandParser.getBooleanArgument("display-keytool-command");
8744    if ((displayKeytoolCommandArgument != null) &&
8745          displayKeytoolCommandArgument.isPresent())
8746    {
8747      final ArrayList<String> keytoolArguments = new ArrayList<>(30);
8748      keytoolArguments.add("-keypasswd");
8749      keytoolArguments.add("-keystore");
8750      keytoolArguments.add(keystorePath.getAbsolutePath());
8751      keytoolArguments.add("-storetype");
8752      keytoolArguments.add(keystoreType);
8753      keytoolArguments.add("-storepass");
8754      keytoolArguments.add("*****REDACTED*****");
8755      keytoolArguments.add("-alias");
8756      keytoolArguments.add(alias);
8757      keytoolArguments.add("-keypass");
8758      keytoolArguments.add("*****REDACTED*****");
8759      keytoolArguments.add("-new");
8760      keytoolArguments.add("*****REDACTED*****");
8761
8762      displayKeytoolCommand(keytoolArguments);
8763    }
8764
8765
8766    // Get the contents of the private key entry.
8767    final Certificate[] chain;
8768    final PrivateKey privateKey;
8769    try
8770    {
8771      chain = keystore.getCertificateChain(alias);
8772      privateKey =
8773           (PrivateKey) keystore.getKey(alias, currentPrivateKeyPassword);
8774    }
8775    catch (final UnrecoverableKeyException e)
8776    {
8777      Debug.debugException(e);
8778      wrapErr(0, WRAP_COLUMN,
8779           ERR_MANAGE_CERTS_CHANGE_PK_PW_WRONG_PK_PW.get(alias));
8780      return ResultCode.PARAM_ERROR;
8781    }
8782    catch (final Exception e)
8783    {
8784      Debug.debugException(e);
8785      wrapErr(0, WRAP_COLUMN,
8786           ERR_MANAGE_CERTS_CHANGE_PK_PW_CANNOT_GET_PK.get(alias));
8787      e.printStackTrace(getErr());
8788      return ResultCode.LOCAL_ERROR;
8789    }
8790
8791
8792    // Remove the existing key entry and re-add it with the new password.
8793    try
8794    {
8795      keystore.deleteEntry(alias);
8796      keystore.setKeyEntry(alias, privateKey, newPrivateKeyPassword, chain);
8797      writeKeystore(keystore, keystorePath, keystorePassword);
8798    }
8799    catch (final Exception e)
8800    {
8801      Debug.debugException(e);
8802      wrapErr(0, WRAP_COLUMN,
8803           ERR_MANAGE_CERTS_CHANGE_PK_PW_CANNOT_UPDATE_KS.get());
8804      e.printStackTrace(getErr());
8805      return ResultCode.LOCAL_ERROR;
8806    }
8807
8808    wrapOut(0, WRAP_COLUMN,
8809         INFO_MANAGE_CERTS_CHANGE_PK_PW_SUCCESSFUL.get(alias));
8810    return ResultCode.SUCCESS;
8811  }
8812
8813
8814
8815  /**
8816   * Performs the necessary processing for the copy-keystore subcommand.
8817   *
8818   * @return  A result code that indicates whether the processing completed
8819   *          successfully.
8820   */
8821  @NotNull()
8822  private ResultCode doCopyKeystore()
8823  {
8824    // Get the source key store.
8825    final String sourceKeyStoreType;
8826    final File sourceKeyStorePath = getKeystorePath("source-keystore");
8827    try
8828    {
8829      sourceKeyStoreType = inferKeystoreType(sourceKeyStorePath, "source");
8830    }
8831    catch (final LDAPException le)
8832    {
8833      Debug.debugException(le);
8834      wrapErr(0, WRAP_COLUMN, le.getMessage());
8835      return le.getResultCode();
8836    }
8837
8838    final char[] sourceKeyStorePassword;
8839    try
8840    {
8841      sourceKeyStorePassword =
8842           getKeystorePassword(sourceKeyStorePath, "source");
8843    }
8844    catch (final LDAPException le)
8845    {
8846      Debug.debugException(le);
8847      wrapErr(0, WRAP_COLUMN, le.getMessage());
8848      return le.getResultCode();
8849    }
8850
8851    final KeyStore sourceKeyStore;
8852    try
8853    {
8854      sourceKeyStore = getKeystore(sourceKeyStoreType, sourceKeyStorePath,
8855           sourceKeyStorePassword);
8856    }
8857    catch (final LDAPException le)
8858    {
8859      Debug.debugException(le);
8860      wrapErr(0, WRAP_COLUMN, le.getMessage());
8861      return le.getResultCode();
8862    }
8863
8864
8865    // Get the destination key store.
8866    final String destinationKeyStoreType;
8867    final File destinationKeyStorePath =
8868         getKeystorePath("destination-keystore");
8869    try
8870    {
8871      destinationKeyStoreType = inferKeystoreType(destinationKeyStorePath,
8872           "destination");
8873    }
8874    catch (final LDAPException le)
8875    {
8876      Debug.debugException(le);
8877      wrapErr(0, WRAP_COLUMN, le.getMessage());
8878      return le.getResultCode();
8879    }
8880
8881    final boolean destinationExists = destinationKeyStorePath.exists();
8882
8883    char[] destinationKeyStorePassword;
8884    try
8885    {
8886      destinationKeyStorePassword =
8887           getKeystorePassword(destinationKeyStorePath, "destination");
8888      if (destinationKeyStorePassword == null)
8889      {
8890        destinationKeyStorePassword = sourceKeyStorePassword;
8891      }
8892    }
8893    catch (final LDAPException le)
8894    {
8895      Debug.debugException(le);
8896      wrapErr(0, WRAP_COLUMN, le.getMessage());
8897      return le.getResultCode();
8898    }
8899
8900    final KeyStore destinationKeyStore;
8901    try
8902    {
8903      destinationKeyStore = getKeystore(destinationKeyStoreType,
8904           destinationKeyStorePath, destinationKeyStorePassword);
8905    }
8906    catch (final LDAPException le)
8907    {
8908      Debug.debugException(le);
8909      wrapErr(0, WRAP_COLUMN, le.getMessage());
8910      return le.getResultCode();
8911    }
8912
8913
8914    // Get the value of the aliases argument, if it was provided.
8915    final Set<String> aliases = new LinkedHashSet<>();
8916    try
8917    {
8918      final StringArgument aliasArg =
8919           subCommandParser.getStringArgument("alias");
8920      if ((aliasArg != null) && aliasArg.isPresent())
8921      {
8922        for (final String alias : aliasArg.getValues())
8923        {
8924          aliases.add(alias);
8925          if (! sourceKeyStore.containsAlias(alias))
8926          {
8927            wrapErr(0, WRAP_COLUMN,
8928                 ERR_MANAGE_CERTS_COPY_KS_NO_SUCH_SOURCE_ALIAS.get(
8929                      sourceKeyStorePath.getAbsolutePath(), alias));
8930            return ResultCode.PARAM_ERROR;
8931          }
8932        }
8933      }
8934      else
8935      {
8936        final Enumeration<String> sourceAliases = sourceKeyStore.aliases();
8937        while (sourceAliases.hasMoreElements())
8938        {
8939          aliases.add(sourceAliases.nextElement());
8940        }
8941      }
8942    }
8943    catch (final Exception e)
8944    {
8945      Debug.debugException(e);
8946      wrapErr(0, WRAP_COLUMN,
8947           ERR_MANAGE_CERTS_COPY_KS_CANNOT_GET_SOURCE_ALIASES.get(
8948                sourceKeyStorePath.getAbsolutePath(),
8949                StaticUtils.getExceptionMessage(e)));
8950      return ResultCode.LOCAL_ERROR;
8951    }
8952
8953
8954    // If the set of aliases is empty and the destination key store already
8955    // exists, then exit without doing anything.
8956    if (aliases.isEmpty() && destinationExists)
8957    {
8958      wrapOut(0, WRAP_COLUMN,
8959           INFO_MANAGE_CERTS_COPY_KS_NO_CERTS_COPIED_EXISTING_KS.get(
8960                sourceKeyStorePath.getAbsolutePath(),
8961                destinationKeyStorePath.getAbsolutePath()));
8962      return ResultCode.SUCCESS;
8963    }
8964
8965
8966    // Make sure that none of the target aliases exist in the destination key
8967    // store.
8968    for (final String alias : aliases)
8969    {
8970      try
8971      {
8972        if (destinationKeyStore.containsAlias(alias))
8973        {
8974          wrapErr(0, WRAP_COLUMN,
8975               ERR_MANAGE_CERTS_COPY_KS_CONFLICTING_ALIAS.get(alias,
8976                    destinationKeyStorePath.getAbsolutePath(),
8977                    subCommandParser.getCommandName()));
8978          return ResultCode.CONSTRAINT_VIOLATION;
8979        }
8980      }
8981      catch (final Exception e)
8982      {
8983        Debug.debugException(e);
8984        wrapErr(0, WRAP_COLUMN,
8985             ERR_MANAGE_CERTS_COPY_KS_CANNOT_CHECK_DEST_ALIAS.get(alias,
8986                  destinationKeyStorePath.getAbsolutePath(),
8987                  StaticUtils.getExceptionMessage(e)));
8988        return ResultCode.LOCAL_ERROR;
8989      }
8990    }
8991
8992
8993    // Copy each of the targeted entries from the source key store into the
8994    // destination key store.
8995    char[] sourcePrivateKeyPassword = null;
8996    char[] destinationPrivateKeyPassword = null;
8997    for (final String alias : aliases)
8998    {
8999      try
9000      {
9001        if (sourceKeyStore.isCertificateEntry(alias))
9002        {
9003          final Certificate certificate = sourceKeyStore.getCertificate(alias);
9004          destinationKeyStore.setCertificateEntry(alias, certificate);
9005        }
9006        else
9007        {
9008          if (sourcePrivateKeyPassword == null)
9009          {
9010            sourcePrivateKeyPassword = getPrivateKeyPassword(sourceKeyStore,
9011                 alias, "source", sourceKeyStorePassword);
9012          }
9013
9014          if (destinationPrivateKeyPassword == null)
9015          {
9016            destinationPrivateKeyPassword = getPrivateKeyPassword(
9017                 destinationKeyStore, alias, "destination",
9018                 destinationKeyStorePassword);
9019          }
9020
9021          final Certificate[] chain = sourceKeyStore.getCertificateChain(alias);
9022          final Key key =
9023               sourceKeyStore.getKey(alias, sourcePrivateKeyPassword);
9024          destinationKeyStore.setKeyEntry(alias, key,
9025               destinationPrivateKeyPassword, chain);
9026        }
9027      }
9028      catch (final Exception e)
9029      {
9030        Debug.debugException(e);
9031        wrapErr(0, WRAP_COLUMN,
9032             ERR_MANAGE_CERTS_COPY_KS_ERROR_COPYING_ENTRY.get(alias,
9033                  sourceKeyStorePath.getAbsolutePath(),
9034                  destinationKeyStorePath.getAbsolutePath(),
9035                  StaticUtils.getExceptionMessage(e)));
9036        return ResultCode.LOCAL_ERROR;
9037      }
9038    }
9039
9040
9041    // Rewrite the destination keystore.
9042    try
9043    {
9044      writeKeystore(destinationKeyStore, destinationKeyStorePath,
9045           destinationKeyStorePassword);
9046    }
9047    catch (final LDAPException le)
9048    {
9049      Debug.debugException(le);
9050      wrapErr(0, WRAP_COLUMN, le.getMessage());
9051      return le.getResultCode();
9052    }
9053
9054    if (aliases.isEmpty())
9055    {
9056      // This should only happen if the alias argument was not provided, the
9057      // source key store is empty, and the destination key store doesn't exist.
9058      // In that case, the destination key store will have been created.
9059      wrapOut(0, WRAP_COLUMN,
9060           INFO_MANAGE_CERTS_COPY_KS_NO_CERTS_COPIED_KS_CREATED.get(
9061                sourceKeyStorePath.getAbsolutePath(),
9062                destinationKeyStoreType,
9063                destinationKeyStorePath.getAbsolutePath()));
9064    }
9065    else
9066    {
9067      // Write a message about the entries that were successfully copied.
9068      wrapOut(0, WRAP_COLUMN,
9069           INFO_MANAGE_CERTS_COPY_KS_CERTS_COPIED_HEADER.get(
9070                sourceKeyStorePath.getAbsolutePath(),
9071                destinationKeyStoreType,
9072                destinationKeyStorePath.getAbsolutePath()));
9073      for (final String alias : aliases)
9074      {
9075        out("* ", alias);
9076      }
9077    }
9078
9079    return ResultCode.SUCCESS;
9080  }
9081
9082
9083
9084  /**
9085   * Performs the necessary processing for the retrieve-server-certificate
9086   * subcommand.
9087   *
9088   * @return  A result code that indicates whether the processing completed
9089   *          successfully.
9090   */
9091  @NotNull()
9092  private ResultCode doRetrieveServerCertificate()
9093  {
9094    // Get the values of a number of configured arguments.
9095    final StringArgument hostnameArgument =
9096         subCommandParser.getStringArgument("hostname");
9097    final String hostname = hostnameArgument.getValue();
9098
9099    final IntegerArgument portArgument =
9100         subCommandParser.getIntegerArgument("port");
9101    final int port = portArgument.getValue();
9102
9103    final BooleanArgument useLDAPStartTLSArgument =
9104         subCommandParser.getBooleanArgument("use-ldap-start-tls");
9105    final boolean useLDAPStartTLS =
9106         ((useLDAPStartTLSArgument != null) &&
9107          useLDAPStartTLSArgument.isPresent());
9108
9109    final BooleanArgument onlyPeerArgument =
9110         subCommandParser.getBooleanArgument("only-peer-certificate");
9111    final boolean onlyPeer =
9112         ((onlyPeerArgument != null) && onlyPeerArgument.isPresent());
9113
9114    final BooleanArgument verboseArgument =
9115         subCommandParser.getBooleanArgument("verbose");
9116    final boolean verbose =
9117         ((verboseArgument != null) && verboseArgument.isPresent());
9118
9119    boolean outputPEM = true;
9120    final StringArgument outputFormatArgument =
9121         subCommandParser.getStringArgument("output-format");
9122    if ((outputFormatArgument != null) && outputFormatArgument.isPresent())
9123    {
9124      final String format = outputFormatArgument.getValue().toLowerCase();
9125      if (format.equals("der") || format.equals("binary") ||
9126          format.equals("bin"))
9127      {
9128        outputPEM = false;
9129      }
9130    }
9131
9132    File outputFile = null;
9133    final FileArgument outputFileArgument =
9134         subCommandParser.getFileArgument("output-file");
9135    if ((outputFileArgument != null) && outputFileArgument.isPresent())
9136    {
9137      outputFile = outputFileArgument.getValue();
9138    }
9139
9140
9141    // Spawn a background thread to establish a connection and get the
9142    // certificate chain from the target server.
9143    final LinkedBlockingQueue<Object> responseQueue =
9144         new LinkedBlockingQueue<>(10);
9145    final ManageCertificatesServerCertificateCollector certificateCollector =
9146         new ManageCertificatesServerCertificateCollector(this, hostname, port,
9147              useLDAPStartTLS, verbose, responseQueue);
9148    certificateCollector.start();
9149
9150    Object responseObject =
9151         ERR_MANAGE_CERTS_RETRIEVE_CERT_NO_CERT_CHAIN_RECEIVED.get(
9152              hostname + ':' + port);
9153    try
9154    {
9155      responseObject = responseQueue.poll(90L, TimeUnit.SECONDS);
9156    }
9157    catch (final Exception e)
9158    {
9159      Debug.debugException(e);
9160    }
9161
9162    final X509Certificate[] chain;
9163    if (responseObject instanceof  X509Certificate[])
9164    {
9165      chain = (X509Certificate[]) responseObject;
9166      if (chain.length == 0)
9167      {
9168        wrapErr(0, WRAP_COLUMN,
9169             ERR_MANAGE_CERTS_RETRIEVE_CERT_EMPTY_CHAIN.get());
9170        return ResultCode.NO_RESULTS_RETURNED;
9171      }
9172    }
9173    else if (responseObject instanceof CertException)
9174    {
9175      // The error message will have already been recorded by the collector
9176      // thread, so we can just return a non-success result.
9177      return ResultCode.LOCAL_ERROR;
9178    }
9179    else
9180    {
9181      wrapErr(0, WRAP_COLUMN, String.valueOf(responseObject));
9182      return ResultCode.LOCAL_ERROR;
9183    }
9184
9185    try
9186    {
9187      certificateCollector.join(10_000L);
9188    }
9189    catch (final Exception e)
9190    {
9191      Debug.debugException(e);
9192    }
9193
9194
9195    // If the certificates should be written to a file, then do that now.
9196    if (outputFile != null)
9197    {
9198      try (PrintStream s = new PrintStream(outputFile))
9199      {
9200        for (final X509Certificate c : chain)
9201        {
9202          if (outputPEM)
9203          {
9204            writePEMCertificate(s, c.getX509CertificateBytes());
9205          }
9206          else
9207          {
9208            s.write(c.getX509CertificateBytes());
9209          }
9210
9211          if (onlyPeer)
9212          {
9213            break;
9214          }
9215        }
9216      }
9217      catch (final Exception e)
9218      {
9219        Debug.debugException(e);
9220        wrapErr(0, WRAP_COLUMN,
9221             ERR_MANAGE_CERTS_RETRIEVE_CERT_CANNOT_WRITE_TO_FILE.get(
9222                  outputFile.getAbsolutePath(),
9223                  StaticUtils.getExceptionMessage(e)));
9224        return ResultCode.LOCAL_ERROR;
9225      }
9226    }
9227
9228
9229    // Display information about the certificates.
9230    for (int i=0; i < chain.length; i++)
9231    {
9232      if (verbose || (i > 0))
9233      {
9234        out();
9235        out();
9236      }
9237
9238      if ((! onlyPeer) && (chain.length > 1))
9239      {
9240        wrapOut(0, WRAP_COLUMN,
9241             INFO_MANAGE_CERTS_RETRIEVE_CERT_DISPLAY_HEADER.get((i+1),
9242                  chain.length));
9243        out();
9244      }
9245
9246      final X509Certificate c = chain[i];
9247      writePEMCertificate(getOut(), c.getX509CertificateBytes());
9248      out();
9249      printCertificate(c, "", verbose);
9250
9251      if (onlyPeer)
9252      {
9253        break;
9254      }
9255    }
9256
9257    return ResultCode.SUCCESS;
9258  }
9259
9260
9261
9262  /**
9263   * Performs the necessary processing for the trust-server-certificate
9264   * subcommand.
9265   *
9266   * @return  A result code that indicates whether the processing completed
9267   *          successfully.
9268   */
9269  @NotNull()
9270  private ResultCode doTrustServerCertificate()
9271  {
9272    // Get the values of a number of configured arguments.
9273    final StringArgument hostnameArgument =
9274         subCommandParser.getStringArgument("hostname");
9275    final String hostname = hostnameArgument.getValue();
9276
9277    final IntegerArgument portArgument =
9278         subCommandParser.getIntegerArgument("port");
9279    final int port = portArgument.getValue();
9280
9281    final String alias;
9282    final StringArgument aliasArgument =
9283         subCommandParser.getStringArgument("alias");
9284    if ((aliasArgument != null) && aliasArgument.isPresent())
9285    {
9286      alias = aliasArgument.getValue();
9287    }
9288    else
9289    {
9290      alias = hostname + ':' + port;
9291    }
9292
9293    final BooleanArgument useLDAPStartTLSArgument =
9294         subCommandParser.getBooleanArgument("use-ldap-start-tls");
9295    final boolean useLDAPStartTLS =
9296         ((useLDAPStartTLSArgument != null) &&
9297          useLDAPStartTLSArgument.isPresent());
9298
9299    final BooleanArgument issuersOnlyArgument =
9300         subCommandParser.getBooleanArgument("issuers-only");
9301    final boolean issuersOnly =
9302         ((issuersOnlyArgument != null) && issuersOnlyArgument.isPresent());
9303
9304    final BooleanArgument noPromptArgument =
9305         subCommandParser.getBooleanArgument("no-prompt");
9306    final boolean noPrompt =
9307         ((noPromptArgument != null) && noPromptArgument.isPresent());
9308
9309    final BooleanArgument verboseArgument =
9310         subCommandParser.getBooleanArgument("verbose");
9311    final boolean verbose =
9312         ((verboseArgument != null) && verboseArgument.isPresent());
9313
9314    final String keystoreType;
9315    final File keystorePath = getKeystorePath();
9316    final boolean isNewKeystore = (! keystorePath.exists());
9317    try
9318    {
9319      keystoreType = inferKeystoreType(keystorePath);
9320    }
9321    catch (final LDAPException le)
9322    {
9323      Debug.debugException(le);
9324      wrapErr(0, WRAP_COLUMN, le.getMessage());
9325      return le.getResultCode();
9326    }
9327
9328    final char[] keystorePassword;
9329    try
9330    {
9331      keystorePassword = getKeystorePassword(keystorePath);
9332    }
9333    catch (final LDAPException le)
9334    {
9335      Debug.debugException(le);
9336      wrapErr(0, WRAP_COLUMN, le.getMessage());
9337      return le.getResultCode();
9338    }
9339
9340
9341    // Get the keystore.
9342    final KeyStore keystore;
9343    try
9344    {
9345      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
9346    }
9347    catch (final LDAPException le)
9348    {
9349      Debug.debugException(le);
9350      wrapErr(0, WRAP_COLUMN, le.getMessage());
9351      return le.getResultCode();
9352    }
9353
9354
9355    // Make sure that the specified alias is not already in use.
9356    if (hasCertificateAlias(keystore, alias) ||
9357         hasKeyAlias(keystore, alias))
9358    {
9359      wrapErr(0, WRAP_COLUMN,
9360           ERR_MANAGE_CERTS_TRUST_SERVER_ALIAS_IN_USE.get(alias));
9361      return ResultCode.PARAM_ERROR;
9362    }
9363
9364
9365    // Spawn a background thread to establish a connection and get the
9366    // certificate chain from the target server.
9367    final LinkedBlockingQueue<Object> responseQueue =
9368         new LinkedBlockingQueue<>(10);
9369    final ManageCertificatesServerCertificateCollector certificateCollector =
9370         new ManageCertificatesServerCertificateCollector(this, hostname, port,
9371              useLDAPStartTLS, verbose, responseQueue);
9372    certificateCollector.start();
9373
9374    Object responseObject =
9375         ERR_MANAGE_CERTS_TRUST_SERVER_NO_CERT_CHAIN_RECEIVED.get(
9376              hostname + ':' + port);
9377    try
9378    {
9379      responseObject = responseQueue.poll(90L, TimeUnit.SECONDS);
9380    }
9381    catch (final Exception e)
9382    {
9383      Debug.debugException(e);
9384    }
9385
9386    final X509Certificate[] chain;
9387    if (responseObject instanceof  X509Certificate[])
9388    {
9389      chain = (X509Certificate[]) responseObject;
9390    }
9391    else if (responseObject instanceof CertException)
9392    {
9393      // The error message will have already been recorded by the collector
9394      // thread, so we can just return a non-success result.
9395      return ResultCode.LOCAL_ERROR;
9396    }
9397    else
9398    {
9399      wrapErr(0, WRAP_COLUMN, String.valueOf(responseObject));
9400      return ResultCode.LOCAL_ERROR;
9401    }
9402
9403    try
9404    {
9405      certificateCollector.join(10_000L);
9406    }
9407    catch (final Exception e)
9408    {
9409      Debug.debugException(e);
9410    }
9411
9412
9413    // If we should prompt the user about whether to trust the certificates,
9414    // then do so now.
9415    if (! noPrompt)
9416    {
9417      out();
9418      wrapOut(0, WRAP_COLUMN,
9419           INFO_MANAGE_CERTS_TRUST_SERVER_RETRIEVED_CHAIN.get(
9420                hostname + ':' + port));
9421
9422      boolean isFirst = true;
9423      for (final X509Certificate c : chain)
9424      {
9425        out();
9426
9427        if (isFirst)
9428        {
9429          isFirst = false;
9430          if (issuersOnly && (chain.length > 1))
9431          {
9432            wrapOut(0, WRAP_COLUMN,
9433                 INFO_MANAGE_CERTS_TRUST_SERVER_NOTE_OMITTED.get());
9434            out();
9435          }
9436        }
9437
9438        printCertificate(c, "", verbose);
9439      }
9440
9441      out();
9442
9443      try
9444      {
9445        if (! promptForYesNo(INFO_MANAGE_CERTS_TRUST_SERVER_PROMPT_TRUST.get()))
9446        {
9447          wrapErr(0, WRAP_COLUMN,
9448               ERR_MANAGE_CERTS_TRUST_SERVER_CHAIN_REJECTED.get());
9449          return ResultCode.USER_CANCELED;
9450        }
9451      }
9452      catch (final LDAPException le)
9453      {
9454        Debug.debugException(le);
9455        err();
9456        wrapErr(0, WRAP_COLUMN, le.getMessage());
9457        return le.getResultCode();
9458      }
9459    }
9460
9461
9462    // Add the certificates to the keystore.
9463    final LinkedHashMap<String,X509Certificate> certsByAlias =
9464         new LinkedHashMap<>(StaticUtils.computeMapCapacity(chain.length));
9465    for (int i=0; i < chain.length; i++)
9466    {
9467      if (i == 0)
9468      {
9469        if (issuersOnly && (chain.length > 1))
9470        {
9471          continue;
9472        }
9473
9474        certsByAlias.put(alias, chain[i]);
9475      }
9476      else if ((i == 1) && (chain.length == 2))
9477      {
9478        certsByAlias.put(alias + "-issuer", chain[i]);
9479      }
9480      else
9481      {
9482        certsByAlias.put(alias + "-issuer-" + i, chain[i]);
9483      }
9484    }
9485
9486    for (final Map.Entry<String,X509Certificate> e : certsByAlias.entrySet())
9487    {
9488      final String certAlias = e.getKey();
9489      final X509Certificate cert = e.getValue();
9490
9491      try
9492      {
9493        Validator.ensureFalse(
9494             (hasCertificateAlias(keystore, certAlias) ||
9495                  hasKeyAlias(keystore, certAlias)),
9496             "ERROR:  Alias '" + certAlias + "' is already in use in the " +
9497                  "keystore.");
9498        keystore.setCertificateEntry(certAlias, cert.toCertificate());
9499      }
9500      catch (final Exception ex)
9501      {
9502        Debug.debugException(ex);
9503        wrapErr(0, WRAP_COLUMN,
9504             ERR_MANAGE_CERTS_TRUST_SERVER_ERROR_ADDING_CERT_TO_KS.get(
9505                  cert.getSubjectDN()));
9506        ex.printStackTrace(getErr());
9507        return ResultCode.LOCAL_ERROR;
9508      }
9509    }
9510
9511
9512    // Save the updated keystore.
9513    try
9514    {
9515      writeKeystore(keystore, keystorePath, keystorePassword);
9516    }
9517    catch (final LDAPException le)
9518    {
9519      Debug.debugException(le);
9520      wrapErr(0, WRAP_COLUMN, le.getMessage());
9521      return le.getResultCode();
9522    }
9523
9524    if (isNewKeystore)
9525    {
9526      out();
9527      wrapOut(0, WRAP_COLUMN,
9528           INFO_MANAGE_CERTS_TRUST_SERVER_CERT_CREATED_KEYSTORE.get(
9529                getUserFriendlyKeystoreType(keystoreType)));
9530    }
9531
9532    out();
9533    if (certsByAlias.size() == 1)
9534    {
9535      wrapOut(0, WRAP_COLUMN,
9536           INFO_MANAGE_CERTS_TRUST_SERVER_ADDED_CERT_TO_KS.get());
9537    }
9538    else
9539    {
9540      wrapOut(0, WRAP_COLUMN,
9541           INFO_MANAGE_CERTS_TRUST_SERVER_ADDED_CERTS_TO_KS.get(
9542                certsByAlias.size()));
9543    }
9544
9545    return ResultCode.SUCCESS;
9546  }
9547
9548
9549
9550  /**
9551   * Performs the necessary processing for the check-certificate-usability
9552   * subcommand.
9553   *
9554   * @return  A result code that indicates whether the processing completed
9555   *          successfully.
9556   */
9557  @NotNull()
9558  private ResultCode doCheckCertificateUsability()
9559  {
9560    // Get the values of a number of configured arguments.
9561    final StringArgument aliasArgument =
9562         subCommandParser.getStringArgument("alias");
9563    final String alias = aliasArgument.getValue();
9564
9565    final String keystoreType;
9566    final File keystorePath = getKeystorePath();
9567    try
9568    {
9569      keystoreType = inferKeystoreType(keystorePath);
9570    }
9571    catch (final LDAPException le)
9572    {
9573      Debug.debugException(le);
9574      wrapErr(0, WRAP_COLUMN, le.getMessage());
9575      return le.getResultCode();
9576    }
9577
9578    final char[] keystorePassword;
9579    try
9580    {
9581      keystorePassword = getKeystorePassword(keystorePath);
9582    }
9583    catch (final LDAPException le)
9584    {
9585      Debug.debugException(le);
9586      wrapErr(0, WRAP_COLUMN, le.getMessage());
9587      return le.getResultCode();
9588    }
9589
9590
9591    // Get the keystore.
9592    final KeyStore keystore;
9593    try
9594    {
9595      keystore = getKeystore(keystoreType, keystorePath, keystorePassword);
9596    }
9597    catch (final LDAPException le)
9598    {
9599      Debug.debugException(le);
9600      wrapErr(0, WRAP_COLUMN, le.getMessage());
9601      return le.getResultCode();
9602    }
9603
9604
9605    // Make sure that the specified entry exists in the keystore and is
9606    // associated with a certificate chain and a private key.
9607    final X509Certificate[] chain;
9608    if (hasKeyAlias(keystore, alias))
9609    {
9610      try
9611      {
9612        final Certificate[] genericChain = keystore.getCertificateChain(alias);
9613        Validator.ensureTrue((genericChain.length > 0),
9614             "ERROR:  The keystore has a private key entry for alias '" +
9615                  alias + "', but the associated certificate chain is empty.");
9616
9617        chain = new X509Certificate[genericChain.length];
9618        for (int i=0; i < genericChain.length; i++)
9619        {
9620          chain[i] = new X509Certificate(genericChain[i].getEncoded());
9621        }
9622
9623        out();
9624        wrapOut(0, WRAP_COLUMN,
9625             INFO_MANAGE_CERTS_CHECK_USABILITY_GOT_CHAIN.get(alias));
9626
9627        for (final X509Certificate c : chain)
9628        {
9629          out();
9630          printCertificate(c, "", false);
9631        }
9632      }
9633      catch (final Exception e)
9634      {
9635        Debug.debugException(e);
9636        wrapErr(0, WRAP_COLUMN,
9637             ERR_MANAGE_CERTS_CHECK_USABILITY_CANNOT_GET_CHAIN.get(alias));
9638        e.printStackTrace(getErr());
9639        return ResultCode.LOCAL_ERROR;
9640      }
9641    }
9642    else if (hasCertificateAlias(keystore, alias))
9643    {
9644      wrapErr(0, WRAP_COLUMN,
9645           ERR_MANAGE_CERTS_CHECK_USABILITY_NO_PRIVATE_KEY.get(alias));
9646      return ResultCode.PARAM_ERROR;
9647    }
9648    else
9649    {
9650      wrapErr(0, WRAP_COLUMN,
9651           ERR_MANAGE_CERTS_CHECK_USABILITY_NO_SUCH_ALIAS.get(alias));
9652      return ResultCode.PARAM_ERROR;
9653    }
9654
9655
9656    // Check to see if the certificate is self-signed.  If so, then that's a
9657    // warning.  If not, then make sure that the chain is complete and that each
9658    // subsequent certificate is the issuer of the previous.
9659    int numWarnings = 0;
9660    int numErrors = 0;
9661    if (chain[0].isSelfSigned())
9662    {
9663      err();
9664      wrapErr(0, WRAP_COLUMN,
9665           WARN_MANAGE_CERTS_CHECK_USABILITY_CERT_IS_SELF_SIGNED.get(
9666                chain[0].getSubjectDN()));
9667      numWarnings++;
9668    }
9669    else if ((chain.length == 1) || (! chain[chain.length - 1].isSelfSigned()))
9670    {
9671      err();
9672      wrapErr(0, WRAP_COLUMN,
9673           ERR_MANAGE_CERTS_CHECK_USABILITY_END_OF_CHAIN_NOT_SELF_SIGNED.get(
9674                alias));
9675      numErrors++;
9676    }
9677    else
9678    {
9679      boolean chainError = false;
9680      final StringBuilder nonMatchReason = new StringBuilder();
9681      for (int i=1; i < chain.length; i++)
9682      {
9683        if (! chain[i].isIssuerFor(chain[i-1], nonMatchReason))
9684        {
9685          err();
9686          wrapErr(0, WRAP_COLUMN,
9687               ERR_MANAGE_CERTS_CHECK_USABILITY_CHAIN_ISSUER_MISMATCH.get(
9688                    alias, chain[i].getSubjectDN(), chain[i-1].getSubjectDN(),
9689                    nonMatchReason));
9690          numErrors++;
9691          chainError = true;
9692        }
9693      }
9694
9695      if (! chainError)
9696      {
9697        out();
9698        wrapOut(0, WRAP_COLUMN,
9699             INFO_MANAGE_CERTS_CHECK_USABILITY_CHAIN_COMPLETE.get());
9700      }
9701    }
9702
9703
9704    // If there are multiple certificates in the chain, and if the last
9705    // certificate in the chain is self-signed, then check to see if it is
9706    // contained in the JVM-default trust manager.  If it isn't, then we'll
9707    // display a notice, but we won't consider it a warning in and of itself.
9708    if ((chain.length > 1) && chain[chain.length-1].isSelfSigned())
9709    {
9710      final X509Certificate caCert = chain[chain.length-1];
9711
9712      try
9713      {
9714        final String jvmDefaultTrustStoreType =
9715             inferKeystoreType(JVM_DEFAULT_CACERTS_FILE);
9716        final KeyStore jvmDefaultTrustStore =
9717             CryptoHelper.getKeyStore(jvmDefaultTrustStoreType);
9718        try (FileInputStream inputStream =
9719                  new FileInputStream(JVM_DEFAULT_CACERTS_FILE))
9720        {
9721          jvmDefaultTrustStore.load(inputStream, null);
9722        }
9723
9724        boolean found = false;
9725        final Enumeration<String> aliases = jvmDefaultTrustStore.aliases();
9726        while (aliases.hasMoreElements())
9727        {
9728          final String jvmDefaultCertAlias = aliases.nextElement();
9729          if (jvmDefaultTrustStore.isCertificateEntry(jvmDefaultCertAlias))
9730          {
9731            final Certificate c =
9732                 jvmDefaultTrustStore.getCertificate(jvmDefaultCertAlias);
9733            final X509Certificate xc = new X509Certificate(c.getEncoded());
9734            if ((caCert.getSubjectDN().equals(xc.getSubjectDN())) &&
9735                 Arrays.equals(caCert.getSignatureValue().getBits(),
9736                      xc.getSignatureValue().getBits()))
9737            {
9738              found = true;
9739              break;
9740            }
9741          }
9742        }
9743
9744        if (found)
9745        {
9746          out();
9747          wrapOut(0, WRAP_COLUMN,
9748               INFO_MANAGE_CERTS_CHECK_USABILITY_CA_TRUSTED_OK.get(
9749                    caCert.getSubjectDN()));
9750        }
9751        else
9752        {
9753          out();
9754          wrapOut(0, WRAP_COLUMN,
9755               INFO_MANAGE_CERTS_CHECK_USABILITY_CA_NOT_IN_JVM_DEFAULT_TS.get(
9756                    caCert.getSubjectDN()));
9757        }
9758      }
9759      catch (final Exception e)
9760      {
9761        Debug.debugException(e);
9762        err();
9763        wrapErr(0, WRAP_COLUMN,
9764             WARN_MANAGE_CERTS_CHECK_USABILITY_CHECK_CA_IN_TS_ERROR.get(
9765                  caCert.getSubjectDN(), StaticUtils.getExceptionMessage(e)));
9766        numWarnings++;
9767      }
9768    }
9769
9770
9771    // Make sure that the signature is valid for each certificate in the
9772    // chain.  If any certificate has an invalid signature, then that's an
9773    // error.
9774    for (int i=0; i < chain.length; i++)
9775    {
9776      final X509Certificate c = chain[i];
9777
9778      try
9779      {
9780        if (c.isSelfSigned())
9781        {
9782          c.verifySignature(null);
9783        }
9784        else if ((i + 1) < chain.length)
9785        {
9786          c.verifySignature(chain[i+1]);
9787        }
9788
9789        out();
9790        wrapOut(0, WRAP_COLUMN,
9791             INFO_MANAGE_CERTS_CHECK_USABILITY_CERT_SIGNATURE_VALID.get(
9792                  c.getSubjectDN()));
9793      }
9794      catch (final CertException ce)
9795      {
9796        err();
9797        wrapErr(0, WRAP_COLUMN, ce.getMessage());
9798        numErrors++;
9799      }
9800    }
9801
9802
9803    // Check the validity window for each certificate in the chain.  If any of
9804    // them is expired or not yet valid, then that's an error.  If any of them
9805    // will expire in the near future, then that's a warning.
9806    final long currentTime = System.currentTimeMillis();
9807    final long thirtyDaysFromNow =
9808         currentTime + (30L * 24L * 60L * 60L * 1000L);
9809    for (int i=0; i < chain.length; i++)
9810    {
9811      final X509Certificate c = chain[i];
9812      if (c.getNotBeforeTime() > currentTime)
9813      {
9814        err();
9815        if (i == 0)
9816        {
9817          wrapErr(0, WRAP_COLUMN,
9818               ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NOT_YET_VALID.get(
9819                    c.getSubjectDN(), formatDateAndTime(c.getNotBeforeDate())));
9820        }
9821        else
9822        {
9823          wrapErr(0, WRAP_COLUMN,
9824               ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_NOT_YET_VALID.get(
9825                    c.getSubjectDN(), formatDateAndTime(c.getNotBeforeDate())));
9826        }
9827
9828        numErrors++;
9829      }
9830      else if (c.getNotAfterTime() < currentTime)
9831      {
9832        err();
9833        if (i == 0)
9834        {
9835          wrapErr(0, WRAP_COLUMN,
9836               ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_EXPIRED.get(
9837                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
9838        }
9839        else
9840        {
9841          wrapErr(0, WRAP_COLUMN,
9842               ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_EXPIRED.get(
9843                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
9844        }
9845
9846        numErrors++;
9847      }
9848      else if (c.getNotAfterTime() < thirtyDaysFromNow)
9849      {
9850        err();
9851        if (i == 0)
9852        {
9853          wrapErr(0, WRAP_COLUMN,
9854               WARN_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NEAR_EXPIRATION.get(
9855                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
9856        }
9857        else
9858        {
9859          wrapErr(0, WRAP_COLUMN,
9860               WARN_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_NEAR_EXPIRATION.
9861                    get(c.getSubjectDN(),
9862                         formatDateAndTime(c.getNotAfterDate())));
9863        }
9864
9865        numWarnings++;
9866      }
9867      else
9868      {
9869        if (i == 0)
9870        {
9871          out();
9872          wrapOut(0, WRAP_COLUMN,
9873               INFO_MANAGE_CERTS_CHECK_USABILITY_END_CERT_VALIDITY_OK.get(
9874                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
9875        }
9876        else
9877        {
9878          out();
9879          wrapOut(0, WRAP_COLUMN,
9880               INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_VALIDITY_OK.get(
9881                    c.getSubjectDN(), formatDateAndTime(c.getNotAfterDate())));
9882        }
9883      }
9884    }
9885
9886
9887    // Look at all of the extensions for all of the certificates and perform the
9888    // following validation:
9889    // - If the certificate at the head of the chain has an extended key usage
9890    //   extension, then make sure it includes the serverAuth usage.  If it
9891    //   does not include an extended key usage extension, then warn that it
9892    //   should.
9893    // - If any of the issuer certificates has a basic constraints extension,
9894    //   then make sure it indicates that the associated certificate is a
9895    //   certification authority.  Further, if it has a path length constraint,
9896    //   then make sure the chain does not exceed that length.  If any issuer
9897    //   certificate does not have a basic constraints extension, then warn that
9898    //   it should.
9899    // - If any of the issuer certificates has a key usage extension, then
9900    //   make sure it has the certSign usage.  If any issuer certificate does
9901    //   not have a key usage extension, then warn that it should.
9902    // - TODO:  If any certificate has a CRL distribution points extension, then
9903    //   retrieve the CRL and make sure the certificate hasn't been revoked.
9904    // - TODO:  If any certificate has an authority information access
9905    //   extension that points to an OCSP service, then consult that service to
9906    //   determine whether the certificate has been revoked.
9907    for (int i=0; i < chain.length; i++)
9908    {
9909      boolean basicConstraintsFound = false;
9910      boolean extendedKeyUsageFound = false;
9911      boolean keyUsageFound = false;
9912      final X509Certificate c = chain[i];
9913      for (final X509CertificateExtension extension : c.getExtensions())
9914      {
9915        if (extension instanceof ExtendedKeyUsageExtension)
9916        {
9917          extendedKeyUsageFound = true;
9918          if (i == 0)
9919          {
9920            final ExtendedKeyUsageExtension e =
9921                 (ExtendedKeyUsageExtension) extension;
9922            if (!e.getKeyPurposeIDs().contains(
9923                 ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID()))
9924            {
9925              err();
9926              wrapErr(0, WRAP_COLUMN,
9927                   ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_BAD_EKU.get(
9928                        c.getSubjectDN()));
9929              numErrors++;
9930            }
9931            else
9932            {
9933              out();
9934              wrapOut(0, WRAP_COLUMN,
9935                   INFO_MANAGE_CERTS_CHECK_USABILITY_END_CERT_GOOD_EKU.get(
9936                        c.getSubjectDN()));
9937            }
9938          }
9939        }
9940        else if (extension instanceof BasicConstraintsExtension)
9941        {
9942          basicConstraintsFound = true;
9943          if (i > 0)
9944          {
9945            final BasicConstraintsExtension e =
9946                 (BasicConstraintsExtension) extension;
9947            if (!e.isCA())
9948            {
9949              err();
9950              wrapErr(0, WRAP_COLUMN,
9951                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_BAD_BC_CA.get(
9952                        c.getSubjectDN()));
9953              numErrors++;
9954            }
9955            else if ((e.getPathLengthConstraint() != null) &&
9956                 ((i - 1) > e.getPathLengthConstraint()))
9957            {
9958              err();
9959              wrapErr(0, WRAP_COLUMN,
9960                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_BAD_BC_LENGTH.
9961                        get(c.getSubjectDN(), e.getPathLengthConstraint(),
9962                             chain[0].getSubjectDN(), (i-1)));
9963              numErrors++;
9964            }
9965            else
9966            {
9967              out();
9968              wrapOut(0, WRAP_COLUMN,
9969                   INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_CERT_GOOD_BC.get(
9970                        c.getSubjectDN()));
9971            }
9972          }
9973        }
9974        else if (extension instanceof KeyUsageExtension)
9975        {
9976          keyUsageFound = true;
9977          if (i > 0)
9978          {
9979            final KeyUsageExtension e = (KeyUsageExtension) extension;
9980            if (! e.isKeyCertSignBitSet())
9981            {
9982              err();
9983              wrapErr(0, WRAP_COLUMN,
9984                   ERR_MANAGE_CERTS_CHECK_USABILITY_ISSUER_NO_CERT_SIGN_KU.get(
9985                        c.getSubjectDN()));
9986              numErrors++;
9987            }
9988            else
9989            {
9990              out();
9991              wrapOut(0, WRAP_COLUMN,
9992                   INFO_MANAGE_CERTS_CHECK_USABILITY_ISSUER_GOOD_KU.get(
9993                        c.getSubjectDN()));
9994            }
9995          }
9996        }
9997      }
9998
9999      if (i == 0)
10000      {
10001        if (! extendedKeyUsageFound)
10002        {
10003          err();
10004          wrapErr(0, WRAP_COLUMN,
10005               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_EKU.get(
10006                    c.getSubjectDN()));
10007          numWarnings++;
10008        }
10009      }
10010      else
10011      {
10012        if (! basicConstraintsFound)
10013        {
10014          err();
10015          wrapErr(0, WRAP_COLUMN,
10016               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_BC.get(
10017                    c.getSubjectDN()));
10018          numWarnings++;
10019        }
10020
10021        if (! keyUsageFound)
10022        {
10023          err();
10024          wrapErr(0, WRAP_COLUMN,
10025               WARN_MANAGE_CERTS_CHECK_USABILITY_NO_KU.get(
10026                    c.getSubjectDN()));
10027          numWarnings++;
10028        }
10029      }
10030    }
10031
10032
10033    // Make sure that none of the certificates has a signature algorithm that
10034    // uses MD5 or SHA-1.  If it uses an unrecognized signature algorithm, then
10035    // that's a warning.
10036    boolean isIssuer = false;
10037    final BooleanArgument ignoreSHA1WarningArg =
10038         subCommandParser.getBooleanArgument(
10039              "allow-sha-1-signature-for-issuer-certificates");
10040    final boolean ignoreSHA1SignatureWarningForIssuerCertificates =
10041         ((ignoreSHA1WarningArg != null) && ignoreSHA1WarningArg.isPresent());
10042    for (final X509Certificate c : chain)
10043    {
10044      final OID signatureAlgorithmOID = c.getSignatureAlgorithmOID();
10045      final SignatureAlgorithmIdentifier id =
10046           SignatureAlgorithmIdentifier.forOID(signatureAlgorithmOID);
10047      if (id == null)
10048      {
10049        err();
10050        wrapErr(0, WRAP_COLUMN,
10051             WARN_MANAGE_CERTS_CHECK_USABILITY_UNKNOWN_SIG_ALG.get(
10052                  c.getSubjectDN(), signatureAlgorithmOID));
10053        numWarnings++;
10054      }
10055      else
10056      {
10057        switch (id)
10058        {
10059          case MD2_WITH_RSA:
10060          case MD5_WITH_RSA:
10061            err();
10062            wrapErr(0, WRAP_COLUMN,
10063                 ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_SIG_ALG.get(
10064                      c.getSubjectDN(), id.getUserFriendlyName()));
10065            numErrors++;
10066            break;
10067
10068          case SHA_1_WITH_RSA:
10069          case SHA_1_WITH_DSA:
10070          case SHA_1_WITH_ECDSA:
10071            if (isIssuer && ignoreSHA1SignatureWarningForIssuerCertificates)
10072            {
10073              err();
10074              wrapErr(0, WRAP_COLUMN,
10075                   WARN_MANAGE_CERTS_CHECK_USABILITY_ISSUER_WITH_SHA1_SIG.get(
10076                        c.getSubjectDN(), id.getUserFriendlyName(),
10077                        ignoreSHA1WarningArg.getIdentifierString()));
10078            }
10079            else
10080            {
10081              err();
10082              wrapErr(0, WRAP_COLUMN,
10083                   ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_SIG_ALG.get(
10084                        c.getSubjectDN(), id.getUserFriendlyName()));
10085              numErrors++;
10086            }
10087            break;
10088
10089          case SHA_224_WITH_RSA:
10090          case SHA_224_WITH_DSA:
10091          case SHA_224_WITH_ECDSA:
10092          case SHA_256_WITH_RSA:
10093          case SHA_256_WITH_DSA:
10094          case SHA_256_WITH_ECDSA:
10095          case SHA_384_WITH_RSA:
10096          case SHA_384_WITH_ECDSA:
10097          case SHA_512_WITH_RSA:
10098          case SHA_512_WITH_ECDSA:
10099            out();
10100            wrapOut(0, WRAP_COLUMN,
10101                 INFO_MANAGE_CERTS_CHECK_USABILITY_SIG_ALG_OK.get(
10102                      c.getSubjectDN(), id.getUserFriendlyName()));
10103            break;
10104        }
10105      }
10106
10107      isIssuer = true;
10108    }
10109
10110
10111    // Make sure that none of the certificates that uses the RSA key algorithm
10112    // has a public modulus size smaller than 2048 bits.
10113    for (final X509Certificate c : chain)
10114    {
10115      if ((c.getDecodedPublicKey() != null) &&
10116          (c.getDecodedPublicKey() instanceof RSAPublicKey))
10117      {
10118        final RSAPublicKey rsaPublicKey =
10119             (RSAPublicKey) c.getDecodedPublicKey();
10120        final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
10121        int modulusSizeBits = modulusBytes.length * 8;
10122        if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
10123        {
10124          modulusSizeBits -= 8;
10125        }
10126
10127        if (modulusSizeBits < 2048)
10128        {
10129          err();
10130          wrapErr(0, WRAP_COLUMN,
10131               ERR_MANAGE_CERTS_CHECK_USABILITY_WEAK_RSA_MODULUS.get(
10132                    c.getSubjectDN(), modulusSizeBits));
10133          numErrors++;
10134        }
10135        else
10136        {
10137          out();
10138          wrapOut(0, WRAP_COLUMN,
10139               INFO_MANAGE_CERTS_CHECK_USABILITY_RSA_MODULUS_OK.get(
10140                    c.getSubjectDN(), modulusSizeBits));
10141        }
10142      }
10143    }
10144
10145
10146    switch (numErrors)
10147    {
10148      case 0:
10149        break;
10150      case 1:
10151        err();
10152        wrapErr(0, WRAP_COLUMN,
10153             ERR_MANAGE_CERTS_CHECK_USABILITY_ONE_ERROR.get());
10154        return ResultCode.PARAM_ERROR;
10155      default:
10156        err();
10157        wrapErr(0, WRAP_COLUMN,
10158             ERR_MANAGE_CERTS_CHECK_USABILITY_MULTIPLE_ERRORS.get(numErrors));
10159        return ResultCode.PARAM_ERROR;
10160    }
10161
10162    switch (numWarnings)
10163    {
10164      case 0:
10165        out();
10166        wrapOut(0, WRAP_COLUMN,
10167             INFO_MANAGE_CERTS_CHECK_USABILITY_NO_ERRORS_OR_WARNINGS.get());
10168        return ResultCode.SUCCESS;
10169      case 1:
10170        err();
10171        wrapErr(0, WRAP_COLUMN,
10172             ERR_MANAGE_CERTS_CHECK_USABILITY_ONE_WARNING.get());
10173        return ResultCode.PARAM_ERROR;
10174      default:
10175        err();
10176        wrapErr(0, WRAP_COLUMN,
10177             ERR_MANAGE_CERTS_CHECK_USABILITY_MULTIPLE_WARNINGS.get(
10178                  numWarnings));
10179        return ResultCode.PARAM_ERROR;
10180    }
10181  }
10182
10183
10184
10185  /**
10186   * Performs the necessary processing for the display-certificate-file
10187   * subcommand.
10188   *
10189   * @return  A result code that indicates whether the processing completed
10190   *          successfully.
10191   */
10192  @NotNull()
10193  private ResultCode doDisplayCertificateFile()
10194  {
10195    // Get the values of a number of configured arguments.
10196    final FileArgument certificateFileArgument =
10197         subCommandParser.getFileArgument("certificate-file");
10198    final File certificateFile = certificateFileArgument.getValue();
10199
10200    final BooleanArgument verboseArgument =
10201         subCommandParser.getBooleanArgument("verbose");
10202    final boolean verbose =
10203         ((verboseArgument != null) && verboseArgument.isPresent());
10204
10205    final BooleanArgument displayKeytoolCommandArgument =
10206         subCommandParser.getBooleanArgument("display-keytool-command");
10207    if ((displayKeytoolCommandArgument != null) &&
10208        displayKeytoolCommandArgument.isPresent())
10209    {
10210      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
10211      keytoolArgs.add("-printcert");
10212      keytoolArgs.add("-file");
10213      keytoolArgs.add(certificateFile.getAbsolutePath());
10214
10215      if (verbose)
10216      {
10217        keytoolArgs.add("-v");
10218      }
10219
10220      displayKeytoolCommand(keytoolArgs);
10221    }
10222
10223
10224    // Read the certificates from the specified file.
10225    final List<X509Certificate> certificates;
10226    try
10227    {
10228      certificates = readCertificatesFromFile(certificateFile);
10229    }
10230    catch (final LDAPException le)
10231    {
10232      Debug.debugException(le);
10233      wrapErr(0, WRAP_COLUMN, le.getMessage());
10234      return le.getResultCode();
10235    }
10236
10237
10238    // If there aren't any certificates in the file, print that.
10239    if (certificates.isEmpty())
10240    {
10241      wrapOut(0, WRAP_COLUMN, INFO_MANAGE_CERTS_DISPLAY_CERT_NO_CERTS.get(
10242           certificateFile.getAbsolutePath()));
10243    }
10244    else
10245    {
10246      for (final X509Certificate c : certificates)
10247      {
10248        out();
10249        printCertificate(c, "", verbose);
10250      }
10251    }
10252
10253    return ResultCode.SUCCESS;
10254  }
10255
10256
10257
10258  /**
10259   * Performs the necessary processing for the
10260   * display-certificate-signing-request-file subcommand.
10261   *
10262   * @return  A result code that indicates whether the processing completed
10263   *          successfully.
10264   */
10265  @NotNull()
10266  private ResultCode doDisplayCertificateSigningRequestFile()
10267  {
10268    // Get the values of a number of configured arguments.
10269    final FileArgument csrFileArgument =
10270         subCommandParser.getFileArgument("certificate-signing-request-file");
10271    final File csrFile = csrFileArgument.getValue();
10272
10273    final BooleanArgument verboseArgument =
10274         subCommandParser.getBooleanArgument("verbose");
10275    final boolean verbose =
10276         ((verboseArgument != null) && verboseArgument.isPresent());
10277
10278    final BooleanArgument displayKeytoolCommandArgument =
10279         subCommandParser.getBooleanArgument("display-keytool-command");
10280    if ((displayKeytoolCommandArgument != null) &&
10281        displayKeytoolCommandArgument.isPresent())
10282    {
10283      final ArrayList<String> keytoolArgs = new ArrayList<>(10);
10284      keytoolArgs.add("-printcertreq");
10285      keytoolArgs.add("-file");
10286      keytoolArgs.add(csrFile.getAbsolutePath());
10287      keytoolArgs.add("-v");
10288
10289      displayKeytoolCommand(keytoolArgs);
10290    }
10291
10292
10293    // Read the certificate signing request from the specified file.
10294    final PKCS10CertificateSigningRequest csr;
10295    try
10296    {
10297      csr = readCertificateSigningRequestFromFile(csrFile);
10298    }
10299    catch (final LDAPException le)
10300    {
10301      Debug.debugException(le);
10302      wrapErr(0, WRAP_COLUMN, le.getMessage());
10303      return le.getResultCode();
10304    }
10305
10306    out();
10307    printCertificateSigningRequest(csr, verbose, "");
10308
10309    return ResultCode.SUCCESS;
10310  }
10311
10312
10313
10314  /**
10315   * Prints a string representation of the provided certificate to standard
10316   * output.
10317   *
10318   * @param  certificate  The certificate to be printed.
10319   * @param  indent       The string to place at the beginning of each line to
10320   *                      indent that line.
10321   * @param  verbose      Indicates whether to display verbose information about
10322   *                      the certificate.
10323   */
10324  private void printCertificate(@NotNull final X509Certificate certificate,
10325                                @NotNull final String indent,
10326                                final boolean verbose)
10327  {
10328    if (verbose)
10329    {
10330      out(indent +
10331           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VERSION.get(
10332                certificate.getVersion().getName()));
10333    }
10334
10335    out(indent +
10336         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_DN.get(
10337              certificate.getSubjectDN()));
10338    out(indent +
10339         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ISSUER_DN.get(
10340              certificate.getIssuerDN()));
10341
10342    if (verbose)
10343    {
10344      out(indent +
10345           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SERIAL_NUMBER.get(
10346                toColonDelimitedHex(
10347                     certificate.getSerialNumber().toByteArray())));
10348    }
10349
10350    out(indent +
10351         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_START.get(
10352              formatDateAndTime(certificate.getNotBeforeDate())));
10353    out(indent +
10354         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_END.get(
10355              formatDateAndTime(certificate.getNotAfterDate())));
10356
10357    final long currentTime = System.currentTimeMillis();
10358    if (currentTime < certificate.getNotBeforeTime())
10359    {
10360      out(indent +
10361           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_NOT_YET_VALID.
10362                get());
10363    }
10364    else if (currentTime > certificate.getNotAfterTime())
10365    {
10366      out(indent +
10367           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_EXPIRED.get());
10368    }
10369    else
10370    {
10371      out(indent +
10372           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_VALIDITY_STATE_VALID.get());
10373    }
10374
10375    out(indent +
10376         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_ALG.get(
10377              certificate.getSignatureAlgorithmNameOrOID()));
10378    if (verbose)
10379    {
10380      String signatureString;
10381      try
10382      {
10383        signatureString =
10384             toColonDelimitedHex(certificate.getSignatureValue().getBytes());
10385      }
10386      catch (final Exception e)
10387      {
10388        Debug.debugException(e);
10389        signatureString = certificate.getSignatureValue().toString();
10390      }
10391      out(indent +
10392           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_VALUE.get());
10393      for (final String line : StaticUtils.wrapLine(signatureString, 78))
10394      {
10395        out(indent + "     " + line);
10396      }
10397    }
10398
10399    final String pkAlg;
10400    final String pkSummary = getPublicKeySummary(
10401         certificate.getPublicKeyAlgorithmOID(),
10402         certificate.getDecodedPublicKey(),
10403         certificate.getPublicKeyAlgorithmParameters());
10404    if (pkSummary == null)
10405    {
10406      pkAlg = certificate.getPublicKeyAlgorithmNameOrOID();
10407    }
10408    else
10409    {
10410      pkAlg = certificate.getPublicKeyAlgorithmNameOrOID() + " (" +
10411           pkSummary + ')';
10412    }
10413    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_PK_ALG.get(pkAlg));
10414
10415    if (verbose)
10416    {
10417      printPublicKey(certificate.getEncodedPublicKey(),
10418           certificate.getDecodedPublicKey(),
10419           certificate.getPublicKeyAlgorithmParameters(), indent);
10420
10421      if (certificate.getSubjectUniqueID() != null)
10422      {
10423        String subjectUniqueID;
10424        try
10425        {
10426          subjectUniqueID = toColonDelimitedHex(
10427               certificate.getSubjectUniqueID().getBytes());
10428        }
10429        catch (final Exception e)
10430        {
10431          Debug.debugException(e);
10432          subjectUniqueID = certificate.getSubjectUniqueID().toString();
10433        }
10434
10435        out(indent +
10436             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_UNIQUE_ID.get());
10437        for (final String line : StaticUtils.wrapLine(subjectUniqueID, 78))
10438        {
10439          out(indent + "     " + line);
10440        }
10441      }
10442
10443      if (certificate.getIssuerUniqueID() != null)
10444      {
10445        String issuerUniqueID;
10446        try
10447        {
10448          issuerUniqueID = toColonDelimitedHex(
10449               certificate.getIssuerUniqueID().getBytes());
10450        }
10451        catch (final Exception e)
10452        {
10453          Debug.debugException(e);
10454          issuerUniqueID = certificate.getIssuerUniqueID().toString();
10455        }
10456
10457        out(indent +
10458             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ISSUER_UNIQUE_ID.get());
10459        for (final String line : StaticUtils.wrapLine(issuerUniqueID, 78))
10460        {
10461          out(indent + "     " + line);
10462        }
10463      }
10464
10465      printExtensions(certificate.getExtensions(), indent);
10466    }
10467
10468    try
10469    {
10470      out(indent +
10471           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_FINGERPRINT.get("SHA-1",
10472                toColonDelimitedHex(certificate.getSHA1Fingerprint())));
10473    }
10474    catch (final Exception e)
10475    {
10476      Debug.debugException(e);
10477    }
10478
10479    try
10480    {
10481      out(indent +
10482           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_FINGERPRINT.get("SHA-256",
10483                toColonDelimitedHex(certificate.getSHA256Fingerprint())));
10484    }
10485    catch (final Exception e)
10486    {
10487      Debug.debugException(e);
10488    }
10489  }
10490
10491
10492
10493  /**
10494   * Prints a string representation of the provided certificate signing request
10495   * to standard output.
10496   *
10497   * @param  csr      The certificate signing request to be printed.
10498   * @param  verbose  Indicates whether to display verbose information about
10499   *                  the contents of the request.
10500   * @param  indent   The string to place at the beginning of each line to
10501   *                  indent that line.
10502   */
10503  private void printCertificateSigningRequest(
10504                    @NotNull final PKCS10CertificateSigningRequest csr,
10505                    final boolean verbose, @NotNull final String indent)
10506  {
10507    out(indent +
10508         INFO_MANAGE_CERTS_PRINT_CSR_LABEL_VERSION.get(
10509              csr.getVersion().getName()));
10510    out(indent +
10511         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SUBJECT_DN.get(
10512              csr.getSubjectDN()));
10513    out(indent +
10514         INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_ALG.get(
10515              csr.getSignatureAlgorithmNameOrOID()));
10516
10517    if (verbose)
10518    {
10519      String signatureString;
10520      try
10521      {
10522        signatureString =
10523             toColonDelimitedHex(csr.getSignatureValue().getBytes());
10524      }
10525      catch (final Exception e)
10526      {
10527        Debug.debugException(e);
10528        signatureString = csr.getSignatureValue().toString();
10529      }
10530      out(indent +
10531           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_SIG_VALUE.get());
10532      for (final String line : StaticUtils.wrapLine(signatureString, 78))
10533      {
10534        out(indent + "     " + line);
10535      }
10536    }
10537
10538    final String pkAlg;
10539    final String pkSummary = getPublicKeySummary(csr.getPublicKeyAlgorithmOID(),
10540         csr.getDecodedPublicKey(), csr.getPublicKeyAlgorithmParameters());
10541    if (pkSummary == null)
10542    {
10543      pkAlg = csr.getPublicKeyAlgorithmNameOrOID();
10544    }
10545    else
10546    {
10547      pkAlg = csr.getPublicKeyAlgorithmNameOrOID() + " (" +
10548           pkSummary + ')';
10549    }
10550    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_PK_ALG.get(pkAlg));
10551
10552    if (verbose)
10553    {
10554      printPublicKey(csr.getEncodedPublicKey(), csr.getDecodedPublicKey(),
10555           csr.getPublicKeyAlgorithmParameters(), indent);
10556      printExtensions(csr.getExtensions(), indent);
10557    }
10558  }
10559
10560
10561
10562  /**
10563   * Prints information about the provided public key.
10564   *
10565   * @param  encodedPublicKey  The encoded representation of the public key.
10566   *                           This must not be {@code null}.
10567   * @param  decodedPublicKey  The decoded representation of the public key, if
10568   *                           available.
10569   * @param  parameters        The public key algorithm parameters, if any.
10570   * @param  indent            The string to place at the beginning of each
10571   *                           line to indent that line.
10572   */
10573  private void printPublicKey(@NotNull final ASN1BitString encodedPublicKey,
10574                              @Nullable final DecodedPublicKey decodedPublicKey,
10575                              @Nullable final ASN1Element parameters,
10576                              @NotNull final String indent)
10577  {
10578    if (decodedPublicKey == null)
10579    {
10580      String pkString;
10581      try
10582      {
10583        pkString = toColonDelimitedHex(encodedPublicKey.getBytes());
10584      }
10585      catch (final Exception e)
10586      {
10587        Debug.debugException(e);
10588        pkString = encodedPublicKey.toString();
10589      }
10590
10591      out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_ENCODED_PK.get());
10592      for (final String line : StaticUtils.wrapLine(pkString, 78))
10593      {
10594        out(indent + "     " + line);
10595      }
10596
10597      return;
10598    }
10599
10600    if (decodedPublicKey instanceof RSAPublicKey)
10601    {
10602      final RSAPublicKey rsaPublicKey = (RSAPublicKey) decodedPublicKey;
10603      final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
10604
10605      int modulusSizeBits = modulusBytes.length * 8;
10606      if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
10607      {
10608        modulusSizeBits -= 8;
10609      }
10610
10611      out(indent +
10612           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_RSA_MODULUS.get(
10613                modulusSizeBits));
10614      final String modulusHex = toColonDelimitedHex(modulusBytes);
10615      for (final String line : StaticUtils.wrapLine(modulusHex, 78))
10616      {
10617        out(indent + "     " + line);
10618      }
10619
10620      out(indent +
10621           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_RSA_EXPONENT.get(
10622                toColonDelimitedHex(
10623                     rsaPublicKey.getPublicExponent().toByteArray())));
10624    }
10625    else if (decodedPublicKey instanceof EllipticCurvePublicKey)
10626    {
10627      final EllipticCurvePublicKey ecPublicKey =
10628           (EllipticCurvePublicKey) decodedPublicKey;
10629
10630      out(indent +
10631           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_IS_COMPRESSED.get(
10632                String.valueOf(ecPublicKey.usesCompressedForm())));
10633      out(indent +
10634           INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_X.get(
10635                String.valueOf(ecPublicKey.getXCoordinate())));
10636      if (ecPublicKey.getYCoordinate() == null)
10637      {
10638        out(indent +
10639             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_Y_IS_EVEN.get(
10640                  String.valueOf(ecPublicKey.yCoordinateIsEven())));
10641      }
10642      else
10643      {
10644        out(indent +
10645             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EC_Y.get(
10646                  String.valueOf(ecPublicKey.getYCoordinate())));
10647      }
10648    }
10649  }
10650
10651
10652
10653  /**
10654   * Retrieves a short summary of the provided public key, if available.  For
10655   * RSA keys, this will be the modulus size in bits.  For elliptic curve keys,
10656   * this will be the named curve, if available.
10657   *
10658   * @param  publicKeyAlgorithmOID  The OID that identifies the type of public
10659   *                                key.
10660   * @param  publicKey              The decoded public key.  This may be
10661   *                                {@code null} if the decoded public key is
10662   *                                not available.
10663   * @param  parameters             The encoded public key algorithm parameters.
10664   *                                This may be {@code null} if no public key
10665   *                                algorithm parameters are available.
10666   *
10667   * @return  A short summary of the provided public key, or {@code null} if
10668   *          no summary is available.
10669   */
10670  @NotNull()
10671  private static String getPublicKeySummary(
10672                             @NotNull final OID publicKeyAlgorithmOID,
10673                             @Nullable final DecodedPublicKey publicKey,
10674                             @Nullable final ASN1Element parameters)
10675  {
10676    if (publicKey instanceof RSAPublicKey)
10677    {
10678      final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
10679      final byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray();
10680
10681      int modulusSizeBits = modulusBytes.length * 8;
10682      if (((modulusBytes.length % 2) != 0) && (modulusBytes[0] == 0x00))
10683      {
10684        modulusSizeBits -= 8;
10685      }
10686
10687      return INFO_MANAGE_CERTS_GET_PK_SUMMARY_RSA_MODULUS_SIZE.get(
10688           modulusSizeBits);
10689    }
10690    else if ((parameters != null) &&
10691         publicKeyAlgorithmOID.equals(PublicKeyAlgorithmIdentifier.EC.getOID()))
10692    {
10693      try
10694      {
10695        final OID namedCurveOID =
10696             parameters.decodeAsObjectIdentifier().getOID();
10697        return NamedCurve.getNameOrOID(namedCurveOID);
10698      }
10699      catch (final Exception e)
10700      {
10701        Debug.debugException(e);
10702      }
10703    }
10704
10705    return null;
10706  }
10707
10708
10709
10710  /**
10711   * Prints information about the provided extensions.
10712   *
10713   * @param  extensions  The list of extensions to be printed.
10714   * @param  indent      The string to place at the beginning of each line to
10715   *                     indent that line.
10716   */
10717  void printExtensions(@NotNull final List<X509CertificateExtension> extensions,
10718                       @NotNull final String indent)
10719  {
10720    if (extensions.isEmpty())
10721    {
10722      return;
10723    }
10724
10725    out(indent + INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXTENSIONS.get());
10726    for (final X509CertificateExtension extension : extensions)
10727    {
10728      if (extension instanceof AuthorityKeyIdentifierExtension)
10729      {
10730        out(indent + "     " +
10731             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_EXT.get());
10732        out(indent + "          " +
10733             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10734                  extension.getOID().toString()));
10735        out(indent + "          " +
10736             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10737                  String.valueOf(extension.isCritical())));
10738
10739        final AuthorityKeyIdentifierExtension e =
10740             (AuthorityKeyIdentifierExtension) extension;
10741        if (e.getKeyIdentifier() != null)
10742        {
10743          out(indent + "          " +
10744               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_ID.get());
10745          final String idHex =
10746               toColonDelimitedHex(e.getKeyIdentifier().getValue());
10747          for (final String line : StaticUtils.wrapLine(idHex, 78))
10748          {
10749            out(indent + "               " + line);
10750          }
10751        }
10752
10753        if (e.getAuthorityCertIssuer() != null)
10754        {
10755          out(indent + "          " +
10756               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_ISSUER.
10757                    get());
10758          printGeneralNames(e.getAuthorityCertIssuer(),
10759               indent + "               ");
10760        }
10761
10762        if (e.getAuthorityCertSerialNumber() != null)
10763        {
10764          out(indent + "          " +
10765               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_AUTH_KEY_ID_SERIAL.get(
10766                    toColonDelimitedHex(e.getAuthorityCertSerialNumber().
10767                         toByteArray())));
10768        }
10769      }
10770      else if (extension instanceof BasicConstraintsExtension)
10771      {
10772        out(indent + "     " +
10773             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_EXT.get());
10774        out(indent + "          " +
10775             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10776                  extension.getOID().toString()));
10777        out(indent + "          " +
10778             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10779                  String.valueOf(extension.isCritical())));
10780
10781        final BasicConstraintsExtension e =
10782             (BasicConstraintsExtension) extension;
10783        out(indent + "          " +
10784             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_IS_CA.get(
10785                  String.valueOf(e.isCA())));
10786
10787        if (e.getPathLengthConstraint() != null)
10788        {
10789          out(indent + "          " +
10790               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_BASIC_CONST_LENGTH.get(
10791                    e.getPathLengthConstraint()));
10792        }
10793      }
10794      else if (extension instanceof CRLDistributionPointsExtension)
10795      {
10796        out(indent + "     " +
10797             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_EXT.get());
10798        out(indent + "          " +
10799             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10800                  extension.getOID().toString()));
10801        out(indent + "          " +
10802             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10803                  String.valueOf(extension.isCritical())));
10804
10805        final CRLDistributionPointsExtension crlDPE =
10806             (CRLDistributionPointsExtension) extension;
10807        for (final CRLDistributionPoint dp :
10808             crlDPE.getCRLDistributionPoints())
10809        {
10810          out(indent + "          " +
10811               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_HEADER.get());
10812          if (dp.getFullName() != null)
10813          {
10814            out(indent + "               " +
10815                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_FULL_NAME.
10816                      get());
10817            printGeneralNames(dp.getFullName(),
10818                 indent + "                    ");
10819          }
10820
10821          if (dp.getNameRelativeToCRLIssuer() != null)
10822          {
10823            out(indent + "               " +
10824                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_REL_NAME.get(
10825                      dp.getNameRelativeToCRLIssuer()));
10826          }
10827
10828          if (! dp.getPotentialRevocationReasons().isEmpty())
10829          {
10830            out(indent + "               " +
10831                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_REASON.get());
10832            for (final CRLDistributionPointRevocationReason r :
10833                 dp.getPotentialRevocationReasons())
10834            {
10835              out(indent + "                    " + r.getName());
10836            }
10837          }
10838
10839          if (dp.getCRLIssuer() != null)
10840          {
10841            out(indent + "              " +
10842                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_CRL_DP_CRL_ISSUER.
10843                      get());
10844            printGeneralNames(dp.getCRLIssuer(),
10845                 indent + "                    ");
10846          }
10847        }
10848      }
10849      else if (extension instanceof ExtendedKeyUsageExtension)
10850      {
10851        out(indent + "     " +
10852             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_EXT.get());
10853        out(indent + "          " +
10854             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10855                  extension.getOID().toString()));
10856        out(indent + "          " +
10857             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10858                  String.valueOf(extension.isCritical())));
10859
10860        final ExtendedKeyUsageExtension e =
10861             (ExtendedKeyUsageExtension) extension;
10862        for (final OID oid : e.getKeyPurposeIDs())
10863        {
10864          final ExtendedKeyUsageID id = ExtendedKeyUsageID.forOID(oid);
10865          if (id == null)
10866          {
10867            out(indent + "          " +
10868                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_ID.get(oid));
10869          }
10870          else
10871          {
10872            out(indent + "          " +
10873                 INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_EKU_ID.get(
10874                      id.getName()));
10875          }
10876        }
10877      }
10878      else if (extension instanceof IssuerAlternativeNameExtension)
10879      {
10880        out(indent + "     " +
10881             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IAN_EXT.get());
10882        out(indent + "          " +
10883             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10884                  extension.getOID().toString()));
10885        out(indent + "          " +
10886             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10887                  String.valueOf(extension.isCritical())));
10888
10889        final IssuerAlternativeNameExtension e =
10890             (IssuerAlternativeNameExtension) extension;
10891        printGeneralNames(e.getGeneralNames(), indent + "          ");
10892      }
10893      else if (extension instanceof KeyUsageExtension)
10894      {
10895        out(indent + "     " +
10896             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_EXT.get());
10897        out(indent + "          " +
10898             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10899                  extension.getOID().toString()));
10900        out(indent + "          " +
10901             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10902                  String.valueOf(extension.isCritical())));
10903
10904        out(indent + "          " +
10905             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_USAGES.get());
10906        final KeyUsageExtension kue = (KeyUsageExtension) extension;
10907        if (kue.isDigitalSignatureBitSet())
10908        {
10909          out(indent + "               " +
10910               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DS.get());
10911        }
10912
10913        if (kue.isNonRepudiationBitSet())
10914        {
10915          out(indent + "               " +
10916               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_NR.get());
10917        }
10918
10919        if (kue.isKeyEnciphermentBitSet())
10920        {
10921          out(indent + "               " +
10922               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KE.get());
10923        }
10924
10925        if (kue.isDataEnciphermentBitSet())
10926        {
10927          out(indent + "               " +
10928               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DE.get());
10929        }
10930
10931        if (kue.isKeyAgreementBitSet())
10932        {
10933          out(indent + "               " +
10934               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KA.get());
10935        }
10936
10937        if (kue.isKeyCertSignBitSet())
10938        {
10939          out(indent + "               " +
10940               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_KCS.get());
10941        }
10942
10943        if (kue.isCRLSignBitSet())
10944        {
10945          out(indent + "               " +
10946               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_CRL_SIGN.get());
10947        }
10948
10949        if (kue.isEncipherOnlyBitSet())
10950        {
10951          out(indent + "               " +
10952               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_EO.get());
10953        }
10954
10955        if (kue.isDecipherOnlyBitSet())
10956        {
10957          out(indent + "               " +
10958               INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_KU_DO.get());
10959        }
10960      }
10961      else if (extension instanceof SubjectAlternativeNameExtension)
10962      {
10963        out(indent + "     " +
10964             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SAN_EXT.get());
10965        out(indent + "          " +
10966             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10967                  extension.getOID().toString()));
10968        out(indent + "          " +
10969             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10970                  String.valueOf(extension.isCritical())));
10971
10972        final SubjectAlternativeNameExtension e =
10973             (SubjectAlternativeNameExtension) extension;
10974        printGeneralNames(e.getGeneralNames(), indent + "          ");
10975      }
10976      else if (extension instanceof SubjectKeyIdentifierExtension)
10977      {
10978        out(indent + "     " +
10979             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SKI_EXT.get());
10980        out(indent + "          " +
10981             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
10982                  extension.getOID().toString()));
10983        out(indent + "          " +
10984             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
10985                  String.valueOf(extension.isCritical())));
10986
10987        final SubjectKeyIdentifierExtension e =
10988             (SubjectKeyIdentifierExtension) extension;
10989        final String idHex =
10990             toColonDelimitedHex(e.getKeyIdentifier().getValue());
10991        out(indent + "          " +
10992             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_SKI_ID.get());
10993        for (final String line  : StaticUtils.wrapLine(idHex, 78))
10994        {
10995          out(indent + "               " + line);
10996        }
10997      }
10998      else
10999      {
11000        out(indent + "     " +
11001             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_GENERIC.get());
11002        out(indent + "          " +
11003             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_OID.get(
11004                  extension.getOID().toString()));
11005        out(indent + "          " +
11006             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_IS_CRITICAL.get(
11007                  String.valueOf(extension.isCritical())));
11008
11009        final String valueHex = toColonDelimitedHex(extension.getValue());
11010        out(indent + "          " +
11011             INFO_MANAGE_CERTS_PRINT_CERT_LABEL_EXT_VALUE.get());
11012        getOut().print(StaticUtils.toHexPlusASCII(extension.getValue(),
11013             (indent.length() + 15)));
11014      }
11015    }
11016  }
11017
11018
11019
11020  /**
11021   * Prints information about the contents of the provided general names object.
11022   *
11023   * @param  generalNames  The general names object to print.
11024   * @param  indent        The string to place at the beginning of each line to
11025   *                       indent that line.
11026   */
11027  private void printGeneralNames(@NotNull final GeneralNames generalNames,
11028                                 @NotNull final String indent)
11029  {
11030    for (final String dnsName : generalNames.getDNSNames())
11031    {
11032      out(indent + INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_DNS.get(dnsName));
11033    }
11034
11035    for (final InetAddress ipAddress : generalNames.getIPAddresses())
11036    {
11037      out(indent +
11038           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_IP.get(
11039                ipAddress.getHostAddress()));
11040    }
11041
11042    for (final String name : generalNames.getRFC822Names())
11043    {
11044      out(indent +
11045           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_RFC_822_NAME.get(name));
11046    }
11047
11048    for (final DN dn : generalNames.getDirectoryNames())
11049    {
11050      out(indent +
11051           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_DIRECTORY_NAME.get(
11052                String.valueOf(dn)));
11053    }
11054
11055    for (final String uri : generalNames.getUniformResourceIdentifiers())
11056    {
11057      out(indent + INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_URI.get(uri));
11058    }
11059
11060    for (final OID oid : generalNames.getRegisteredIDs())
11061    {
11062      out(indent +
11063           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_REGISTERED_ID.get(
11064                oid.toString()));
11065    }
11066
11067    if (! generalNames.getOtherNames().isEmpty())
11068    {
11069      out(indent +
11070           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_OTHER_NAME_COUNT.get(
11071                generalNames.getOtherNames().size()));
11072    }
11073
11074    if (! generalNames.getX400Addresses().isEmpty())
11075    {
11076      out(indent +
11077           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_X400_ADDR_COUNT.get(
11078                generalNames.getX400Addresses().size()));
11079    }
11080
11081    if (! generalNames.getEDIPartyNames().isEmpty())
11082    {
11083      out(indent +
11084           INFO_MANAGE_CERTS_GENERAL_NAMES_LABEL_EDI_PARTY_NAME_COUNT.get(
11085                generalNames.getEDIPartyNames().size()));
11086    }
11087  }
11088
11089
11090
11091  /**
11092   * Writes a PEM-encoded representation of the provided encoded certificate to
11093   * the given print stream.
11094   *
11095   * @param  printStream         The print stream to which the PEM-encoded
11096   *                             certificate should be written.  It must not be
11097   *                             {@code null}.
11098   * @param  encodedCertificate  The bytes that comprise the encoded
11099   *                             certificate.  It must not be {@code null}.
11100   */
11101  private static void writePEMCertificate(
11102                           @NotNull final PrintStream printStream,
11103                           @NotNull final byte[] encodedCertificate)
11104  {
11105    final String certBase64 = Base64.encode(encodedCertificate);
11106    printStream.println("-----BEGIN CERTIFICATE-----");
11107    for (final String line : StaticUtils.wrapLine(certBase64, 64))
11108    {
11109      printStream.println(line);
11110    }
11111    printStream.println("-----END CERTIFICATE-----");
11112  }
11113
11114
11115
11116  /**
11117   * Writes a PEM-encoded representation of the provided encoded certificate
11118   * signing request to the given print stream.
11119   *
11120   * @param  printStream  The print stream to which the PEM-encoded certificate
11121   *                      signing request should be written.  It must not be
11122   *                      {@code null}.
11123   * @param  encodedCSR   The bytes that comprise the encoded certificate
11124   *                      signing request.  It must not be {@code null}.
11125   */
11126  private static void writePEMCertificateSigningRequest(
11127                           @NotNull final PrintStream printStream,
11128                           @NotNull final byte[] encodedCSR)
11129  {
11130    final String certBase64 = Base64.encode(encodedCSR);
11131    printStream.println("-----BEGIN CERTIFICATE REQUEST-----");
11132    for (final String line : StaticUtils.wrapLine(certBase64, 64))
11133    {
11134      printStream.println(line);
11135    }
11136    printStream.println("-----END CERTIFICATE REQUEST-----");
11137  }
11138
11139
11140
11141  /**
11142   * Writes a PEM-encoded representation of the provided encoded private key to
11143   * the given print stream.
11144   *
11145   * @param  printStream         The print stream to which the PEM-encoded
11146   *                             private key should be written.  It must not be
11147   *                             {@code null}.
11148   * @param  encodedPrivateKey   The bytes that comprise the encoded private
11149   *                             key.  It must not be {@code null}.
11150   * @param  encryptionPassword  The password to use to encrypt the private key.
11151   *                             It may be {@code null} if the private key
11152   *                             should not be encrypted.
11153   *
11154   * @throws  LDAPException  If a problem occurs while trying write the private
11155   *                         key.
11156   */
11157  private static void writePEMPrivateKey(
11158                           @NotNull final PrintStream printStream,
11159                           @NotNull final byte[] encodedPrivateKey,
11160                           @Nullable final char[] encryptionPassword)
11161          throws LDAPException
11162  {
11163    if (encryptionPassword == null)
11164    {
11165      final String certBase64 = Base64.encode(encodedPrivateKey);
11166      printStream.println("-----BEGIN PRIVATE KEY-----");
11167      for (final String line : StaticUtils.wrapLine(certBase64, 64))
11168      {
11169        printStream.println(line);
11170      }
11171      printStream.println("-----END PRIVATE KEY-----");
11172    }
11173    else
11174    {
11175      final byte[] encryptedPrivateKey;
11176      try
11177      {
11178        encryptedPrivateKey = PKCS8EncryptionHandler.encryptPrivateKey(
11179             encodedPrivateKey, encryptionPassword,
11180             new PKCS8EncryptionProperties());
11181      }
11182      catch (final CertException e)
11183      {
11184        Debug.debugException(e);
11185        throw new LDAPException(ResultCode.LOCAL_ERROR, e.getMessage(), e);
11186      }
11187
11188      final String encryptedCertBase64 = Base64.encode(encryptedPrivateKey);
11189      printStream.println("-----BEGIN ENCRYPTED PRIVATE KEY-----");
11190      for (final String line : StaticUtils.wrapLine(encryptedCertBase64, 64))
11191      {
11192        printStream.println(line);
11193      }
11194      printStream.println("-----END ENCRYPTED PRIVATE KEY-----");
11195    }
11196  }
11197
11198
11199
11200  /**
11201   * Displays the keytool command that can be invoked to produce approximately
11202   * equivalent functionality.
11203   *
11204   * @param  keytoolArgs  The arguments to provide to the keytool command.
11205   */
11206  private void displayKeytoolCommand(@NotNull final List<String> keytoolArgs)
11207  {
11208    final StringBuilder buffer = new StringBuilder();
11209    buffer.append("#      keytool");
11210
11211    boolean lastWasArgName = false;
11212    for (final String arg : keytoolArgs)
11213    {
11214      if (arg.startsWith("-"))
11215      {
11216        buffer.append(' ');
11217        buffer.append(StaticUtils.getCommandLineContinuationString());
11218        buffer.append(StaticUtils.EOL);
11219        buffer.append("#           ");
11220        buffer.append(arg);
11221        lastWasArgName = true;
11222      }
11223      else if (lastWasArgName)
11224      {
11225        buffer.append(' ');
11226        buffer.append(StaticUtils.cleanExampleCommandLineArgument(arg));
11227        lastWasArgName = false;
11228      }
11229      else
11230      {
11231        buffer.append(' ');
11232        buffer.append(StaticUtils.getCommandLineContinuationString());
11233        buffer.append(StaticUtils.EOL);
11234        buffer.append("#           ");
11235        buffer.append(arg);
11236        lastWasArgName = false;
11237      }
11238    }
11239
11240    out();
11241    out(INFO_MANAGE_CERTS_APPROXIMATE_KEYTOOL_COMMAND.get());
11242    out(buffer);
11243    out();
11244  }
11245
11246
11247
11248  /**
11249   * Retrieves the path to the target key store file.
11250   *
11251   * @return  The path to the target key store file, or {@code null} if no
11252   *          keystore path was configured.
11253   */
11254  @Nullable()
11255  private File getKeystorePath()
11256  {
11257    return getKeystorePath("keystore");
11258  }
11259
11260
11261
11262  /**
11263   * Retrieves the path to the target key store file.
11264   *
11265   * @param  keystoreArgumentName  The name of the argument used to specify the
11266   *                               path to the target key store.
11267   *
11268   * @return  The path to the target keystore file, or {@code null} if no
11269   *          keystore path was configured.
11270   */
11271  @Nullable()
11272  private File getKeystorePath(@NotNull final String keystoreArgumentName)
11273  {
11274    final FileArgument keystoreArgument =
11275         subCommandParser.getFileArgument(keystoreArgumentName);
11276    if ((keystoreArgument != null) && keystoreArgument.isPresent())
11277    {
11278      return keystoreArgument.getValue();
11279    }
11280
11281    final BooleanArgument useJVMDefaultTrustStoreArgument =
11282         subCommandParser.getBooleanArgument("useJVMDefaultTrustStore");
11283    if ((useJVMDefaultTrustStoreArgument != null) &&
11284         useJVMDefaultTrustStoreArgument.isPresent())
11285    {
11286      return JVM_DEFAULT_CACERTS_FILE;
11287    }
11288
11289    return null;
11290  }
11291
11292
11293
11294  /**
11295   * Retrieves the password needed to access the keystore.
11296   *
11297   * @param  keystoreFile  The path to the keystore file for which to get the
11298   *                       password.
11299   *
11300   * @return  The password needed to access the keystore, or {@code null} if
11301   *          no keystore password was configured.
11302   *
11303   * @throws  LDAPException  If a problem is encountered while trying to get the
11304   *                         keystore password.
11305   */
11306  @Nullable()
11307  private char[] getKeystorePassword(@NotNull final File keystoreFile)
11308          throws LDAPException
11309  {
11310    return getKeystorePassword(keystoreFile, null);
11311  }
11312
11313
11314
11315  /**
11316   * Retrieves the password needed to access the keystore.
11317   *
11318   * @param  keystoreFile  The path to the keystore file for which to get the
11319   *                       password.
11320   * @param  prefix        The prefix string to use for the arguments.  This may
11321   *                       be {@code null} if no prefix is needed.
11322   *
11323   * @return  The password needed to access the keystore, or {@code null} if
11324   *          no keystore password was configured.
11325   *
11326   * @throws  LDAPException  If a problem is encountered while trying to get the
11327   *                         keystore password.
11328   */
11329  @Nullable()
11330  private char[] getKeystorePassword(@NotNull final File keystoreFile,
11331                                     @Nullable final String prefix)
11332          throws LDAPException
11333  {
11334    final String prefixDash;
11335    if (prefix == null)
11336    {
11337      prefixDash = "";
11338    }
11339    else
11340    {
11341      prefixDash = prefix + '-';
11342    }
11343
11344    final StringArgument keystorePasswordArgument =
11345         subCommandParser.getStringArgument(prefixDash + "keystore-password");
11346    if ((keystorePasswordArgument != null) &&
11347         keystorePasswordArgument.isPresent())
11348    {
11349      final char[] keystorePWChars =
11350           keystorePasswordArgument.getValue().toCharArray();
11351      if ((! keystoreFile.exists()) && (keystorePWChars.length < 6))
11352      {
11353        throw new LDAPException(ResultCode.PARAM_ERROR,
11354             ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
11355      }
11356
11357      return keystorePWChars;
11358    }
11359
11360
11361    final FileArgument keystorePasswordFileArgument =
11362         subCommandParser.getFileArgument(
11363              prefixDash + "keystore-password-file");
11364    if ((keystorePasswordFileArgument != null) &&
11365        keystorePasswordFileArgument.isPresent())
11366    {
11367      final File f = keystorePasswordFileArgument.getValue();
11368      try
11369      {
11370        final char[] passwordChars = getPasswordFileReader().readPassword(f);
11371        if (passwordChars.length < 6)
11372        {
11373          throw new LDAPException(ResultCode.PARAM_ERROR,
11374               ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
11375        }
11376        return passwordChars;
11377      }
11378      catch (final LDAPException e)
11379      {
11380        Debug.debugException(e);
11381        throw e;
11382      }
11383      catch (final Exception e)
11384      {
11385        Debug.debugException(e);
11386        throw new LDAPException(ResultCode.LOCAL_ERROR,
11387             ERR_MANAGE_CERTS_GET_KS_PW_ERROR_READING_FILE.get(
11388                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
11389             e);
11390      }
11391    }
11392
11393
11394    final BooleanArgument promptArgument = subCommandParser.getBooleanArgument(
11395         "prompt-for-" + prefixDash + "keystore-password");
11396    if ((promptArgument != null) && promptArgument.isPresent())
11397    {
11398      out();
11399      if (keystoreFile.exists() && (! "new".equals(prefix)))
11400      {
11401        // We're only going to prompt once.
11402        if ((prefix != null) && prefix.equals("current"))
11403        {
11404          return promptForPassword(
11405               INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_CURRENT_PROMPT.get(
11406                    keystoreFile.getAbsolutePath()),
11407               false);
11408        }
11409        else
11410        {
11411          return promptForPassword(
11412               INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_PROMPT.get(
11413                    keystoreFile.getAbsolutePath()),
11414               false);
11415        }
11416      }
11417      else
11418      {
11419        // We're creating a new keystore, so we should prompt for the password
11420        // twice to prevent setting the wrong password because of a typo.
11421        while (true)
11422        {
11423          final String prompt1;
11424          if ("new".equals(prefix))
11425          {
11426            prompt1 = INFO_MANAGE_CERTS_KEY_KS_PW_EXISTING_NEW_PROMPT.get();
11427          }
11428          else
11429          {
11430            prompt1 = INFO_MANAGE_CERTS_KEY_KS_PW_NEW_PROMPT_1.get(
11431                 keystoreFile.getAbsolutePath());
11432          }
11433          final char[] pwChars = promptForPassword(prompt1, false);
11434
11435          if (pwChars.length < 6)
11436          {
11437            wrapErr(0, WRAP_COLUMN,
11438                 ERR_MANAGE_CERTS_GET_KS_PW_TOO_SHORT.get());
11439            err();
11440            continue;
11441          }
11442
11443          final char[] confirmChars = promptForPassword(
11444               INFO_MANAGE_CERTS_KEY_KS_PW_NEW_PROMPT_2.get(), true);
11445
11446          if (Arrays.equals(pwChars, confirmChars))
11447          {
11448            Arrays.fill(confirmChars, '\u0000');
11449            return pwChars;
11450          }
11451          else
11452          {
11453            wrapErr(0, WRAP_COLUMN,
11454                 ERR_MANAGE_CERTS_KEY_KS_PW_PROMPT_MISMATCH.get());
11455            err();
11456          }
11457        }
11458      }
11459    }
11460
11461
11462    return null;
11463  }
11464
11465
11466
11467  /**
11468   * Retrieves the password used to encrypt a private key.
11469   *
11470   * @param  forDecrypt  Indicates whether the password will be used to decrypt
11471   *                     the private key rather than to encrypt it.
11472   *
11473   * @return  The password used to encrypt a private key, or {@code null} if no
11474   *          encryption password was specified.
11475   *
11476   * @throws  LDAPException  If a problem is encountered while trying to get the
11477   *                         keystore password.
11478   */
11479  @Nullable()
11480  private char[] getPrivateKeyEncryptionPassword(final boolean forDecrypt)
11481          throws LDAPException
11482  {
11483    final StringArgument passwordArgument =
11484         subCommandParser.getStringArgument("encryption-password");
11485    if ((passwordArgument != null) && passwordArgument.isPresent())
11486    {
11487      return passwordArgument.getValue().toCharArray();
11488    }
11489
11490
11491    final FileArgument passwordFileArgument =
11492         subCommandParser.getFileArgument("encryption-password-file");
11493    if ((passwordFileArgument != null) && passwordFileArgument.isPresent())
11494    {
11495      final File f = passwordFileArgument.getValue();
11496      try
11497      {
11498        return getPasswordFileReader().readPassword(f);
11499      }
11500      catch (final LDAPException e)
11501      {
11502        Debug.debugException(e);
11503        throw e;
11504      }
11505      catch (final Exception e)
11506      {
11507        Debug.debugException(e);
11508        throw new LDAPException(ResultCode.LOCAL_ERROR,
11509             ERR_MANAGE_CERTS_GET_PK_ENC_PW_ERROR_READING_FILE.get(
11510                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
11511             e);
11512      }
11513    }
11514
11515
11516    final BooleanArgument promptArgument = subCommandParser.getBooleanArgument(
11517         "prompt-for-encryption-password");
11518    if ((promptArgument != null) && promptArgument.isPresent())
11519    {
11520      out();
11521      if (forDecrypt)
11522      {
11523        // We'll be decrypting an existing private key, so we only need to
11524        // prompt for the password once.
11525        return promptForPassword(
11526             INFO_MANAGE_CERTS_GET_PK_ENC_PW_PROMPT_DECRYPT.get(), false);
11527      }
11528      else
11529      {
11530        // We'll be encrypting the private key, so we need to prompt twice to
11531        // confirm that the entered password is what the user intended.
11532        while (true)
11533        {
11534          final char[] pwChars = promptForPassword(
11535               INFO_MANAGE_CERTS_GET_PK_ENC_PW_PROMPT_ENCRYPT_1.get(), false);
11536          final char[] confirmChars = promptForPassword(
11537               INFO_MANAGE_CERTS_GET_PK_ENC_PW_PROMPT_ENCRYPT_2.get(), true);
11538
11539          if (Arrays.equals(pwChars, confirmChars))
11540          {
11541            Arrays.fill(confirmChars, '\u0000');
11542            return pwChars;
11543          }
11544          else
11545          {
11546            wrapErr(0, WRAP_COLUMN,
11547                 INFO_MANAGE_CERTS_GET_PK_ENC_PW_PROMPT_MISMATCH.get());
11548            err();
11549          }
11550        }
11551      }
11552    }
11553
11554
11555    return null;
11556  }
11557
11558
11559
11560  /**
11561   * Prompts for a password and retrieves the value that the user entered.
11562   *
11563   * @param  prompt      The prompt to display to the user.
11564   * @param  allowEmpty  Indicates whether to allow the password to be empty.
11565   *
11566   * @return  The password that was read, or an empty array if the user did not
11567   *          type a password before pressing ENTER.
11568   *
11569   * @throws  LDAPException  If a problem is encountered while reading the
11570   *                         password.
11571   */
11572  @NotNull()
11573  private char[] promptForPassword(@NotNull final String prompt,
11574                                   final boolean allowEmpty)
11575          throws LDAPException
11576  {
11577    final Iterator<String> iterator =
11578         StaticUtils.wrapLine(prompt, WRAP_COLUMN).iterator();
11579    while (iterator.hasNext())
11580    {
11581      final String line = iterator.next();
11582      if (iterator.hasNext())
11583      {
11584        out(line);
11585      }
11586      else
11587      {
11588        getOut().print(line);
11589      }
11590    }
11591
11592    final char[] passwordChars = PasswordReader.readPasswordChars();
11593    if ((passwordChars.length == 0) && (! allowEmpty))
11594    {
11595      wrapErr(0, WRAP_COLUMN,
11596           ERR_MANAGE_CERTS_PROMPT_FOR_PW_EMPTY_PW.get());
11597      err();
11598      return promptForPassword(prompt, allowEmpty);
11599    }
11600
11601    return passwordChars;
11602  }
11603
11604
11605
11606  /**
11607   * Prompts the user for a yes or no response.
11608   *
11609   * @param  prompt  The prompt to display to the end user.
11610   *
11611   * @return  {@code true} if the user chooses the "yes" response, or
11612   *          {@code false} if the user chooses the "no" throws.
11613   *
11614   * @throws  LDAPException  If a problem is encountered while reading data from
11615   *                         the client.
11616   */
11617  private boolean promptForYesNo(@NotNull final String prompt)
11618          throws LDAPException
11619  {
11620    while (true)
11621    {
11622      final List<String> lines =
11623           StaticUtils.wrapLine((prompt + ' '), WRAP_COLUMN);
11624
11625      final Iterator<String> lineIterator = lines.iterator();
11626      while (lineIterator.hasNext())
11627      {
11628        final String line = lineIterator.next();
11629        if (lineIterator.hasNext())
11630        {
11631          out(line);
11632        }
11633        else
11634        {
11635          getOut().print(line);
11636        }
11637      }
11638
11639      try
11640      {
11641        final String response = readLineFromIn();
11642        if (response.equalsIgnoreCase("yes") || response.equalsIgnoreCase("y"))
11643        {
11644          return true;
11645        }
11646        else if (response.equalsIgnoreCase("no") ||
11647             response.equalsIgnoreCase("n"))
11648        {
11649          return false;
11650        }
11651        else
11652        {
11653          err();
11654          wrapErr(0, WRAP_COLUMN,
11655               ERR_MANAGE_CERTS_PROMPT_FOR_YES_NO_INVALID_RESPONSE.get());
11656          err();
11657        }
11658      }
11659      catch (final Exception e)
11660      {
11661        Debug.debugException(e);
11662        throw new LDAPException(ResultCode.LOCAL_ERROR,
11663             ERR_MANAGE_CERTS_PROMPT_FOR_YES_NO_READ_ERROR.get(
11664                  StaticUtils.getExceptionMessage(e)),
11665             e);
11666      }
11667    }
11668  }
11669
11670
11671
11672  /**
11673   * Reads a line of input from standard input.
11674   *
11675   * @return  The line read from standard input.
11676   *
11677   * @throws  IOException  If a problem is encountered while reading from
11678   *                       standard input.
11679   */
11680  @NotNull()
11681  private String readLineFromIn()
11682          throws IOException
11683  {
11684    final ByteStringBuffer buffer = new ByteStringBuffer();
11685    while (true)
11686    {
11687      final int byteRead = in.read();
11688      if (byteRead < 0)
11689      {
11690        if (buffer.isEmpty())
11691        {
11692          return null;
11693        }
11694        else
11695        {
11696          return buffer.toString();
11697        }
11698      }
11699
11700      if (byteRead == '\n')
11701      {
11702        return buffer.toString();
11703      }
11704      else if (byteRead == '\r')
11705      {
11706        final int nextByteRead = in.read();
11707        Validator.ensureTrue(((nextByteRead < 0) || (nextByteRead == '\n')),
11708             "ERROR:  Read a carriage return from standard input that was " +
11709                  "not followed by a new line.");
11710        return buffer.toString();
11711      }
11712      else
11713      {
11714        buffer.append((byte) (byteRead & 0xFF));
11715      }
11716    }
11717  }
11718
11719
11720
11721  /**
11722   * Retrieves the password needed to access the private key.
11723   *
11724   * @param  keystore          The keystore that contains the target private
11725   *                           key.  This must not be {@code null}.
11726   * @param  alias             The alias of the target private key.  This must
11727   *                           not be {@code null}.
11728   * @param  keystorePassword  The keystore password to use if no specific
11729   *                           private key password was provided.
11730   *
11731   * @return  The password needed to access the private key, or the provided
11732   *          keystore password if no arguments were provided to specify a
11733   *          different private key password.
11734   *
11735   * @throws  LDAPException  If a problem is encountered while trying to get the
11736   *                         private key password.
11737   */
11738  @Nullable()
11739  private char[] getPrivateKeyPassword(@NotNull final KeyStore keystore,
11740                                       @NotNull final String alias,
11741                                       @Nullable final char[] keystorePassword)
11742          throws LDAPException
11743  {
11744    return getPrivateKeyPassword(keystore, alias, null, keystorePassword);
11745  }
11746
11747
11748
11749  /**
11750   * Retrieves the password needed to access the private key.
11751   *
11752   * @param  keystore          The keystore that contains the target private
11753   *                           key.  This must not be {@code null}.
11754   * @param  alias             The alias of the target private key.  This must
11755   *                           not be {@code null}.
11756   * @param  prefix            The prefix string to use for the arguments.  This
11757   *                           may be {@code null} if no prefix is needed.
11758   * @param  keystorePassword  The keystore password to use if no specific
11759   *                           private key password was provided.
11760   *
11761   * @return  The password needed to access the private key, or the provided
11762   *          keystore password if no arguments were provided to specify a
11763   *          different private key password.
11764   *
11765   * @throws  LDAPException  If a problem is encountered while trying to get the
11766   *                         private key password.
11767   */
11768  @Nullable()
11769  private char[] getPrivateKeyPassword(@NotNull final KeyStore keystore,
11770                                       @NotNull final String alias,
11771                                       @Nullable final String prefix,
11772                                       @Nullable final char[] keystorePassword)
11773          throws LDAPException
11774  {
11775    final String prefixDash;
11776    if (prefix == null)
11777    {
11778      prefixDash = "";
11779    }
11780    else
11781    {
11782      prefixDash = prefix + '-';
11783    }
11784
11785    final StringArgument privateKeyPasswordArgument =
11786         subCommandParser.getStringArgument(
11787              prefixDash + "private-key-password");
11788    if ((privateKeyPasswordArgument != null) &&
11789         privateKeyPasswordArgument.isPresent())
11790    {
11791      final char[] pkPasswordChars =
11792           privateKeyPasswordArgument.getValue().toCharArray();
11793      if ((pkPasswordChars.length < 6) &&
11794          (! (hasCertificateAlias(keystore, alias) ||
11795              hasKeyAlias(keystore, alias))))
11796      {
11797        throw new LDAPException(ResultCode.PARAM_ERROR,
11798             ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
11799      }
11800
11801      return pkPasswordChars;
11802    }
11803
11804
11805    final FileArgument privateKeyPasswordFileArgument =
11806         subCommandParser.getFileArgument(
11807              prefixDash + "private-key-password-file");
11808    if ((privateKeyPasswordFileArgument != null) &&
11809        privateKeyPasswordFileArgument.isPresent())
11810    {
11811      final File f = privateKeyPasswordFileArgument.getValue();
11812      try
11813      {
11814        final char[] passwordChars = getPasswordFileReader().readPassword(f);
11815        if (passwordChars.length < 6)
11816        {
11817          throw new LDAPException(ResultCode.PARAM_ERROR,
11818               ERR_MANAGE_CERTS_GET_PK_PW_EMPTY_FILE.get(f.getAbsolutePath()));
11819        }
11820
11821        return passwordChars;
11822      }
11823      catch (final LDAPException e)
11824      {
11825        Debug.debugException(e);
11826        throw e;
11827      }
11828      catch (final Exception e)
11829      {
11830        Debug.debugException(e);
11831        throw new LDAPException(ResultCode.LOCAL_ERROR,
11832             ERR_MANAGE_CERTS_GET_PK_PW_ERROR_READING_FILE.get(
11833                  f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
11834             e);
11835      }
11836    }
11837
11838
11839    final BooleanArgument promptArgument =
11840         subCommandParser.getBooleanArgument(
11841              "prompt-for-" + prefixDash + "private-key-password");
11842    if ((promptArgument != null) && promptArgument.isPresent())
11843    {
11844      out();
11845
11846      try
11847      {
11848        if ((hasKeyAlias(keystore, alias) ||
11849             hasCertificateAlias(keystore, alias)) &&
11850            (! "new".equals(prefix)) && (! "destination".equals(prefix)))
11851        {
11852          // This means that the private key already exists, so we just need to
11853          // prompt once.
11854          final String prompt;
11855          if ("current".equals(prefix))
11856          {
11857            prompt =
11858                 INFO_MANAGE_CERTS_GET_PK_PW_CURRENT_PROMPT.get(alias);
11859          }
11860          else
11861          {
11862            prompt =
11863                 INFO_MANAGE_CERTS_GET_PK_PW_EXISTING_PROMPT.get(alias);
11864          }
11865
11866          return promptForPassword(prompt, false);
11867        }
11868        else
11869        {
11870          // This means that we'll be creating a new private key, so we need to
11871          // prompt twice.
11872          while (true)
11873          {
11874            final String prompt;
11875            if ("new".equals(prefix))
11876            {
11877              prompt = INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT.get();
11878            }
11879            else
11880            {
11881              prompt = INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT_1.get(alias);
11882            }
11883
11884            final char[] pwChars = promptForPassword(prompt, false);
11885            if (pwChars.length < 6)
11886            {
11887              wrapErr(0, WRAP_COLUMN,
11888                   ERR_MANAGE_CERTS_GET_PK_PW_TOO_SHORT.get());
11889              err();
11890              continue;
11891            }
11892
11893            final char[] confirmChars = promptForPassword(
11894                 INFO_MANAGE_CERTS_GET_PK_PW_NEW_PROMPT_2.get(), true);
11895
11896            if (Arrays.equals(pwChars, confirmChars))
11897            {
11898              Arrays.fill(confirmChars, '\u0000');
11899              return pwChars;
11900            }
11901            else
11902            {
11903              wrapErr(0, WRAP_COLUMN,
11904                   ERR_MANAGE_CERTS_GET_PK_PW_PROMPT_MISMATCH.get());
11905              err();
11906            }
11907          }
11908        }
11909      }
11910      catch (final LDAPException le)
11911      {
11912        Debug.debugException(le);
11913        throw le;
11914      }
11915      catch (final Exception e)
11916      {
11917        Debug.debugException(e);
11918        throw new LDAPException(ResultCode.LOCAL_ERROR,
11919             ERR_MANAGE_CERTS_GET_PK_PW_PROMPT_ERROR.get(alias,
11920                  StaticUtils.getExceptionMessage(e)),
11921             e);
11922      }
11923    }
11924
11925
11926    return keystorePassword;
11927  }
11928
11929
11930
11931  /**
11932   * Infers the keystore type from the provided keystore file.
11933   *
11934   * @param  keystorePath  The path to the file to examine.
11935   *
11936   * @return  The keystore type inferred from the provided keystore file.
11937   *
11938   * @throws  LDAPException  If a problem is encountered while trying to infer
11939   *                         the keystore type.
11940   */
11941  @NotNull()
11942  private String inferKeystoreType(@NotNull final File keystorePath)
11943          throws LDAPException
11944  {
11945    return inferKeystoreType(keystorePath, null);
11946  }
11947
11948
11949
11950  /**
11951   * Infers the keystore type from the provided keystore file.
11952   *
11953   * @param  keystorePath  The path to the file to examine.
11954   * @param  prefix        The prefix string to use for the arguments.  This
11955   *                       may be {@code null} if no prefix is needed.
11956   *
11957   * @return  The keystore type inferred from the provided keystore file.
11958   *
11959   * @throws  LDAPException  If a problem is encountered while trying to infer
11960   *                         the keystore type.
11961   */
11962  @NotNull()
11963  private String inferKeystoreType(@NotNull final File keystorePath,
11964                                   @Nullable final String prefix)
11965          throws LDAPException
11966  {
11967    // If the keystore type argument was specified, then use its value.
11968    final StringArgument keystoreTypeArgument;
11969    if (prefix == null)
11970    {
11971      keystoreTypeArgument =
11972           subCommandParser.getStringArgument("keystore-type");
11973    }
11974    else
11975    {
11976      keystoreTypeArgument =
11977           subCommandParser.getStringArgument(prefix + "-keystore-type");
11978    }
11979
11980    if ((keystoreTypeArgument != null) && keystoreTypeArgument.isPresent())
11981    {
11982      final String ktaValue = keystoreTypeArgument.getValue();
11983      if (ktaValue.equalsIgnoreCase("PKCS11") ||
11984          ktaValue.equalsIgnoreCase("PKCS 11") ||
11985          ktaValue.equalsIgnoreCase("PKCS#11") ||
11986          ktaValue.equalsIgnoreCase("PKCS #11"))
11987      {
11988        return CryptoHelper.KEY_STORE_TYPE_PKCS_11;
11989      }
11990      else if (ktaValue.equalsIgnoreCase("PKCS12") ||
11991          ktaValue.equalsIgnoreCase("PKCS 12") ||
11992          ktaValue.equalsIgnoreCase("PKCS#12") ||
11993          ktaValue.equalsIgnoreCase("PKCS #12"))
11994      {
11995        return CryptoHelper.KEY_STORE_TYPE_PKCS_12;
11996      }
11997      else if (ktaValue.equalsIgnoreCase(BCFKS_KEYSTORE_TYPE))
11998      {
11999        return BCFKS_KEYSTORE_TYPE;
12000      }
12001      else
12002      {
12003        return CryptoHelper.KEY_STORE_TYPE_JKS;
12004      }
12005    }
12006
12007
12008    // If we've gotten here, then the keystore type was not explicitly specified
12009    // so we will need to infer it.  If the LDAP SDK is running in FIPS mode,
12010    // then we'll always use the BCFKS key store type.
12011    if (CryptoHelper.usingFIPSMode())
12012    {
12013      return BouncyCastleFIPSHelper.FIPS_KEY_STORE_TYPE;
12014    }
12015
12016
12017    // If the key store file doesn't exist, then we must be creating it.  Use
12018    // the default key store type.
12019    if (! keystorePath.exists())
12020    {
12021      return DEFAULT_KEYSTORE_TYPE;
12022    }
12023
12024
12025    try
12026    {
12027      return CryptoHelper.inferKeyStoreType(keystorePath);
12028    }
12029    catch (final Exception e)
12030    {
12031      Debug.debugException(e);
12032      throw new LDAPException(ResultCode.PARAM_ERROR, e.getMessage(), e);
12033    }
12034  }
12035
12036
12037
12038  /**
12039   * Retrieves a user-friendly representation of the provided keystore type.
12040   *
12041   * @param  keystoreType  The keystore type for which to get the user-friendly
12042   *                       name.
12043   *
12044   * @return  "JKS" if the provided keystore type is for a JKS keystore,
12045   *          "PKCS #12" if the provided keystore type is for a PKCS #12
12046   *          keystore, or the provided string if it is for some other keystore
12047   *          type.
12048   */
12049  @NotNull()
12050  static String getUserFriendlyKeystoreType(@NotNull final String keystoreType)
12051  {
12052    if (keystoreType.equalsIgnoreCase("JKS"))
12053    {
12054      return "JKS";
12055    }
12056    else if (keystoreType.equalsIgnoreCase("PKCS11") ||
12057         keystoreType.equalsIgnoreCase("PKCS 11") ||
12058         keystoreType.equalsIgnoreCase("PKCS#11") ||
12059         keystoreType.equalsIgnoreCase("PKCS #11"))
12060    {
12061      return "PKCS #11";
12062    }
12063    else if (keystoreType.equalsIgnoreCase("PKCS12") ||
12064         keystoreType.equalsIgnoreCase("PKCS 12") ||
12065         keystoreType.equalsIgnoreCase("PKCS#12") ||
12066         keystoreType.equalsIgnoreCase("PKCS #12"))
12067    {
12068      return "PKCS #12";
12069    }
12070    else if (keystoreType.equalsIgnoreCase(BCFKS_KEYSTORE_TYPE))
12071    {
12072      return BCFKS_KEYSTORE_TYPE;
12073    }
12074    else
12075    {
12076      return keystoreType;
12077    }
12078  }
12079
12080
12081
12082  /**
12083   * Gets access to a keystore based on information included in command-line
12084   * arguments.
12085   *
12086   * @param  keystoreType      The keystore type for the keystore to access.
12087   * @param  keystorePath      The path to the keystore file.
12088   * @param  keystorePassword  The password to use to access the keystore.
12089   *
12090   * @return  The configured keystore instance.
12091   *
12092   * @throws  LDAPException  If it is not possible to access the keystore.
12093   */
12094  @NotNull()
12095  static KeyStore getKeystore(@NotNull final String keystoreType,
12096                              @NotNull final File keystorePath,
12097                              @Nullable final char[] keystorePassword)
12098          throws LDAPException
12099  {
12100    // Instantiate a keystore instance of the desired keystore type.
12101    final KeyStore keystore;
12102    try
12103    {
12104      if (keystoreType.equals(CryptoHelper.KEY_STORE_TYPE_PKCS_11))
12105      {
12106        // NOTE:  For PKCS #11 key store types, the key store path will be
12107        // treated as the path to the provider configuration file.
12108        final Provider pkcs11Provider = PKCS11KeyManager.getProvider(null,
12109             keystorePath, keystoreType, true);
12110        keystore = CryptoHelper.getKeyStore(keystoreType, pkcs11Provider);
12111      }
12112      else if (keystoreType.equals(BCFKS_KEYSTORE_TYPE))
12113      {
12114        keystore = CryptoHelper.getKeyStore(keystoreType,
12115             BouncyCastleFIPSHelper.getBouncyCastleFIPSProvider());
12116      }
12117      else
12118      {
12119        keystore = CryptoHelper.getKeyStore(keystoreType);
12120      }
12121    }
12122    catch (final Exception e)
12123    {
12124      Debug.debugException(e);
12125      throw new LDAPException(ResultCode.LOCAL_ERROR,
12126           ERR_MANAGE_CERTS_CANNOT_INSTANTIATE_KS_TYPE.get(keystoreType,
12127                StaticUtils.getExceptionMessage(e)),
12128           e);
12129    }
12130
12131
12132    // Get an input stream that may be used to access the keystore.
12133    final InputStream inputStream;
12134    try
12135    {
12136      if (keystorePath.exists() &&
12137           (! keystoreType.equals(CryptoHelper.KEY_STORE_TYPE_PKCS_11)))
12138      {
12139        inputStream = new FileInputStream(keystorePath);
12140      }
12141      else
12142      {
12143        inputStream = null;
12144      }
12145    }
12146    catch (final Exception e)
12147    {
12148      Debug.debugException(e);
12149      throw new LDAPException(ResultCode.LOCAL_ERROR,
12150           ERR_MANAGE_CERTS_CANNOT_OPEN_KS_FILE_FOR_READING.get(
12151                keystorePath.getAbsolutePath(),
12152                StaticUtils.getExceptionMessage(e)),
12153           e);
12154    }
12155
12156    try
12157    {
12158      keystore.load(inputStream, keystorePassword);
12159    }
12160    catch (final Exception e)
12161    {
12162      Debug.debugException(e);
12163      final Throwable cause = e.getCause();
12164      if ((e instanceof IOException) && (cause != null) &&
12165          (cause instanceof UnrecoverableKeyException) &&
12166          (keystorePassword != null))
12167      {
12168        throw new LDAPException(ResultCode.PARAM_ERROR,
12169             ERR_MANAGE_CERTS_CANNOT_LOAD_KS_WRONG_PW.get(
12170                  keystorePath.getAbsolutePath()),
12171             e);
12172      }
12173      else
12174      {
12175        throw new LDAPException(ResultCode.PARAM_ERROR,
12176             ERR_MANAGE_CERTS_ERROR_CANNOT_LOAD_KS.get(
12177                  keystorePath.getAbsolutePath(),
12178                  StaticUtils.getExceptionMessage(e)),
12179             e);
12180      }
12181    }
12182    finally
12183    {
12184      try
12185      {
12186        if (inputStream != null)
12187        {
12188          inputStream.close();
12189        }
12190      }
12191      catch (final Exception e)
12192      {
12193        Debug.debugException(e);
12194      }
12195    }
12196
12197    return keystore;
12198  }
12199
12200
12201
12202  /**
12203   * Reads all of the certificates contained in the specified file.  The file
12204   * must exist and may contain zero or more certificates that are either all in
12205   * PEM format or all in DER format.
12206   *
12207   * @param  f  The path to the certificate file to read.  It must not be
12208   *            {@code null}.
12209   *
12210   * @return  A list of the certificates read from the specified file.
12211   *
12212   * @throws  LDAPException  If a problem is encountered while reading
12213   *                         certificates from the specified file.
12214   */
12215  @NotNull()
12216  public static List<X509Certificate> readCertificatesFromFile(
12217                                           @NotNull final File f)
12218         throws LDAPException
12219  {
12220    // Read the first byte of the file to see if it contains DER-formatted data,
12221    // which we can determine by seeing if the first byte is 0x30.
12222    try (BufferedInputStream inputStream =
12223              new BufferedInputStream(new FileInputStream(f)))
12224    {
12225      inputStream.mark(1);
12226      final int firstByte = inputStream.read();
12227
12228      if (firstByte < 0)
12229      {
12230        // This means that the file is empty.
12231        return Collections.emptyList();
12232      }
12233      else
12234      {
12235        inputStream.reset();
12236      }
12237
12238      final ArrayList<X509Certificate> certList = new ArrayList<>(5);
12239      if ((firstByte & 0xFF) == 0x30)
12240      {
12241        // It is a DER-encoded file.  Read ASN.1 elements and decode them as
12242        // X.509 certificates.
12243        while (true)
12244        {
12245          final ASN1Element certElement;
12246          try
12247          {
12248            certElement = ASN1Element.readFrom(inputStream);
12249          }
12250          catch (final Exception e)
12251          {
12252            Debug.debugException(e);
12253            throw new LDAPException(ResultCode.LOCAL_ERROR,
12254                 ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DER_NOT_VALID_ASN1.get(
12255                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
12256                 e);
12257          }
12258
12259          if (certElement == null)
12260          {
12261            // We've reached the end of the input stream.
12262            return certList;
12263          }
12264
12265          try
12266          {
12267            certList.add(new X509Certificate(certElement.encode()));
12268          }
12269          catch (final CertException e)
12270          {
12271            Debug.debugException(e);
12272            throw new LDAPException(ResultCode.PARAM_ERROR,
12273                 ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DER_NOT_VALID_CERT.get(
12274                      f.getAbsolutePath(), e.getMessage()),
12275                 e);
12276          }
12277        }
12278      }
12279      else
12280      {
12281        try (BufferedReader reader =
12282                  new BufferedReader(new InputStreamReader(inputStream)))
12283        {
12284          boolean inCert = false;
12285          final StringBuilder buffer = new StringBuilder();
12286          while (true)
12287          {
12288            String line = reader.readLine();
12289            if (line == null)
12290            {
12291              if (inCert)
12292              {
12293                throw new LDAPException(ResultCode.PARAM_ERROR,
12294                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_EOF_WITHOUT_END.get(
12295                          f.getAbsolutePath()));
12296              }
12297
12298              return certList;
12299            }
12300
12301            line = line.trim();
12302            if (line.isEmpty() || line.startsWith("#"))
12303            {
12304              continue;
12305            }
12306
12307            if (line.equals("-----BEGIN CERTIFICATE-----"))
12308            {
12309              if (inCert)
12310              {
12311                throw new LDAPException(ResultCode.PARAM_ERROR,
12312                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_MULTIPLE_BEGIN.get(
12313                          f.getAbsolutePath()));
12314              }
12315              else
12316              {
12317                inCert = true;
12318              }
12319            }
12320            else if (line.equals("-----END CERTIFICATE-----"))
12321            {
12322              if (! inCert)
12323              {
12324                throw new LDAPException(ResultCode.PARAM_ERROR,
12325                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_END_WITHOUT_BEGIN.
12326                          get(f.getAbsolutePath()));
12327              }
12328
12329              inCert = false;
12330              final byte[] certBytes;
12331              try
12332              {
12333                certBytes = Base64.decode(buffer.toString());
12334              }
12335              catch (final Exception e)
12336              {
12337                Debug.debugException(e);
12338                throw new LDAPException(ResultCode.PARAM_ERROR,
12339                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_PEM_CERT_NOT_BASE64.
12340                          get(f.getAbsolutePath(),
12341                               StaticUtils.getExceptionMessage(e)),
12342                     e);
12343              }
12344
12345              try
12346              {
12347                certList.add(new X509Certificate(certBytes));
12348              }
12349              catch (final CertException e)
12350              {
12351                Debug.debugException(e);
12352                throw new LDAPException(ResultCode.PARAM_ERROR,
12353                     ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_PEM_CERT_NOT_CERT.
12354                          get(f.getAbsolutePath(), e.getMessage()),
12355                     e);
12356              }
12357
12358              buffer.setLength(0);
12359            }
12360            else if (inCert)
12361            {
12362              buffer.append(line);
12363            }
12364            else
12365            {
12366              throw new LDAPException(ResultCode.PARAM_ERROR,
12367                   ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_DATA_WITHOUT_BEGIN.get(
12368                        f.getAbsolutePath()));
12369            }
12370          }
12371        }
12372      }
12373    }
12374    catch (final LDAPException le)
12375    {
12376      Debug.debugException(le);
12377      throw le;
12378    }
12379    catch (final Exception e)
12380    {
12381      Debug.debugException(e);
12382      throw new LDAPException(ResultCode.LOCAL_ERROR,
12383           ERR_MANAGE_CERTS_READ_CERTS_FROM_FILE_READ_ERROR.get(
12384                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
12385           e);
12386    }
12387  }
12388
12389
12390
12391  /**
12392   * Reads a private key from the specified file.  The file must exist and must
12393   * contain exactly one PEM-encoded or DER-encoded PKCS #8 private key.
12394   *
12395   * @param  f                   The path to the private key file to read.  It
12396   *                             must not be {@code null}.
12397   * @param  encryptionPassword  The password to use to encrypt the private key.
12398   *                             It may be {@code null} if the private key is
12399   *                             not encrypted.
12400   *
12401   * @return  The private key read from the file.
12402   *
12403   * @throws  LDAPException  If a problem is encountered while reading the
12404   *                         private key.
12405   */
12406  @NotNull()
12407  static PKCS8PrivateKey readPrivateKeyFromFile(@NotNull final File f,
12408              @Nullable final char[] encryptionPassword)
12409         throws LDAPException
12410  {
12411    // Read the first byte of the file to see if it contains DER-formatted data,
12412    // which we can determine by seeing if the first byte is 0x30.
12413    try (BufferedInputStream inputStream =
12414              new BufferedInputStream(new FileInputStream(f)))
12415    {
12416      inputStream.mark(1);
12417      final int firstByte = inputStream.read();
12418
12419      if (firstByte < 0)
12420      {
12421        // This means that the file is empty.
12422        throw new LDAPException(ResultCode.PARAM_ERROR,
12423             ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
12424                  f.getAbsolutePath()));
12425      }
12426      else
12427      {
12428        inputStream.reset();
12429      }
12430
12431      PKCS8PrivateKey privateKey = null;
12432      if ((firstByte & 0xFF) == 0x30)
12433      {
12434        // It is a DER-encoded file.  Read an ASN.1 element and decode it as a
12435        // certificate.
12436        while (true)
12437        {
12438          final ASN1Element pkElement;
12439          try
12440          {
12441            pkElement = ASN1Element.readFrom(inputStream);
12442          }
12443          catch (final Exception e)
12444          {
12445            Debug.debugException(e);
12446            throw new LDAPException(ResultCode.LOCAL_ERROR,
12447                 ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DER_NOT_VALID_ASN1.get(
12448                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
12449                 e);
12450          }
12451
12452          if (pkElement == null)
12453          {
12454            // We've reached the end of the input stream.
12455            if (privateKey == null)
12456            {
12457              throw new LDAPException(ResultCode.PARAM_ERROR,
12458                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
12459                        f.getAbsolutePath()));
12460            }
12461            else
12462            {
12463              return privateKey;
12464            }
12465          }
12466          else if (privateKey == null)
12467          {
12468            try
12469            {
12470              if (encryptionPassword == null)
12471              {
12472                privateKey = new PKCS8PrivateKey(pkElement.encode());
12473              }
12474              else
12475              {
12476                privateKey = PKCS8EncryptionHandler.decryptPrivateKey(
12477                     pkElement.encode(), encryptionPassword);
12478              }
12479            }
12480            catch (final Exception e)
12481            {
12482              Debug.debugException(e);
12483              throw new LDAPException(ResultCode.PARAM_ERROR,
12484                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DER_NOT_VALID_PK.get(
12485                        f.getAbsolutePath(), e.getMessage()),
12486                   e);
12487            }
12488          }
12489          else
12490          {
12491            throw new LDAPException(ResultCode.PARAM_ERROR,
12492                 ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_KEYS.get(
12493                      f.getAbsolutePath()));
12494          }
12495        }
12496      }
12497      else
12498      {
12499        try (BufferedReader reader =
12500                  new BufferedReader(new InputStreamReader(inputStream)))
12501        {
12502          boolean inKey = false;
12503          boolean isRSAKey = false;
12504          boolean isEncryptedKey = false;
12505          final StringBuilder buffer = new StringBuilder();
12506          while (true)
12507          {
12508            String line = reader.readLine();
12509            if (line == null)
12510            {
12511              if (inKey)
12512              {
12513                throw new LDAPException(ResultCode.PARAM_ERROR,
12514                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EOF_WITHOUT_END.get(
12515                          f.getAbsolutePath()));
12516              }
12517
12518              if (privateKey == null)
12519              {
12520                throw new LDAPException(ResultCode.PARAM_ERROR,
12521                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_EMPTY_FILE.get(
12522                          f.getAbsolutePath()));
12523              }
12524              else
12525              {
12526                return privateKey;
12527              }
12528            }
12529
12530            line = line.trim();
12531            if (line.isEmpty() || line.startsWith("#"))
12532            {
12533              continue;
12534            }
12535
12536            if (line.equals("-----BEGIN PRIVATE KEY-----") ||
12537                 line.equals("-----BEGIN RSA PRIVATE KEY-----") ||
12538                 line.equals("-----BEGIN ENCRYPTED PRIVATE KEY-----"))
12539            {
12540              if (inKey)
12541              {
12542                throw new LDAPException(ResultCode.PARAM_ERROR,
12543                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_BEGIN.get(
12544                          f.getAbsolutePath()));
12545              }
12546              else if (privateKey != null)
12547              {
12548                throw new LDAPException(ResultCode.PARAM_ERROR,
12549                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_MULTIPLE_KEYS.get(
12550                          f.getAbsolutePath()));
12551              }
12552              else
12553              {
12554                inKey = true;
12555                if (line.equals("-----BEGIN RSA PRIVATE KEY-----"))
12556                {
12557                  isRSAKey = true;
12558                }
12559                else if (line.equals("-----BEGIN ENCRYPTED PRIVATE KEY-----"))
12560                {
12561                  isEncryptedKey = true;
12562                  if (encryptionPassword == null)
12563                  {
12564                    throw new LDAPException(ResultCode.PARAM_ERROR,
12565                         ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PK_ENCRYPTED_NO_PW.
12566                              get(f.getAbsolutePath()));
12567                  }
12568                }
12569              }
12570            }
12571            else if (line.equals("-----END PRIVATE KEY-----") ||
12572                 line.equals("-----END RSA PRIVATE KEY-----") ||
12573                 line.equals("-----END ENCRYPTED PRIVATE KEY-----"))
12574            {
12575              if (! inKey)
12576              {
12577                throw new LDAPException(ResultCode.PARAM_ERROR,
12578                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_END_WITHOUT_BEGIN.get(
12579                          f.getAbsolutePath()));
12580              }
12581
12582              inKey = false;
12583              byte[] pkBytes;
12584              try
12585              {
12586                pkBytes = Base64.decode(buffer.toString());
12587              }
12588              catch (final Exception e)
12589              {
12590                Debug.debugException(e);
12591                throw new LDAPException(ResultCode.PARAM_ERROR,
12592                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PEM_PK_NOT_BASE64.get(
12593                          f.getAbsolutePath(),
12594                          StaticUtils.getExceptionMessage(e)),
12595                     e);
12596              }
12597
12598              if (isRSAKey)
12599              {
12600                pkBytes = PKCS8PrivateKey.wrapRSAPrivateKey(pkBytes);
12601              }
12602
12603              try
12604              {
12605                if (isEncryptedKey)
12606                {
12607                  privateKey = PKCS8EncryptionHandler.
12608                       decryptPrivateKey(pkBytes, encryptionPassword);
12609                }
12610                else
12611                {
12612                  privateKey = new PKCS8PrivateKey(pkBytes);
12613                }
12614              }
12615              catch (final CertException e)
12616              {
12617                Debug.debugException(e);
12618                throw new LDAPException(ResultCode.PARAM_ERROR,
12619                     ERR_MANAGE_CERTS_READ_PK_FROM_FILE_PEM_PK_NOT_PK.get(
12620                          f.getAbsolutePath(), e.getMessage()),
12621                     e);
12622              }
12623
12624              buffer.setLength(0);
12625            }
12626            else if (inKey)
12627            {
12628              buffer.append(line);
12629            }
12630            else
12631            {
12632              throw new LDAPException(ResultCode.PARAM_ERROR,
12633                   ERR_MANAGE_CERTS_READ_PK_FROM_FILE_DATA_WITHOUT_BEGIN.get(
12634                        f.getAbsolutePath()));
12635            }
12636          }
12637        }
12638      }
12639    }
12640    catch (final LDAPException le)
12641    {
12642      Debug.debugException(le);
12643      throw le;
12644    }
12645    catch (final Exception e)
12646    {
12647      Debug.debugException(e);
12648      throw new LDAPException(ResultCode.LOCAL_ERROR,
12649           ERR_MANAGE_CERTS_READ_PK_FROM_FILE_READ_ERROR.get(
12650                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
12651           e);
12652    }
12653  }
12654
12655
12656
12657  /**
12658   * Reads a certificate signing request from the specified file.  The file must
12659   * exist and must contain exactly one PEM-encoded or DER-encoded PKCS #10
12660   * certificate signing request.
12661   *
12662   * @param  f  The path to the private key file to read.  It must not be
12663   *            {@code null}.
12664   *
12665   * @return  The certificate signing request read from the file.
12666   *
12667   * @throws  LDAPException  If a problem is encountered while reading the
12668   *                         certificate signing request.
12669   */
12670  @NotNull()
12671  public static PKCS10CertificateSigningRequest
12672                     readCertificateSigningRequestFromFile(
12673                          @NotNull final File f)
12674         throws LDAPException
12675  {
12676    // Read the first byte of the file to see if it contains DER-formatted data,
12677    // which we can determine by seeing if the first byte is 0x30.
12678    try (BufferedInputStream inputStream =
12679              new BufferedInputStream(new FileInputStream(f)))
12680    {
12681      inputStream.mark(1);
12682      final int firstByte = inputStream.read();
12683
12684      if (firstByte < 0)
12685      {
12686        // This means that the file is empty.
12687        throw new LDAPException(ResultCode.PARAM_ERROR,
12688             ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
12689                  f.getAbsolutePath()));
12690      }
12691      else
12692      {
12693        inputStream.reset();
12694      }
12695
12696      PKCS10CertificateSigningRequest csr = null;
12697      if ((firstByte & 0xFF) == 0x30)
12698      {
12699        // It is a DER-encoded file.  Read an ASN.1 element and decode it as a
12700        // certificate.
12701        while (true)
12702        {
12703          final ASN1Element csrElement;
12704          try
12705          {
12706            csrElement = ASN1Element.readFrom(inputStream);
12707          }
12708          catch (final Exception e)
12709          {
12710            Debug.debugException(e);
12711            throw new LDAPException(ResultCode.LOCAL_ERROR,
12712                 ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DER_NOT_VALID_ASN1.get(
12713                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
12714                 e);
12715          }
12716
12717          if (csrElement == null)
12718          {
12719            // We've reached the end of the input stream.
12720            if (csr == null)
12721            {
12722              throw new LDAPException(ResultCode.PARAM_ERROR,
12723                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
12724                        f.getAbsolutePath()));
12725            }
12726            else
12727            {
12728              return csr;
12729            }
12730          }
12731          else if (csr == null)
12732          {
12733            try
12734            {
12735              csr = new PKCS10CertificateSigningRequest(csrElement.encode());
12736            }
12737            catch (final Exception e)
12738            {
12739              Debug.debugException(e);
12740              throw new LDAPException(ResultCode.PARAM_ERROR,
12741                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DER_NOT_VALID_CSR.get(
12742                        f.getAbsolutePath(), e.getMessage()),
12743                   e);
12744            }
12745          }
12746          else
12747          {
12748            throw new LDAPException(ResultCode.PARAM_ERROR,
12749                 ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_CSRS.get(
12750                      f.getAbsolutePath()));
12751          }
12752        }
12753      }
12754      else
12755      {
12756        try (BufferedReader reader =
12757                  new BufferedReader(new InputStreamReader(inputStream)))
12758        {
12759          boolean inCSR = false;
12760          final StringBuilder buffer = new StringBuilder();
12761          while (true)
12762          {
12763            String line = reader.readLine();
12764            if (line == null)
12765            {
12766              if (inCSR)
12767              {
12768                throw new LDAPException(ResultCode.PARAM_ERROR,
12769                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EOF_WITHOUT_END.get(
12770                          f.getAbsolutePath()));
12771              }
12772
12773              if (csr == null)
12774              {
12775                throw new LDAPException(ResultCode.PARAM_ERROR,
12776                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_EMPTY_FILE.get(
12777                          f.getAbsolutePath()));
12778              }
12779              else
12780              {
12781                return csr;
12782              }
12783            }
12784
12785            line = line.trim();
12786            if (line.isEmpty() || line.startsWith("#"))
12787            {
12788              continue;
12789            }
12790
12791            if (line.equals("-----BEGIN CERTIFICATE REQUEST-----") ||
12792                line.equals("-----BEGIN NEW CERTIFICATE REQUEST-----"))
12793            {
12794              if (inCSR)
12795              {
12796                throw new LDAPException(ResultCode.PARAM_ERROR,
12797                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_BEGIN.get(
12798                          f.getAbsolutePath()));
12799              }
12800              else if (csr != null)
12801              {
12802                throw new LDAPException(ResultCode.PARAM_ERROR,
12803                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_MULTIPLE_CSRS.get(
12804                          f.getAbsolutePath()));
12805              }
12806              else
12807              {
12808                inCSR = true;
12809              }
12810            }
12811            else if (line.equals("-----END CERTIFICATE REQUEST-----") ||
12812                 line.equals("-----END NEW CERTIFICATE REQUEST-----"))
12813            {
12814              if (! inCSR)
12815              {
12816                throw new LDAPException(ResultCode.PARAM_ERROR,
12817                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_END_WITHOUT_BEGIN.get(
12818                          f.getAbsolutePath()));
12819              }
12820
12821              inCSR = false;
12822              final byte[] csrBytes;
12823              try
12824              {
12825                csrBytes = Base64.decode(buffer.toString());
12826              }
12827              catch (final Exception e)
12828              {
12829                Debug.debugException(e);
12830                throw new LDAPException(ResultCode.PARAM_ERROR,
12831                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_PEM_CSR_NOT_BASE64.get(
12832                          f.getAbsolutePath(),
12833                          StaticUtils.getExceptionMessage(e)),
12834                     e);
12835              }
12836
12837              try
12838              {
12839                csr = new PKCS10CertificateSigningRequest(csrBytes);
12840              }
12841              catch (final CertException e)
12842              {
12843                Debug.debugException(e);
12844                throw new LDAPException(ResultCode.PARAM_ERROR,
12845                     ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_PEM_CSR_NOT_CSR.get(
12846                          f.getAbsolutePath(), e.getMessage()),
12847                     e);
12848              }
12849
12850              buffer.setLength(0);
12851            }
12852            else if (inCSR)
12853            {
12854              buffer.append(line);
12855            }
12856            else
12857            {
12858              throw new LDAPException(ResultCode.PARAM_ERROR,
12859                   ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_DATA_WITHOUT_BEGIN.get(
12860                        f.getAbsolutePath()));
12861            }
12862          }
12863        }
12864      }
12865    }
12866    catch (final LDAPException le)
12867    {
12868      Debug.debugException(le);
12869      throw le;
12870    }
12871    catch (final Exception e)
12872    {
12873      Debug.debugException(e);
12874      throw new LDAPException(ResultCode.LOCAL_ERROR,
12875           ERR_MANAGE_CERTS_READ_CSR_FROM_FILE_READ_ERROR.get(
12876                f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
12877           e);
12878    }
12879  }
12880
12881
12882
12883  /**
12884   * Retrieves a colon-delimited hexadecimal representation of the contents of
12885   * the provided byte array.
12886   *
12887   * @param  bytes  The byte array for which to get the hexadecimal
12888   *                representation.  It must not be {@code null}.
12889   *
12890   * @return  A colon-delimited hexadecimal representation of the contents of
12891   *          the provided byte array.
12892   */
12893  @NotNull()
12894  private static String toColonDelimitedHex(@NotNull final byte... bytes)
12895  {
12896    final StringBuilder buffer = new StringBuilder(bytes.length * 3);
12897    StaticUtils.toHex(bytes, ":", buffer);
12898    return buffer.toString();
12899  }
12900
12901
12902
12903  /**
12904   * Retrieves a formatted representation of the provided date in a
12905   * human-readable format that includes an offset from the current time.
12906   *
12907   * @param  d  The date to format.  It must not be {@code null}.
12908   *
12909   * @return  A formatted representation of the provided date.
12910   */
12911  @NotNull()
12912  private static String formatDateAndTime(@NotNull final Date d)
12913  {
12914    // Example:  Sunday, January 1, 2017
12915    final String dateFormatString = "EEEE, MMMM d, yyyy";
12916    final String formattedDate =
12917         new SimpleDateFormat(dateFormatString).format(d);
12918
12919    // Example:  12:34:56 AM CDT
12920    final String timeFormatString = "hh:mm:ss aa z";
12921    final String formattedTime =
12922         new SimpleDateFormat(timeFormatString).format(d);
12923
12924    final long providedTime = d.getTime();
12925    final long currentTime = System.currentTimeMillis();
12926    if (providedTime > currentTime)
12927    {
12928      final long secondsInFuture = ((providedTime - currentTime) / 1000L);
12929      final String durationInFuture =
12930           StaticUtils.secondsToHumanReadableDuration(secondsInFuture);
12931      return INFO_MANAGE_CERTS_FORMAT_DATE_AND_TIME_IN_FUTURE.get(formattedDate,
12932           formattedTime, durationInFuture);
12933    }
12934    else
12935    {
12936      final long secondsInPast = ((currentTime - providedTime) / 1000L);
12937      final String durationInPast =
12938           StaticUtils.secondsToHumanReadableDuration(secondsInPast);
12939      return INFO_MANAGE_CERTS_FORMAT_DATE_AND_TIME_IN_PAST.get(formattedDate,
12940           formattedTime, durationInPast);
12941    }
12942  }
12943
12944
12945
12946  /**
12947   * Retrieves a formatted representation of the provided date in a format
12948   * suitable for use as the validity start time value provided to the keytool
12949   * command.
12950   *
12951   * @param  d  The date to format.  It must not be {@code null}.
12952   *
12953   * @return  A formatted representation of the provided date.
12954   */
12955  @NotNull()
12956  private static String formatValidityStartTime(@NotNull final Date d)
12957  {
12958    // Example:  2017/01/01 01:23:45
12959    final String dateFormatString = "yyyy'/'MM'/'dd HH':'mm':'ss";
12960    return new SimpleDateFormat(dateFormatString).format(d);
12961  }
12962
12963
12964
12965  /**
12966   * Retrieves the certificate chain for the specified certificate from the
12967   * given keystore.  If any issuer certificate is not in the provided keystore,
12968   * then the JVM-default trust store will be checked to see if it can be found
12969   * there.
12970   *
12971   * @param  alias             The alias of the certificate for which to get the
12972   *                           certificate chain.  This must not be
12973   *                           {@code null}.
12974   * @param  keystore          The keystore from which to get the certificate
12975   *                           chain.  This must not be {@code null}.
12976   * @param  missingIssuerRef  A reference that will be updated with the DN of a
12977   *                           missing issuer certificate, if any certificate in
12978   *                           the chain cannot be located.  This must not be
12979   *                           {@code null}.
12980   *
12981   * @return  The certificate chain for the specified certificate, or an empty
12982   *          array if no certificate exists with the specified alias.
12983   *
12984   * @throws  LDAPException  If a problem is encountered while getting the
12985   *                         certificate chain.
12986   */
12987  @NotNull()
12988  private static X509Certificate[] getCertificateChain(
12989                      @NotNull final String alias,
12990                      @NotNull final KeyStore keystore,
12991                      @NotNull final AtomicReference<DN> missingIssuerRef)
12992          throws LDAPException
12993  {
12994    try
12995    {
12996      // First, see if the keystore will give us the certificate chain.  This
12997      // will only happen if the alias references an entry that includes the
12998      // private key, but it will save us a lot of work.
12999      final Certificate[] chain = keystore.getCertificateChain(alias);
13000      if ((chain != null) && (chain.length > 0))
13001      {
13002        final X509Certificate[] x509Chain = new X509Certificate[chain.length];
13003        for (int i=0; i < chain.length; i++)
13004        {
13005          x509Chain[i] = new X509Certificate(chain[i].getEncoded());
13006        }
13007        return x509Chain;
13008      }
13009
13010
13011      // We couldn't get the keystore to give us the chain, but see if we can
13012      // get a certificate with the specified alias.
13013      final Certificate endCert = keystore.getCertificate(alias);
13014      if (endCert == null)
13015      {
13016        // This means there isn't any certificate with the specified alias.
13017        // Return an empty chain.
13018        return new X509Certificate[0];
13019      }
13020
13021      final ArrayList<X509Certificate> chainList = new ArrayList<>(5);
13022      X509Certificate certificate = new X509Certificate(endCert.getEncoded());
13023      chainList.add(certificate);
13024
13025      final AtomicReference<KeyStore> jvmDefaultTrustStoreRef =
13026           new AtomicReference<>();
13027      while (true)
13028      {
13029        final X509Certificate issuerCertificate =
13030             getIssuerCertificate(certificate, keystore,
13031                  jvmDefaultTrustStoreRef, missingIssuerRef);
13032        if (issuerCertificate == null)
13033        {
13034          break;
13035        }
13036
13037        chainList.add(issuerCertificate);
13038        certificate = issuerCertificate;
13039      }
13040
13041      final X509Certificate[] x509Chain = new X509Certificate[chainList.size()];
13042      return chainList.toArray(x509Chain);
13043    }
13044    catch (final Exception e)
13045    {
13046      Debug.debugException(e);
13047      throw new LDAPException(ResultCode.LOCAL_ERROR,
13048           ERR_MANAGE_CERTS_GET_CHAIN_ERROR.get(alias,
13049                StaticUtils.getExceptionMessage(e)),
13050           e);
13051    }
13052  }
13053
13054
13055
13056  /**
13057   * Attempts to retrieve the issuer certificate for the provided certificate
13058   * from the given keystore or the JVM-default trust store.
13059   *
13060   * @param  certificate              The certificate for which to retrieve the
13061   *                                  issuer certificate.
13062   * @param  keystore                 The keystore in which to look for the
13063   *                                  issuer certificate.
13064   * @param  jvmDefaultTrustStoreRef  A reference that will be used to hold the
13065   *                                  JVM-default trust store if it is obtained
13066   *                                  in the process of retrieving the issuer
13067   *                                  certificate.
13068   * @param  missingIssuerRef         A reference that will be updated with the
13069   *                                  DN of a missing issuer certificate, if any
13070   *                                  certificate in the chain cannot be
13071   *                                  located.  This must not be {@code null}.
13072   *
13073   * @return  The issuer certificate for the provided certificate, or
13074   *          {@code null} if the issuer certificate could not be retrieved.
13075   *
13076   * @throws  Exception   If a problem is encountered while trying to retrieve
13077   *                      the issuer certificate.
13078   */
13079  @Nullable()
13080  private static X509Certificate getIssuerCertificate(
13081               @NotNull final X509Certificate certificate,
13082               @NotNull final KeyStore keystore,
13083               @NotNull final AtomicReference<KeyStore> jvmDefaultTrustStoreRef,
13084               @NotNull final AtomicReference<DN> missingIssuerRef)
13085          throws Exception
13086  {
13087    final DN subjectDN = certificate.getSubjectDN();
13088    final DN issuerDN = certificate.getIssuerDN();
13089    if (subjectDN.equals(issuerDN))
13090    {
13091      // This means that the certificate is self-signed, so there is no issuer.
13092      return null;
13093    }
13094
13095
13096    // See if we can find the issuer certificate in the provided keystore.
13097    X509Certificate issuerCertificate = getIssuerCertificate(certificate,
13098         keystore);
13099    if (issuerCertificate != null)
13100    {
13101      return issuerCertificate;
13102    }
13103
13104
13105    // See if we can get the JVM-default trust store.
13106    KeyStore jvmDefaultTrustStore = jvmDefaultTrustStoreRef.get();
13107    if (jvmDefaultTrustStore == null)
13108    {
13109      if (JVM_DEFAULT_CACERTS_FILE == null)
13110      {
13111        missingIssuerRef.set(issuerDN);
13112        return null;
13113      }
13114
13115      final String[] keystoreTypes =
13116      {
13117        CryptoHelper.KEY_STORE_TYPE_JKS,
13118        CryptoHelper.KEY_STORE_TYPE_PKCS_12,
13119        BouncyCastleFIPSHelper.FIPS_KEY_STORE_TYPE
13120      };
13121
13122      for (final String keystoreType : keystoreTypes)
13123      {
13124        try
13125        {
13126          final KeyStore ks =
13127               CryptoHelper.getKeyStore(keystoreType, null, true);
13128          try (FileInputStream inputStream =
13129                    new FileInputStream(JVM_DEFAULT_CACERTS_FILE))
13130          {
13131            ks.load(inputStream, null);
13132            jvmDefaultTrustStore = ks;
13133            jvmDefaultTrustStoreRef.set(jvmDefaultTrustStore);
13134            break;
13135          }
13136        }
13137        catch (final Exception e)
13138        {
13139          Debug.debugException(e);
13140        }
13141      }
13142    }
13143
13144    if (jvmDefaultTrustStore != null)
13145    {
13146      issuerCertificate = getIssuerCertificate(certificate,
13147           jvmDefaultTrustStore);
13148    }
13149
13150    if (issuerCertificate == null)
13151    {
13152      missingIssuerRef.set(issuerDN);
13153    }
13154
13155    return issuerCertificate;
13156  }
13157
13158
13159
13160  /**
13161   * Attempts to retrieve the issuer certificate for the provided certificate
13162   * from the given keystore.
13163   *
13164   * @param  certificate  The certificate for which to retrieve the issuer
13165   *                      certificate.
13166   * @param  keystore     The keystore in which to look for the issuer
13167   *                      certificate.
13168   *
13169   * @return  The issuer certificate for the provided certificate, or
13170   *          {@code null} if the issuer certificate could not be retrieved.
13171   *
13172   * @throws  Exception   If a problem is encountered while trying to retrieve
13173   *                      the issuer certificate.
13174   */
13175  @Nullable()
13176  private static X509Certificate getIssuerCertificate(
13177                      @NotNull final X509Certificate certificate,
13178                      @NotNull final KeyStore keystore)
13179          throws Exception
13180  {
13181    final Enumeration<String> aliases = keystore.aliases();
13182    while (aliases.hasMoreElements())
13183    {
13184      final String alias = aliases.nextElement();
13185
13186      Certificate[] certs = null;
13187      if (hasCertificateAlias(keystore, alias))
13188      {
13189        final Certificate c = keystore.getCertificate(alias);
13190        if (c == null)
13191        {
13192          continue;
13193        }
13194
13195        certs = new Certificate[] { c };
13196      }
13197      else if (hasKeyAlias(keystore, alias))
13198      {
13199        certs = keystore.getCertificateChain(alias);
13200      }
13201
13202      if (certs != null)
13203      {
13204        for (final Certificate c : certs)
13205        {
13206          final X509Certificate xc = new X509Certificate(c.getEncoded());
13207          if (xc.isIssuerFor(certificate))
13208          {
13209            return xc;
13210          }
13211        }
13212      }
13213    }
13214
13215    return null;
13216  }
13217
13218
13219
13220  /**
13221   * Retrieves the authority key identifier value for the provided certificate,
13222   * if present.
13223   *
13224   * @param  c  The certificate for which to retrieve the authority key
13225   *            identifier.
13226   *
13227   * @return  The authority key identifier value for the provided certificate,
13228   *          or {@code null} if the certificate does not have an authority
13229   *          key identifier.
13230   */
13231  @Nullable()
13232  private static byte[] getAuthorityKeyIdentifier(
13233                             @NotNull final X509Certificate c)
13234  {
13235    for (final X509CertificateExtension extension : c.getExtensions())
13236    {
13237      if (extension instanceof AuthorityKeyIdentifierExtension)
13238      {
13239        final AuthorityKeyIdentifierExtension e =
13240             (AuthorityKeyIdentifierExtension) extension;
13241        if (e.getKeyIdentifier() != null)
13242        {
13243          return e.getKeyIdentifier().getValue();
13244        }
13245      }
13246    }
13247
13248    return null;
13249  }
13250
13251
13252
13253  /**
13254   * Writes the provided keystore to the specified file.  If the keystore file
13255   * already exists, a new temporary file will be created, the old file renamed
13256   * out of the way, the new file renamed into place, and the old file deleted.
13257   * If the keystore file does not exist, then it will simply be created in the
13258   * correct place.
13259   *
13260   * @param  keystore          The keystore to be written.
13261   * @param  keystorePath      The path to the keystore file to be written.
13262   * @param  keystorePassword  The password to use for the keystore.
13263   *
13264   * @throws  LDAPException  If a problem is encountered while writing the
13265   *                         keystore.
13266   */
13267  static void writeKeystore(@NotNull final KeyStore keystore,
13268                            @NotNull final File keystorePath,
13269                            @Nullable final char[] keystorePassword)
13270          throws LDAPException
13271  {
13272    // If the key store type is PKCS #11, then we don't need to worry about a
13273    // key store file.
13274    if (keystore.getType().equals(CryptoHelper.KEY_STORE_TYPE_PKCS_11))
13275    {
13276      try
13277      {
13278        keystore.store(null);
13279        return;
13280      }
13281      catch (final Exception e)
13282      {
13283        Debug.debugException(e);
13284        throw new LDAPException(ResultCode.LOCAL_ERROR,
13285             ERR_MANAGE_CERTS_PKCS11_WRITE_ERROR.get(
13286                  StaticUtils.getExceptionMessage(e)),
13287             e);
13288      }
13289    }
13290
13291
13292    File copyOfExistingKeystore = null;
13293    final String timestamp =
13294         StaticUtils.encodeGeneralizedTime(System.currentTimeMillis());
13295    if (keystorePath.exists())
13296    {
13297      copyOfExistingKeystore = new File(keystorePath.getAbsolutePath() +
13298           ".backup-" + timestamp);
13299      try
13300      {
13301        Files.copy(keystorePath.toPath(), copyOfExistingKeystore.toPath());
13302      }
13303      catch (final Exception e)
13304      {
13305        Debug.debugException(e);
13306        throw new LDAPException(ResultCode.LOCAL_ERROR,
13307             ERR_MANAGE_CERTS_WRITE_KS_ERROR_COPYING_EXISTING_KS.get(
13308                  keystorePath.getAbsolutePath(),
13309                  copyOfExistingKeystore.getAbsolutePath(),
13310                  StaticUtils.getExceptionMessage(e)),
13311             e);
13312      }
13313    }
13314
13315    try (FileOutputStream outputStream = new FileOutputStream(keystorePath))
13316    {
13317      keystore.store(outputStream, keystorePassword);
13318    }
13319    catch (final Exception e)
13320    {
13321      Debug.debugException(e);
13322      if (copyOfExistingKeystore == null)
13323      {
13324        throw new LDAPException(ResultCode.LOCAL_ERROR,
13325             ERR_MANAGE_CERTS_WRITE_KS_ERROR_WRITING_NEW_KS.get(
13326                  keystorePath.getAbsolutePath(),
13327                  StaticUtils.getExceptionMessage(e)),
13328             e);
13329      }
13330      else
13331      {
13332        throw new LDAPException(ResultCode.LOCAL_ERROR,
13333             ERR_MANAGE_CERTS_WRITE_KS_ERROR_OVERWRITING_KS.get(
13334                  keystorePath.getAbsolutePath(),
13335                  StaticUtils.getExceptionMessage(e),
13336                  copyOfExistingKeystore.getAbsolutePath()),
13337             e);
13338      }
13339    }
13340
13341    if (copyOfExistingKeystore != null)
13342    {
13343      try
13344      {
13345        Files.delete(copyOfExistingKeystore.toPath());
13346      }
13347      catch (final Exception e)
13348      {
13349        Debug.debugException(e);
13350        throw new LDAPException(ResultCode.LOCAL_ERROR,
13351             ERR_MANAGE_CERTS_WRITE_KS_ERROR_DELETING_KS_BACKUP.get(
13352                  copyOfExistingKeystore.getAbsolutePath(),
13353                  keystorePath.getAbsolutePath(),
13354                  StaticUtils.getExceptionMessage(e)),
13355             e);
13356      }
13357    }
13358  }
13359
13360
13361
13362  /**
13363   * Indicates whether the provided keystore has a certificate entry with the
13364   * specified alias.
13365   *
13366   * @param  keystore  The keystore to examine.
13367   * @param  alias     The alias for which to make the determination.
13368   *
13369   * @return  {@code true} if the keystore has a certificate entry with the
13370   *          specified alias, or {@code false} if the alias doesn't exist or
13371   *          is associated with some other type of entry (like a key).
13372   */
13373  private static boolean hasCertificateAlias(@NotNull final KeyStore keystore,
13374                                             @NotNull final String alias)
13375  {
13376    try
13377    {
13378      return keystore.isCertificateEntry(alias);
13379    }
13380    catch (final Exception e)
13381    {
13382      // This should never happen.  If it does, then we'll assume the alias
13383      // doesn't exist or isn't associated with a certificate.
13384      Debug.debugException(e);
13385      return false;
13386    }
13387  }
13388
13389
13390
13391  /**
13392   * Indicates whether the provided keystore has a key entry with the specified
13393   * alias.
13394   *
13395   * @param  keystore  The keystore to examine.
13396   * @param  alias     The alias for which to make the determination.
13397   *
13398   * @return  {@code true} if the keystore has a key entry with the specified
13399   *          alias, or {@code false} if the alias doesn't exist or is
13400   *          associated with some other type of entry (like a certificate).
13401   */
13402  private static boolean hasKeyAlias(@NotNull final KeyStore keystore,
13403                                     @NotNull final String alias)
13404  {
13405    try
13406    {
13407      return keystore.isKeyEntry(alias);
13408    }
13409    catch (final Exception e)
13410    {
13411      // This should never happen.  If it does, then we'll assume the alias
13412      // doesn't exist or isn't associated with a key.
13413      Debug.debugException(e);
13414      return false;
13415    }
13416  }
13417
13418
13419
13420  /**
13421   * Adds arguments for each of the provided extensions to the given list.
13422   *
13423   * @param  keytoolArguments   The list to which the extension arguments should
13424   *                            be added.
13425   * @param  basicConstraints   The basic constraints extension to include.  It
13426   *                            may be {@code null} if this extension should not
13427   *                            be included.
13428   * @param  keyUsage           The key usage extension to include.  It may be
13429   *                            {@code null} if this extension should not be
13430   *                            included.
13431   * @param  extendedKeyUsage   The extended key usage extension to include.  It
13432   *                            may be {@code null} if this extension should not
13433   *                            be included.
13434   * @param  sanValues          The list of subject alternative name values to
13435   *                            include.  It must not be {@code null} but may be
13436   *                            empty.
13437   * @param  ianValues          The list of issuer alternative name values to
13438   *                            include.  It must not be {@code null} but may be
13439   *                            empty.
13440   * @param  genericExtensions  The list of generic extensions to include.  It
13441   *                            must not be {@code null} but may be empty.
13442   */
13443  private static void addExtensionArguments(
13444               @NotNull final List<String> keytoolArguments,
13445               @Nullable final BasicConstraintsExtension basicConstraints,
13446               @Nullable final KeyUsageExtension keyUsage,
13447               @Nullable final ExtendedKeyUsageExtension extendedKeyUsage,
13448               @NotNull final Set<String> sanValues,
13449               @NotNull final Set<String> ianValues,
13450               @NotNull final List<X509CertificateExtension> genericExtensions)
13451  {
13452    if (basicConstraints != null)
13453    {
13454      final StringBuilder basicConstraintsValue = new StringBuilder();
13455      basicConstraintsValue.append("ca:");
13456      basicConstraintsValue.append(basicConstraints.isCA());
13457
13458      if (basicConstraints.getPathLengthConstraint() != null)
13459      {
13460        basicConstraintsValue.append(",pathlen:");
13461        basicConstraintsValue.append(
13462             basicConstraints.getPathLengthConstraint());
13463      }
13464
13465      keytoolArguments.add("-ext");
13466      keytoolArguments.add("BasicConstraints=" + basicConstraintsValue);
13467    }
13468
13469    if (keyUsage != null)
13470    {
13471      final StringBuilder keyUsageValue = new StringBuilder();
13472      if (keyUsage.isDigitalSignatureBitSet())
13473      {
13474        commaAppend(keyUsageValue, "digitalSignature");
13475      }
13476
13477      if (keyUsage.isNonRepudiationBitSet())
13478      {
13479        commaAppend(keyUsageValue, "nonRepudiation");
13480      }
13481
13482      if (keyUsage.isKeyEnciphermentBitSet())
13483      {
13484        commaAppend(keyUsageValue, "keyEncipherment");
13485      }
13486
13487      if (keyUsage.isDataEnciphermentBitSet())
13488      {
13489        commaAppend(keyUsageValue, "dataEncipherment");
13490      }
13491
13492      if (keyUsage.isKeyAgreementBitSet())
13493      {
13494        commaAppend(keyUsageValue, "keyAgreement");
13495      }
13496
13497      if (keyUsage.isKeyCertSignBitSet())
13498      {
13499        commaAppend(keyUsageValue, "keyCertSign");
13500      }
13501
13502      if (keyUsage.isCRLSignBitSet())
13503      {
13504        commaAppend(keyUsageValue, "cRLSign");
13505      }
13506
13507      if (keyUsage.isEncipherOnlyBitSet())
13508      {
13509        commaAppend(keyUsageValue, "encipherOnly");
13510      }
13511
13512      if (keyUsage.isEncipherOnlyBitSet())
13513      {
13514        commaAppend(keyUsageValue, "decipherOnly");
13515      }
13516
13517      keytoolArguments.add("-ext");
13518      keytoolArguments.add("KeyUsage=" + keyUsageValue);
13519    }
13520
13521    if (extendedKeyUsage != null)
13522    {
13523      final StringBuilder extendedKeyUsageValue = new StringBuilder();
13524      for (final OID oid : extendedKeyUsage.getKeyPurposeIDs())
13525      {
13526        final ExtendedKeyUsageID id = ExtendedKeyUsageID.forOID(oid);
13527        if (id == null)
13528        {
13529          commaAppend(extendedKeyUsageValue, oid.toString());
13530        }
13531        else
13532        {
13533          switch (id)
13534          {
13535            case TLS_SERVER_AUTHENTICATION:
13536              commaAppend(extendedKeyUsageValue, "serverAuth");
13537              break;
13538            case TLS_CLIENT_AUTHENTICATION:
13539              commaAppend(extendedKeyUsageValue, "clientAuth");
13540              break;
13541            case CODE_SIGNING:
13542              commaAppend(extendedKeyUsageValue, "codeSigning");
13543              break;
13544            case EMAIL_PROTECTION:
13545              commaAppend(extendedKeyUsageValue, "emailProtection");
13546              break;
13547            case TIME_STAMPING:
13548              commaAppend(extendedKeyUsageValue, "timeStamping");
13549              break;
13550            case OCSP_SIGNING:
13551              commaAppend(extendedKeyUsageValue, "OCSPSigning");
13552              break;
13553            default:
13554              // This should never happen.
13555              commaAppend(extendedKeyUsageValue, id.getOID().toString());
13556              break;
13557          }
13558        }
13559      }
13560
13561      keytoolArguments.add("-ext");
13562      keytoolArguments.add("ExtendedKeyUsage=" + extendedKeyUsageValue);
13563    }
13564
13565    if (! sanValues.isEmpty())
13566    {
13567      final StringBuilder subjectAltNameValue = new StringBuilder();
13568      for (final String sanValue : sanValues)
13569      {
13570        commaAppend(subjectAltNameValue, sanValue);
13571      }
13572
13573      keytoolArguments.add("-ext");
13574      keytoolArguments.add("SAN=" + subjectAltNameValue);
13575    }
13576
13577    if (! ianValues.isEmpty())
13578    {
13579      final StringBuilder issuerAltNameValue = new StringBuilder();
13580      for (final String ianValue : ianValues)
13581      {
13582        commaAppend(issuerAltNameValue, ianValue);
13583      }
13584
13585      keytoolArguments.add("-ext");
13586      keytoolArguments.add("IAN=" + issuerAltNameValue);
13587    }
13588
13589    for (final X509CertificateExtension e : genericExtensions)
13590    {
13591      keytoolArguments.add("-ext");
13592      if (e.isCritical())
13593      {
13594        keytoolArguments.add(e.getOID().toString() + ":critical=" +
13595             toColonDelimitedHex(e.getValue()));
13596      }
13597      else
13598      {
13599        keytoolArguments.add(e.getOID().toString() + '=' +
13600             toColonDelimitedHex(e.getValue()));
13601      }
13602    }
13603  }
13604
13605
13606
13607  /**
13608   * Appends the provided value to the given buffer.  If the buffer is not
13609   * empty, the new value will be preceded by a comma.  There will not be any
13610   * spaces on either side of the comma.
13611   *
13612   * @param  buffer  The buffer to which the value should be appended.
13613   * @param  value   The value to append to the buffer.
13614   */
13615  private static void commaAppend(@NotNull final StringBuilder buffer,
13616                                  @NotNull final String value)
13617  {
13618    if (buffer.length() > 0)
13619    {
13620      buffer.append(',');
13621    }
13622
13623    buffer.append(value);
13624  }
13625
13626
13627
13628  /**
13629   * Retrieves a set of information that may be used to generate example usage
13630   * information.  Each element in the returned map should consist of a map
13631   * between an example set of arguments and a string that describes the
13632   * behavior of the tool when invoked with that set of arguments.
13633   *
13634   * @return  A set of information that may be used to generate example usage
13635   *          information.  It may be {@code null} or empty if no example usage
13636   *          information is available.
13637   */
13638  @Override()
13639  @NotNull()
13640  public LinkedHashMap<String[],String> getExampleUsages()
13641  {
13642    final String keystorePath = getPlatformSpecificPath("config", "keystore");
13643    final String keystorePWPath =
13644         getPlatformSpecificPath("config", "keystore.pin");
13645    final String privateKeyPWPath =
13646         getPlatformSpecificPath("config", "server-cert-private-key.pin");
13647    final String exportCertOutputFile =
13648         getPlatformSpecificPath("server-cert.crt");
13649    final String exportKeyOutputFile =
13650         getPlatformSpecificPath("server-cert.private-key");
13651    final String genCSROutputFile = getPlatformSpecificPath("server-cert.csr");
13652    final String truststorePath =
13653         getPlatformSpecificPath("config", "truststore");
13654    final String truststorePWPath =
13655         getPlatformSpecificPath("config", "truststore.pin");
13656
13657    final LinkedHashMap<String[],String> examples =
13658         new LinkedHashMap<>(StaticUtils.computeMapCapacity(20));
13659
13660    examples.put(
13661         new String[]
13662         {
13663           "list-certificates",
13664           "--keystore", keystorePath,
13665           "--keystore-password-file", keystorePWPath,
13666           "--verbose",
13667           "--display-keytool-command"
13668         },
13669         INFO_MANAGE_CERTS_EXAMPLE_LIST_1.get(keystorePath));
13670
13671    examples.put(
13672         new String[]
13673         {
13674           "export-certificate",
13675           "--keystore", keystorePath,
13676           "--keystore-password-file", keystorePWPath,
13677           "--alias", "server-cert",
13678           "--output-file", exportCertOutputFile,
13679           "--output-format", "PEM",
13680           "--verbose",
13681           "--display-keytool-command"
13682         },
13683         INFO_MANAGE_CERTS_EXAMPLE_EXPORT_CERT_1.get(keystorePath,
13684              exportCertOutputFile));
13685
13686    examples.put(
13687         new String[]
13688         {
13689           "export-private-key",
13690           "--keystore", keystorePath,
13691           "--keystore-password-file", keystorePWPath,
13692           "--private-key-password-file", privateKeyPWPath,
13693           "--alias", "server-cert",
13694           "--output-file", exportKeyOutputFile,
13695           "--output-format", "PEM",
13696           "--verbose",
13697           "--display-keytool-command"
13698         },
13699         INFO_MANAGE_CERTS_EXAMPLE_EXPORT_KEY_1.get(keystorePath,
13700              exportKeyOutputFile));
13701
13702    examples.put(
13703         new String[]
13704         {
13705           "import-certificate",
13706           "--keystore", keystorePath,
13707           "--keystore-type", "JKS",
13708           "--keystore-password-file", keystorePWPath,
13709           "--alias", "server-cert",
13710           "--certificate-file", exportCertOutputFile,
13711           "--private-key-file", exportKeyOutputFile,
13712           "--display-keytool-command"
13713         },
13714         INFO_MANAGE_CERTS_EXAMPLE_IMPORT_1.get(exportCertOutputFile,
13715              exportKeyOutputFile, keystorePath));
13716
13717    examples.put(
13718         new String[]
13719         {
13720           "delete-certificate",
13721           "--keystore", keystorePath,
13722           "--keystore-password-file", keystorePWPath,
13723           "--alias", "server-cert"
13724         },
13725         INFO_MANAGE_CERTS_EXAMPLE_DELETE_1.get(keystorePath));
13726
13727    examples.put(
13728         new String[]
13729         {
13730           "generate-self-signed-certificate",
13731           "--keystore", keystorePath,
13732           "--keystore-type", "PKCS12",
13733           "--keystore-password-file", keystorePWPath,
13734           "--alias", "ca-cert",
13735           "--subject-dn", "CN=Example Authority,O=Example Corporation,C=US",
13736           "--days-valid", "7300",
13737           "--validity-start-time", "20170101000000",
13738           "--key-algorithm", "RSA",
13739           "--key-size-bits", "4096",
13740           "--signature-algorithm", "SHA256withRSA",
13741           "--basic-constraints-is-ca", "true",
13742           "--key-usage", "key-cert-sign",
13743           "--key-usage", "crl-sign",
13744           "--display-keytool-command"
13745         },
13746         INFO_MANAGE_CERTS_EXAMPLE_GEN_CERT_1.get(keystorePath));
13747
13748    examples.put(
13749         new String[]
13750         {
13751           "generate-certificate-signing-request",
13752           "--keystore", keystorePath,
13753           "--keystore-type", "PKCS12",
13754           "--keystore-password-file", keystorePWPath,
13755           "--output-file", genCSROutputFile,
13756           "--alias", "server-cert",
13757           "--subject-dn", "CN=ldap.example.com,O=Example Corporation,C=US",
13758           "--key-algorithm", "EC",
13759           "--key-size-bits", "256",
13760           "--signature-algorithm", "SHA256withECDSA",
13761           "--subject-alternative-name-dns", "ldap1.example.com",
13762           "--subject-alternative-name-dns", "ldap2.example.com",
13763           "--extended-key-usage", "server-auth",
13764           "--extended-key-usage", "client-auth",
13765           "--display-keytool-command"
13766         },
13767         INFO_MANAGE_CERTS_EXAMPLE_GEN_CSR_1.get(keystorePath,
13768              genCSROutputFile));
13769
13770    examples.put(
13771         new String[]
13772         {
13773           "generate-certificate-signing-request",
13774           "--keystore", keystorePath,
13775           "--keystore-password-file", keystorePWPath,
13776           "--alias", "server-cert",
13777           "--use-existing-key-pair",
13778           "--inherit-extensions",
13779           "--display-keytool-command"
13780         },
13781         INFO_MANAGE_CERTS_EXAMPLE_GEN_CSR_2.get(keystorePath));
13782
13783    examples.put(
13784         new String[]
13785         {
13786           "sign-certificate-signing-request",
13787           "--keystore", keystorePath,
13788           "--keystore-password-file", keystorePWPath,
13789           "--request-input-file", genCSROutputFile,
13790           "--certificate-output-file", exportCertOutputFile,
13791           "--alias", "ca-cert",
13792           "--days-valid", "730",
13793           "--include-requested-extensions",
13794           "--display-keytool-command"
13795         },
13796         INFO_MANAGE_CERTS_EXAMPLE_SIGN_CERT_1.get(keystorePath,
13797              genCSROutputFile, exportCertOutputFile));
13798
13799    examples.put(
13800         new String[]
13801         {
13802           "change-certificate-alias",
13803           "--keystore", keystorePath,
13804           "--keystore-password-file", keystorePWPath,
13805           "--current-alias", "server-cert",
13806           "--new-alias", "server-certificate",
13807           "--display-keytool-command"
13808         },
13809         INFO_MANAGE_CERTS_EXAMPLE_CHANGE_ALIAS_1.get(keystorePath,
13810              genCSROutputFile, exportCertOutputFile));
13811
13812    examples.put(
13813         new String[]
13814         {
13815           "change-keystore-password",
13816           "--keystore", getPlatformSpecificPath("config", "keystore"),
13817           "--current-keystore-password-file",
13818                getPlatformSpecificPath("config", "current.pin"),
13819           "--new-keystore-password-file",
13820                getPlatformSpecificPath("config", "new.pin"),
13821           "--display-keytool-command"
13822         },
13823         INFO_MANAGE_CERTS_SC_CHANGE_KS_PW_EXAMPLE_1.get(
13824              getPlatformSpecificPath("config", "keystore"),
13825              getPlatformSpecificPath("config", "current.pin"),
13826              getPlatformSpecificPath("config", "new.pin")));
13827
13828    examples.put(
13829         new String[]
13830         {
13831           "retrieve-server-certificate",
13832           "--hostname", "ds.example.com",
13833           "--port", "636"
13834         },
13835         INFO_MANAGE_CERTS_SC_RETRIEVE_CERT_EXAMPLE_1.get(
13836              getPlatformSpecificPath("config", "truststore")));
13837
13838    examples.put(
13839         new String[]
13840         {
13841           "copy-keystore",
13842           "--source-keystore",
13843                getPlatformSpecificPath("config", "keystore.jks"),
13844           "--source-keystore-password-file",
13845                getPlatformSpecificPath("config", "keystore.pin"),
13846           "--source-keystore-type", "JKS",
13847           "--destination-keystore",
13848                getPlatformSpecificPath("config", "keystore.p12"),
13849           "--destination-keystore-password-file",
13850                getPlatformSpecificPath("config", "keystore.pin"),
13851           "--destination-keystore-type", "PKCS12"
13852         },
13853         INFO_MANAGE_CERTS_SC_COPY_KS_EXAMPLE_1.get("keystore.jks",
13854              "keystore.p12"));
13855
13856    examples.put(
13857         new String[]
13858         {
13859           "trust-server-certificate",
13860           "--hostname", "ldap.example.com",
13861           "--port", "636",
13862           "--keystore", truststorePath,
13863           "--keystore-password-file", truststorePWPath,
13864           "--alias", "ldap.example.com:636"
13865         },
13866         INFO_MANAGE_CERTS_EXAMPLE_TRUST_SERVER_1.get(truststorePath));
13867
13868    examples.put(
13869         new String[]
13870         {
13871           "check-certificate-usability",
13872           "--keystore", keystorePath,
13873           "--keystore-password-file", keystorePWPath,
13874           "--alias", "server-cert"
13875         },
13876         INFO_MANAGE_CERTS_EXAMPLE_CHECK_USABILITY_1.get(keystorePath));
13877
13878    examples.put(
13879         new String[]
13880         {
13881           "display-certificate-file",
13882           "--certificate-file", exportCertOutputFile,
13883           "--verbose",
13884           "--display-keytool-command"
13885         },
13886         INFO_MANAGE_CERTS_EXAMPLE_DISPLAY_CERT_1.get(keystorePath));
13887
13888    examples.put(
13889         new String[]
13890         {
13891           "display-certificate-signing-request-file",
13892           "--certificate-signing-request-file", genCSROutputFile,
13893           "--display-keytool-command"
13894         },
13895         INFO_MANAGE_CERTS_EXAMPLE_DISPLAY_CSR_1.get(keystorePath));
13896
13897    examples.put(
13898         new String[]
13899         {
13900           "--help-subcommands"
13901         },
13902         INFO_MANAGE_CERTS_EXAMPLE_HELP_SUBCOMMANDS_1.get(keystorePath));
13903
13904    return examples;
13905  }
13906}