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