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