| 1 |
779 |
jeremybenn |
/* Command.java -- Abstract implementation of a keytool command handler
|
| 2 |
|
|
Copyright (C) 2006 Free Software Foundation, Inc.
|
| 3 |
|
|
|
| 4 |
|
|
This file is part of GNU Classpath.
|
| 5 |
|
|
|
| 6 |
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
| 7 |
|
|
it under the terms of the GNU General Public License as published by
|
| 8 |
|
|
the Free Software Foundation; either version 2, or (at your option)
|
| 9 |
|
|
any later version.
|
| 10 |
|
|
|
| 11 |
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
| 12 |
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 13 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| 14 |
|
|
General Public License for more details.
|
| 15 |
|
|
|
| 16 |
|
|
You should have received a copy of the GNU General Public License
|
| 17 |
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
| 18 |
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
| 19 |
|
|
02110-1301 USA.
|
| 20 |
|
|
|
| 21 |
|
|
Linking this library statically or dynamically with other modules is
|
| 22 |
|
|
making a combined work based on this library. Thus, the terms and
|
| 23 |
|
|
conditions of the GNU General Public License cover the whole
|
| 24 |
|
|
combination.
|
| 25 |
|
|
|
| 26 |
|
|
As a special exception, the copyright holders of this library give you
|
| 27 |
|
|
permission to link this library with independent modules to produce an
|
| 28 |
|
|
executable, regardless of the license terms of these independent
|
| 29 |
|
|
modules, and to copy and distribute the resulting executable under
|
| 30 |
|
|
terms of your choice, provided that you also meet, for each linked
|
| 31 |
|
|
independent module, the terms and conditions of the license of that
|
| 32 |
|
|
module. An independent module is a module which is not derived from
|
| 33 |
|
|
or based on this library. If you modify this library, you may extend
|
| 34 |
|
|
this exception to your version of the library, but you are not
|
| 35 |
|
|
obligated to do so. If you do not wish to do so, delete this
|
| 36 |
|
|
exception statement from your version. */
|
| 37 |
|
|
|
| 38 |
|
|
|
| 39 |
|
|
package gnu.classpath.tools.keytool;
|
| 40 |
|
|
|
| 41 |
|
|
import gnu.classpath.Configuration;
|
| 42 |
|
|
import gnu.classpath.SystemProperties;
|
| 43 |
|
|
import gnu.classpath.tools.common.CallbackUtil;
|
| 44 |
|
|
import gnu.classpath.tools.common.ProviderUtil;
|
| 45 |
|
|
import gnu.classpath.tools.common.SecurityProviderInfo;
|
| 46 |
|
|
import gnu.classpath.tools.getopt.Parser;
|
| 47 |
|
|
import gnu.java.security.OID;
|
| 48 |
|
|
import gnu.java.security.Registry;
|
| 49 |
|
|
import gnu.java.security.der.BitString;
|
| 50 |
|
|
import gnu.java.security.der.DER;
|
| 51 |
|
|
import gnu.java.security.der.DERReader;
|
| 52 |
|
|
import gnu.java.security.der.DERValue;
|
| 53 |
|
|
import gnu.java.security.der.DERWriter;
|
| 54 |
|
|
import gnu.java.security.hash.IMessageDigest;
|
| 55 |
|
|
import gnu.java.security.hash.MD5;
|
| 56 |
|
|
import gnu.java.security.hash.Sha160;
|
| 57 |
|
|
import gnu.java.security.util.Util;
|
| 58 |
|
|
import gnu.java.security.x509.X500DistinguishedName;
|
| 59 |
|
|
|
| 60 |
|
|
import java.io.ByteArrayOutputStream;
|
| 61 |
|
|
import java.io.File;
|
| 62 |
|
|
import java.io.FileInputStream;
|
| 63 |
|
|
import java.io.FileNotFoundException;
|
| 64 |
|
|
import java.io.FileOutputStream;
|
| 65 |
|
|
import java.io.IOException;
|
| 66 |
|
|
import java.io.InputStream;
|
| 67 |
|
|
import java.io.OutputStream;
|
| 68 |
|
|
import java.io.PrintWriter;
|
| 69 |
|
|
import java.math.BigInteger;
|
| 70 |
|
|
import java.net.URL;
|
| 71 |
|
|
import java.net.URLConnection;
|
| 72 |
|
|
import java.security.InvalidKeyException;
|
| 73 |
|
|
import java.security.InvalidParameterException;
|
| 74 |
|
|
import java.security.Key;
|
| 75 |
|
|
import java.security.KeyPairGenerator;
|
| 76 |
|
|
import java.security.KeyStore;
|
| 77 |
|
|
import java.security.KeyStoreException;
|
| 78 |
|
|
import java.security.NoSuchAlgorithmException;
|
| 79 |
|
|
import java.security.PrivateKey;
|
| 80 |
|
|
import java.security.Provider;
|
| 81 |
|
|
import java.security.PublicKey;
|
| 82 |
|
|
import java.security.Signature;
|
| 83 |
|
|
import java.security.SignatureException;
|
| 84 |
|
|
import java.security.UnrecoverableKeyException;
|
| 85 |
|
|
import java.security.cert.Certificate;
|
| 86 |
|
|
import java.security.cert.CertificateEncodingException;
|
| 87 |
|
|
import java.security.cert.CertificateException;
|
| 88 |
|
|
import java.security.cert.X509Certificate;
|
| 89 |
|
|
import java.security.interfaces.DSAKey;
|
| 90 |
|
|
import java.security.interfaces.RSAKey;
|
| 91 |
|
|
import java.util.ArrayList;
|
| 92 |
|
|
import java.util.Date;
|
| 93 |
|
|
import java.util.logging.Logger;
|
| 94 |
|
|
import java.util.prefs.Preferences;
|
| 95 |
|
|
|
| 96 |
|
|
import javax.security.auth.callback.Callback;
|
| 97 |
|
|
import javax.security.auth.callback.CallbackHandler;
|
| 98 |
|
|
import javax.security.auth.callback.NameCallback;
|
| 99 |
|
|
import javax.security.auth.callback.PasswordCallback;
|
| 100 |
|
|
import javax.security.auth.callback.UnsupportedCallbackException;
|
| 101 |
|
|
|
| 102 |
|
|
/**
|
| 103 |
|
|
* A base class of the keytool command to facilitate implementation of concrete
|
| 104 |
|
|
* keytool Handlers.
|
| 105 |
|
|
*/
|
| 106 |
|
|
abstract class Command
|
| 107 |
|
|
{
|
| 108 |
|
|
// Fields and constants -----------------------------------------------------
|
| 109 |
|
|
|
| 110 |
|
|
private static final Logger log = Logger.getLogger(Command.class.getName());
|
| 111 |
|
|
/** Default value for the ALIAS argument. */
|
| 112 |
|
|
private static final String DEFAULT_ALIAS = "mykey"; //$NON-NLS-1$
|
| 113 |
|
|
/** Default algorithm for key-pair generation. */
|
| 114 |
|
|
private static final String DEFAULT_KEY_ALGORITHM = "DSA"; //$NON-NLS-1$
|
| 115 |
|
|
/** Default DSA digital signature algorithm to use with DSA keys. */
|
| 116 |
|
|
private static final String DSA_SIGNATURE_ALGORITHM = "SHA1withDSA"; //$NON-NLS-1$
|
| 117 |
|
|
/** Default RSA digital signature algorithm to use with RSA keys. */
|
| 118 |
|
|
private static final String RSA_SIGNATURE_ALGORITHM = "MD5withRSA"; //$NON-NLS-1$
|
| 119 |
|
|
/** Default validity (in days) of newly generated certificates. */
|
| 120 |
|
|
private static final int DEFAULT_VALIDITY = 90;
|
| 121 |
|
|
/** OID of SHA1withDSA signature algorithm as stated in RFC-2459. */
|
| 122 |
|
|
protected static final OID SHA1_WITH_DSA = new OID("1.2.840.10040.4.3"); //$NON-NLS-1$
|
| 123 |
|
|
/** OID of MD2withRSA signature algorithm as stated in RFC-2459. */
|
| 124 |
|
|
private static final OID MD2_WITH_RSA = new OID("1.2.840.113549.1.1.2"); //$NON-NLS-1$
|
| 125 |
|
|
/** OID of MD5withRSA signature algorithm as stated in RFC-2459. */
|
| 126 |
|
|
private static final OID MD5_WITH_RSA = new OID("1.2.840.113549.1.1.4"); //$NON-NLS-1$
|
| 127 |
|
|
/** OID of SHA1withRSA signature algorithm as stated in RFC-2459. */
|
| 128 |
|
|
private static final OID SHA1_WITH_RSA = new OID("1.2.840.113549.1.1.5"); //$NON-NLS-1$
|
| 129 |
|
|
/** Number of milliseconds in one day. */
|
| 130 |
|
|
private static final long MILLIS_IN_A_DAY = 24 * 60 * 60 * 1000L;
|
| 131 |
|
|
|
| 132 |
|
|
/** The Alias to use. */
|
| 133 |
|
|
protected String alias;
|
| 134 |
|
|
/** The password characters protecting a Key Entry. */
|
| 135 |
|
|
protected char[] keyPasswordChars;
|
| 136 |
|
|
/** A security provider to add. */
|
| 137 |
|
|
protected Provider provider;
|
| 138 |
|
|
/** The key store type. */
|
| 139 |
|
|
protected String storeType;
|
| 140 |
|
|
/** The password characters protecting the key store. */
|
| 141 |
|
|
protected char[] storePasswordChars;
|
| 142 |
|
|
/** The key store URL. */
|
| 143 |
|
|
protected URL storeURL;
|
| 144 |
|
|
/** The input stream from the key store URL. */
|
| 145 |
|
|
protected InputStream storeStream;
|
| 146 |
|
|
/** The key store instance to use. */
|
| 147 |
|
|
protected KeyStore store;
|
| 148 |
|
|
/** The output stream the concrete handler will use. */
|
| 149 |
|
|
protected OutputStream outStream;
|
| 150 |
|
|
/** Whether we are printing to System.out. */
|
| 151 |
|
|
protected boolean systemOut;
|
| 152 |
|
|
/** The key-pair generation algorithm instance to use. */
|
| 153 |
|
|
protected KeyPairGenerator keyPairGenerator;
|
| 154 |
|
|
/** The digital signature algorithm instance to use. */
|
| 155 |
|
|
protected Signature signatureAlgorithm;
|
| 156 |
|
|
/** Validity period, in number of days, to use when generating certificates. */
|
| 157 |
|
|
protected int validityInDays;
|
| 158 |
|
|
/** The input stream the concrete handler will use. */
|
| 159 |
|
|
protected InputStream inStream;
|
| 160 |
|
|
/** Whether verbose output is required or not. */
|
| 161 |
|
|
protected boolean verbose;
|
| 162 |
|
|
|
| 163 |
|
|
/** MD5 hash to use when generating certificate fingerprints. */
|
| 164 |
|
|
private IMessageDigest md5 = new MD5();
|
| 165 |
|
|
/** SHA1 hash to use when generating certificate fingerprints. */
|
| 166 |
|
|
private IMessageDigest sha = new Sha160();
|
| 167 |
|
|
/** The new position of a user-defined provider if it is not already installed. */
|
| 168 |
|
|
private int providerNdx = -2;
|
| 169 |
|
|
/** The callback handler to use when needing to interact with user. */
|
| 170 |
|
|
private CallbackHandler handler;
|
| 171 |
|
|
/** The shutdown hook. */
|
| 172 |
|
|
private ShutdownHook shutdownThread;
|
| 173 |
|
|
|
| 174 |
|
|
// Constructor(s) -----------------------------------------------------------
|
| 175 |
|
|
|
| 176 |
|
|
protected Command()
|
| 177 |
|
|
{
|
| 178 |
|
|
super();
|
| 179 |
|
|
shutdownThread = new ShutdownHook();
|
| 180 |
|
|
Runtime.getRuntime().addShutdownHook(shutdownThread);
|
| 181 |
|
|
}
|
| 182 |
|
|
|
| 183 |
|
|
// Methods ------------------------------------------------------------------
|
| 184 |
|
|
|
| 185 |
|
|
/**
|
| 186 |
|
|
* A public method to allow using any keytool command handler programmatically
|
| 187 |
|
|
* by using a JavaBeans style of parameter(s) initialization. The user is
|
| 188 |
|
|
* assumed to have set individually the required options through their
|
| 189 |
|
|
* respective setters before invoking this method.
|
| 190 |
|
|
* <p>
|
| 191 |
|
|
* If an exception is encountered during the processing of the command, this
|
| 192 |
|
|
* implementation attempts to release any resources that may have been
|
| 193 |
|
|
* allocated at the time the exception occurs, before re-throwing that
|
| 194 |
|
|
* exception.
|
| 195 |
|
|
*
|
| 196 |
|
|
* @throws Exception if an exception occurs during the processing of this
|
| 197 |
|
|
* command. For a more comprehensive list of exceptions that may
|
| 198 |
|
|
* occur, see the documentation of the {@link #setup()} and
|
| 199 |
|
|
* {@link #start()} methods.
|
| 200 |
|
|
*/
|
| 201 |
|
|
public void doCommand() throws Exception
|
| 202 |
|
|
{
|
| 203 |
|
|
try
|
| 204 |
|
|
{
|
| 205 |
|
|
setup();
|
| 206 |
|
|
start();
|
| 207 |
|
|
}
|
| 208 |
|
|
finally
|
| 209 |
|
|
{
|
| 210 |
|
|
teardown();
|
| 211 |
|
|
if (shutdownThread != null)
|
| 212 |
|
|
Runtime.getRuntime().removeShutdownHook(shutdownThread);
|
| 213 |
|
|
}
|
| 214 |
|
|
}
|
| 215 |
|
|
|
| 216 |
|
|
/**
|
| 217 |
|
|
* @param flag whether to use, or not, more verbose output while processing
|
| 218 |
|
|
* the command.
|
| 219 |
|
|
*/
|
| 220 |
|
|
public void setVerbose(String flag)
|
| 221 |
|
|
{
|
| 222 |
|
|
this.verbose = Boolean.valueOf(flag).booleanValue();
|
| 223 |
|
|
}
|
| 224 |
|
|
|
| 225 |
|
|
// life-cycle methods -------------------------------------------------------
|
| 226 |
|
|
|
| 227 |
|
|
/**
|
| 228 |
|
|
* Given a potential sub-array of options for this concrete handler, starting
|
| 229 |
|
|
* at position <code>startIndex + 1</code>, potentially followed by other
|
| 230 |
|
|
* commands and their options, this method sets up this concrete command
|
| 231 |
|
|
* handler with its own options and returns the index of the first unprocessed
|
| 232 |
|
|
* argument in the array.
|
| 233 |
|
|
* <p>
|
| 234 |
|
|
* The general contract of this method is that it is invoked with the
|
| 235 |
|
|
* <code>startIndex</code> argument pointing to the keyword argument that
|
| 236 |
|
|
* uniquelly identifies the command itself; e.g. <code>-genkey</code> or
|
| 237 |
|
|
* <code>-list</code>, etc...
|
| 238 |
|
|
*
|
| 239 |
|
|
* @param args an array of options for this handler and possibly other
|
| 240 |
|
|
* commands and their options.
|
| 241 |
|
|
* @return the remaining un-processed <code>args</code>.
|
| 242 |
|
|
*/
|
| 243 |
|
|
String[] processArgs(String[] args)
|
| 244 |
|
|
{
|
| 245 |
|
|
if (Configuration.DEBUG)
|
| 246 |
|
|
log.entering(this.getClass().getName(), "processArgs", args); //$NON-NLS-1$
|
| 247 |
|
|
Parser cmdOptionsParser = getParser();
|
| 248 |
|
|
String[] result = cmdOptionsParser.parse(args);
|
| 249 |
|
|
if (Configuration.DEBUG)
|
| 250 |
|
|
log.exiting(this.getClass().getName(), "processArgs", result); //$NON-NLS-1$
|
| 251 |
|
|
return result;
|
| 252 |
|
|
}
|
| 253 |
|
|
|
| 254 |
|
|
/**
|
| 255 |
|
|
* Initialize this concrete command handler for later invocation of the
|
| 256 |
|
|
* {@link #start()} or {@link #doCommand()} methods.
|
| 257 |
|
|
* <p>
|
| 258 |
|
|
* Handlers usually initialize their local variables and resources within the
|
| 259 |
|
|
* scope of this call.
|
| 260 |
|
|
*
|
| 261 |
|
|
* @throws IOException if an I/O related exception, such as opening an input
|
| 262 |
|
|
* stream, occurs during the execution of this method.
|
| 263 |
|
|
* @throws UnsupportedCallbackException if a requested callback handler
|
| 264 |
|
|
* implementation was not found, or was found but encountered an
|
| 265 |
|
|
* exception during its processing.
|
| 266 |
|
|
* @throws ClassNotFoundException if a designated security provider class was
|
| 267 |
|
|
* not found.
|
| 268 |
|
|
* @throws IllegalAccessException no 0-arguments constructor for the
|
| 269 |
|
|
* designated security provider class was found.
|
| 270 |
|
|
* @throws InstantiationException the designated security provider class is
|
| 271 |
|
|
* not instantiable.
|
| 272 |
|
|
* @throws KeyStoreException if an exception occurs during the instantiation
|
| 273 |
|
|
* of the KeyStore.
|
| 274 |
|
|
* @throws CertificateException if a certificate related exception, such as
|
| 275 |
|
|
* expiry, occurs during the loading of the KeyStore.
|
| 276 |
|
|
* @throws NoSuchAlgorithmException if no current security provider can
|
| 277 |
|
|
* provide a needed algorithm referenced by the KeyStore or one of
|
| 278 |
|
|
* its Key Entries or Certificates.
|
| 279 |
|
|
*/
|
| 280 |
|
|
abstract void setup() throws Exception;
|
| 281 |
|
|
|
| 282 |
|
|
/**
|
| 283 |
|
|
* Do the real work this handler is supposed to do.
|
| 284 |
|
|
* <p>
|
| 285 |
|
|
* The code in this (abstract) class throws a <i>Not implemented yet</i>
|
| 286 |
|
|
* runtime exception. Concrete implementations MUST override this method.
|
| 287 |
|
|
*
|
| 288 |
|
|
* @throws CertificateException If no concrete implementation was found for a
|
| 289 |
|
|
* certificate Factory of a designated type. In this tool, the type
|
| 290 |
|
|
* is usually X.509 v1.
|
| 291 |
|
|
* @throws KeyStoreException if a keys-store related exception occurs; e.g.
|
| 292 |
|
|
* the key store has not been initialized.
|
| 293 |
|
|
* @throws IOException if an I/O related exception occurs during the process.
|
| 294 |
|
|
* @throws SignatureException if a digital signature related exception occurs.
|
| 295 |
|
|
* @throws InvalidKeyException if the genereated keys are invalid.
|
| 296 |
|
|
* @throws UnrecoverableKeyException if the password used to unlock a key in
|
| 297 |
|
|
* the key store was invalid.
|
| 298 |
|
|
* @throws NoSuchAlgorithmException if a concrete implementation of an
|
| 299 |
|
|
* algorithm used to store a Key Entry was not found at runtime.
|
| 300 |
|
|
* @throws UnsupportedCallbackException if a requested callback handler
|
| 301 |
|
|
* implementation was not found, or was found but encountered an
|
| 302 |
|
|
* exception during its processing.
|
| 303 |
|
|
*/
|
| 304 |
|
|
void start() throws Exception
|
| 305 |
|
|
{
|
| 306 |
|
|
throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
|
| 307 |
|
|
}
|
| 308 |
|
|
|
| 309 |
|
|
/**
|
| 310 |
|
|
* Tear down the handler, releasing any resources which may have been
|
| 311 |
|
|
* allocated at setup time.
|
| 312 |
|
|
*/
|
| 313 |
|
|
void teardown()
|
| 314 |
|
|
{
|
| 315 |
|
|
if (Configuration.DEBUG)
|
| 316 |
|
|
log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$
|
| 317 |
|
|
if (storeStream != null)
|
| 318 |
|
|
try
|
| 319 |
|
|
{
|
| 320 |
|
|
storeStream.close();
|
| 321 |
|
|
}
|
| 322 |
|
|
catch (IOException ignored)
|
| 323 |
|
|
{
|
| 324 |
|
|
if (Configuration.DEBUG)
|
| 325 |
|
|
log.fine("Exception while closing key store URL stream. Ignored: " //$NON-NLS-1$
|
| 326 |
|
|
+ ignored);
|
| 327 |
|
|
}
|
| 328 |
|
|
|
| 329 |
|
|
if (outStream != null)
|
| 330 |
|
|
{
|
| 331 |
|
|
try
|
| 332 |
|
|
{
|
| 333 |
|
|
outStream.flush();
|
| 334 |
|
|
}
|
| 335 |
|
|
catch (IOException ignored)
|
| 336 |
|
|
{
|
| 337 |
|
|
}
|
| 338 |
|
|
|
| 339 |
|
|
if (! systemOut)
|
| 340 |
|
|
try
|
| 341 |
|
|
{
|
| 342 |
|
|
outStream.close();
|
| 343 |
|
|
}
|
| 344 |
|
|
catch (IOException ignored)
|
| 345 |
|
|
{
|
| 346 |
|
|
}
|
| 347 |
|
|
}
|
| 348 |
|
|
|
| 349 |
|
|
if (inStream != null)
|
| 350 |
|
|
try
|
| 351 |
|
|
{
|
| 352 |
|
|
inStream.close();
|
| 353 |
|
|
}
|
| 354 |
|
|
catch (IOException ignored)
|
| 355 |
|
|
{
|
| 356 |
|
|
}
|
| 357 |
|
|
|
| 358 |
|
|
if (providerNdx > 0)
|
| 359 |
|
|
ProviderUtil.removeProvider(provider.getName());
|
| 360 |
|
|
|
| 361 |
|
|
if (Configuration.DEBUG)
|
| 362 |
|
|
log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$
|
| 363 |
|
|
}
|
| 364 |
|
|
|
| 365 |
|
|
// parameter setup and validation methods -----------------------------------
|
| 366 |
|
|
|
| 367 |
|
|
/**
|
| 368 |
|
|
* @return a {@link Parser} that knows how to parse the concrete command's
|
| 369 |
|
|
* options.
|
| 370 |
|
|
*/
|
| 371 |
|
|
abstract Parser getParser();
|
| 372 |
|
|
|
| 373 |
|
|
/**
|
| 374 |
|
|
* Convenience method to setup the key store given its type, its password, its
|
| 375 |
|
|
* location and portentially a specialized security provider.
|
| 376 |
|
|
* <p>
|
| 377 |
|
|
* Calls the method with the same name and 5 arguments passing
|
| 378 |
|
|
* <code>false</code> to the first argument implying that no attempt to
|
| 379 |
|
|
* create the keystore will be made if one was not found at the designated
|
| 380 |
|
|
* location.
|
| 381 |
|
|
*
|
| 382 |
|
|
* @param className the potentially null fully qualified class name of a
|
| 383 |
|
|
* security provider to add at runtime, if no installed provider is
|
| 384 |
|
|
* able to provide a key store implementation of the desired type.
|
| 385 |
|
|
* @param type the potentially null type of the key store to request from the
|
| 386 |
|
|
* key store factory.
|
| 387 |
|
|
* @param password the potentially null password protecting the key store.
|
| 388 |
|
|
* @param url the URL of the key store.
|
| 389 |
|
|
*/
|
| 390 |
|
|
protected void setKeyStoreParams(String className, String type,
|
| 391 |
|
|
String password, String url)
|
| 392 |
|
|
throws IOException, UnsupportedCallbackException, KeyStoreException,
|
| 393 |
|
|
NoSuchAlgorithmException, CertificateException
|
| 394 |
|
|
{
|
| 395 |
|
|
setKeyStoreParams(false, className, type, password, url);
|
| 396 |
|
|
}
|
| 397 |
|
|
|
| 398 |
|
|
/**
|
| 399 |
|
|
* Convenience method to setup the key store given its type, its password, its
|
| 400 |
|
|
* location and portentially a specialized security provider.
|
| 401 |
|
|
*
|
| 402 |
|
|
* @param createIfNotFound if <code>true</code> then create the keystore if
|
| 403 |
|
|
* it was not found; otherwise do not.
|
| 404 |
|
|
* @param className the potentially null fully qualified class name of a
|
| 405 |
|
|
* security provider to add at runtime, if no installed provider is
|
| 406 |
|
|
* able to provide a key store implementation of the desired type.
|
| 407 |
|
|
* @param type the potentially null type of the key store to request from the
|
| 408 |
|
|
* key store factory.
|
| 409 |
|
|
* @param password the potentially null password protecting the key store.
|
| 410 |
|
|
* @param url the URL of the key store.
|
| 411 |
|
|
*/
|
| 412 |
|
|
protected void setKeyStoreParams(boolean createIfNotFound, String className,
|
| 413 |
|
|
String type, String password, String url)
|
| 414 |
|
|
throws IOException, UnsupportedCallbackException, KeyStoreException,
|
| 415 |
|
|
NoSuchAlgorithmException, CertificateException
|
| 416 |
|
|
{
|
| 417 |
|
|
setProviderClassNameParam(className);
|
| 418 |
|
|
setKeystoreTypeParam(type);
|
| 419 |
|
|
setKeystoreURLParam(createIfNotFound, url, password);
|
| 420 |
|
|
}
|
| 421 |
|
|
|
| 422 |
|
|
/**
|
| 423 |
|
|
* Set a security provider class name to (install and) use for key store
|
| 424 |
|
|
* related operations.
|
| 425 |
|
|
*
|
| 426 |
|
|
* @param className the possibly null, fully qualified class name of a
|
| 427 |
|
|
* security provider to add, if it is not already installed, to the
|
| 428 |
|
|
* set of available providers.
|
| 429 |
|
|
*/
|
| 430 |
|
|
private void setProviderClassNameParam(String className)
|
| 431 |
|
|
{
|
| 432 |
|
|
if (Configuration.DEBUG)
|
| 433 |
|
|
log.fine("setProviderClassNameParam(" + className + ")"); //$NON-NLS-1$ //$NON-NLS-2$
|
| 434 |
|
|
if (className != null && className.trim().length() > 0)
|
| 435 |
|
|
{
|
| 436 |
|
|
className = className.trim();
|
| 437 |
|
|
SecurityProviderInfo spi = ProviderUtil.addProvider(className);
|
| 438 |
|
|
provider = spi.getProvider();
|
| 439 |
|
|
if (provider == null)
|
| 440 |
|
|
{
|
| 441 |
|
|
if (Configuration.DEBUG)
|
| 442 |
|
|
log.fine("Was unable to add provider from class " + className);
|
| 443 |
|
|
}
|
| 444 |
|
|
providerNdx = spi.getPosition();
|
| 445 |
|
|
}
|
| 446 |
|
|
}
|
| 447 |
|
|
|
| 448 |
|
|
/**
|
| 449 |
|
|
* Set the type of key store to initialize, load and use.
|
| 450 |
|
|
*
|
| 451 |
|
|
* @param type the possibly null type of the key store. if this argument is
|
| 452 |
|
|
* <code>null</code>, or is an empty string, then this method sets
|
| 453 |
|
|
* the type of the key store to be the default value returned from
|
| 454 |
|
|
* the invocation of the {@link KeyStore#getDefaultType()} method.
|
| 455 |
|
|
* For GNU Classpath this is <i>gkr</i> which stands for the "Gnu
|
| 456 |
|
|
* KeyRing" specifications.
|
| 457 |
|
|
*/
|
| 458 |
|
|
private void setKeystoreTypeParam(String type)
|
| 459 |
|
|
{
|
| 460 |
|
|
if (Configuration.DEBUG)
|
| 461 |
|
|
log.fine("setKeystoreTypeParam(" + type + ")"); //$NON-NLS-1$ //$NON-NLS-2$
|
| 462 |
|
|
if (type == null || type.trim().length() == 0)
|
| 463 |
|
|
storeType = KeyStore.getDefaultType();
|
| 464 |
|
|
else
|
| 465 |
|
|
storeType = type.trim();
|
| 466 |
|
|
}
|
| 467 |
|
|
|
| 468 |
|
|
/**
|
| 469 |
|
|
* Set the key password given a command line option argument. If no value was
|
| 470 |
|
|
* present on the command line then prompt the user to provide one.
|
| 471 |
|
|
*
|
| 472 |
|
|
* @param password a possibly null key password gleaned from the command line.
|
| 473 |
|
|
* @throws IOException if an I/O related exception occurs.
|
| 474 |
|
|
* @throws UnsupportedCallbackException if no concrete implementation of a
|
| 475 |
|
|
* password callback was found at runtime.
|
| 476 |
|
|
*/
|
| 477 |
|
|
protected void setKeyPasswordParam(String password) throws IOException,
|
| 478 |
|
|
UnsupportedCallbackException
|
| 479 |
|
|
{
|
| 480 |
|
|
setKeyPasswordNoPrompt(password);
|
| 481 |
|
|
if (keyPasswordChars == null)
|
| 482 |
|
|
setKeyPasswordParam();
|
| 483 |
|
|
}
|
| 484 |
|
|
|
| 485 |
|
|
/**
|
| 486 |
|
|
* Set the Alias to use when associating Key Entries and Trusted Certificates
|
| 487 |
|
|
* in the current key store.
|
| 488 |
|
|
*
|
| 489 |
|
|
* @param name the possibly null alias to use. If this arfument is
|
| 490 |
|
|
* <code>null</code>, then a default value of <code>mykey</code>
|
| 491 |
|
|
* will be used instead.
|
| 492 |
|
|
*/
|
| 493 |
|
|
protected void setAliasParam(String name)
|
| 494 |
|
|
{
|
| 495 |
|
|
alias = name == null ? DEFAULT_ALIAS : name.trim();
|
| 496 |
|
|
}
|
| 497 |
|
|
|
| 498 |
|
|
/**
|
| 499 |
|
|
* Set the key password given a command line option argument.
|
| 500 |
|
|
*
|
| 501 |
|
|
* @param password a possibly null key password gleaned from the command line.
|
| 502 |
|
|
*/
|
| 503 |
|
|
protected void setKeyPasswordNoPrompt(String password)
|
| 504 |
|
|
{
|
| 505 |
|
|
if (password != null)
|
| 506 |
|
|
keyPasswordChars = password.toCharArray();
|
| 507 |
|
|
}
|
| 508 |
|
|
|
| 509 |
|
|
/**
|
| 510 |
|
|
* Prompt the user to provide a password to protect a Key Entry in the key
|
| 511 |
|
|
* store.
|
| 512 |
|
|
*
|
| 513 |
|
|
* @throws IOException if an I/O related exception occurs.
|
| 514 |
|
|
* @throws UnsupportedCallbackException if no concrete implementation of a
|
| 515 |
|
|
* password callback was found at runtime.
|
| 516 |
|
|
* @throws SecurityException if no password is available, even after prompting
|
| 517 |
|
|
* the user.
|
| 518 |
|
|
*/
|
| 519 |
|
|
private void setKeyPasswordParam() throws IOException,
|
| 520 |
|
|
UnsupportedCallbackException
|
| 521 |
|
|
{
|
| 522 |
|
|
String prompt = Messages.getFormattedString("Command.21", alias); //$NON-NLS-1$
|
| 523 |
|
|
PasswordCallback pcb = new PasswordCallback(prompt, false);
|
| 524 |
|
|
getCallbackHandler().handle(new Callback[] { pcb });
|
| 525 |
|
|
keyPasswordChars = pcb.getPassword();
|
| 526 |
|
|
pcb.clearPassword();
|
| 527 |
|
|
if (keyPasswordChars == null)
|
| 528 |
|
|
throw new SecurityException(Messages.getString("Command.23")); //$NON-NLS-1$
|
| 529 |
|
|
}
|
| 530 |
|
|
|
| 531 |
|
|
private void setKeystorePasswordParam(String password) throws IOException,
|
| 532 |
|
|
UnsupportedCallbackException
|
| 533 |
|
|
{
|
| 534 |
|
|
if (password != null)
|
| 535 |
|
|
storePasswordChars = password.toCharArray();
|
| 536 |
|
|
else // ask the user to provide one
|
| 537 |
|
|
{
|
| 538 |
|
|
String prompt = Messages.getString("Command.24"); //$NON-NLS-1$
|
| 539 |
|
|
PasswordCallback pcb = new PasswordCallback(prompt, false);
|
| 540 |
|
|
getCallbackHandler().handle(new Callback[] { pcb });
|
| 541 |
|
|
storePasswordChars = pcb.getPassword();
|
| 542 |
|
|
pcb.clearPassword();
|
| 543 |
|
|
}
|
| 544 |
|
|
}
|
| 545 |
|
|
|
| 546 |
|
|
/**
|
| 547 |
|
|
* Set the key store URL to use.
|
| 548 |
|
|
*
|
| 549 |
|
|
* @param createIfNotFound when <code>true</code> an attempt to create a
|
| 550 |
|
|
* keystore at the designated location will be made. If
|
| 551 |
|
|
* <code>false</code> then no file creation is carried out, which
|
| 552 |
|
|
* may cause an exception to be thrown later.
|
| 553 |
|
|
* @param url the full, or partial, URL to the keystore location.
|
| 554 |
|
|
* @param password an eventually null string to use when loading the keystore.
|
| 555 |
|
|
* @throws IOException
|
| 556 |
|
|
* @throws KeyStoreException
|
| 557 |
|
|
* @throws UnsupportedCallbackException
|
| 558 |
|
|
* @throws NoSuchAlgorithmException
|
| 559 |
|
|
* @throws CertificateException
|
| 560 |
|
|
*/
|
| 561 |
|
|
private void setKeystoreURLParam(boolean createIfNotFound, String url,
|
| 562 |
|
|
String password) throws IOException,
|
| 563 |
|
|
KeyStoreException, UnsupportedCallbackException, NoSuchAlgorithmException,
|
| 564 |
|
|
CertificateException
|
| 565 |
|
|
{
|
| 566 |
|
|
if (Configuration.DEBUG)
|
| 567 |
|
|
log.fine("setKeystoreURLParam(" + url + ")"); //$NON-NLS-1$ //$NON-NLS-2$
|
| 568 |
|
|
if (url == null || url.trim().length() == 0)
|
| 569 |
|
|
{
|
| 570 |
|
|
String userHome = SystemProperties.getProperty("user.home"); //$NON-NLS-1$
|
| 571 |
|
|
if (userHome == null || userHome.trim().length() == 0)
|
| 572 |
|
|
throw new InvalidParameterException(Messages.getString("Command.36")); //$NON-NLS-1$
|
| 573 |
|
|
|
| 574 |
|
|
url = userHome.trim() + "/.keystore"; //$NON-NLS-1$
|
| 575 |
|
|
// if it does not exist create it if required
|
| 576 |
|
|
if (createIfNotFound)
|
| 577 |
|
|
new File(url).createNewFile();
|
| 578 |
|
|
url = "file:" + url; //$NON-NLS-1$
|
| 579 |
|
|
}
|
| 580 |
|
|
else
|
| 581 |
|
|
{
|
| 582 |
|
|
url = url.trim();
|
| 583 |
|
|
if (url.indexOf(":") == -1) // if it does not exist create it //$NON-NLS-1$
|
| 584 |
|
|
{
|
| 585 |
|
|
if (createIfNotFound)
|
| 586 |
|
|
new File(url).createNewFile();
|
| 587 |
|
|
}
|
| 588 |
|
|
url = "file:" + url; //$NON-NLS-1$
|
| 589 |
|
|
}
|
| 590 |
|
|
|
| 591 |
|
|
boolean newKeyStore = false;
|
| 592 |
|
|
storeURL = new URL(url);
|
| 593 |
|
|
storeStream = storeURL.openStream();
|
| 594 |
|
|
if (storeStream.available() == 0)
|
| 595 |
|
|
{
|
| 596 |
|
|
if (Configuration.DEBUG)
|
| 597 |
|
|
log.fine("Store is empty. Will use <null> when loading, to create it"); //$NON-NLS-1$
|
| 598 |
|
|
newKeyStore = true;
|
| 599 |
|
|
}
|
| 600 |
|
|
|
| 601 |
|
|
try
|
| 602 |
|
|
{
|
| 603 |
|
|
store = KeyStore.getInstance(storeType);
|
| 604 |
|
|
}
|
| 605 |
|
|
catch (KeyStoreException x)
|
| 606 |
|
|
{
|
| 607 |
|
|
if (provider != null)
|
| 608 |
|
|
throw x;
|
| 609 |
|
|
|
| 610 |
|
|
if (Configuration.DEBUG)
|
| 611 |
|
|
log.fine("Exception while getting key store with default provider(s)." //$NON-NLS-1$
|
| 612 |
|
|
+ " Will prompt user for another provider and continue"); //$NON-NLS-1$
|
| 613 |
|
|
String prompt = Messages.getString("Command.40"); //$NON-NLS-1$
|
| 614 |
|
|
NameCallback ncb = new NameCallback(prompt);
|
| 615 |
|
|
getCallbackHandler().handle(new Callback[] { ncb });
|
| 616 |
|
|
String className = ncb.getName();
|
| 617 |
|
|
setProviderClassNameParam(className); // we may have a Provider
|
| 618 |
|
|
if (provider == null)
|
| 619 |
|
|
{
|
| 620 |
|
|
x.fillInStackTrace();
|
| 621 |
|
|
throw x;
|
| 622 |
|
|
}
|
| 623 |
|
|
// try again
|
| 624 |
|
|
store = KeyStore.getInstance(storeType, provider);
|
| 625 |
|
|
}
|
| 626 |
|
|
|
| 627 |
|
|
setKeystorePasswordParam(password);
|
| 628 |
|
|
|
| 629 |
|
|
// now we have a KeyStore instance. load it
|
| 630 |
|
|
// KeyStore public API claims: "...In order to create an empty keystore,
|
| 631 |
|
|
// you pass null as the InputStream argument to the load method.
|
| 632 |
|
|
if (newKeyStore)
|
| 633 |
|
|
store.load(null, storePasswordChars);
|
| 634 |
|
|
else
|
| 635 |
|
|
store.load(storeStream, storePasswordChars);
|
| 636 |
|
|
|
| 637 |
|
|
// close the stream
|
| 638 |
|
|
try
|
| 639 |
|
|
{
|
| 640 |
|
|
storeStream.close();
|
| 641 |
|
|
storeStream = null;
|
| 642 |
|
|
}
|
| 643 |
|
|
catch (IOException x)
|
| 644 |
|
|
{
|
| 645 |
|
|
if (Configuration.DEBUG)
|
| 646 |
|
|
log.fine("Exception while closing the key store input stream: " + x //$NON-NLS-1$
|
| 647 |
|
|
+ ". Ignore"); //$NON-NLS-1$
|
| 648 |
|
|
}
|
| 649 |
|
|
}
|
| 650 |
|
|
|
| 651 |
|
|
protected void setOutputStreamParam(String fileName) throws SecurityException,
|
| 652 |
|
|
IOException
|
| 653 |
|
|
{
|
| 654 |
|
|
if (fileName == null || fileName.trim().length() == 0)
|
| 655 |
|
|
{
|
| 656 |
|
|
outStream = System.out;
|
| 657 |
|
|
systemOut = true;
|
| 658 |
|
|
}
|
| 659 |
|
|
else
|
| 660 |
|
|
{
|
| 661 |
|
|
fileName = fileName.trim();
|
| 662 |
|
|
File outFile = new File(fileName);
|
| 663 |
|
|
if (! outFile.exists())
|
| 664 |
|
|
{
|
| 665 |
|
|
boolean ok = outFile.createNewFile();
|
| 666 |
|
|
if (!ok)
|
| 667 |
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.19", //$NON-NLS-1$
|
| 668 |
|
|
fileName));
|
| 669 |
|
|
}
|
| 670 |
|
|
else
|
| 671 |
|
|
{
|
| 672 |
|
|
if (! outFile.isFile())
|
| 673 |
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.42", //$NON-NLS-1$
|
| 674 |
|
|
fileName));
|
| 675 |
|
|
if (! outFile.canWrite())
|
| 676 |
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.44", //$NON-NLS-1$
|
| 677 |
|
|
fileName));
|
| 678 |
|
|
}
|
| 679 |
|
|
outStream = new FileOutputStream(outFile);
|
| 680 |
|
|
}
|
| 681 |
|
|
}
|
| 682 |
|
|
|
| 683 |
|
|
protected void setInputStreamParam(String fileName)
|
| 684 |
|
|
throws FileNotFoundException
|
| 685 |
|
|
{
|
| 686 |
|
|
if (fileName == null || fileName.trim().length() == 0)
|
| 687 |
|
|
inStream = System.in;
|
| 688 |
|
|
else
|
| 689 |
|
|
{
|
| 690 |
|
|
fileName = fileName.trim();
|
| 691 |
|
|
File inFile = new File(fileName);
|
| 692 |
|
|
if (! (inFile.exists() && inFile.isFile() && inFile.canRead()))
|
| 693 |
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.46", //$NON-NLS-1$
|
| 694 |
|
|
fileName));
|
| 695 |
|
|
inStream = new FileInputStream(inFile);
|
| 696 |
|
|
}
|
| 697 |
|
|
}
|
| 698 |
|
|
|
| 699 |
|
|
/**
|
| 700 |
|
|
* Set both the key-pair generation algorithm, and the digital signature
|
| 701 |
|
|
* algorithm instances to use when generating new entries.
|
| 702 |
|
|
*
|
| 703 |
|
|
* @param kpAlg the possibly null name of a key-pair generator algorithm.
|
| 704 |
|
|
* if this argument is <code>null</code> or is an empty string, the
|
| 705 |
|
|
* "DSS" algorithm will be used.
|
| 706 |
|
|
* @param sigAlg the possibly null name of a digital signature algorithm.
|
| 707 |
|
|
* If this argument is <code>null</code> or is an empty string, this
|
| 708 |
|
|
* method uses the "SHA1withDSA" (Digital Signature Standard, a.k.a.
|
| 709 |
|
|
* DSA, with the Secure Hash Algorithm function) as the default
|
| 710 |
|
|
* algorithm if, and only if, the key-pair generation algorithm ends
|
| 711 |
|
|
* up being "DSS"; otherwise, if the key-pair generation algorithm
|
| 712 |
|
|
* was "RSA", then the "MD5withRSA" signature algorithm will be used.
|
| 713 |
|
|
* If the key-pair generation algorithm is neither "DSS" (or its
|
| 714 |
|
|
* alias "DSA"), nor is it "RSA", then an exception is thrown.
|
| 715 |
|
|
* @throws NoSuchAlgorithmException if no concrete implementation of the
|
| 716 |
|
|
* designated algorithm is available.
|
| 717 |
|
|
*/
|
| 718 |
|
|
protected void setAlgorithmParams(String kpAlg, String sigAlg)
|
| 719 |
|
|
throws NoSuchAlgorithmException
|
| 720 |
|
|
{
|
| 721 |
|
|
if (kpAlg == null || kpAlg.trim().length() == 0)
|
| 722 |
|
|
kpAlg = DEFAULT_KEY_ALGORITHM;
|
| 723 |
|
|
else
|
| 724 |
|
|
kpAlg = kpAlg.trim().toLowerCase();
|
| 725 |
|
|
|
| 726 |
|
|
keyPairGenerator = KeyPairGenerator.getInstance(kpAlg);
|
| 727 |
|
|
|
| 728 |
|
|
if (sigAlg == null || sigAlg.trim().length() == 0)
|
| 729 |
|
|
if (kpAlg.equalsIgnoreCase(Registry.DSS_KPG)
|
| 730 |
|
|
|| kpAlg.equalsIgnoreCase(Registry.DSA_KPG))
|
| 731 |
|
|
sigAlg = DSA_SIGNATURE_ALGORITHM;
|
| 732 |
|
|
else if (kpAlg.equalsIgnoreCase(Registry.RSA_KPG))
|
| 733 |
|
|
sigAlg = RSA_SIGNATURE_ALGORITHM;
|
| 734 |
|
|
else
|
| 735 |
|
|
throw new IllegalArgumentException(
|
| 736 |
|
|
Messages.getFormattedString("Command.20", //$NON-NLS-1$
|
| 737 |
|
|
new String[] { sigAlg, kpAlg }));
|
| 738 |
|
|
else
|
| 739 |
|
|
sigAlg = sigAlg.trim().toLowerCase();
|
| 740 |
|
|
|
| 741 |
|
|
signatureAlgorithm = Signature.getInstance(sigAlg);
|
| 742 |
|
|
}
|
| 743 |
|
|
|
| 744 |
|
|
/**
|
| 745 |
|
|
* Set the signature algorithm to use when digitally signing private keys,
|
| 746 |
|
|
* certificates, etc...
|
| 747 |
|
|
* <p>
|
| 748 |
|
|
* If the designated algorithm name is <code>null</code> or is an empty
|
| 749 |
|
|
* string, this method checks the private key (the second argument) and based
|
| 750 |
|
|
* on its type decides which algorithm to use. The keytool public
|
| 751 |
|
|
* specification states that if the private key is a DSA key, then the
|
| 752 |
|
|
* signature algorithm will be <code>SHA1withDSA</code>, otherwise if it is
|
| 753 |
|
|
* an RSA private key, then the signature algorithm will be
|
| 754 |
|
|
* <code>MD5withRSA</code>. If the private key is neither a private DSA nor
|
| 755 |
|
|
* a private RSA key, then this method throws an
|
| 756 |
|
|
* {@link IllegalArgumentException}.
|
| 757 |
|
|
*
|
| 758 |
|
|
* @param algorithm the possibly null name of a digital signature algorithm.
|
| 759 |
|
|
* @param privateKey an instance of a private key to use as a fal-back option
|
| 760 |
|
|
* when <code>algorithm</code> is invalid.
|
| 761 |
|
|
* @throws NoSuchAlgorithmException if no concrete implementation of the
|
| 762 |
|
|
* designated, or default, signature algorithm is available.
|
| 763 |
|
|
*/
|
| 764 |
|
|
protected void setSignatureAlgorithmParam(String algorithm, Key privateKey)
|
| 765 |
|
|
throws NoSuchAlgorithmException
|
| 766 |
|
|
{
|
| 767 |
|
|
if (algorithm == null || algorithm.trim().length() == 0)
|
| 768 |
|
|
if (privateKey instanceof DSAKey)
|
| 769 |
|
|
algorithm = DSA_SIGNATURE_ALGORITHM;
|
| 770 |
|
|
else if (privateKey instanceof RSAKey)
|
| 771 |
|
|
algorithm = RSA_SIGNATURE_ALGORITHM;
|
| 772 |
|
|
else
|
| 773 |
|
|
throw new InvalidParameterException(Messages.getString("Command.48")); //$NON-NLS-1$
|
| 774 |
|
|
else
|
| 775 |
|
|
algorithm = algorithm.trim();
|
| 776 |
|
|
|
| 777 |
|
|
signatureAlgorithm = Signature.getInstance(algorithm);
|
| 778 |
|
|
}
|
| 779 |
|
|
|
| 780 |
|
|
/**
|
| 781 |
|
|
* Set the validity period, in number of days, to use when issuing new
|
| 782 |
|
|
* certificates.
|
| 783 |
|
|
*
|
| 784 |
|
|
* @param days the number of days, as a string, the generated certificate will
|
| 785 |
|
|
* be valid for, starting from today's date. if this argument is
|
| 786 |
|
|
* <code>null</code>, a default value of <code>90</code> days
|
| 787 |
|
|
* will be used.
|
| 788 |
|
|
* @throws NumberFormatException if the designated string is not a decimal
|
| 789 |
|
|
* integer.
|
| 790 |
|
|
* @throws InvalidParameterException if the integer value of the non-null
|
| 791 |
|
|
* string is not greater than zero.
|
| 792 |
|
|
*/
|
| 793 |
|
|
protected void setValidityParam(String days)
|
| 794 |
|
|
{
|
| 795 |
|
|
if (days == null || days.trim().length() == 0)
|
| 796 |
|
|
validityInDays = DEFAULT_VALIDITY;
|
| 797 |
|
|
else
|
| 798 |
|
|
{
|
| 799 |
|
|
days = days.trim();
|
| 800 |
|
|
validityInDays = Integer.parseInt(days);
|
| 801 |
|
|
if (validityInDays < 1)
|
| 802 |
|
|
throw new InvalidParameterException(Messages.getString("Command.51")); //$NON-NLS-1$
|
| 803 |
|
|
}
|
| 804 |
|
|
}
|
| 805 |
|
|
|
| 806 |
|
|
/**
|
| 807 |
|
|
* RFC-2459 (http://rfc.net/rfc2459.html) fully describes the structure and
|
| 808 |
|
|
* semantics of X.509 certificates. The ASN.1 structures below are gleaned
|
| 809 |
|
|
* from that reference.
|
| 810 |
|
|
*
|
| 811 |
|
|
* <pre>
|
| 812 |
|
|
* Certificate ::= SEQUENCE {
|
| 813 |
|
|
* tbsCertificate TBSCertificate,
|
| 814 |
|
|
* signatureAlgorithm AlgorithmIdentifier,
|
| 815 |
|
|
* signatureValue BIT STRING
|
| 816 |
|
|
* }
|
| 817 |
|
|
*
|
| 818 |
|
|
* TBSCertificate ::= SEQUENCE {
|
| 819 |
|
|
* version [0] EXPLICIT Version DEFAULT v1,
|
| 820 |
|
|
* serialNumber CertificateSerialNumber,
|
| 821 |
|
|
* signature AlgorithmIdentifier,
|
| 822 |
|
|
* issuer Name,
|
| 823 |
|
|
* validity Validity,
|
| 824 |
|
|
* subject Name,
|
| 825 |
|
|
* subjectPublicKeyInfo SubjectPublicKeyInfo
|
| 826 |
|
|
* }
|
| 827 |
|
|
*
|
| 828 |
|
|
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
| 829 |
|
|
*
|
| 830 |
|
|
* CertificateSerialNumber ::= INTEGER
|
| 831 |
|
|
*
|
| 832 |
|
|
* Validity ::= SEQUENCE {
|
| 833 |
|
|
* notBefore Time,
|
| 834 |
|
|
* notAfter Time
|
| 835 |
|
|
* }
|
| 836 |
|
|
*
|
| 837 |
|
|
* Time ::= CHOICE {
|
| 838 |
|
|
* utcTime UTCTime,
|
| 839 |
|
|
* generalTime GeneralizedTime
|
| 840 |
|
|
* }
|
| 841 |
|
|
*
|
| 842 |
|
|
* UniqueIdentifier ::= BIT STRING
|
| 843 |
|
|
*
|
| 844 |
|
|
* SubjectPublicKeyInfo ::= SEQUENCE {
|
| 845 |
|
|
* algorithm AlgorithmIdentifier,
|
| 846 |
|
|
* subjectPublicKey BIT STRING
|
| 847 |
|
|
* }
|
| 848 |
|
|
* </pre>
|
| 849 |
|
|
*
|
| 850 |
|
|
* @param distinguishedName the X.500 Distinguished Name to use as both the
|
| 851 |
|
|
* Issuer and Subject of the self-signed certificate to generate.
|
| 852 |
|
|
* @param publicKey the public key of the issuer/subject.
|
| 853 |
|
|
* @param privateKey the private key of the issuer/signer.
|
| 854 |
|
|
* @return the DER encoded form of a self-signed X.509 v1 certificate.
|
| 855 |
|
|
* @throws IOException If an I/O related exception occurs during the process.
|
| 856 |
|
|
* @throws SignatureException If a digital signature related exception occurs.
|
| 857 |
|
|
* @throws InvalidKeyException if the designated private key is invalid.
|
| 858 |
|
|
* @throws InvalidParameterException if the concrete signature algorithm does
|
| 859 |
|
|
* not know its name, no OID is known/supported for that name, or we
|
| 860 |
|
|
* were unable to match the name to a known string for which we can
|
| 861 |
|
|
* use a standard OID.
|
| 862 |
|
|
*/
|
| 863 |
|
|
protected byte[] getSelfSignedCertificate(X500DistinguishedName distinguishedName,
|
| 864 |
|
|
PublicKey publicKey,
|
| 865 |
|
|
PrivateKey privateKey)
|
| 866 |
|
|
throws IOException, SignatureException, InvalidKeyException
|
| 867 |
|
|
{
|
| 868 |
|
|
if (Configuration.DEBUG)
|
| 869 |
|
|
log.entering(this.getClass().getName(), "getSelfSignedCertificate", //$NON-NLS-1$
|
| 870 |
|
|
new Object[] { distinguishedName, publicKey, privateKey });
|
| 871 |
|
|
byte[] versionBytes = new DERValue(DER.INTEGER, BigInteger.ZERO).getEncoded();
|
| 872 |
|
|
DERValue derVersion = new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0,
|
| 873 |
|
|
versionBytes.length, versionBytes, null);
|
| 874 |
|
|
|
| 875 |
|
|
// NOTE (rsn): the next 3 lines should be atomic but they're not.
|
| 876 |
|
|
Preferences prefs = Preferences.systemNodeForPackage(this.getClass());
|
| 877 |
|
|
int lastSerialNumber = prefs.getInt(Main.LAST_SERIAL_NUMBER, 0) + 1;
|
| 878 |
|
|
prefs.putInt(Main.LAST_SERIAL_NUMBER, lastSerialNumber);
|
| 879 |
|
|
DERValue derSerialNumber = new DERValue(DER.INTEGER,
|
| 880 |
|
|
BigInteger.valueOf(lastSerialNumber));
|
| 881 |
|
|
|
| 882 |
|
|
OID signatureID = getSignatureAlgorithmOID();
|
| 883 |
|
|
DERValue derSignatureID = new DERValue(DER.OBJECT_IDENTIFIER, signatureID);
|
| 884 |
|
|
ArrayList signature = new ArrayList(1);
|
| 885 |
|
|
signature.add(derSignatureID);
|
| 886 |
|
|
// rfc-2459 states the following:
|
| 887 |
|
|
//
|
| 888 |
|
|
// for the DSA signature:
|
| 889 |
|
|
// ...Where the id-dsa-with-sha1 algorithm identifier appears as the
|
| 890 |
|
|
// algorithm field in an AlgorithmIdentifier, the encoding shall omit
|
| 891 |
|
|
// the parameters field. That is, the AlgorithmIdentifier shall be a
|
| 892 |
|
|
// SEQUENCE of one component - the OBJECT IDENTIFIER id-dsa-with-sha1.
|
| 893 |
|
|
//
|
| 894 |
|
|
// for RSA signatures:
|
| 895 |
|
|
// ...When any of these three OIDs (i.e. xxxWithRSAEncryption) appears
|
| 896 |
|
|
// within the ASN.1 type AlgorithmIdentifier, the parameters component of
|
| 897 |
|
|
// that type shall be the ASN.1 type NULL.
|
| 898 |
|
|
if (! signatureID.equals(SHA1_WITH_DSA))
|
| 899 |
|
|
signature.add(new DERValue(DER.NULL, null));
|
| 900 |
|
|
|
| 901 |
|
|
DERValue derSignature = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
| 902 |
|
|
signature);
|
| 903 |
|
|
|
| 904 |
|
|
DERValue derIssuer = new DERReader(distinguishedName.getDer()).read();
|
| 905 |
|
|
|
| 906 |
|
|
long notBefore = System.currentTimeMillis();
|
| 907 |
|
|
long notAfter = notBefore + validityInDays * MILLIS_IN_A_DAY;
|
| 908 |
|
|
|
| 909 |
|
|
ArrayList validity = new ArrayList(2);
|
| 910 |
|
|
validity.add(new DERValue(DER.UTC_TIME, new Date(notBefore)));
|
| 911 |
|
|
validity.add(new DERValue(DER.UTC_TIME, new Date(notAfter)));
|
| 912 |
|
|
DERValue derValidity = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
| 913 |
|
|
validity);
|
| 914 |
|
|
|
| 915 |
|
|
// for a self-signed certificate subject and issuer are identical
|
| 916 |
|
|
DERValue derSubject = derIssuer;
|
| 917 |
|
|
|
| 918 |
|
|
DERValue derSubjectPublicKeyInfo = new DERReader(publicKey.getEncoded()).read();
|
| 919 |
|
|
|
| 920 |
|
|
ArrayList tbsCertificate = new ArrayList(7);
|
| 921 |
|
|
tbsCertificate.add(derVersion);
|
| 922 |
|
|
tbsCertificate.add(derSerialNumber);
|
| 923 |
|
|
tbsCertificate.add(derSignature);
|
| 924 |
|
|
tbsCertificate.add(derIssuer);
|
| 925 |
|
|
tbsCertificate.add(derValidity);
|
| 926 |
|
|
tbsCertificate.add(derSubject);
|
| 927 |
|
|
tbsCertificate.add(derSubjectPublicKeyInfo);
|
| 928 |
|
|
DERValue derTBSCertificate = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
| 929 |
|
|
tbsCertificate);
|
| 930 |
|
|
|
| 931 |
|
|
// The 'signature' field MUST contain the same algorithm identifier as the
|
| 932 |
|
|
// 'signatureAlgorithm' field in the sequence Certificate.
|
| 933 |
|
|
DERValue derSignatureAlgorithm = derSignature;
|
| 934 |
|
|
|
| 935 |
|
|
signatureAlgorithm.initSign(privateKey);
|
| 936 |
|
|
signatureAlgorithm.update(derTBSCertificate.getEncoded());
|
| 937 |
|
|
byte[] sigBytes = signatureAlgorithm.sign();
|
| 938 |
|
|
DERValue derSignatureValue = new DERValue(DER.BIT_STRING,
|
| 939 |
|
|
new BitString(sigBytes));
|
| 940 |
|
|
|
| 941 |
|
|
ArrayList certificate = new ArrayList(3);
|
| 942 |
|
|
certificate.add(derTBSCertificate);
|
| 943 |
|
|
certificate.add(derSignatureAlgorithm);
|
| 944 |
|
|
certificate.add(derSignatureValue);
|
| 945 |
|
|
DERValue derCertificate = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
|
| 946 |
|
|
certificate);
|
| 947 |
|
|
|
| 948 |
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
| 949 |
|
|
DERWriter.write(baos, derCertificate);
|
| 950 |
|
|
byte[] result = baos.toByteArray();
|
| 951 |
|
|
if (Configuration.DEBUG)
|
| 952 |
|
|
log.exiting(this.getClass().getName(), "getSelfSignedCertificate"); //$NON-NLS-1$
|
| 953 |
|
|
return result;
|
| 954 |
|
|
}
|
| 955 |
|
|
|
| 956 |
|
|
/**
|
| 957 |
|
|
* This method attempts to find, and return, an OID representing the digital
|
| 958 |
|
|
* signature algorithm used to sign the certificate. The OIDs returned are
|
| 959 |
|
|
* those described in RFC-2459. They are listed here for the sake of
|
| 960 |
|
|
* completness.
|
| 961 |
|
|
*
|
| 962 |
|
|
* <pre>
|
| 963 |
|
|
* id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
|
| 964 |
|
|
* iso(1) member-body(2) us(840) x9-57 (10040) x9cm(4) 3
|
| 965 |
|
|
* }
|
| 966 |
|
|
*
|
| 967 |
|
|
* md2WithRSAEncryption OBJECT IDENTIFIER ::= {
|
| 968 |
|
|
* iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 2
|
| 969 |
|
|
* }
|
| 970 |
|
|
*
|
| 971 |
|
|
* md5WithRSAEncryption OBJECT IDENTIFIER ::= {
|
| 972 |
|
|
* iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 4
|
| 973 |
|
|
* }
|
| 974 |
|
|
*
|
| 975 |
|
|
* sha-1WithRSAEncryption OBJECT IDENTIFIER ::= {
|
| 976 |
|
|
* iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 5
|
| 977 |
|
|
* }
|
| 978 |
|
|
* </pre>
|
| 979 |
|
|
*
|
| 980 |
|
|
* <b>IMPORTANT</b>: This method checks the signature algorithm name against
|
| 981 |
|
|
* (a) The GNU algorithm implementation's name, and (b) publicly referenced
|
| 982 |
|
|
* names of the same algorithm. In other words this search is not
|
| 983 |
|
|
* comprehensive and may fail for uncommon names of the same algorithms.
|
| 984 |
|
|
*
|
| 985 |
|
|
* @return the OID of the signature algorithm in use.
|
| 986 |
|
|
* @throws InvalidParameterException if the concrete signature algorithm does
|
| 987 |
|
|
* not know its name, no OID is known/supported for that name, or we
|
| 988 |
|
|
* were unable to match the name to a known string for which we can
|
| 989 |
|
|
* return an OID.
|
| 990 |
|
|
*/
|
| 991 |
|
|
protected OID getSignatureAlgorithmOID()
|
| 992 |
|
|
{
|
| 993 |
|
|
String algorithm = signatureAlgorithm.getAlgorithm();
|
| 994 |
|
|
// if we already have a non-null signature then the name was valid. the
|
| 995 |
|
|
// only case where algorithm is invalid would be if the implementation is
|
| 996 |
|
|
// flawed. check anyway
|
| 997 |
|
|
if (algorithm == null || algorithm.trim().length() == 0)
|
| 998 |
|
|
throw new InvalidParameterException(Messages.getString("Command.52")); //$NON-NLS-1$
|
| 999 |
|
|
|
| 1000 |
|
|
algorithm = algorithm.trim();
|
| 1001 |
|
|
if (algorithm.equalsIgnoreCase(Registry.DSS_SIG)
|
| 1002 |
|
|
|| algorithm.equalsIgnoreCase("SHA1withDSA")) //$NON-NLS-1$
|
| 1003 |
|
|
return SHA1_WITH_DSA;
|
| 1004 |
|
|
|
| 1005 |
|
|
if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$
|
| 1006 |
|
|
+ Registry.MD2_HASH)
|
| 1007 |
|
|
|| algorithm.equalsIgnoreCase("MD2withRSA")) //$NON-NLS-1$
|
| 1008 |
|
|
return MD2_WITH_RSA;
|
| 1009 |
|
|
|
| 1010 |
|
|
if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$
|
| 1011 |
|
|
+ Registry.MD5_HASH)
|
| 1012 |
|
|
|| algorithm.equalsIgnoreCase("MD5withRSA") //$NON-NLS-1$
|
| 1013 |
|
|
|| algorithm.equalsIgnoreCase("rsa")) //$NON-NLS-1$
|
| 1014 |
|
|
return MD5_WITH_RSA;
|
| 1015 |
|
|
|
| 1016 |
|
|
if (algorithm.equalsIgnoreCase(Registry.RSA_PKCS1_V1_5_SIG + "-" //$NON-NLS-1$
|
| 1017 |
|
|
+ Registry.SHA160_HASH)
|
| 1018 |
|
|
|| algorithm.equalsIgnoreCase("SHA1withRSA")) //$NON-NLS-1$
|
| 1019 |
|
|
return SHA1_WITH_RSA;
|
| 1020 |
|
|
|
| 1021 |
|
|
throw new InvalidParameterException(Messages.getFormattedString("Command.60", //$NON-NLS-1$
|
| 1022 |
|
|
algorithm));
|
| 1023 |
|
|
}
|
| 1024 |
|
|
|
| 1025 |
|
|
/**
|
| 1026 |
|
|
* Saves the key store using the designated password. This operation is called
|
| 1027 |
|
|
* by handlers if/when the key store password has changed, or amendements have
|
| 1028 |
|
|
* been made to the contents of the store; e.g. addition of a new Key Entry or
|
| 1029 |
|
|
* a Trusted Certificate.
|
| 1030 |
|
|
*
|
| 1031 |
|
|
* @param password the password protecting the key store.
|
| 1032 |
|
|
* @throws IOException if an I/O related exception occurs during the process.
|
| 1033 |
|
|
* @throws CertificateException if any of the certificates in the current key
|
| 1034 |
|
|
* store could not be persisted.
|
| 1035 |
|
|
* @throws NoSuchAlgorithmException if a required data integrity algorithm
|
| 1036 |
|
|
* implementation was not found.
|
| 1037 |
|
|
* @throws KeyStoreException if the key store has not been loaded previously.
|
| 1038 |
|
|
*/
|
| 1039 |
|
|
protected void saveKeyStore(char[] password) throws IOException,
|
| 1040 |
|
|
KeyStoreException, NoSuchAlgorithmException, CertificateException
|
| 1041 |
|
|
{
|
| 1042 |
|
|
if (Configuration.DEBUG)
|
| 1043 |
|
|
log.entering(this.getClass().getName(), "saveKeyStore"); //$NON-NLS-1$
|
| 1044 |
|
|
URLConnection con = storeURL.openConnection();
|
| 1045 |
|
|
con.setDoOutput(true);
|
| 1046 |
|
|
con.setUseCaches(false);
|
| 1047 |
|
|
OutputStream out = con.getOutputStream();
|
| 1048 |
|
|
if (verbose)
|
| 1049 |
|
|
System.out.println(Messages.getFormattedString("Command.63", storeURL.getPath())); //$NON-NLS-1$
|
| 1050 |
|
|
|
| 1051 |
|
|
store.store(out, password);
|
| 1052 |
|
|
out.flush();
|
| 1053 |
|
|
out.close();
|
| 1054 |
|
|
if (Configuration.DEBUG)
|
| 1055 |
|
|
log.exiting(this.getClass().getName(), "saveKeyStore"); //$NON-NLS-1$
|
| 1056 |
|
|
}
|
| 1057 |
|
|
|
| 1058 |
|
|
/**
|
| 1059 |
|
|
* Convenience method. Calls the method with the same name passing it the
|
| 1060 |
|
|
* same password characters used to initially load the key-store.
|
| 1061 |
|
|
*
|
| 1062 |
|
|
* @throws IOException if an I/O related exception occurs during the process.
|
| 1063 |
|
|
* @throws KeyStoreException if the key store has not been loaded previously.
|
| 1064 |
|
|
* @throws NoSuchAlgorithmException if a required data integrity algorithm
|
| 1065 |
|
|
* implementation was not found.
|
| 1066 |
|
|
* @throws CertificateException if any of the certificates in the current key
|
| 1067 |
|
|
* store could not be persisted.
|
| 1068 |
|
|
*/
|
| 1069 |
|
|
protected void saveKeyStore() throws IOException, KeyStoreException,
|
| 1070 |
|
|
NoSuchAlgorithmException, CertificateException
|
| 1071 |
|
|
{
|
| 1072 |
|
|
saveKeyStore(storePasswordChars);
|
| 1073 |
|
|
}
|
| 1074 |
|
|
|
| 1075 |
|
|
/**
|
| 1076 |
|
|
* Prints a human-readable form of the designated certificate to a designated
|
| 1077 |
|
|
* {@link PrintWriter}.
|
| 1078 |
|
|
*
|
| 1079 |
|
|
* @param certificate the certificate to process.
|
| 1080 |
|
|
* @param writer where to print it.
|
| 1081 |
|
|
* @throws CertificateEncodingException if an exception occurs while obtaining
|
| 1082 |
|
|
* the DER encoded form <code>certificate</code>.
|
| 1083 |
|
|
*/
|
| 1084 |
|
|
protected void printVerbose(Certificate certificate, PrintWriter writer)
|
| 1085 |
|
|
throws CertificateEncodingException
|
| 1086 |
|
|
{
|
| 1087 |
|
|
X509Certificate x509 = (X509Certificate) certificate;
|
| 1088 |
|
|
writer.println(Messages.getFormattedString("Command.66", x509.getSubjectDN())); //$NON-NLS-1$
|
| 1089 |
|
|
writer.println(Messages.getFormattedString("Command.67", x509.getIssuerDN())); //$NON-NLS-1$
|
| 1090 |
|
|
writer.println(Messages.getFormattedString("Command.68", x509.getSerialNumber())); //$NON-NLS-1$
|
| 1091 |
|
|
writer.println(Messages.getFormattedString("Command.69", x509.getNotBefore())); //$NON-NLS-1$
|
| 1092 |
|
|
writer.println(Messages.getFormattedString("Command.70", x509.getNotAfter())); //$NON-NLS-1$
|
| 1093 |
|
|
writer.println(Messages.getString("Command.71")); //$NON-NLS-1$
|
| 1094 |
|
|
byte[] derBytes = certificate.getEncoded();
|
| 1095 |
|
|
writer.println(Messages.getFormattedString("Command.72", digest(md5, derBytes))); //$NON-NLS-1$
|
| 1096 |
|
|
writer.println(Messages.getFormattedString("Command.73", digest(sha, derBytes))); //$NON-NLS-1$
|
| 1097 |
|
|
}
|
| 1098 |
|
|
|
| 1099 |
|
|
/**
|
| 1100 |
|
|
* Convenience method. Prints a human-readable form of the designated
|
| 1101 |
|
|
* certificate to <code>System.out</code>.
|
| 1102 |
|
|
*
|
| 1103 |
|
|
* @param certificate the certificate to process.
|
| 1104 |
|
|
* @throws CertificateEncodingException if an exception occurs while obtaining
|
| 1105 |
|
|
* the DER encoded form <code>certificate</code>.
|
| 1106 |
|
|
*/
|
| 1107 |
|
|
protected void printVerbose(Certificate certificate)
|
| 1108 |
|
|
throws CertificateEncodingException
|
| 1109 |
|
|
{
|
| 1110 |
|
|
printVerbose(certificate, new PrintWriter(System.out, true));
|
| 1111 |
|
|
}
|
| 1112 |
|
|
|
| 1113 |
|
|
/**
|
| 1114 |
|
|
* Digest the designated contents with MD5 and return a string representation
|
| 1115 |
|
|
* suitable for use as a fingerprint; i.e. sequence of hexadecimal pairs of
|
| 1116 |
|
|
* characters separated by a colon.
|
| 1117 |
|
|
*
|
| 1118 |
|
|
* @param contents the non-null contents to digest.
|
| 1119 |
|
|
* @return a sequence of hexadecimal pairs of characters separated by colons.
|
| 1120 |
|
|
*/
|
| 1121 |
|
|
protected String digestWithMD5(byte[] contents)
|
| 1122 |
|
|
{
|
| 1123 |
|
|
return digest(md5, contents);
|
| 1124 |
|
|
}
|
| 1125 |
|
|
|
| 1126 |
|
|
private String digest(IMessageDigest hash, byte[] encoded)
|
| 1127 |
|
|
{
|
| 1128 |
|
|
hash.update(encoded);
|
| 1129 |
|
|
byte[] b = hash.digest();
|
| 1130 |
|
|
StringBuilder sb = new StringBuilder().append(Util.toString(b, 0, 1));
|
| 1131 |
|
|
for (int i = 1; i < b.length; i++)
|
| 1132 |
|
|
sb.append(":").append(Util.toString(b, i, 1)); //$NON-NLS-1$
|
| 1133 |
|
|
|
| 1134 |
|
|
String result = sb.toString();
|
| 1135 |
|
|
return result;
|
| 1136 |
|
|
}
|
| 1137 |
|
|
|
| 1138 |
|
|
/**
|
| 1139 |
|
|
* Ensure that the currently set Alias is contained in the currently set key
|
| 1140 |
|
|
* store; otherwise throw an exception.
|
| 1141 |
|
|
*
|
| 1142 |
|
|
* @throws KeyStoreException if the keystore has not been loaded.
|
| 1143 |
|
|
* @throws IllegalArgumentException if the currently set alias is not known to
|
| 1144 |
|
|
* the currently set key store.
|
| 1145 |
|
|
*/
|
| 1146 |
|
|
protected void ensureStoreContainsAlias() throws KeyStoreException
|
| 1147 |
|
|
{
|
| 1148 |
|
|
if (! store.containsAlias(alias))
|
| 1149 |
|
|
throw new IllegalArgumentException(Messages.getFormattedString("Command.75", //$NON-NLS-1$
|
| 1150 |
|
|
alias));
|
| 1151 |
|
|
}
|
| 1152 |
|
|
|
| 1153 |
|
|
/**
|
| 1154 |
|
|
* Ensure that the currently set Alias is associated with a Key Entry in the
|
| 1155 |
|
|
* currently set key store; otherwise throw an exception.
|
| 1156 |
|
|
*
|
| 1157 |
|
|
* @throws KeyStoreException if the keystore has not been loaded.
|
| 1158 |
|
|
* @throws SecurityException if the currently set alias is not a Key Entry in
|
| 1159 |
|
|
* the currently set key store.
|
| 1160 |
|
|
*/
|
| 1161 |
|
|
protected void ensureAliasIsKeyEntry() throws KeyStoreException
|
| 1162 |
|
|
{
|
| 1163 |
|
|
if (! store.isKeyEntry(alias))
|
| 1164 |
|
|
throw new SecurityException(Messages.getFormattedString("Command.77", //$NON-NLS-1$
|
| 1165 |
|
|
alias));
|
| 1166 |
|
|
}
|
| 1167 |
|
|
|
| 1168 |
|
|
protected Key getAliasPrivateKey() throws KeyStoreException,
|
| 1169 |
|
|
NoSuchAlgorithmException, IOException, UnsupportedCallbackException,
|
| 1170 |
|
|
UnrecoverableKeyException
|
| 1171 |
|
|
{
|
| 1172 |
|
|
ensureAliasIsKeyEntry();
|
| 1173 |
|
|
Key result;
|
| 1174 |
|
|
if (keyPasswordChars == null)
|
| 1175 |
|
|
try
|
| 1176 |
|
|
{
|
| 1177 |
|
|
result = store.getKey(alias, storePasswordChars);
|
| 1178 |
|
|
// it worked. assign to keyPasswordChars for later use
|
| 1179 |
|
|
keyPasswordChars = storePasswordChars;
|
| 1180 |
|
|
}
|
| 1181 |
|
|
catch (UnrecoverableKeyException x)
|
| 1182 |
|
|
{
|
| 1183 |
|
|
// prompt the user to provide one
|
| 1184 |
|
|
setKeyPasswordParam();
|
| 1185 |
|
|
result = store.getKey(alias, keyPasswordChars);
|
| 1186 |
|
|
}
|
| 1187 |
|
|
else
|
| 1188 |
|
|
result = store.getKey(alias, keyPasswordChars);
|
| 1189 |
|
|
|
| 1190 |
|
|
return result;
|
| 1191 |
|
|
}
|
| 1192 |
|
|
|
| 1193 |
|
|
/**
|
| 1194 |
|
|
* Return a CallbackHandler which uses the Console (System.in and System.out)
|
| 1195 |
|
|
* for interacting with the user.
|
| 1196 |
|
|
* <p>
|
| 1197 |
|
|
* This method first finds all currently installed security providers capable
|
| 1198 |
|
|
* of providing such service and then in turn attempts to instantiate the
|
| 1199 |
|
|
* handler from those providers. As soon as one provider returns a non-null
|
| 1200 |
|
|
* instance of the callback handler, the search stops and that instance is
|
| 1201 |
|
|
* set to be used from now on.
|
| 1202 |
|
|
* <p>
|
| 1203 |
|
|
* If no installed providers were found, this method falls back on the GNU
|
| 1204 |
|
|
* provider, by-passing the Security search mechanism. The default console
|
| 1205 |
|
|
* callback handler implementation is
|
| 1206 |
|
|
* {@link gnu.javax.security.auth.callback.ConsoleCallbackHandler}.
|
| 1207 |
|
|
*
|
| 1208 |
|
|
* @return a console-based {@link CallbackHandler}.
|
| 1209 |
|
|
*/
|
| 1210 |
|
|
protected CallbackHandler getCallbackHandler()
|
| 1211 |
|
|
{
|
| 1212 |
|
|
if (handler == null)
|
| 1213 |
|
|
handler = CallbackUtil.getConsoleHandler();
|
| 1214 |
|
|
|
| 1215 |
|
|
return handler;
|
| 1216 |
|
|
}
|
| 1217 |
|
|
|
| 1218 |
|
|
// Inner class(es) ==========================================================
|
| 1219 |
|
|
|
| 1220 |
|
|
private class ShutdownHook
|
| 1221 |
|
|
extends Thread
|
| 1222 |
|
|
{
|
| 1223 |
|
|
public void run()
|
| 1224 |
|
|
{
|
| 1225 |
|
|
teardown();
|
| 1226 |
|
|
}
|
| 1227 |
|
|
}
|
| 1228 |
|
|
}
|