mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
1229 lines
48 KiB
Java
1229 lines
48 KiB
Java
/* Command.java -- Abstract implementation of a keytool command handler
|
|
Copyright (C) 2006 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
package gnu.classpath.tools.keytool;
|
|
|
|
import gnu.classpath.Configuration;
|
|
import gnu.classpath.SystemProperties;
|
|
import gnu.classpath.tools.common.CallbackUtil;
|
|
import gnu.classpath.tools.common.ProviderUtil;
|
|
import gnu.classpath.tools.common.SecurityProviderInfo;
|
|
import gnu.classpath.tools.getopt.Parser;
|
|
import gnu.java.security.OID;
|
|
import gnu.java.security.Registry;
|
|
import gnu.java.security.der.BitString;
|
|
import gnu.java.security.der.DER;
|
|
import gnu.java.security.der.DERReader;
|
|
import gnu.java.security.der.DERValue;
|
|
import gnu.java.security.der.DERWriter;
|
|
import gnu.java.security.hash.IMessageDigest;
|
|
import gnu.java.security.hash.MD5;
|
|
import gnu.java.security.hash.Sha160;
|
|
import gnu.java.security.util.Util;
|
|
import gnu.java.security.x509.X500DistinguishedName;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.PrintWriter;
|
|
import java.math.BigInteger;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.InvalidParameterException;
|
|
import java.security.Key;
|
|
import java.security.KeyPairGenerator;
|
|
import java.security.KeyStore;
|
|
import java.security.KeyStoreException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.PrivateKey;
|
|
import java.security.Provider;
|
|
import java.security.PublicKey;
|
|
import java.security.Signature;
|
|
import java.security.SignatureException;
|
|
import java.security.UnrecoverableKeyException;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.CertificateEncodingException;
|
|
import java.security.cert.CertificateException;
|
|
import java.security.cert.X509Certificate;
|
|
import java.security.interfaces.DSAKey;
|
|
import java.security.interfaces.RSAKey;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.logging.Logger;
|
|
import java.util.prefs.Preferences;
|
|
|
|
import javax.security.auth.callback.Callback;
|
|
import javax.security.auth.callback.CallbackHandler;
|
|
import javax.security.auth.callback.NameCallback;
|
|
import javax.security.auth.callback.PasswordCallback;
|
|
import javax.security.auth.callback.UnsupportedCallbackException;
|
|
|
|
/**
|
|
* A base class of the keytool command to facilitate implementation of concrete
|
|
* keytool Handlers.
|
|
*/
|
|
abstract class Command
|
|
{
|
|
// Fields and constants -----------------------------------------------------
|
|
|
|
private static final Logger log = Logger.getLogger(Command.class.getName());
|
|
/** Default value for the ALIAS argument. */
|
|
private static final String DEFAULT_ALIAS = "mykey"; //$NON-NLS-1$
|
|
/** Default algorithm for key-pair generation. */
|
|
private static final String DEFAULT_KEY_ALGORITHM = "DSA"; //$NON-NLS-1$
|
|
/** Default DSA digital signature algorithm to use with DSA keys. */
|
|
private static final String DSA_SIGNATURE_ALGORITHM = "SHA1withDSA"; //$NON-NLS-1$
|
|
/** Default RSA digital signature algorithm to use with RSA keys. */
|
|
private static final String RSA_SIGNATURE_ALGORITHM = "MD5withRSA"; //$NON-NLS-1$
|
|
/** Default validity (in days) of newly generated certificates. */
|
|
private static final int DEFAULT_VALIDITY = 90;
|
|
/** OID of SHA1withDSA signature algorithm as stated in RFC-2459. */
|
|
protected static final OID SHA1_WITH_DSA = new OID("1.2.840.10040.4.3"); //$NON-NLS-1$
|
|
/** OID of MD2withRSA signature algorithm as stated in RFC-2459. */
|
|
private static final OID MD2_WITH_RSA = new OID("1.2.840.113549.1.1.2"); //$NON-NLS-1$
|
|
/** OID of MD5withRSA signature algorithm as stated in RFC-2459. */
|
|
private static final OID MD5_WITH_RSA = new OID("1.2.840.113549.1.1.4"); //$NON-NLS-1$
|
|
/** OID of SHA1withRSA signature algorithm as stated in RFC-2459. */
|
|
private static final OID SHA1_WITH_RSA = new OID("1.2.840.113549.1.1.5"); //$NON-NLS-1$
|
|
/** Number of milliseconds in one day. */
|
|
private static final long MILLIS_IN_A_DAY = 24 * 60 * 60 * 1000L;
|
|
|
|
/** The Alias to use. */
|
|
protected String alias;
|
|
/** The password characters protecting a Key Entry. */
|
|
protected char[] keyPasswordChars;
|
|
/** A security provider to add. */
|
|
protected Provider provider;
|
|
/** The key store type. */
|
|
protected String storeType;
|
|
/** The password characters protecting the key store. */
|
|
protected char[] storePasswordChars;
|
|
/** The key store URL. */
|
|
protected URL storeURL;
|
|
/** The input stream from the key store URL. */
|
|
protected InputStream storeStream;
|
|
/** The key store instance to use. */
|
|
protected KeyStore store;
|
|
/** The output stream the concrete handler will use. */
|
|
protected OutputStream outStream;
|
|
/** Whether we are printing to System.out. */
|
|
protected boolean systemOut;
|
|
/** The key-pair generation algorithm instance to use. */
|
|
protected KeyPairGenerator keyPairGenerator;
|
|
/** The digital signature algorithm instance to use. */
|
|
protected Signature signatureAlgorithm;
|
|
/** Validity period, in number of days, to use when generating certificates. */
|
|
protected int validityInDays;
|
|
/** The input stream the concrete handler will use. */
|
|
protected InputStream inStream;
|
|
/** Whether verbose output is required or not. */
|
|
protected boolean verbose;
|
|
|
|
/** MD5 hash to use when generating certificate fingerprints. */
|
|
private IMessageDigest md5 = new MD5();
|
|
/** SHA1 hash to use when generating certificate fingerprints. */
|
|
private IMessageDigest sha = new Sha160();
|
|
/** The new position of a user-defined provider if it is not already installed. */
|
|
private int providerNdx = -2;
|
|
/** The callback handler to use when needing to interact with user. */
|
|
private CallbackHandler handler;
|
|
/** The shutdown hook. */
|
|
private ShutdownHook shutdownThread;
|
|
|
|
// Constructor(s) -----------------------------------------------------------
|
|
|
|
protected Command()
|
|
{
|
|
super();
|
|
shutdownThread = new ShutdownHook();
|
|
Runtime.getRuntime().addShutdownHook(shutdownThread);
|
|
}
|
|
|
|
// Methods ------------------------------------------------------------------
|
|
|
|
/**
|
|
* A public method to allow using any keytool command handler programmatically
|
|
* by using a JavaBeans style of parameter(s) initialization. The user is
|
|
* assumed to have set individually the required options through their
|
|
* respective setters before invoking this method.
|
|
* <p>
|
|
* If an exception is encountered during the processing of the command, this
|
|
* implementation attempts to release any resources that may have been
|
|
* allocated at the time the exception occurs, before re-throwing that
|
|
* exception.
|
|
*
|
|
* @throws Exception if an exception occurs during the processing of this
|
|
* command. For a more comprehensive list of exceptions that may
|
|
* occur, see the documentation of the {@link #setup()} and
|
|
* {@link #start()} methods.
|
|
*/
|
|
public void doCommand() throws Exception
|
|
{
|
|
try
|
|
{
|
|
setup();
|
|
start();
|
|
}
|
|
finally
|
|
{
|
|
teardown();
|
|
if (shutdownThread != null)
|
|
Runtime.getRuntime().removeShutdownHook(shutdownThread);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param flag whether to use, or not, more verbose output while processing
|
|
* the command.
|
|
*/
|
|
public void setVerbose(String flag)
|
|
{
|
|
this.verbose = Boolean.valueOf(flag).booleanValue();
|
|
}
|
|
|
|
// life-cycle methods -------------------------------------------------------
|
|
|
|
/**
|
|
* Given a potential sub-array of options for this concrete handler, starting
|
|
* at position <code>startIndex + 1</code>, potentially followed by other
|
|
* commands and their options, this method sets up this concrete command
|
|
* handler with its own options and returns the index of the first unprocessed
|
|
* argument in the array.
|
|
* <p>
|
|
* The general contract of this method is that it is invoked with the
|
|
* <code>startIndex</code> argument pointing to the keyword argument that
|
|
* uniquelly identifies the command itself; e.g. <code>-genkey</code> or
|
|
* <code>-list</code>, etc...
|
|
*
|
|
* @param args an array of options for this handler and possibly other
|
|
* commands and their options.
|
|
* @return the remaining un-processed <code>args</code>.
|
|
*/
|
|
String[] processArgs(String[] args)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.entering(this.getClass().getName(), "processArgs", args); //$NON-NLS-1$
|
|
Parser cmdOptionsParser = getParser();
|
|
String[] result = cmdOptionsParser.parse(args);
|
|
if (Configuration.DEBUG)
|
|
log.exiting(this.getClass().getName(), "processArgs", result); //$NON-NLS-1$
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Initialize this concrete command handler for later invocation of the
|
|
* {@link #start()} or {@link #doCommand()} methods.
|
|
* <p>
|
|
* Handlers usually initialize their local variables and resources within the
|
|
* scope of this call.
|
|
*
|
|
* @throws IOException if an I/O related exception, such as opening an input
|
|
* stream, occurs during the execution of this method.
|
|
* @throws UnsupportedCallbackException if a requested callback handler
|
|
* implementation was not found, or was found but encountered an
|
|
* exception during its processing.
|
|
* @throws ClassNotFoundException if a designated security provider class was
|
|
* not found.
|
|
* @throws IllegalAccessException no 0-arguments constructor for the
|
|
* designated security provider class was found.
|
|
* @throws InstantiationException the designated security provider class is
|
|
* not instantiable.
|
|
* @throws KeyStoreException if an exception occurs during the instantiation
|
|
* of the KeyStore.
|
|
* @throws CertificateException if a certificate related exception, such as
|
|
* expiry, occurs during the loading of the KeyStore.
|
|
* @throws NoSuchAlgorithmException if no current security provider can
|
|
* provide a needed algorithm referenced by the KeyStore or one of
|
|
* its Key Entries or Certificates.
|
|
*/
|
|
abstract void setup() throws Exception;
|
|
|
|
/**
|
|
* Do the real work this handler is supposed to do.
|
|
* <p>
|
|
* The code in this (abstract) class throws a <i>Not implemented yet</i>
|
|
* runtime exception. Concrete implementations MUST override this method.
|
|
*
|
|
* @throws CertificateException If no concrete implementation was found for a
|
|
* certificate Factory of a designated type. In this tool, the type
|
|
* is usually X.509 v1.
|
|
* @throws KeyStoreException if a keys-store related exception occurs; e.g.
|
|
* the key store has not been initialized.
|
|
* @throws IOException if an I/O related exception occurs during the process.
|
|
* @throws SignatureException if a digital signature related exception occurs.
|
|
* @throws InvalidKeyException if the genereated keys are invalid.
|
|
* @throws UnrecoverableKeyException if the password used to unlock a key in
|
|
* the key store was invalid.
|
|
* @throws NoSuchAlgorithmException if a concrete implementation of an
|
|
* algorithm used to store a Key Entry was not found at runtime.
|
|
* @throws UnsupportedCallbackException if a requested callback handler
|
|
* implementation was not found, or was found but encountered an
|
|
* exception during its processing.
|
|
*/
|
|
void start() throws Exception
|
|
{
|
|
throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
|
|
}
|
|
|
|
/**
|
|
* Tear down the handler, releasing any resources which may have been
|
|
* allocated at setup time.
|
|
*/
|
|
void teardown()
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$
|
|
if (storeStream != null)
|
|
try
|
|
{
|
|
storeStream.close();
|
|
}
|
|
catch (IOException ignored)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("Exception while closing key store URL stream. Ignored: " //$NON-NLS-1$
|
|
+ ignored);
|
|
}
|
|
|
|
if (outStream != null)
|
|
{
|
|
try
|
|
{
|
|
outStream.flush();
|
|
}
|
|
catch (IOException ignored)
|
|
{
|
|
}
|
|
|
|
if (! systemOut)
|
|
try
|
|
{
|
|
outStream.close();
|
|
}
|
|
catch (IOException ignored)
|
|
{
|
|
}
|
|
}
|
|
|
|
if (inStream != null)
|
|
try
|
|
{
|
|
inStream.close();
|
|
}
|
|
catch (IOException ignored)
|
|
{
|
|
}
|
|
|
|
if (providerNdx > 0)
|
|
ProviderUtil.removeProvider(provider.getName());
|
|
|
|
if (Configuration.DEBUG)
|
|
log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$
|
|
}
|
|
|
|
// parameter setup and validation methods -----------------------------------
|
|
|
|
/**
|
|
* @return a {@link Parser} that knows how to parse the concrete command's
|
|
* options.
|
|
*/
|
|
abstract Parser getParser();
|
|
|
|
/**
|
|
* Convenience method to setup the key store given its type, its password, its
|
|
* location and portentially a specialized security provider.
|
|
* <p>
|
|
* Calls the method with the same name and 5 arguments passing
|
|
* <code>false</code> to the first argument implying that no attempt to
|
|
* create the keystore will be made if one was not found at the designated
|
|
* location.
|
|
*
|
|
* @param className the potentially null fully qualified class name of a
|
|
* security provider to add at runtime, if no installed provider is
|
|
* able to provide a key store implementation of the desired type.
|
|
* @param type the potentially null type of the key store to request from the
|
|
* key store factory.
|
|
* @param password the potentially null password protecting the key store.
|
|
* @param url the URL of the key store.
|
|
*/
|
|
protected void setKeyStoreParams(String className, String type,
|
|
String password, String url)
|
|
throws IOException, UnsupportedCallbackException, KeyStoreException,
|
|
NoSuchAlgorithmException, CertificateException
|
|
{
|
|
setKeyStoreParams(false, className, type, password, url);
|
|
}
|
|
|
|
/**
|
|
* Convenience method to setup the key store given its type, its password, its
|
|
* location and portentially a specialized security provider.
|
|
*
|
|
* @param createIfNotFound if <code>true</code> then create the keystore if
|
|
* it was not found; otherwise do not.
|
|
* @param className the potentially null fully qualified class name of a
|
|
* security provider to add at runtime, if no installed provider is
|
|
* able to provide a key store implementation of the desired type.
|
|
* @param type the potentially null type of the key store to request from the
|
|
* key store factory.
|
|
* @param password the potentially null password protecting the key store.
|
|
* @param url the URL of the key store.
|
|
*/
|
|
protected void setKeyStoreParams(boolean createIfNotFound, String className,
|
|
String type, String password, String url)
|
|
throws IOException, UnsupportedCallbackException, KeyStoreException,
|
|
NoSuchAlgorithmException, CertificateException
|
|
{
|
|
setProviderClassNameParam(className);
|
|
setKeystoreTypeParam(type);
|
|
setKeystoreURLParam(createIfNotFound, url, password);
|
|
}
|
|
|
|
/**
|
|
* Set a security provider class name to (install and) use for key store
|
|
* related operations.
|
|
*
|
|
* @param className the possibly null, fully qualified class name of a
|
|
* security provider to add, if it is not already installed, to the
|
|
* set of available providers.
|
|
*/
|
|
private void setProviderClassNameParam(String className)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("setProviderClassNameParam(" + className + ")"); //$NON-NLS-1$ //$NON-NLS-2$
|
|
if (className != null && className.trim().length() > 0)
|
|
{
|
|
className = className.trim();
|
|
SecurityProviderInfo spi = ProviderUtil.addProvider(className);
|
|
provider = spi.getProvider();
|
|
if (provider == null)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("Was unable to add provider from class " + className);
|
|
}
|
|
providerNdx = spi.getPosition();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the type of key store to initialize, load and use.
|
|
*
|
|
* @param type the possibly null type of the key store. if this argument is
|
|
* <code>null</code>, or is an empty string, then this method sets
|
|
* the type of the key store to be the default value returned from
|
|
* the invocation of the {@link KeyStore#getDefaultType()} method.
|
|
* For GNU Classpath this is <i>gkr</i> which stands for the "Gnu
|
|
* KeyRing" specifications.
|
|
*/
|
|
private void setKeystoreTypeParam(String type)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("setKeystoreTypeParam(" + type + ")"); //$NON-NLS-1$ //$NON-NLS-2$
|
|
if (type == null || type.trim().length() == 0)
|
|
storeType = KeyStore.getDefaultType();
|
|
else
|
|
storeType = type.trim();
|
|
}
|
|
|
|
/**
|
|
* Set the key password given a command line option argument. If no value was
|
|
* present on the command line then prompt the user to provide one.
|
|
*
|
|
* @param password a possibly null key password gleaned from the command line.
|
|
* @throws IOException if an I/O related exception occurs.
|
|
* @throws UnsupportedCallbackException if no concrete implementation of a
|
|
* password callback was found at runtime.
|
|
*/
|
|
protected void setKeyPasswordParam(String password) throws IOException,
|
|
UnsupportedCallbackException
|
|
{
|
|
setKeyPasswordNoPrompt(password);
|
|
if (keyPasswordChars == null)
|
|
setKeyPasswordParam();
|
|
}
|
|
|
|
/**
|
|
* Set the Alias to use when associating Key Entries and Trusted Certificates
|
|
* in the current key store.
|
|
*
|
|
* @param name the possibly null alias to use. If this arfument is
|
|
* <code>null</code>, then a default value of <code>mykey</code>
|
|
* will be used instead.
|
|
*/
|
|
protected void setAliasParam(String name)
|
|
{
|
|
alias = name == null ? DEFAULT_ALIAS : name.trim();
|
|
}
|
|
|
|
/**
|
|
* Set the key password given a command line option argument.
|
|
*
|
|
* @param password a possibly null key password gleaned from the command line.
|
|
*/
|
|
protected void setKeyPasswordNoPrompt(String password)
|
|
{
|
|
if (password != null)
|
|
keyPasswordChars = password.toCharArray();
|
|
}
|
|
|
|
/**
|
|
* Prompt the user to provide a password to protect a Key Entry in the key
|
|
* store.
|
|
*
|
|
* @throws IOException if an I/O related exception occurs.
|
|
* @throws UnsupportedCallbackException if no concrete implementation of a
|
|
* password callback was found at runtime.
|
|
* @throws SecurityException if no password is available, even after prompting
|
|
* the user.
|
|
*/
|
|
private void setKeyPasswordParam() throws IOException,
|
|
UnsupportedCallbackException
|
|
{
|
|
String prompt = Messages.getFormattedString("Command.21", alias); //$NON-NLS-1$
|
|
PasswordCallback pcb = new PasswordCallback(prompt, false);
|
|
getCallbackHandler().handle(new Callback[] { pcb });
|
|
keyPasswordChars = pcb.getPassword();
|
|
pcb.clearPassword();
|
|
if (keyPasswordChars == null)
|
|
throw new SecurityException(Messages.getString("Command.23")); //$NON-NLS-1$
|
|
}
|
|
|
|
private void setKeystorePasswordParam(String password) throws IOException,
|
|
UnsupportedCallbackException
|
|
{
|
|
if (password != null)
|
|
storePasswordChars = password.toCharArray();
|
|
else // ask the user to provide one
|
|
{
|
|
String prompt = Messages.getString("Command.24"); //$NON-NLS-1$
|
|
PasswordCallback pcb = new PasswordCallback(prompt, false);
|
|
getCallbackHandler().handle(new Callback[] { pcb });
|
|
storePasswordChars = pcb.getPassword();
|
|
pcb.clearPassword();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the key store URL to use.
|
|
*
|
|
* @param createIfNotFound when <code>true</code> an attempt to create a
|
|
* keystore at the designated location will be made. If
|
|
* <code>false</code> then no file creation is carried out, which
|
|
* may cause an exception to be thrown later.
|
|
* @param url the full, or partial, URL to the keystore location.
|
|
* @param password an eventually null string to use when loading the keystore.
|
|
* @throws IOException
|
|
* @throws KeyStoreException
|
|
* @throws UnsupportedCallbackException
|
|
* @throws NoSuchAlgorithmException
|
|
* @throws CertificateException
|
|
*/
|
|
private void setKeystoreURLParam(boolean createIfNotFound, String url,
|
|
String password) throws IOException,
|
|
KeyStoreException, UnsupportedCallbackException, NoSuchAlgorithmException,
|
|
CertificateException
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("setKeystoreURLParam(" + url + ")"); //$NON-NLS-1$ //$NON-NLS-2$
|
|
if (url == null || url.trim().length() == 0)
|
|
{
|
|
String userHome = SystemProperties.getProperty("user.home"); //$NON-NLS-1$
|
|
if (userHome == null || userHome.trim().length() == 0)
|
|
throw new InvalidParameterException(Messages.getString("Command.36")); //$NON-NLS-1$
|
|
|
|
url = userHome.trim() + "/.keystore"; //$NON-NLS-1$
|
|
// if it does not exist create it if required
|
|
if (createIfNotFound)
|
|
new File(url).createNewFile();
|
|
url = "file:" + url; //$NON-NLS-1$
|
|
}
|
|
else
|
|
{
|
|
url = url.trim();
|
|
if (url.indexOf(":") == -1) // if it does not exist create it //$NON-NLS-1$
|
|
{
|
|
if (createIfNotFound)
|
|
new File(url).createNewFile();
|
|
}
|
|
url = "file:" + url; //$NON-NLS-1$
|
|
}
|
|
|
|
boolean newKeyStore = false;
|
|
storeURL = new URL(url);
|
|
storeStream = storeURL.openStream();
|
|
if (storeStream.available() == 0)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("Store is empty. Will use <null> when loading, to create it"); //$NON-NLS-1$
|
|
newKeyStore = true;
|
|
}
|
|
|
|
try
|
|
{
|
|
store = KeyStore.getInstance(storeType);
|
|
}
|
|
catch (KeyStoreException x)
|
|
{
|
|
if (provider != null)
|
|
throw x;
|
|
|
|
if (Configuration.DEBUG)
|
|
log.fine("Exception while getting key store with default provider(s)." //$NON-NLS-1$
|
|
+ " Will prompt user for another provider and continue"); //$NON-NLS-1$
|
|
String prompt = Messages.getString("Command.40"); //$NON-NLS-1$
|
|
NameCallback ncb = new NameCallback(prompt);
|
|
getCallbackHandler().handle(new Callback[] { ncb });
|
|
String className = ncb.getName();
|
|
setProviderClassNameParam(className); // we may have a Provider
|
|
if (provider == null)
|
|
{
|
|
x.fillInStackTrace();
|
|
throw x;
|
|
}
|
|
// try again
|
|
store = KeyStore.getInstance(storeType, provider);
|
|
}
|
|
|
|
setKeystorePasswordParam(password);
|
|
|
|
// now we have a KeyStore instance. load it
|
|
// KeyStore public API claims: "...In order to create an empty keystore,
|
|
// you pass null as the InputStream argument to the load method.
|
|
if (newKeyStore)
|
|
store.load(null, storePasswordChars);
|
|
else
|
|
store.load(storeStream, storePasswordChars);
|
|
|
|
// close the stream
|
|
try
|
|
{
|
|
storeStream.close();
|
|
storeStream = null;
|
|
}
|
|
catch (IOException x)
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.fine("Exception while closing the key store input stream: " + x //$NON-NLS-1$
|
|
+ ". Ignore"); //$NON-NLS-1$
|
|
}
|
|
}
|
|
|
|
protected void setOutputStreamParam(String fileName) throws SecurityException,
|
|
IOException
|
|
{
|
|
if (fileName == null || fileName.trim().length() == 0)
|
|
{
|
|
outStream = System.out;
|
|
systemOut = true;
|
|
}
|
|
else
|
|
{
|
|
fileName = fileName.trim();
|
|
File outFile = new File(fileName);
|
|
if (! outFile.exists())
|
|
{
|
|
boolean ok = outFile.createNewFile();
|
|
if (!ok)
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.19", //$NON-NLS-1$
|
|
fileName));
|
|
}
|
|
else
|
|
{
|
|
if (! outFile.isFile())
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.42", //$NON-NLS-1$
|
|
fileName));
|
|
if (! outFile.canWrite())
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.44", //$NON-NLS-1$
|
|
fileName));
|
|
}
|
|
outStream = new FileOutputStream(outFile);
|
|
}
|
|
}
|
|
|
|
protected void setInputStreamParam(String fileName)
|
|
throws FileNotFoundException
|
|
{
|
|
if (fileName == null || fileName.trim().length() == 0)
|
|
inStream = System.in;
|
|
else
|
|
{
|
|
fileName = fileName.trim();
|
|
File inFile = new File(fileName);
|
|
if (! (inFile.exists() && inFile.isFile() && inFile.canRead()))
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.46", //$NON-NLS-1$
|
|
fileName));
|
|
inStream = new FileInputStream(inFile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set both the key-pair generation algorithm, and the digital signature
|
|
* algorithm instances to use when generating new entries.
|
|
*
|
|
* @param kpAlg the possibly null name of a key-pair generator algorithm.
|
|
* if this argument is <code>null</code> or is an empty string, the
|
|
* "DSS" algorithm will be used.
|
|
* @param sigAlg the possibly null name of a digital signature algorithm.
|
|
* If this argument is <code>null</code> or is an empty string, this
|
|
* method uses the "SHA1withDSA" (Digital Signature Standard, a.k.a.
|
|
* DSA, with the Secure Hash Algorithm function) as the default
|
|
* algorithm if, and only if, the key-pair generation algorithm ends
|
|
* up being "DSS"; otherwise, if the key-pair generation algorithm
|
|
* was "RSA", then the "MD5withRSA" signature algorithm will be used.
|
|
* If the key-pair generation algorithm is neither "DSS" (or its
|
|
* alias "DSA"), nor is it "RSA", then an exception is thrown.
|
|
* @throws NoSuchAlgorithmException if no concrete implementation of the
|
|
* designated algorithm is available.
|
|
*/
|
|
protected void setAlgorithmParams(String kpAlg, String sigAlg)
|
|
throws NoSuchAlgorithmException
|
|
{
|
|
if (kpAlg == null || kpAlg.trim().length() == 0)
|
|
kpAlg = DEFAULT_KEY_ALGORITHM;
|
|
else
|
|
kpAlg = kpAlg.trim().toLowerCase();
|
|
|
|
keyPairGenerator = KeyPairGenerator.getInstance(kpAlg);
|
|
|
|
if (sigAlg == null || sigAlg.trim().length() == 0)
|
|
if (kpAlg.equalsIgnoreCase(Registry.DSS_KPG)
|
|
|| kpAlg.equalsIgnoreCase(Registry.DSA_KPG))
|
|
sigAlg = DSA_SIGNATURE_ALGORITHM;
|
|
else if (kpAlg.equalsIgnoreCase(Registry.RSA_KPG))
|
|
sigAlg = RSA_SIGNATURE_ALGORITHM;
|
|
else
|
|
throw new IllegalArgumentException(
|
|
Messages.getFormattedString("Command.20", //$NON-NLS-1$
|
|
new String[] { sigAlg, kpAlg }));
|
|
else
|
|
sigAlg = sigAlg.trim().toLowerCase();
|
|
|
|
signatureAlgorithm = Signature.getInstance(sigAlg);
|
|
}
|
|
|
|
/**
|
|
* Set the signature algorithm to use when digitally signing private keys,
|
|
* certificates, etc...
|
|
* <p>
|
|
* If the designated algorithm name is <code>null</code> or is an empty
|
|
* string, this method checks the private key (the second argument) and based
|
|
* on its type decides which algorithm to use. The keytool public
|
|
* specification states that if the private key is a DSA key, then the
|
|
* signature algorithm will be <code>SHA1withDSA</code>, otherwise if it is
|
|
* an RSA private key, then the signature algorithm will be
|
|
* <code>MD5withRSA</code>. If the private key is neither a private DSA nor
|
|
* a private RSA key, then this method throws an
|
|
* {@link IllegalArgumentException}.
|
|
*
|
|
* @param algorithm the possibly null name of a digital signature algorithm.
|
|
* @param privateKey an instance of a private key to use as a fal-back option
|
|
* when <code>algorithm</code> is invalid.
|
|
* @throws NoSuchAlgorithmException if no concrete implementation of the
|
|
* designated, or default, signature algorithm is available.
|
|
*/
|
|
protected void setSignatureAlgorithmParam(String algorithm, Key privateKey)
|
|
throws NoSuchAlgorithmException
|
|
{
|
|
if (algorithm == null || algorithm.trim().length() == 0)
|
|
if (privateKey instanceof DSAKey)
|
|
algorithm = DSA_SIGNATURE_ALGORITHM;
|
|
else if (privateKey instanceof RSAKey)
|
|
algorithm = RSA_SIGNATURE_ALGORITHM;
|
|
else
|
|
throw new InvalidParameterException(Messages.getString("Command.48")); //$NON-NLS-1$
|
|
else
|
|
algorithm = algorithm.trim();
|
|
|
|
signatureAlgorithm = Signature.getInstance(algorithm);
|
|
}
|
|
|
|
/**
|
|
* Set the validity period, in number of days, to use when issuing new
|
|
* certificates.
|
|
*
|
|
* @param days the number of days, as a string, the generated certificate will
|
|
* be valid for, starting from today's date. if this argument is
|
|
* <code>null</code>, a default value of <code>90</code> days
|
|
* will be used.
|
|
* @throws NumberFormatException if the designated string is not a decimal
|
|
* integer.
|
|
* @throws InvalidParameterException if the integer value of the non-null
|
|
* string is not greater than zero.
|
|
*/
|
|
protected void setValidityParam(String days)
|
|
{
|
|
if (days == null || days.trim().length() == 0)
|
|
validityInDays = DEFAULT_VALIDITY;
|
|
else
|
|
{
|
|
days = days.trim();
|
|
validityInDays = Integer.parseInt(days);
|
|
if (validityInDays < 1)
|
|
throw new InvalidParameterException(Messages.getString("Command.51")); //$NON-NLS-1$
|
|
}
|
|
}
|
|
|
|
/**
|
|
* RFC-2459 (http://rfc.net/rfc2459.html) fully describes the structure and
|
|
* semantics of X.509 certificates. The ASN.1 structures below are gleaned
|
|
* from that reference.
|
|
*
|
|
* <pre>
|
|
* Certificate ::= SEQUENCE {
|
|
* tbsCertificate TBSCertificate,
|
|
* signatureAlgorithm AlgorithmIdentifier,
|
|
* signatureValue BIT STRING
|
|
* }
|
|
*
|
|
* TBSCertificate ::= SEQUENCE {
|
|
* version [0] EXPLICIT Version DEFAULT v1,
|
|
* serialNumber CertificateSerialNumber,
|
|
* signature AlgorithmIdentifier,
|
|
* issuer Name,
|
|
* validity Validity,
|
|
* subject Name,
|
|
* subjectPublicKeyInfo SubjectPublicKeyInfo
|
|
* }
|
|
*
|
|
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
|
*
|
|
* CertificateSerialNumber ::= INTEGER
|
|
*
|
|
* Validity ::= SEQUENCE {
|
|
* notBefore Time,
|
|
* notAfter Time
|
|
* }
|
|
*
|
|
* Time ::= CHOICE {
|
|
* utcTime UTCTime,
|
|
* generalTime GeneralizedTime
|
|
* }
|
|
*
|
|
* UniqueIdentifier ::= BIT STRING
|
|
*
|
|
* SubjectPublicKeyInfo ::= SEQUENCE {
|
|
* algorithm AlgorithmIdentifier,
|
|
* subjectPublicKey BIT STRING
|
|
* }
|
|
* </pre>
|
|
*
|
|
* @param distinguishedName the X.500 Distinguished Name to use as both the
|
|
* Issuer and Subject of the self-signed certificate to generate.
|
|
* @param publicKey the public key of the issuer/subject.
|
|
* @param privateKey the private key of the issuer/signer.
|
|
* @return the DER encoded form of a self-signed X.509 v1 certificate.
|
|
* @throws IOException If an I/O related exception occurs during the process.
|
|
* @throws SignatureException If a digital signature related exception occurs.
|
|
* @throws InvalidKeyException if the designated private key is invalid.
|
|
* @throws InvalidParameterException if the concrete signature algorithm does
|
|
* not know its name, no OID is known/supported for that name, or we
|
|
* were unable to match the name to a known string for which we can
|
|
* use a standard OID.
|
|
*/
|
|
protected byte[] getSelfSignedCertificate(X500DistinguishedName distinguishedName,
|
|
PublicKey publicKey,
|
|
PrivateKey privateKey)
|
|
throws IOException, SignatureException, InvalidKeyException
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.entering(this.getClass().getName(), "getSelfSignedCertificate", //$NON-NLS-1$
|
|
new Object[] { distinguishedName, publicKey, privateKey });
|
|
byte[] versionBytes = new DERValue(DER.INTEGER, BigInteger.ZERO).getEncoded();
|
|
DERValue derVersion = new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0,
|
|
versionBytes.length, versionBytes, null);
|
|
|
|
// NOTE (rsn): the next 3 lines should be atomic but they're not.
|
|
Preferences prefs = Preferences.systemNodeForPackage(this.getClass());
|
|
int lastSerialNumber = prefs.getInt(Main.LAST_SERIAL_NUMBER, 0) + 1;
|
|
prefs.putInt(Main.LAST_SERIAL_NUMBER, lastSerialNumber);
|
|
DERValue derSerialNumber = new DERValue(DER.INTEGER,
|
|
BigInteger.valueOf(lastSerialNumber));
|
|
|
|
OID signatureID = getSignatureAlgorithmOID();
|
|
DERValue derSignatureID = new DERValue(DER.OBJECT_IDENTIFIER, signatureID);
|
|
ArrayList signature = new ArrayList(1);
|
|
signature.add(derSignatureID);
|
|
// rfc-2459 states the following:
|
|
//
|
|
// for the DSA signature:
|
|
// ...Where the id-dsa-with-sha1 algorithm identifier appears as the
|
|
// algorithm field in an AlgorithmIdentifier, the encoding shall omit
|
|
// the parameters field. That is, the AlgorithmIdentifier shall be a
|
|
// SEQUENCE of one component - the OBJECT IDENTIFIER id-dsa-with-sha1.
|
|
//
|
|
// for RSA signatures:
|
|
// ...When any of these three OIDs (i.e. xxxWithRSAEncryption) appears
|
|
// within the ASN.1 type AlgorithmIdentifier, the parameters component of
|
|
// that type shall be the ASN.1 type NULL.
|
|
if (! signatureID.equals(SHA1_WITH_DSA))
|
|
signature.add(new DERValue(DER.NULL, null));
|
|
|
|
DERValue derSignature = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
|
signature);
|
|
|
|
DERValue derIssuer = new DERReader(distinguishedName.getDer()).read();
|
|
|
|
long notBefore = System.currentTimeMillis();
|
|
long notAfter = notBefore + validityInDays * MILLIS_IN_A_DAY;
|
|
|
|
ArrayList validity = new ArrayList(2);
|
|
validity.add(new DERValue(DER.UTC_TIME, new Date(notBefore)));
|
|
validity.add(new DERValue(DER.UTC_TIME, new Date(notAfter)));
|
|
DERValue derValidity = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
|
validity);
|
|
|
|
// for a self-signed certificate subject and issuer are identical
|
|
DERValue derSubject = derIssuer;
|
|
|
|
DERValue derSubjectPublicKeyInfo = new DERReader(publicKey.getEncoded()).read();
|
|
|
|
ArrayList tbsCertificate = new ArrayList(7);
|
|
tbsCertificate.add(derVersion);
|
|
tbsCertificate.add(derSerialNumber);
|
|
tbsCertificate.add(derSignature);
|
|
tbsCertificate.add(derIssuer);
|
|
tbsCertificate.add(derValidity);
|
|
tbsCertificate.add(derSubject);
|
|
tbsCertificate.add(derSubjectPublicKeyInfo);
|
|
DERValue derTBSCertificate = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
|
tbsCertificate);
|
|
|
|
// The 'signature' field MUST contain the same algorithm identifier as the
|
|
// 'signatureAlgorithm' field in the sequence Certificate.
|
|
DERValue derSignatureAlgorithm = derSignature;
|
|
|
|
signatureAlgorithm.initSign(privateKey);
|
|
signatureAlgorithm.update(derTBSCertificate.getEncoded());
|
|
byte[] sigBytes = signatureAlgorithm.sign();
|
|
DERValue derSignatureValue = new DERValue(DER.BIT_STRING,
|
|
new BitString(sigBytes));
|
|
|
|
ArrayList certificate = new ArrayList(3);
|
|
certificate.add(derTBSCertificate);
|
|
certificate.add(derSignatureAlgorithm);
|
|
certificate.add(derSignatureValue);
|
|
DERValue derCertificate = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
|
certificate);
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
DERWriter.write(baos, derCertificate);
|
|
byte[] result = baos.toByteArray();
|
|
if (Configuration.DEBUG)
|
|
log.exiting(this.getClass().getName(), "getSelfSignedCertificate"); //$NON-NLS-1$
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method attempts to find, and return, an OID representing the digital
|
|
* signature algorithm used to sign the certificate. The OIDs returned are
|
|
* those described in RFC-2459. They are listed here for the sake of
|
|
* completness.
|
|
*
|
|
* <pre>
|
|
* id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
|
|
* iso(1) member-body(2) us(840) x9-57 (10040) x9cm(4) 3
|
|
* }
|
|
*
|
|
* md2WithRSAEncryption OBJECT IDENTIFIER ::= {
|
|
* iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 2
|
|
* }
|
|
*
|
|
* md5WithRSAEncryption OBJECT IDENTIFIER ::= {
|
|
* iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 4
|
|
* }
|
|
*
|
|
* sha-1WithRSAEncryption OBJECT IDENTIFIER ::= {
|
|
* iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 5
|
|
* }
|
|
* </pre>
|
|
*
|
|
* <b>IMPORTANT</b>: This method checks the signature algorithm name against
|
|
* (a) The GNU algorithm implementation's name, and (b) publicly referenced
|
|
* names of the same algorithm. In other words this search is not
|
|
* comprehensive and may fail for uncommon names of the same algorithms.
|
|
*
|
|
* @return the OID of the signature algorithm in use.
|
|
* @throws InvalidParameterException if the concrete signature algorithm does
|
|
* not know its name, no OID is known/supported for that name, or we
|
|
* were unable to match the name to a known string for which we can
|
|
* return an OID.
|
|
*/
|
|
protected OID getSignatureAlgorithmOID()
|
|
{
|
|
String algorithm = signatureAlgorithm.getAlgorithm();
|
|
// if we already have a non-null signature then the name was valid. the
|
|
// only case where algorithm is invalid would be if the implementation is
|
|
// flawed. check anyway
|
|
if (algorithm == null || algorithm.trim().length() == 0)
|
|
throw new InvalidParameterException(Messages.getString("Command.52")); //$NON-NLS-1$
|
|
|
|
algorithm = algorithm.trim();
|
|
if (algorithm.equalsIgnoreCase(Registry.DSS_SIG)
|
|
|| algorithm.equalsIgnoreCase("SHA1withDSA")) //$NON-NLS-1$
|
|
return SHA1_WITH_DSA;
|
|
|
|
if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$
|
|
+ Registry.MD2_HASH)
|
|
|| algorithm.equalsIgnoreCase("MD2withRSA")) //$NON-NLS-1$
|
|
return MD2_WITH_RSA;
|
|
|
|
if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$
|
|
+ Registry.MD5_HASH)
|
|
|| algorithm.equalsIgnoreCase("MD5withRSA") //$NON-NLS-1$
|
|
|| algorithm.equalsIgnoreCase("rsa")) //$NON-NLS-1$
|
|
return MD5_WITH_RSA;
|
|
|
|
if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$
|
|
+ Registry.SHA160_HASH)
|
|
|| algorithm.equalsIgnoreCase("SHA1withRSA")) //$NON-NLS-1$
|
|
return SHA1_WITH_RSA;
|
|
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.60", //$NON-NLS-1$
|
|
algorithm));
|
|
}
|
|
|
|
/**
|
|
* Saves the key store using the designated password. This operation is called
|
|
* by handlers if/when the key store password has changed, or amendements have
|
|
* been made to the contents of the store; e.g. addition of a new Key Entry or
|
|
* a Trusted Certificate.
|
|
*
|
|
* @param password the password protecting the key store.
|
|
* @throws IOException if an I/O related exception occurs during the process.
|
|
* @throws CertificateException if any of the certificates in the current key
|
|
* store could not be persisted.
|
|
* @throws NoSuchAlgorithmException if a required data integrity algorithm
|
|
* implementation was not found.
|
|
* @throws KeyStoreException if the key store has not been loaded previously.
|
|
*/
|
|
protected void saveKeyStore(char[] password) throws IOException,
|
|
KeyStoreException, NoSuchAlgorithmException, CertificateException
|
|
{
|
|
if (Configuration.DEBUG)
|
|
log.entering(this.getClass().getName(), "saveKeyStore"); //$NON-NLS-1$
|
|
URLConnection con = storeURL.openConnection();
|
|
con.setDoOutput(true);
|
|
con.setUseCaches(false);
|
|
OutputStream out = con.getOutputStream();
|
|
if (verbose)
|
|
System.out.println(Messages.getFormattedString("Command.63", storeURL.getPath())); //$NON-NLS-1$
|
|
|
|
store.store(out, password);
|
|
out.flush();
|
|
out.close();
|
|
if (Configuration.DEBUG)
|
|
log.exiting(this.getClass().getName(), "saveKeyStore"); //$NON-NLS-1$
|
|
}
|
|
|
|
/**
|
|
* Convenience method. Calls the method with the same name passing it the
|
|
* same password characters used to initially load the key-store.
|
|
*
|
|
* @throws IOException if an I/O related exception occurs during the process.
|
|
* @throws KeyStoreException if the key store has not been loaded previously.
|
|
* @throws NoSuchAlgorithmException if a required data integrity algorithm
|
|
* implementation was not found.
|
|
* @throws CertificateException if any of the certificates in the current key
|
|
* store could not be persisted.
|
|
*/
|
|
protected void saveKeyStore() throws IOException, KeyStoreException,
|
|
NoSuchAlgorithmException, CertificateException
|
|
{
|
|
saveKeyStore(storePasswordChars);
|
|
}
|
|
|
|
/**
|
|
* Prints a human-readable form of the designated certificate to a designated
|
|
* {@link PrintWriter}.
|
|
*
|
|
* @param certificate the certificate to process.
|
|
* @param writer where to print it.
|
|
* @throws CertificateEncodingException if an exception occurs while obtaining
|
|
* the DER encoded form <code>certificate</code>.
|
|
*/
|
|
protected void printVerbose(Certificate certificate, PrintWriter writer)
|
|
throws CertificateEncodingException
|
|
{
|
|
X509Certificate x509 = (X509Certificate) certificate;
|
|
writer.println(Messages.getFormattedString("Command.66", x509.getSubjectDN())); //$NON-NLS-1$
|
|
writer.println(Messages.getFormattedString("Command.67", x509.getIssuerDN())); //$NON-NLS-1$
|
|
writer.println(Messages.getFormattedString("Command.68", x509.getSerialNumber())); //$NON-NLS-1$
|
|
writer.println(Messages.getFormattedString("Command.69", x509.getNotBefore())); //$NON-NLS-1$
|
|
writer.println(Messages.getFormattedString("Command.70", x509.getNotAfter())); //$NON-NLS-1$
|
|
writer.println(Messages.getString("Command.71")); //$NON-NLS-1$
|
|
byte[] derBytes = certificate.getEncoded();
|
|
writer.println(Messages.getFormattedString("Command.72", digest(md5, derBytes))); //$NON-NLS-1$
|
|
writer.println(Messages.getFormattedString("Command.73", digest(sha, derBytes))); //$NON-NLS-1$
|
|
}
|
|
|
|
/**
|
|
* Convenience method. Prints a human-readable form of the designated
|
|
* certificate to <code>System.out</code>.
|
|
*
|
|
* @param certificate the certificate to process.
|
|
* @throws CertificateEncodingException if an exception occurs while obtaining
|
|
* the DER encoded form <code>certificate</code>.
|
|
*/
|
|
protected void printVerbose(Certificate certificate)
|
|
throws CertificateEncodingException
|
|
{
|
|
printVerbose(certificate, new PrintWriter(System.out, true));
|
|
}
|
|
|
|
/**
|
|
* Digest the designated contents with MD5 and return a string representation
|
|
* suitable for use as a fingerprint; i.e. sequence of hexadecimal pairs of
|
|
* characters separated by a colon.
|
|
*
|
|
* @param contents the non-null contents to digest.
|
|
* @return a sequence of hexadecimal pairs of characters separated by colons.
|
|
*/
|
|
protected String digestWithMD5(byte[] contents)
|
|
{
|
|
return digest(md5, contents);
|
|
}
|
|
|
|
private String digest(IMessageDigest hash, byte[] encoded)
|
|
{
|
|
hash.update(encoded);
|
|
byte[] b = hash.digest();
|
|
StringBuilder sb = new StringBuilder().append(Util.toString(b, 0, 1));
|
|
for (int i = 1; i < b.length; i++)
|
|
sb.append(":").append(Util.toString(b, i, 1)); //$NON-NLS-1$
|
|
|
|
String result = sb.toString();
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Ensure that the currently set Alias is contained in the currently set key
|
|
* store; otherwise throw an exception.
|
|
*
|
|
* @throws KeyStoreException if the keystore has not been loaded.
|
|
* @throws IllegalArgumentException if the currently set alias is not known to
|
|
* the currently set key store.
|
|
*/
|
|
protected void ensureStoreContainsAlias() throws KeyStoreException
|
|
{
|
|
if (! store.containsAlias(alias))
|
|
throw new IllegalArgumentException(Messages.getFormattedString("Command.75", //$NON-NLS-1$
|
|
alias));
|
|
}
|
|
|
|
/**
|
|
* Ensure that the currently set Alias is associated with a Key Entry in the
|
|
* currently set key store; otherwise throw an exception.
|
|
*
|
|
* @throws KeyStoreException if the keystore has not been loaded.
|
|
* @throws SecurityException if the currently set alias is not a Key Entry in
|
|
* the currently set key store.
|
|
*/
|
|
protected void ensureAliasIsKeyEntry() throws KeyStoreException
|
|
{
|
|
if (! store.isKeyEntry(alias))
|
|
throw new SecurityException(Messages.getFormattedString("Command.77", //$NON-NLS-1$
|
|
alias));
|
|
}
|
|
|
|
protected Key getAliasPrivateKey() throws KeyStoreException,
|
|
NoSuchAlgorithmException, IOException, UnsupportedCallbackException,
|
|
UnrecoverableKeyException
|
|
{
|
|
ensureAliasIsKeyEntry();
|
|
Key result;
|
|
if (keyPasswordChars == null)
|
|
try
|
|
{
|
|
result = store.getKey(alias, storePasswordChars);
|
|
// it worked. assign to keyPasswordChars for later use
|
|
keyPasswordChars = storePasswordChars;
|
|
}
|
|
catch (UnrecoverableKeyException x)
|
|
{
|
|
// prompt the user to provide one
|
|
setKeyPasswordParam();
|
|
result = store.getKey(alias, keyPasswordChars);
|
|
}
|
|
else
|
|
result = store.getKey(alias, keyPasswordChars);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return a CallbackHandler which uses the Console (System.in and System.out)
|
|
* for interacting with the user.
|
|
* <p>
|
|
* This method first finds all currently installed security providers capable
|
|
* of providing such service and then in turn attempts to instantiate the
|
|
* handler from those providers. As soon as one provider returns a non-null
|
|
* instance of the callback handler, the search stops and that instance is
|
|
* set to be used from now on.
|
|
* <p>
|
|
* If no installed providers were found, this method falls back on the GNU
|
|
* provider, by-passing the Security search mechanism. The default console
|
|
* callback handler implementation is
|
|
* {@link gnu.javax.security.auth.callback.ConsoleCallbackHandler}.
|
|
*
|
|
* @return a console-based {@link CallbackHandler}.
|
|
*/
|
|
protected CallbackHandler getCallbackHandler()
|
|
{
|
|
if (handler == null)
|
|
handler = CallbackUtil.getConsoleHandler();
|
|
|
|
return handler;
|
|
}
|
|
|
|
// Inner class(es) ==========================================================
|
|
|
|
private class ShutdownHook
|
|
extends Thread
|
|
{
|
|
public void run()
|
|
{
|
|
teardown();
|
|
}
|
|
}
|
|
}
|