diff options
author | Raif S. Naffah <raif@swiftdsl.com.au> | 2006-05-02 01:24:37 +0000 |
---|---|---|
committer | Raif S. Naffah <raif@swiftdsl.com.au> | 2006-05-02 01:24:37 +0000 |
commit | 2c5e33550737362b8e9277c9a8caeb86dc9db762 (patch) | |
tree | 667a81c5aa6e93bc2fb9ece5b4bf81afe13916b5 | |
parent | 2f87c016b0638a8174c9d5d63b85f7ca080413d9 (diff) | |
download | classpath-2c5e33550737362b8e9277c9a8caeb86dc9db762.tar.gz |
2006-05-02 Raif S. Naffah <raif@swiftdsl.com.au>
* tools/keytool.sh.in: New file.
* tools/gnu/classpath/tools/keytool/CertReqCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/Command.java: Likewise.
* tools/gnu/classpath/tools/keytool/DeleteCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/ExportCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/GenKeyCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/IdentityDBCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/ImportCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/KeyCloneCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/ListCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/Main.java: Likewise.
* tools/gnu/classpath/tools/keytool/Messages.java: Likewise.
* tools/gnu/classpath/tools/keytool/PrintCertCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/SelfCertCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/StorePasswdCmd.java: Likewise.
* tools/gnu/classpath/tools/keytool/keytool.txt: Likewise.
* tools/gnu/classpath/tools/keytool/package.html: Likewise.
* resource/gnu/classpath/tools/keytool/MessageBundle.properties: Likewise.
20 files changed, 6527 insertions, 0 deletions
@@ -1,5 +1,27 @@ 2006-05-02 Raif S. Naffah <raif@swiftdsl.com.au> + * tools/keytool.sh.in: New file. + * tools/gnu/classpath/tools/keytool/CertReqCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/Command.java: Likewise. + * tools/gnu/classpath/tools/keytool/DeleteCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/ExportCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/GenKeyCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/IdentityDBCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/ImportCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/KeyCloneCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/ListCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/Main.java: Likewise. + * tools/gnu/classpath/tools/keytool/Messages.java: Likewise. + * tools/gnu/classpath/tools/keytool/PrintCertCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/SelfCertCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/StorePasswdCmd.java: Likewise. + * tools/gnu/classpath/tools/keytool/keytool.txt: Likewise. + * tools/gnu/classpath/tools/keytool/package.html: Likewise. + * resource/gnu/classpath/tools/keytool/MessageBundle.properties: Likewise. + +2006-05-02 Raif S. Naffah <raif@swiftdsl.com.au> + * tools/gnu/classpath/tools/jarsigner/jarsigner.txt: Re-arranged to resemble more closely man-page style text. * tools/gnu/classpath/tools/jarsigner/SFHelper.java: diff --git a/resource/gnu/classpath/tools/keytool/MessageBundle.properties b/resource/gnu/classpath/tools/keytool/MessageBundle.properties new file mode 100644 index 000000000..2dd3ce2fc --- /dev/null +++ b/resource/gnu/classpath/tools/keytool/MessageBundle.properties @@ -0,0 +1,95 @@ +# default locale messages for gnu.classpath.tools.keytool package + +Main.6=keytool: +Main.8=keytool error: + +Command.19=Failed creating new file at {0} +Command.20=Unable to find a suitable signature algorithm named {0}, although we found a key-pair generation algorithm named {1} +Command.21=Enter key password for <{0}>: +Command.23=A correct key password MUST be provided +Command.24=Enter key store password: +#Command.36=Option '-keystore' is undefined, or is an empty string, and 'user.home' is unknown +Command.36=Unable to locate a valid key store +Command.40=Provider fully qualified class name: +Command.42=File object [{0}] exists but is NOT a file +Command.44=File [{0}] exists but is NOT writable +Command.46=File object [{0}] MUST be an existing readable file +Command.48=Signature algorithm is missing and private key is of unknown or unsupported type +Command.51=Validity period MUST be greater than zero +Command.52=Unable to get signature algorithm name +Command.60=Unknown or unsupported signature algorithm: {0} +Command.63=Saving key store at {0} +Command.66=Owner: {0} +Command.67=Issuer: {0} +Command.68=Serial number: {0,number} +Command.69=Valid from: {0,date,full} - {0,time,full} +Command.70=\ \ \ \ \ until: {0,date,full} - {0,time,full} +Command.71=Certificate fingerprints +Command.72=\ \ \ \ \ \ MD5: {0} +Command.73=\ \ SHA-160: {0} +Command.75=Alias [{0}] MUST be knwon to the key store +Command.77=Alias [{0}] MUST be associated with a Key Entry + +CertReqCmd.27=Certification request stored in {0} +CertReqCmd.28=Submit this to your CA + +DeleteCmd.19=Enter the Alias to delete: +DeleteCmd.20=Alias MUST NOT be null or an empty string + +GenKeyCmd.0=\nYou are about to enter information that will be incorporated into\n\ +your certificate request. This information is what is called a\n\ +Distinguished Name or DN. There are quite a few fields but you\n\ +can use supplied default values, displayed between brackets, by just\n\ +hitting <Enter>, or blank the field by entering the <.> character\n\ +before hitting <Enter>.\n\n +GenKeyCmd.6=The Sample Company +GenKeyCmd.7=Sydney +GenKeyCmd.8=NSW +GenKeyCmd.9=AU +GenKeyCmd.10=Common Name (hostname, IP, or your name): +GenKeyCmd.11=Organization Name (company) [{0}]: +GenKeyCmd.13=Organizational Unit Name (department, division): +GenKeyCmd.14=Locality Name (city, district) [{0}]: +GenKeyCmd.16=State or Province Name (full name) [{0}]: +GenKeyCmd.18=Country Name (2 letter code) [{0}]: +GenKeyCmd.54=Key size MUST be greater than zero + +StorePasswdCmd.19=Too many failed attempts +StorePasswdCmd.20=Enter new key store password: +StorePasswdCmd.21=Password MUST be at least 6 characters. +StorePasswdCmd.22=New password MUST be different than the old one. +StorePasswdCmd.23=Re-enter new key store password: +StorePasswdCmd.24=Passwords MUST be the same in both attempts. + +KeyPasswdCmd.24=Enter new key password for <{0}>: +KeyPasswdCmd.28=Re-enter new key password for <{0}>: + +KeyCloneCmd.23=Destination Alias MUST NOT exist in key store +KeyCloneCmd.26=Enter destination alias: +KeyCloneCmd.27=Destination alias MUST NOT be null nor empty +KeyCloneCmd.28=Enter new key password for <{0}> [{1}]: + +ListCmd.21=Key store type: {0} +ListCmd.22=Key store provider: {0} +ListCmd.24=Key store contains {0,number} entry(ies) +ListCmd.30=Alias name: {0} +ListCmd.31=Creation timestamp: {0,date,full} - {0,time,full} +ListCmd.32=Entry type: trusted-certificate +ListCmd.33=Entry type: key-entry +ListCmd.34=Alias [{0}] is unknown to the key store +ListCmd.38=Certificate chain length: {0,number} +ListCmd.39=Certificate[1]: +ListCmd.40=Certificate[{0,number}]: +ListCmd.42=******************************************* +ListCmd.43=-----BEGIN CERTIFICATE----- +ListCmd.44=-----END CERTIFICATE----- +ListCmd.45=Certificate fingerprint (MD5): {0} + +ImportCmd.34=Failed to establish chain-of-trust from reply +ImportCmd.37=Unable to find anchor certificate for {0} +ImportCmd.38=Public keys, in key store and certificate, MUST be of the same type +ImportCmd.32=Can this certificate be trusted? +ImportCmd.40=Key entry associated with {0} has an unknown or unsupported public key type {1} +ImportCmd.41=Public keys, in key store and certificate, MUST be the same +ImportCmd.29=Certificate was added to the key store +ImportCmd.28=Certificate was not added to the key store diff --git a/tools/gnu/classpath/tools/keytool/CertReqCmd.java b/tools/gnu/classpath/tools/keytool/CertReqCmd.java new file mode 100644 index 000000000..0c64246e8 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/CertReqCmd.java @@ -0,0 +1,405 @@ +/* CertReqCmd.java -- The certreq command handler of the keytool + 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.java.security.OID; +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.util.Base64; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.logging.Logger; + +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.x500.X500Principal; + +/** + * The <b>-certreq</b> keytool command handler is used to generate a Certificate + * Signing Request (CSR) in PKCS#10 format. + * <p> + * The ASN.1 specification of a CSR, as stated in RFC-2986 is as follows: + * <p> + * <pre> + * CertificationRequest ::= SEQUENCE { + * certificationRequestInfo CertificationRequestInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + * + * CertificationRequestInfo ::= SEQUENCE { + * version INTEGER -- v1(0) + * subject Name, + * subjectPKInfo SubjectPublicKeyInfo, + * attributes [0] IMPLICIT Attributes -- see note later + * } + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + * </pre> + * <b>IMPORTANT</b>: Some documentation (e.g. RSA examples) claims that the + * <code>attributes</code> field is <i>OPTIONAL</i> while <i>RFC-2986</i> + * implies the opposite. This implementation considers this field, by default, + * as <i>OPTIONAL</i>, unless the option <code>-attributes</code> is included + * on the command line. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-sigalg ALGORITHM</dt> + * <dd>The canonical name of the digital signature algorithm to use for + * signing the certificate. If this option is omitted, a default value will + * be chosen based on the type of the private key associated with the + * designated <i>Alias</i>. If the private key is a <code>DSA</code> one, + * the value for the signature algorithm will be <code>SHA1withDSA</code>. + * If on the other hand the private key is an <code>RSA</code> one, then + * the tool will use <code>MD5withRSA</code> as the signature algorithm. + * <p></dd> + * + * <dt>-file FILE_NAME</dt> + * + * <dt>-keypass PASSWORD</dt> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output. + * <p></dd> + * + * <dt>-attributes</dt> + * <dd>Use this option to force the tool to encode a NULL DER value in the + * CSR as the value of the Attributes field.</dd> + * </dl> + */ +class CertReqCmd extends Command +{ + private static final Logger log = Logger.getLogger(CertReqCmd.class.getName()); + private String _alias; + private String _sigAlgorithm; + private String _certReqFileName; + private String _password; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private boolean nullAttributes; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** + * @param algorithm the canonical name of the digital signature algorithm to + * use. + */ + public void setSigalg(String algorithm) + { + this._sigAlgorithm = algorithm; + } + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certReqFileName = pathName; + } + + /** @param password the (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + /** + * @param flag whether to use, or not, a <code>NULL</code> DER value for + * the certificate's Attributes field. + */ + public void setAttributes(String flag) + { + this.nullAttributes = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-sigalg".equals(opt)) // -sigalg ALGORITHM //$NON-NLS-1$ + _sigAlgorithm = args[++i]; + else if ("-file".equals(opt)) // -file FILE_NAME //$NON-NLS-1$ + _certReqFileName = args[++i]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD //$NON-NLS-1$ + _password = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else if ("-attributes".equals(opt)) //$NON-NLS-1$ + nullAttributes = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setOutputStreamParam(_certReqFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); +// setSignatureAlgorithm(_sigAlgorithm); + + log.finer("-certreq handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -sigalg=" + _sigAlgorithm); //$NON-NLS-1$ + log.finer(" -file=" + _certReqFileName); //$NON-NLS-1$ + log.finer(" -keypass=" + _password); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + log.finer(" -attributes=" + nullAttributes); //$NON-NLS-1$ + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, UnrecoverableKeyException, + InvalidKeyException, SignatureException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + // 1. get the key entry and certificate chain associated to alias + Key privateKey = getAliasPrivateKey(); + Certificate[] chain = store.getCertificateChain(alias); + + // 2. get alias's DN and public key to use in the CSR + X509Certificate bottomCertificate = (X509Certificate) chain[0]; + X500Principal aliasName = bottomCertificate.getIssuerX500Principal(); + PublicKey publicKey = bottomCertificate.getPublicKey(); + + // 3. generate the CSR + setSignatureAlgorithmParam(_sigAlgorithm, privateKey); + byte[] derBytes = getCSR(aliasName, publicKey, (PrivateKey) privateKey); + + // 4. encode it in base-64 and write it to outStream + String encoded = Base64.encode(derBytes, 0, derBytes.length, true); + PrintWriter writer = new PrintWriter(outStream, true); + writer.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); //$NON-NLS-1$ + writer.println(encoded); + writer.println("-----END NEW CERTIFICATE REQUEST-----"); //$NON-NLS-1$ + + if (verbose) + { + if (! systemOut) + System.out.println(Messages.getFormattedString("CertReqCmd.27", //$NON-NLS-1$ + _certReqFileName)); + System.out.println(Messages.getString("CertReqCmd.28")); //$NON-NLS-1$ + } + + writer.close(); + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /** + * @param aliasName + * @param publicKey + * @param privateKey + * @return the DER encoded Certificate Signing Request. + * @throws IOException + * @throws InvalidKeyException + * @throws SignatureException + */ + private byte[] getCSR(X500Principal aliasName, PublicKey publicKey, + PrivateKey privateKey) + throws IOException, InvalidKeyException, SignatureException + { + DERValue derVersion = new DERValue(DER.INTEGER, BigInteger.ZERO); + DERValue derSubject = new DERReader(aliasName.getEncoded()).read(); + DERValue derSubjectPKInfo = new DERReader(publicKey.getEncoded()).read(); + byte[] b = nullAttributes ? new byte[] { 0x05, 0x00 } : new byte[0]; + DERValue derAttributes = new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0, + b.length, b, null); + ArrayList certRequestInfo = new ArrayList(4); + certRequestInfo.add(derVersion); + certRequestInfo.add(derSubject); + certRequestInfo.add(derSubjectPKInfo); + certRequestInfo.add(derAttributes); + DERValue derCertRequestInfo = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + certRequestInfo); + + OID sigAlgorithmID = getSignatureAlgorithmOID(); + DERValue derSigAlgorithmID = new DERValue(DER.OBJECT_IDENTIFIER, + sigAlgorithmID); + ArrayList sigAlgorithm = new ArrayList(2); + sigAlgorithm.add(derSigAlgorithmID); + if (! sigAlgorithmID.equals(Command.SHA1_WITH_DSA)) // it's an RSA-based + sigAlgorithm.add(new DERValue(DER.NULL, null)); + + sigAlgorithm.trimToSize(); + DERValue derSignatureAlgorithm = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, + sigAlgorithm); + + signatureAlgorithm.initSign(privateKey); + signatureAlgorithm.update(derCertRequestInfo.getEncoded()); + byte[] sigBytes = signatureAlgorithm.sign(); + DERValue derSignature = new DERValue(DER.BIT_STRING, new BitString(sigBytes)); + + ArrayList csr = new ArrayList(3); + csr.add(derCertRequestInfo); + csr.add(derSignatureAlgorithm); + csr.add(derSignature); + DERValue derCSR = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, csr); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DERWriter.write(baos, derCSR); + byte[] result = baos.toByteArray(); + + return result; + } +} diff --git a/tools/gnu/classpath/tools/keytool/Command.java b/tools/gnu/classpath/tools/keytool/Command.java new file mode 100644 index 000000000..235f2cc56 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/Command.java @@ -0,0 +1,1147 @@ +/* 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.SystemProperties; +import gnu.classpath.tools.common.CallbackUtil; +import gnu.classpath.tools.common.ProviderUtil; +import gnu.classpath.tools.common.SecurityProviderInfo; +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 gnu.javax.security.auth.callback.ConsoleCallbackHandler; + +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; + + // Constructor(s) ----------------------------------------------------------- + + // default 0-arguments constructor + + // 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(); + } + } + + /** + * @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. + * @param startIndex the index of the first argument in <code>args</code> to + * process. + * @return the index of the first unprocessed argument in <code>args</code>. + */ + abstract int processArgs(String[] args, int startIndex); + + /** + * 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() + { + log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + + if (storeStream != null) + try + { + storeStream.close(); + } + catch (IOException ignored) + { + 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()); + + log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + } + + // parameter setup and validation methods ----------------------------------- + + /** + * Convenience method to setup the key store given its type, its password, its + * location and portentially a specialized security provider. + * + * @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 + { + setProviderClassNameParam(className); + setKeystoreTypeParam(type); + setKeystorePasswordParam(password); + setKeystoreURLParam(url); + } + + /** + * 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. + */ + protected void setProviderClassNameParam(String className) + { + log.finest("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) + 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. + */ + protected void setKeystoreTypeParam(String type) + { + log.finest("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. + */ + protected 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$ + } + + protected 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(); + } + log.finest("storePasswordChars = [" + String.valueOf(storePasswordChars)+ "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Set the key store URL to use. + * + * @param url + * @throws IOException + * @throws KeyStoreException + * @throws UnsupportedCallbackException + * @throws NoSuchAlgorithmException + * @throws CertificateException + */ + protected void setKeystoreURLParam(String url) throws IOException, + KeyStoreException, UnsupportedCallbackException, NoSuchAlgorithmException, + CertificateException + { + log.finest("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 + 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$ + new File(url).createNewFile(); + + url = "file:" + url; //$NON-NLS-1$ + } + + boolean newKeyStore = false; + storeURL = new URL(url); + storeStream = storeURL.openStream(); + if (storeStream.available() == 0) + { + 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; + + 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); + } + + // 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) + { + 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 + { + 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(); + + 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 + { + log.entering(this.getClass().getName(), "saveKeyStore", String.valueOf(password)); //$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(); + + 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 ConsoleCallbackHandler}. + * + * @return a console-based {@link CallbackHandler}. + */ + protected CallbackHandler getCallbackHandler() + { + if (handler == null) + CallbackUtil.getConsoleHandler(); + + return handler; + } +} diff --git a/tools/gnu/classpath/tools/keytool/DeleteCmd.java b/tools/gnu/classpath/tools/keytool/DeleteCmd.java new file mode 100644 index 000000000..968af50f8 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/DeleteCmd.java @@ -0,0 +1,235 @@ +/* DeleteCmd.java -- The delete command handler of the keytool + 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 java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-delete</b> keytool command handler is used to delete from the key + * store the entry associated with a designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class DeleteCmd extends Command +{ + private static final Logger log = Logger.getLogger(DeleteCmd.class.getName()); + private String _alias; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setTheAlias(_alias); + + log.finer("-delete handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + ensureStoreContainsAlias(); + store.deleteEntry(alias); + saveKeyStore(); + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /** + * Set the alias to delete from the key store. + * <p> + * Unlike in other keytool handlers, the default value (<i>mykey</i>) for the + * Alias is not used. Instead, if an alias was not found on the command line, + * the user is prompted to enter one. + * + * @param anAlias a possibly null Alias gleaned from the command line. + * @throws IOException if an I/O related exception occurs during the process. + * @throws UnsupportedCallbackException if no implementation of a password + * callback handler was found. + */ + private void setTheAlias(String anAlias) throws IOException, + UnsupportedCallbackException + { + if (anAlias == null || anAlias.trim().length() == 0) + { + String prompt = Messages.getString("DeleteCmd.19"); //$NON-NLS-1$ + NameCallback ncb = new NameCallback(prompt); + getCallbackHandler().handle(new Callback[] { ncb }); + anAlias = ncb.getName(); + if (anAlias == null || anAlias.trim().length() == 0) + throw new SecurityException(Messages.getString("DeleteCmd.20")); //$NON-NLS-1$ + } + alias = anAlias.trim(); + } +} diff --git a/tools/gnu/classpath/tools/keytool/ExportCmd.java b/tools/gnu/classpath/tools/keytool/ExportCmd.java new file mode 100644 index 000000000..0d1692245 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/ExportCmd.java @@ -0,0 +1,266 @@ +/* ExportCmd.java -- The export command handler of the keytool + 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.java.security.util.Base64; + +import java.io.IOException; +import java.io.PrintWriter; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.util.logging.Logger; + +/** + * The <b>-export</b> keytool command handler is used to read the certificate + * associated with a designated alias from the key store, and write it to a + * designated file. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file where the certificate will be + * exported to. If omitted, STDOUT will be used instead. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-rfc</dt> + * <dd>Use RFC-1421 specifications when encoding the output. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Output the certificate in binary DER encoding. This is the default + * output format of the command if neither <code>-rfc</code> nor + * <code>-v</code> options were detected on the command line. If both this + * option and the <code>-rfc</code> option are detected on the command + * line, the tool will opt for the RFC-1421 style encoding.</dd> + * </dl> + */ +class ExportCmd extends Command +{ + private static final Logger log = Logger.getLogger(ExportCmd.class.getName()); + private String _alias; + private String _certFileName; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private boolean rfc; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + /** + * @param flag whether to use, or not, RFC-1421 format when exporting the + * certificate(s). + */ + public void setRfc(String flag) + { + this.rfc = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS + _alias = args[++i]; + else if ("-file".equals(opt)) // -file FILE_NAME + _certFileName = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME + _providerClassName = args[++i]; + else if ("-rfc".equals(opt)) + rfc = true; + else if ("-v".equals(opt)) + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setOutputStreamParam(_certFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(alias); + + log.finer("-export handler will use the following options:"); + log.finer(" -alias=" + alias); + log.finer(" -file=" + _certFileName); + log.finer(" -storetype=" + storeType); + log.finer(" -keystore=" + storeURL); + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); + log.finer(" -provider=" + provider); + log.finer(" -rfc=" + rfc); + log.finer(" -v=" + verbose); + } + + void start() throws KeyStoreException, CertificateEncodingException, + IOException + { + log.entering(this.getClass().getName(), "start"); + + ensureStoreContainsAlias(); + Certificate certificate; + if (store.isCertificateEntry(alias)) + { + log.fine("Alias [" + alias + "] is a trusted certificate"); + certificate = store.getCertificate(alias); + } + else + { + log.fine("Alias [" + alias + "] is a key entry"); + Certificate[] chain = store.getCertificateChain(alias); + certificate = chain[0]; + } + + byte[] derBytes = certificate.getEncoded(); + if (rfc) + { + String encoded = Base64.encode(derBytes, 0, derBytes.length, true); + PrintWriter pw = new PrintWriter(outStream, true); + pw.println("-----BEGIN CERTIFICATE-----"); + pw.println(encoded); + pw.println("-----END CERTIFICATE-----"); + } + else + outStream.write(derBytes); + + // stream is closed in Command.teardown() + log.exiting(this.getClass().getName(), "start"); + } +} diff --git a/tools/gnu/classpath/tools/keytool/GenKeyCmd.java b/tools/gnu/classpath/tools/keytool/GenKeyCmd.java new file mode 100644 index 000000000..2d92134c2 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/GenKeyCmd.java @@ -0,0 +1,511 @@ +/* GenKeyCmd.java -- The genkey command handler of the keytool + 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.java.security.util.Util; +import gnu.java.security.x509.X500DistinguishedName; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.TextInputCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-genkey</b> keytool command handler is used to generate a key pair (a + * public, and associated private keys). It then generates a self-signed X509 v1 + * certificate (authenticating the public key) and stores this certificate and + * the private key in the key store associating both to a designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-keyalg ALGORITHM</dt> + * <dd>Use this option to specify the canonical name of the key-pair + * generation algorithm. The default value for this option is + * <code>DSS</code> (a synonym for the Digital Signature Algorithm also + * known as <code>DSA</code>). + * <p></dd> + * + * <dt>-keysize KEY_SIZE</dt> + * <dd>Use this option to specify the number of bits of the shared modulus + * (for both the public and private keys) to use when generating new keys. + * A default value of <code>1024</code> will be used if this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-sigalg ALGORITHM</dt> + * <dd>The canonical name of the digital signature algorithm to use for + * signing certificates. If this option is omitted, a default value will be + * chosen based on the type of the key-pair; i.e. the algorithm that ends + * up being used by the <code>-keyalg</code> option. If the key-pair + * generation algorithm is <code>DSA</code>, the value for the signature + * algorithm will be <code>SHA1withDSA</code>. If on the other hand the + * key-pair generation algorithm is <code>RSA</code>, then the tool will + * use <code>MD5withRSA</code> as the signature algorithm. + * <p></dd> + * + * <dt>-dname NAME</dt> + * <dd>This a mandatory value for this command. If this option is omitted + * the tool will prompt you to enter a <i>Distinguished Name</i> to use as + * both the <i>Owner</i> and <i>Issuer</i> of the generated self-signed + * certificate. + * <p> + * The syntax of a valid value for this option MUST follow RFC-2253 + * specifications. Namely the following components (with their accepted + * meaning) will be recognized. Note that the component name is case- + * insensitive: + * <dl> + * <dt>CN</dt> + * <dd>The Common Name; e.g. "host.domain.com"</dd> + * + * <dt>OU</dt> + * <dd>The Organizational Unit; e.g. "IT Department"</dd> + * + * <dt>O</dt> + * <dd>The Organization Name; e.g. "The Sample Company"</dd> + * + * <dt>L</dt> + * <dd>The Locality Name; e.g. "Sydney"</dd> + * + * <dt>ST</dt> + * <dd>The State Name; e.g. "New South Wales"</dd> + * + * <dt>C</dt> + * <dd>The 2-letter Country identifier; e.g. "AU"</dd> + * </dl> + * <p> + * When specified with a <code>-dname</code> option, each pair of component + * / value will be separated from the other with a comma. Each component + * and value pair MUST be separated by an equal sign. For example, the + * following is a valid DN value: + * <pre> + * CN=host.domain.com, O=The Sample Company, L=Sydney, ST=NSW, C=AU + * </pre> + * If this option is omitted, the tool will prompt you to enter the + * information through the console. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * protect the newly created Key Entry. + * <p> + * If this option is omitted, you will be prompted to provide a password. + * <p></dd> + * + * <dt>-validity DAY_COUNT</dt> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class GenKeyCmd extends Command +{ + private static final Logger log = Logger.getLogger(GenKeyCmd.class.getName()); + /** Default key size in bits. */ + private static final int DEFAULT_KEY_SIZE = 1024; + + private String _alias; + private String _keyAlgorithm; + private String _keySizeStr; + private String _sigAlgorithm; + private String _dName; + private String _password; + private String _validityStr; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private int keySize; + private X500DistinguishedName distinguishedName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param algorithm the canonical name of the key-pair algorithm to use. */ + public void setKeyalg(String algorithm) + { + this._keyAlgorithm = algorithm; + } + + /** + * @param bits the string representation of the number of bits (a decimal + * positive integer) the modulus of the generated keys (private and + * public) should have. + */ + public void setKeysize(String bits) + { + this._validityStr = bits; + } + + /** + * @param algorithm the canonical name of the digital signature algorithm to + * use. + */ + public void setSigalg(String algorithm) + { + this._sigAlgorithm = algorithm; + } + + /** @param name the distiniguished name to use. */ + public void setDname(String name) + { + this._dName = name; + } + + /** @param password the (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** + * @param days the string representation of the number of days (a decimal, + * positive integer) to assign to the generated certificate. + */ + public void setValidity(String days) + { + this._validityStr = days; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-keyalg".equals(opt)) // -keyalg ALGORITHM //$NON-NLS-1$ + _keyAlgorithm = args[++i]; + else if ("-keysize".equals(opt)) // -keysize KEY_SIZE //$NON-NLS-1$ + _keySizeStr = args[++i]; + else if ("-sigalg".equals(opt)) // -sigalg ALGORITHM //$NON-NLS-1$ + _sigAlgorithm = args[++i]; + else if ("-dname".equals(opt)) // -dname NAME //$NON-NLS-1$ + _dName = args[++i]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD //$NON-NLS-1$ + _password = args[++i]; + else if ("-validity".equals(opt)) // -validity DAY_COUNT //$NON-NLS-1$ + _validityStr = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordParam(_password); + setAlgorithmParams(_keyAlgorithm, _sigAlgorithm); + setKeySize(_keySizeStr); + setDName(_dName); + setValidityParam(_validityStr); + + log.finer("-genkey handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -keyalg=" + keyPairGenerator.getAlgorithm()); //$NON-NLS-1$ + log.finer(" -keysize=" + keySize); //$NON-NLS-1$ + log.finer(" -sigalg=" + signatureAlgorithm.getAlgorithm()); //$NON-NLS-1$ + log.finer(" -dname=" + distinguishedName); //$NON-NLS-1$ + log.finer(" -keypass=" + String.valueOf(keyPasswordChars)); //$NON-NLS-1$ + log.finer(" -validity=" + validityInDays); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + } + + void start() throws CertificateException, KeyStoreException, + InvalidKeyException, SignatureException, IOException, + NoSuchAlgorithmException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + // 1. generate a new key-pair + log.fine("About to generate key-pair..."); + keyPairGenerator.initialize(keySize); + KeyPair kp = keyPairGenerator.generateKeyPair(); + PublicKey publicKey = kp.getPublic(); + PrivateKey privateKey = kp.getPrivate(); + + // 2. generate a self-signed certificate + log.fine("About to generate a self-signed certificate..."); + byte[] derBytes = getSelfSignedCertificate(distinguishedName, + publicKey, + privateKey); + log.finest(Util.dumpString(derBytes, "derBytes ")); //$NON-NLS-1$ + CertificateFactory x509Factory = CertificateFactory.getInstance(Main.X_509); + ByteArrayInputStream bais = new ByteArrayInputStream(derBytes); + Certificate certificate = x509Factory.generateCertificate(bais); + log.finest("certificate = " + certificate); //$NON-NLS-1$ + + // 3. store it, w/ its private key, associating them to alias + Certificate[] chain = new Certificate[] { certificate }; + log.finest("About to store newly generated material in key store..."); //$NON-NLS-1$ + store.setKeyEntry(alias, privateKey, keyPasswordChars, chain); + + // 4. persist the key store + saveKeyStore(); + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /** + * @param size the desired key size as a string. + * @throws NumberFormatException if the string does not represent a valid + * decimal integer value. + */ + private void setKeySize(String size) + { + if (size == null || size.trim().length() == 0) + this.keySize = DEFAULT_KEY_SIZE; + else + { + size = size.trim(); + keySize = Integer.parseInt(size); + // When generating a DSA key pair, the key size must be in the range + // from 512 to 1024 bits, and must be a multiple of 64. The default + // key size for any algorithm is 1024 bits + if (keySize < 1) + throw new IllegalArgumentException(Messages.getString("GenKeyCmd.54")); //$NON-NLS-1$ + } + } + + /** + * @param name the X.500 distinguished name of the principal for whom the + * key/certificate are being generated. + * @throws UnsupportedCallbackException if no implementation of a name + * callback is available. + * @throws IOException if an I/O related exception occurs during the process. + * @throws IllegalArgumentException if the designated, or captured, value is + * not a valid X.500 distinguished name. + */ + private void setDName(String name) throws IOException, + UnsupportedCallbackException + { + if (name != null && name.trim().length() > 0) + name = name.trim(); + else + { + // prompt user to provide one + String dnTxt = Messages.getString("GenKeyCmd.0"); //$NON-NLS-1$ + String oDefault = Messages.getString("GenKeyCmd.6"); //$NON-NLS-1$ + String lDefault = Messages.getString("GenKeyCmd.7"); //$NON-NLS-1$ + String stDefault = Messages.getString("GenKeyCmd.8"); //$NON-NLS-1$ + String cDefault = Messages.getString("GenKeyCmd.9"); //$NON-NLS-1$ + String cnPrompt = Messages.getString("GenKeyCmd.10"); //$NON-NLS-1$ + String oPrompt = Messages.getFormattedString("GenKeyCmd.11", oDefault); //$NON-NLS-1$ + String ouPrompt = Messages.getString("GenKeyCmd.13"); //$NON-NLS-1$ + String lPrompt = Messages.getFormattedString("GenKeyCmd.14", lDefault); //$NON-NLS-1$ + String stPrompt = Messages.getFormattedString("GenKeyCmd.16", stDefault); //$NON-NLS-1$ + String cPrompt = Messages.getFormattedString("GenKeyCmd.18", cDefault); //$NON-NLS-1$ + + TextOutputCallback dnCB = new TextOutputCallback(TextOutputCallback.INFORMATION, + dnTxt); + TextInputCallback cnCB = new TextInputCallback(cnPrompt); + TextInputCallback oCB = new TextInputCallback(oPrompt, oDefault); + TextInputCallback ouCB = new TextInputCallback(ouPrompt); + TextInputCallback lCB = new TextInputCallback(lPrompt, lDefault); + TextInputCallback sCB = new TextInputCallback(stPrompt, stDefault); + TextInputCallback cCB = new TextInputCallback(cPrompt, cDefault); + getCallbackHandler().handle(new Callback[] { dnCB, cnCB, oCB, ouCB, lCB, sCB, cCB }); + StringBuilder sb = new StringBuilder(); + + // handle CN + name = parseUserPrompt(cnCB); + if (name != null && name.length() > 0) + sb.append("CN=").append(name); //$NON-NLS-1$ + + // handle O + name = parseUserPrompt(oCB); + if (name != null && name.length() > 0) + sb.append(",O=").append(name); //$NON-NLS-1$ + + // handle OU + name = parseUserPrompt(ouCB); + if (name != null && name.length() > 0) + sb.append(",OU=").append(name.trim()); //$NON-NLS-1$ + + // handle L + name = parseUserPrompt(lCB); + if (name != null && name.length() > 0) + sb.append(",L=").append(name.trim()); //$NON-NLS-1$ + + // handle ST + name = parseUserPrompt(sCB); + if (name != null && name.length() > 0) + sb.append(",ST=").append(name.trim()); //$NON-NLS-1$ + + // handle C + name = parseUserPrompt(cCB); + if (name != null && name.length() > 0) + sb.append(",C=").append(name.trim()); //$NON-NLS-1$ + + name = sb.toString().trim(); + } + + log.fine("dName=[" + name + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + distinguishedName = new X500DistinguishedName(name); + } + + private String parseUserPrompt(TextInputCallback ticb) + { + String result = ticb.getText(); + if (result == null || result.trim().length() == 0) + result = ticb.getDefaultText(); + else if (result.trim().equals(".")) //$NON-NLS-1$ + result = null; + else + result = result.trim(); + + return result; + } +} diff --git a/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java b/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java new file mode 100644 index 000000000..cb6b6dac2 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java @@ -0,0 +1,185 @@ +/* IdentityDBCmd.java -- The identitydb command handler of the keytool + 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 java.util.logging.Logger; + +/** + * <b>NOT IMPLEMENTED YET</b> + * <p> + * The <b>-identitydb</b> keytool command handler is used to read the JDK 1.1.x- + * style identity database and add its entries to the key store. If a key store + * does not exist, it is created. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the identity file to import. If this + * option is omitted, the tool will process STDIN. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class IdentityDBCmd extends Command +{ + private static final Logger log = Logger.getLogger(IdentityDBCmd.class.getName()); + private String _idbFileName; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._idbFileName = pathName; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); + if (opt == null || opt.length() == 0) + continue; + + if ("-file".equals(opt)) // -file FILE_NAME + _idbFileName = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME + _providerClassName = args[++i]; + else if ("-v".equals(opt)) + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setInputStreamParam(_idbFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + + log.finer("-identitydb handler will use the following options:"); + log.finer(" -file=" + _idbFileName); + log.finer(" -storetype=" + storeType); + log.finer(" -keystore=" + storeURL); + log.finer(" -storepass=" + new String(storePasswordChars)); + log.finer(" -provider=" + provider); + log.finer(" -v=" + verbose); + } +} diff --git a/tools/gnu/classpath/tools/keytool/ImportCmd.java b/tools/gnu/classpath/tools/keytool/ImportCmd.java new file mode 100644 index 000000000..b5058b581 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/ImportCmd.java @@ -0,0 +1,751 @@ +/* ImportCmd.java -- The import command handler of the keytool + 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.SystemProperties; +import gnu.java.security.x509.X509CertPath; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXCertPathValidatorResult; +import java.security.cert.PKIXParameters; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Collection; +import java.util.LinkedList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.ConfirmationCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <code>-import</code> keytool command handler is used to read an X.509 + * certificate, or a PKCS#7 Certificate Reply from a designated input source and + * incorporate the certificates into the key store. + * <p> + * If the <i>Alias</i> does not already exist in the key store, the tool treats + * the certificate read from the input source as a new Trusted Certificate. It + * then attempts to discover a chain-of-trust, starting from that certificate + * and ending at another <i>Trusted Certificate</i>, already stored in the key + * store. If the <code>-trustcacerts</code> option is present, an additional + * key store, of type <code>JKS</code> named <code>cacerts</code>, and assumed + * to be present in <code>${JAVA_HOME}/lib/security</code> will also be + * consulted if found --<code>${JAVA_HOME}</code> refers to the location of an + * installed Java Runtime Environment (JRE). If no chain-of-trust can be + * established, and unless the <code>-noprompt</code> option has been specified, + * the certificate is printed to STDOUT and the user is prompted for a + * confirmation. + * <p> + * If <i>Alias</i> exists in the key store, the tool will treat the + * certificate(s) read from the input source as a <i>Certificate Reply</i>, + * which can be a chain of certificates, that eventually would replace the chain + * of certificates associated with the <i>Key Entry</i> of that <i>Alias</i>. + * The substitution of the certificates only occurs if a chain-of-trust can be + * established between the bottom certificate of the chain read from the input + * file and the <i>Trusted Certificates</i> already present in the key store. + * Again, if the <code>-trustcacerts</code> option is specified, additional + * <i>Trusted Certificates</i> in the same <code>cacerts</code> key store will + * be considered. If no chain-of-trust can be established, the operation will + * abort. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file to read from. If omitted, the + * tool will process STDIN. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * protect the <i>Key Entry</i> associated with the designated <i>Alias</i>, + * when replacing this <i>Alias</i>' chain of certificates with that found + * in the certificate reply. + * <p> + * If this option is omitted, and the chain-of-trust for the certificate + * reply has been established, the tool will first attempt to unlock the + * <i>Key Entry</i> using the same password protecting the key store. If + * this fails, you will then be prompted to provide a password. + * <p></dd> + * + * <dt>-noprompt</dt> + * <dd>Use this option to prevent the tool from prompting the user. + * <p></dd> + * + * <dt>-trustcacerts</dt> + * <dd>Use this option to indicate to the tool that a key store, of type + * <code>JKS</code>, named <code>cacerts</code>, and usually located in + * <code>lib/security</code> in an installed Java Runtime Environment + * should be considered when trying to establish chain-of-trusts. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class ImportCmd extends Command +{ + private static final Logger log = Logger.getLogger(ImportCmd.class.getName()); + private String _alias; + private String _certFileName; + private String _password; + private boolean noPrompt; + private boolean trustCACerts; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private CertificateFactory x509Factory; + private boolean imported; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the existing alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + /** @param password the existing (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** + * @param flag whether to prompt, or not, the user to verify certificate + * fingerprints. + */ + public void setNoprompt(String flag) + { + this.noPrompt = Boolean.valueOf(flag).booleanValue(); + } + + /** + * @param flag whether to trust, or not, certificates found in the + * <code>cacerts</code> key store. + */ + public void setTrustcacerts(String flag) + { + this.trustCACerts = Boolean.valueOf(flag).booleanValue(); + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-file".equals(opt)) // -file FILE_NAME //$NON-NLS-1$ + _certFileName = args[++i]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD //$NON-NLS-1$ + _password = args[++i]; + else if ("-noprompt".equals(opt)) //$NON-NLS-1$ + noPrompt = true; + else if ("-trustcacerts".equals(opt)) //$NON-NLS-1$ + trustCACerts = true; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setInputStreamParam(_certFileName); + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + + log.finer("-import handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -file=" + _certFileName); //$NON-NLS-1$ + log.finer(" -keypass=" + _password); //$NON-NLS-1$ + log.finer(" -noprompt=" + noPrompt); //$NON-NLS-1$ + log.finer(" -trustcacerts=" + trustCACerts); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + } + + void start() throws CertificateException, KeyStoreException, IOException, + UnsupportedCallbackException, NoSuchAlgorithmException, + CertPathValidatorException, UnrecoverableKeyException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + x509Factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + // the alias will tell us whether we're dealing with + // a new trusted certificate or a certificate reply + if (! store.containsAlias(alias)) + importNewTrustedCertificate(); + else + { + ensureAliasIsKeyEntry(); + importCertificateReply(); + } + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /** + * When importing a new trusted certificate, <i>alias</i> MUST NOT yet exist + * in the key store. + * <p> + * Before adding the certificate to the key store and associate it with the + * designated Alias, this method tries to verify it by attempting to construct + * a chain of trust from that certificate to a self-signed certificate + * (belonging to a root CA), using (already) trusted certificates that are + * available in the key store. + * <p> + * If the <code>-trustcacerts</code> option was detected on the command + * line, additional trusted certificates are considered for establishing the + * chain of trust. Those additional certificates are assumed to be in a key + * store, of type <code>JKS</code> named <code>cacerts</code> and usually + * located in <code>${JAVA_HOME}/lib/security</code>, where + * <code>${JAVA_HOME}</code> is the root folder location of a Java runtime. + * <p> + * If this method fails to establish a trust path from the certificate to be + * imported up to a trusted self-signed certificate, the certificate is + * printed to <code>STDOUT</code>, and the user is prompted to verify it, + * with the option of aborting the import operation. If however the option + * <code>-noprompt</code> was detected on the command line, no interaction + * with the user will take place and the import operation will abort. + * + * @throws CertificateException + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws CertPathValidatorException + */ + private void importNewTrustedCertificate() throws CertificateException, + KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, CertPathValidatorException, + UnrecoverableKeyException + { + log.entering(this.getClass().getName(), "importNewTrustedCertificate"); //$NON-NLS-1$ + + Certificate certificate = x509Factory.generateCertificate(inStream); + log.finest("certificate = " + certificate); //$NON-NLS-1$ + LinkedList orderedReply = new LinkedList(); + orderedReply.addLast(certificate); + + if (findTrustAndUpdate(orderedReply, ! noPrompt)) + { + store.setCertificateEntry(alias, certificate); + System.out.println(Messages.getString("ImportCmd.29")); //$NON-NLS-1$ + saveKeyStore(); + } + else + System.out.println(Messages.getString("ImportCmd.28")); //$NON-NLS-1$ + + log.exiting(this.getClass().getName(), "importNewTrustedCertificate"); //$NON-NLS-1$ + } + + /** + * A certificate reply is a certificate, whose Owner is stored in the key + * store associated to the designated Alias, and now signed by supposedly a + * trusted CA (Certificate Authority). In other words, the Subject in this + * certificate reply is Alias's own and the Issuer is a CA. + * <p> + * When importing a certificate reply, the reply is validated using trusted + * certificates from the key store, and optionally (if the option + * <code>-trustcacerts</code> was detected on the command line) certificates + * found in the key store, of type <code>JKS</code> named <code>cacerts</code> + * located in <code>${JAVA_HOME}/lib/security</code>, where + * <code>${JAVA_HOME}</code> is the root folder location of a Java runtime. + * + * @throws CertificateException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws KeyStoreException + * @throws CertPathValidatorException + * @throws NoSuchAlgorithmException + * @throws UnrecoverableKeyException + */ + private void importCertificateReply() throws CertificateException, + IOException, UnsupportedCallbackException, KeyStoreException, + NoSuchAlgorithmException, CertPathValidatorException, + UnrecoverableKeyException + { + log.entering(this.getClass().getName(), "importCertificateReply"); //$NON-NLS-1$ + + Collection certificates = x509Factory.generateCertificates(inStream); + ensureReplyIsOurs(certificates); + // we now have established that the public keys are the same. + // find a chain-of-trust if one exists + if (certificates.size() == 1) + importCertificate((Certificate) certificates.iterator().next()); + else + importChain(certificates); + + log.exiting(this.getClass().getName(), "importCertificateReply"); //$NON-NLS-1$ + } + + /** + * If the reply is a single X.509 certificate, keytool attempts to establish a + * trust chain, starting at the certificate reply and ending at a self-signed + * certificate (belonging to a root CA). The certificate reply and the + * hierarchy of certificates used to authenticate the certificate reply form + * the new certificate chain of alias. If a trust chain cannot be established, + * the certificate reply is not imported. In this case, keytool does not print + * out the certificate, nor does it prompt the user to verify it. This is + * because it is very hard (if not impossible) for a user to determine the + * authenticity of the certificate reply. + * + * @param certificate the certificate reply to import into the key store. + * @throws NoSuchAlgorithmException + * @throws CertPathValidatorException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws KeyStoreException + * @throws CertificateException + */ + private void importCertificate(Certificate certificate) + throws NoSuchAlgorithmException, CertPathValidatorException, + KeyStoreException, UnrecoverableKeyException, IOException, + UnsupportedCallbackException, CertificateException + { + log.entering(this.getClass().getName(), "importCertificate", certificate); //$NON-NLS-1$ + + LinkedList reply = new LinkedList(); + reply.addLast(certificate); + + if (! findTrustAndUpdate(reply, false)) + throw new CertPathValidatorException(Messages.getString("ImportCmd.34")); //$NON-NLS-1$ + + Certificate[] newChain = (Certificate[]) reply.toArray(new Certificate[0]); + Key privateKey = getAliasPrivateKey(); + store.setKeyEntry(alias, privateKey, keyPasswordChars, newChain); + saveKeyStore(); + + log.exiting(this.getClass().getName(), "importCertificate"); //$NON-NLS-1$ + } + + /** + * If the reply is a PKCS#7 formatted certificate chain, the chain is first + * ordered (with the user certificate first and the self-signed root CA + * certificate last), before keytool attempts to match the root CA certificate + * provided in the reply with any of the trusted certificates in the key store + * or the "cacerts" keystore file (if the -trustcacerts option was specified). + * If no match can be found, the information of the root CA certificate is + * printed out, and the user is prompted to verify it, e.g., by comparing the + * displayed certificate fingerprints with the fingerprints obtained from some + * other (trusted) source of information, which might be the root CA itself. + * The user then has the option of aborting the import operation. If the + * -noprompt option is given, however, there will be no interaction with the + * user. + * + * @param chain the collection of certificates parsed from the user + * designated input. + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws KeyStoreException + * @throws CertPathValidatorException + * @throws NoSuchAlgorithmException + * @throws CertificateException + */ + private void importChain(Collection chain) throws NoSuchAlgorithmException, + CertPathValidatorException, KeyStoreException, UnrecoverableKeyException, + IOException, UnsupportedCallbackException, CertificateException + { + log.entering(this.getClass().getName(), "importChain", chain); //$NON-NLS-1$ + + LinkedList reply = orderChain(chain); + if (findTrustAndUpdate(reply, ! noPrompt)) + { + Certificate[] newChain = (Certificate[]) reply.toArray(new Certificate[0]); + Key privateKey = getAliasPrivateKey(); + store.setKeyEntry(alias, privateKey, keyPasswordChars, newChain); + saveKeyStore(); + } + + log.exiting(this.getClass().getName(), "importChain"); //$NON-NLS-1$ + } + + /** + * Check to ensure that alias's public key is the subject of the first + * certificate in the passed certificate collection. Throws an exception if + * the public keys do not match. + * + * @param certificates a {@link Collection} of certificate replies (either a + * signle certificate reply, or a PKCS#7 certificate reply chain) + * usually sent by a CA as a response to a Certificate Signing + * Request (CSR). + * @throws IOException + * @throws UnsupportedCallbackException + * @throws KeyStoreException + */ + private void ensureReplyIsOurs(Collection certificates) throws IOException, + UnsupportedCallbackException, KeyStoreException + { + log.entering(this.getClass().getName(), "ensureReplyIsOurs"); //$NON-NLS-1$ + + Certificate certificate = (Certificate) certificates.iterator().next(); + log.finest("certificate = " + certificate); //$NON-NLS-1$ + Certificate[] chain = store.getCertificateChain(alias); + if (chain == null) + throw new IllegalArgumentException(Messages.getFormattedString("ImportCmd.37", //$NON-NLS-1$ + alias)); + Certificate anchor = chain[0]; + PublicKey anchorPublicKey = anchor.getPublicKey(); + PublicKey certPublicKey = certificate.getPublicKey(); + boolean sameKey; + if (anchorPublicKey instanceof DSAPublicKey) + { + DSAPublicKey pk1 = (DSAPublicKey) anchorPublicKey; + if (!(certPublicKey instanceof DSAPublicKey)) + throw new IllegalArgumentException(Messages.getString("ImportCmd.38")); //$NON-NLS-1$ + + sameKey = areEqual(pk1, (DSAPublicKey) certPublicKey); + } + else if (anchorPublicKey instanceof RSAPublicKey) + { + RSAPublicKey pk1 = (RSAPublicKey) anchorPublicKey; + if (!(certPublicKey instanceof RSAPublicKey)) + throw new IllegalArgumentException(Messages.getString("ImportCmd.38")); //$NON-NLS-1$ + + sameKey = areEqual(pk1, (RSAPublicKey) certPublicKey); + } + else + throw new IllegalArgumentException( + Messages.getFormattedString("ImportCmd.40", //$NON-NLS-1$ + new String[] { alias, + anchorPublicKey.getClass().getName() })); + if (! sameKey) + throw new IllegalArgumentException(Messages.getString("ImportCmd.41")); //$NON-NLS-1$ + + log.exiting(this.getClass().getName(), "ensureReplyIsOurs"); //$NON-NLS-1$ + } + + private boolean areEqual(DSAPublicKey pk1, DSAPublicKey pk2) + { + if (pk1.getY().compareTo(pk2.getY()) != 0) + return false; + + DSAParams p1 = pk1.getParams(); + DSAParams p2 = pk2.getParams(); + if (p1.getG().compareTo(p2.getG()) != 0) + return false; + + if (p1.getP().compareTo(p2.getP()) != 0) + return false; + + return p1.getQ().compareTo(p2.getQ()) == 0; + } + + private boolean areEqual(RSAPublicKey pk1, RSAPublicKey pk2) + { + if (pk1.getPublicExponent().compareTo(pk2.getPublicExponent()) != 0) + return false; + + return pk1.getModulus().compareTo(pk2.getModulus()) == 0; + } + + /** + * @param chain + * @return the input collection, ordered with own certificate first, and CA's + * self-signed certificate last. + */ + private LinkedList orderChain(Collection chain) + { + log.entering(this.getClass().getName(), "orderChain"); //$NON-NLS-1$ + + LinkedList result = new LinkedList(); + + + log.entering(this.getClass().getName(), "orderChain", result); //$NON-NLS-1$ + return result; + } + + /** + * Given an ordered list of certificates, this method attempts to validate the + * chain, and if successful, updates the key store entry for the designated + * alias. The list of certificates is expected to be ordered as a chain, where + * the first is the alias's own certificate and the last being a self-signed + * CA certificate. + * <p> + * if <code>promptUser</code> is <code>true</code>, then even if no + * anchor trust certificate is found, the user is prompted to approve, or not, + * the import operation. On the other hand if the <code>promptUser</code> + * parameter is <code>false</code> then this method will throw an exception + * if no trust anchor is to be found. + * + * @param reply an ordered certificate path, where the last entry is the CA's + * self-signed certificate. + * @param promptUser a boolean flag indicating whether or not to prompt the + * user for explicit trust in a CA certificate. + * @return <code>true</code> if the validation succeeds; or <code>false</code> + * otherwise. + * @throws NoSuchAlgorithmException + * @throws CertPathValidatorException + * @throws UnsupportedCallbackException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws KeyStoreException + * @throws CertificateEncodingException + */ + private boolean findTrustAndUpdate(LinkedList reply, boolean promptUser) + throws IOException, NoSuchAlgorithmException, CertPathValidatorException, + KeyStoreException, UnrecoverableKeyException, UnsupportedCallbackException, + CertificateEncodingException + { + log.entering(this.getClass().getName(), "findTrustAndUpdate"); //$NON-NLS-1$ + + X509CertPath certPath = new X509CertPath(reply); + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); //$NON-NLS-1$ + PKIXCertPathValidatorResult cpvr = findTrustInStore(certPath, validator); + if (cpvr == null && trustCACerts) + cpvr = findTrustInCACerts(certPath, validator); + + boolean result = false; + if (cpvr == null) + { + if (promptUser) + { + printVerbose((Certificate) reply.getLast()); + ConfirmationCallback ccb; + ccb = new ConfirmationCallback(Messages.getString("ImportCmd.32"), //$NON-NLS-1$ + ConfirmationCallback.INFORMATION, + ConfirmationCallback.YES_NO_OPTION, + ConfirmationCallback.NO); + getCallbackHandler().handle(new Callback[] { ccb }); + int answer = ccb.getSelectedIndex(); + result = answer == ConfirmationCallback.YES; + } + } + else + { + log.fine("Found a chain-of-trust anchored by " + cpvr.getTrustAnchor()); //$NON-NLS-1$ + Certificate trustedCert = cpvr.getTrustAnchor().getTrustedCert(); + reply.addLast(trustedCert); + result = true; + } + + log.entering(this.getClass().getName(), "findTrustAndUpdate", //$NON-NLS-1$ + Boolean.valueOf(result)); + return result; + } + + private PKIXCertPathValidatorResult findTrustInStore(X509CertPath certPath, + CertPathValidator validator) + { + log.entering(this.getClass().getName(), "findTrustInStore"); //$NON-NLS-1$ + + PKIXCertPathValidatorResult result; + try + { + PKIXParameters params = new PKIXParameters(store); + result = (PKIXCertPathValidatorResult) validator.validate(certPath, params); + } + catch (Exception x) + { + log.log(Level.FINE, + "Exception in findTrustInStore(). Ignore + Return NULL", //$NON-NLS-1$ + x); + result = null; + } + + log.exiting(this.getClass().getName(), "findTrustInStore", result); //$NON-NLS-1$ + return result; + } + + private PKIXCertPathValidatorResult findTrustInCACerts(X509CertPath certPath, + CertPathValidator validator) + { + log.entering(this.getClass().getName(), "findTrustInCACerts"); //$NON-NLS-1$ + + FileInputStream stream = null; + PKIXCertPathValidatorResult result = null; + try + { + KeyStore cacerts = KeyStore.getInstance("jks"); //$NON-NLS-1$ + String cacertsPath = SystemProperties.getProperty("java.home"); + String fs = SystemProperties.getProperty("file.separator"); //$NON-NLS-1$ + cacertsPath = new StringBuilder(cacertsPath).append(fs) + .append("lib").append(fs) //$NON-NLS-1$ + .append("security").append(fs) //$NON-NLS-1$ + .append("cacerts").toString(); //$NON-NLS-1$ + stream = new FileInputStream(cacertsPath); + cacerts.load(stream, "changeit".toCharArray()); //$NON-NLS-1$ + PKIXParameters params = new PKIXParameters(cacerts); + result = (PKIXCertPathValidatorResult) validator.validate(certPath, + params); + } + catch (Exception x) + { + log.log(Level.FINE, + "Exception in findTrustInCACerts(). Ignore + Return NULL", //$NON-NLS-1$ + x); + } + finally + { + if (stream != null) + try + { + stream.close(); + } + catch (Exception ignored) + { + } + } + + log.exiting(this.getClass().getName(), "findTrustInCACerts", result); //$NON-NLS-1$ + return result; + } +} diff --git a/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java b/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java new file mode 100644 index 000000000..5936719f7 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java @@ -0,0 +1,344 @@ +/* KeyCloneCmd.java -- The keyclone command handler of the keytool + 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 java.io.IOException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.logging.Logger; + +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.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-keyclone</b> keytool command handler is used to clone an existing + * key store entry associated with a designated alias, with its private key and + * chain of certificates. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-dest ALIAS</dt> + * <dd>Use this option to specify the new <i>Alias</i> which will be used + * to identify the cloned copy of the <i>Key Entry</i>. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * unlock the <i>Key Entry</i> associated with the designated <i>Alias</i>. + * <p> + * If this option is omitted, the tool will first attempt to unlock the + * <i>Key Entry</i> using the same password protecting the key store. If + * this fails, you will then be prompted to provide a password. + * <p></dd> + * + * <dt>-new PASSWORD</dt> + * <dd>Use this option to specify the password protecting the private key + * material of the newly cloned copy of the <i>Key Entry</i>. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class KeyCloneCmd extends Command +{ + private static final Logger log = Logger.getLogger(KeyCloneCmd.class.getName()); + private String _alias; + private String _destAlias; + private String _password; + private String _newPassword; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private String destinationAlias; + private char[] newKeyPasswordChars; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the existing alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param alias the new alias to use. */ + public void setDest(String alias) + { + this._destAlias = alias; + } + + /** @param password the existing (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param password the new (private) key password to use. */ + public void setNew(String password) + { + this._newPassword = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-dest".equals(opt)) // -dest ALIAS //$NON-NLS-1$ + _destAlias = args[++i]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD //$NON-NLS-1$ + _password = args[++i]; + else if ("-new".equals(opt)) // -new PASSWORD //$NON-NLS-1$ + _newPassword = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); + setDestinationAlias(_destAlias); +// setNewKeyPassword(_newPassword); + + log.finer("-keyclone handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -dest=" + destinationAlias); //$NON-NLS-1$ + log.finer(" -keypass=" + _password); //$NON-NLS-1$ + log.finer(" -new=" + _newPassword); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, UnrecoverableKeyException, + CertificateException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + if (store.containsAlias(destinationAlias)) + throw new SecurityException(Messages.getString("KeyCloneCmd.23")); //$NON-NLS-1$ + + Key privateKey = getAliasPrivateKey(); + + setNewKeyPassword(_newPassword); + Certificate[] chain = store.getCertificateChain(alias); + + store.setKeyEntry(destinationAlias, privateKey, newKeyPasswordChars, chain); + + saveKeyStore(); + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + private void setDestinationAlias(String name) throws IOException, + UnsupportedCallbackException + { + if (name == null || name.trim().length() == 0) // ask user to provide one + { + NameCallback ncb = new NameCallback(Messages.getString("KeyCloneCmd.26")); //$NON-NLS-1$ + getCallbackHandler().handle(new Callback[] { ncb }); + name = ncb.getName(); + if (name == null || name.trim().length() == 0) + throw new IllegalArgumentException(Messages.getString("KeyCloneCmd.27")); //$NON-NLS-1$ + } + + destinationAlias = name.trim(); + } + + private void setNewKeyPassword(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) // ask user to provide one + newKeyPasswordChars = password.toCharArray(); + else + { + boolean ok = false; + Callback[] prompts = new Callback[1]; + Callback[] errors = new Callback[1]; + for (int i = 0; i < 3; i++) + if (prompt4NewPassword(getCallbackHandler(), prompts, errors)) + { + ok = true; + break; + } + if (! ok) + throw new SecurityException(Messages.getString("StorePasswdCmd.19")); //$NON-NLS-1$ + } + } + + private boolean prompt4NewPassword(CallbackHandler handler, + Callback[] prompts, Callback[] errors) + throws IOException, UnsupportedCallbackException + { + String p = Messages.getFormattedString("KeyCloneCmd.28", //$NON-NLS-1$ + new String[] { destinationAlias, + String.valueOf(keyPasswordChars) }); + PasswordCallback pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd1 = pcb.getPassword(); + pcb.clearPassword(); + if (pwd1 == null || pwd1.length == 0) + { + newKeyPasswordChars = (char[]) keyPasswordChars.clone(); + return true; + } + + if (pwd1.length < 6) + { + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, + Messages.getString("StorePasswdCmd.21")); //$NON-NLS-1$ + handler.handle(errors); + return false; + } + + newKeyPasswordChars = pwd1; + return true; + } +} diff --git a/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java b/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java new file mode 100644 index 000000000..9dc7b8164 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java @@ -0,0 +1,339 @@ +/* KeyPasswdCmd.java -- The keypasswd command handler of the keytool + 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.SystemProperties; + +import java.io.IOException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-keypasswd</b> keytool command handler is used to change the password + * protecting the private key associated to a designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-keypass PASSWORD</dt> + * <dd>Use this option to specify the password which the tool will use to + * unlock the <i>Key Entry</i> associated with the designated <i>Alias</i>. + * <p> + * If this option is omitted, the tool will first attempt to unlock the + * <i>Key Entry</i> using the same password protecting the key store. If + * this fails, you will then be prompted to provide a password. + * <p></dd> + * + * <dt>-new PASSWORD</dt> + * <dd>The new, and different, password which will be used to protect the + * private key material of the designated Key Entry. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class KeyPasswdCmd extends Command +{ + private static final Logger log = Logger.getLogger(KeyPasswdCmd.class.getName()); + private String _alias; + private String _password; + private String _newPassword; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private char[] newPasswordChars; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param password the existing (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param password the new (private) key password to use. */ + public void setNew(String password) + { + this._newPassword = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD //$NON-NLS-1$ + _password = args[++i]; + else if ("-new".equals(opt)) // -new PASSWORD //$NON-NLS-1$ + _newPassword = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); +// setNewKeyPassword(_newPassword); + + log.finer("-keypasswd handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -keypass=" + _password); //$NON-NLS-1$ + log.finer(" -new=" + _newPassword); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, IOException, + UnsupportedCallbackException, UnrecoverableKeyException, + CertificateException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + // 1. get the key entry and certificate chain associated to alias + Key privateKey = getAliasPrivateKey(); + Certificate[] chain = store.getCertificateChain(alias); + + // 2. replace the old entry + setNewKeyPassword(_newPassword); + store.setKeyEntry(alias, privateKey, newPasswordChars, chain); + + // 3. persist the key store + saveKeyStore(); + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /** + * Set the new password to use for protecting Alias's private key. + * + * @param password the new key password. if <code>null</code> prompt the + * user to provide one. When prompting, the password is entered twice + * and compared for a match. + * @throws IOException if an I/O related exception occurs during the process. + * @throws UnsupportedCallbackException if no implementation of a password + * callback handler was found. + */ + private void setNewKeyPassword(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) + newPasswordChars = password.toCharArray(); + else + { + boolean ok = false; + Callback[] prompts = new Callback[1]; + Callback[] errors = new Callback[1]; + for (int i = 0; i < 3; i++) + if (prompt4NewPassword(getCallbackHandler(), prompts, errors)) + { + ok = true; + break; + } + if (! ok) + throw new SecurityException(Messages.getString("StorePasswdCmd.19")); //$NON-NLS-1$ + } + } + + private boolean prompt4NewPassword(CallbackHandler handler, + Callback[] prompts, Callback[] errors) + throws IOException, UnsupportedCallbackException + { + // prompt user (1st time) to provide one + String p = Messages.getFormattedString("KeyPasswdCmd.24", alias); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd1 = pcb.getPassword(); + pcb.clearPassword(); + String ls = SystemProperties.getProperty("line.separator"); //$NON-NLS-1$ + if (pwd1 == null || pwd1.length < 6) + { + String m = Messages.getString("StorePasswdCmd.21") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + if (Arrays.equals(keyPasswordChars, pwd1)) + { + String m = Messages.getString("StorePasswdCmd.22") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + // prompt user (2nd time) for confirmation + p = Messages.getFormattedString("KeyPasswdCmd.28", alias); //$NON-NLS-1$ + pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd2 = pcb.getPassword(); + pcb.clearPassword(); + if (! Arrays.equals(pwd1, pwd2)) + { + String m = Messages.getString("StorePasswdCmd.24") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + newPasswordChars = pwd2; + return true; + } +} diff --git a/tools/gnu/classpath/tools/keytool/ListCmd.java b/tools/gnu/classpath/tools/keytool/ListCmd.java new file mode 100644 index 000000000..655242785 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/ListCmd.java @@ -0,0 +1,380 @@ +/* ListCmd.java -- The list command handler of the keytool + 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.java.security.util.Base64; + +import java.io.IOException; +import java.io.PrintWriter; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.util.Enumeration; +import java.util.logging.Logger; + +/** + * The <b>-list</b> keytool command handler is used to output one or all key + * store entries. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-rfc</dt> + * <dd>Use RFC-1421 specifications when encoding the output. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Output the certificate in human-readable format. If both this option + * and the <code>-rfc</code> option are detected on the command line, the + * tool will opt for the human-readable form and will not abort the + * command.</dd> + * </dl> + */ +class ListCmd extends Command +{ + private static final Logger log = Logger.getLogger(ListCmd.class.getName()); + private String _alias; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private boolean rfc; + private boolean all; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + /** + * @param flag whether to use, or not, RFC-1421 format when listing the + * certificate(s). + */ + public void setRfc(String flag) + { + this.rfc = Boolean.valueOf(flag).booleanValue(); + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$ + _alias = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else if ("-rfc".equals(opt)) //$NON-NLS-1$ + rfc = true; + else + break; + } + + all = _alias == null; + + return i; + } + + void setup() throws Exception + { + setOutputStreamParam(null); // use stdout + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + if (! all) + setAliasParam(_alias); + + if (verbose & rfc) + { + log.warning("Both -v and -rfc options were found on the command line. " //$NON-NLS-1$ + + "Only the former will be considered"); //$NON-NLS-1$ + rfc = false; + } + + log.finer("-list handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -alias=" + alias); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + log.finer(" -rfc=" + rfc); //$NON-NLS-1$ + } + + void start() throws KeyStoreException, CertificateEncodingException, + IOException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + PrintWriter writer = new PrintWriter(outStream, true); + writer.println(Messages.getFormattedString("ListCmd.21", store.getType())); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("ListCmd.22", //$NON-NLS-1$ + store.getProvider().getName())); + if (all) + { + log.finest("About to list all aliases in key store..."); //$NON-NLS-1$ + writer.println(); + writer.println(Messages.getFormattedString("ListCmd.24", //$NON-NLS-1$ + Integer.valueOf(store.size()))); + for (Enumeration e = store.aliases(); e.hasMoreElements(); ) + { + String anAlias = (String) e.nextElement(); + if (anAlias != null) + list1Alias(anAlias, writer); + } + } + else + list1Alias(alias, writer); + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + /** + * Prints the certificate(s) associated with the designated alias. + * + * @param anAlias a non-null string denoting an alias in the key-store. + * @param writer where to print. + * @throws KeyStoreException if an exception occurs while obtaining the + * certificate associated to the designated alias. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form of the certificate. + * @throws IOException if an I/O related exception occurs during the process. + */ + private void list1Alias(String anAlias, PrintWriter writer) + throws KeyStoreException, CertificateEncodingException, IOException + { + log.entering(this.getClass().getName(), "list1Alias", anAlias); //$NON-NLS-1$ + + writer.println(); + writer.println(Messages.getFormattedString("ListCmd.30", anAlias)); //$NON-NLS-1$ + writer.println(Messages.getFormattedString("ListCmd.31", store.getCreationDate(anAlias))); //$NON-NLS-1$ + + if (store.isCertificateEntry(anAlias)) + { + writer.println(Messages.getString("ListCmd.32")); //$NON-NLS-1$ + Certificate certificate = store.getCertificate(anAlias); + print1Certificate(certificate, writer); + } + else if (store.isKeyEntry(anAlias)) + { + writer.println(Messages.getString("ListCmd.33")); //$NON-NLS-1$ + Certificate[] chain = store.getCertificateChain(anAlias); + print1Chain(chain, writer); + } + else + throw new IllegalArgumentException(Messages.getFormattedString("ListCmd.34", //$NON-NLS-1$ + anAlias)); + log.exiting(this.getClass().getName(), "list1Alias"); //$NON-NLS-1$ + } + + /** + * Prints the designated certificate chain, or a fingerprint of the first + * certificate (bottom) in the chain, depending on the values of the flags + * <code>v</code> (for verbose) and <code>rfc</code>. + * <p> + * If both flags are <code>false</code>, only the fingerprint is generated, + * otherwise, if the <code>v</code> flag is set, then a human readable output + * is generated. If <code>rfc</code> is set, then an RFC-1421 like output + * is generated. + * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be + * <code>true</code> at the same time. + * + * @param chain the certificate chain to process. + * @param writer where to print. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form of the certificate. + */ + private void print1Chain(Certificate[] chain, PrintWriter writer) + throws CertificateEncodingException + { + if (!verbose && !rfc) + fingerprint(chain[0], writer); + else + { + int limit = chain.length; + writer.println(Messages.getFormattedString("ListCmd.38", //$NON-NLS-1$ + Integer.valueOf(limit))); + writer.println(Messages.getString("ListCmd.39")); //$NON-NLS-1$ + print1Certificate(chain[0], writer); + for (int i = 1; i < limit; i++) + { + writer.println(); + writer.println(Messages.getFormattedString("ListCmd.40", //$NON-NLS-1$ + Integer.valueOf(i + 1))); + print1Certificate(chain[i], writer); + } + writer.println(); + writer.println(Messages.getString("ListCmd.42")); //$NON-NLS-1$ + } + } + + /** + * Prints the designated certificate, or its fingerprint, depending on the + * values of the flags <code>v</code> (for verbose) and <code>rfc</code>. + * <p> + * If both flags are <code>false</code>, only a fingerprint is generated, + * otherwise, if the <code>v</code> flag is set, then a human readable output + * is generated. If <code>rfc</code> is set, then an RFC-1421 like output + * is generated. + * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be + * <code>true</code> at the same time. + * + * @param certificate the certificate to process. + * @param writer where to print. + * @throws CertificateEncodingException if an exception occurs while obtaining + * the DER encoded form of the certificate. + */ + private void print1Certificate(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + if (verbose) + printVerbose(certificate, writer); + else if (rfc) + printRFC1421(certificate, writer); + else + fingerprint(certificate, writer); + } + + private void printRFC1421(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + byte[] derBytes = certificate.getEncoded(); + String encoded = Base64.encode(derBytes, 0, derBytes.length, true); + writer.println(Messages.getString("ListCmd.43")); //$NON-NLS-1$ + writer.println(encoded); + writer.println(Messages.getString("ListCmd.44")); //$NON-NLS-1$ + } + + private void fingerprint(Certificate certificate, PrintWriter writer) + throws CertificateEncodingException + { + byte[] derBytes = certificate.getEncoded(); + String fingerPrint = digestWithMD5(derBytes); + writer.println(Messages.getFormattedString("ListCmd.45", fingerPrint)); //$NON-NLS-1$ + } +} diff --git a/tools/gnu/classpath/tools/keytool/Main.java b/tools/gnu/classpath/tools/keytool/Main.java new file mode 100644 index 000000000..fb7aa4509 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/Main.java @@ -0,0 +1,219 @@ +/* Main.java -- Implementation of the keytool security tool + 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.tools.HelpPrinter; +import gnu.classpath.tools.common.ProviderUtil; +import gnu.java.security.Registry; +import gnu.javax.crypto.jce.GnuCrypto; +import gnu.javax.security.auth.callback.GnuCallbacks; + +import java.util.logging.Logger; + +/** + * The GNU Classpath implementation of the keytool security tool. + * <p> + * Except for the <code>-identitydb</code> command, available for importing + * JDK 1.1 <i>identities</i> into a key store, this implementation is intended + * to be compatible with the behaviour described in the public documentation of + * the same tool included in JDK 1.4. + */ +public class Main +{ + private static final Logger log = Logger.getLogger(Main.class.getName()); + /** The relative file path to the command tool's help text. */ + private static final String HELP_PATH = "keytool/keytool.txt"; //$NON-NLS-1$ + /** The Preferences key name for the last issued certificate serial nbr. */ + static final String LAST_SERIAL_NUMBER = "lastSerialNumber"; //$NON-NLS-1$ + /** Constant denoting the X.509 certificate type. */ + static final String X_509 = "X.509"; //$NON-NLS-1$ + + /** Whether we have already printed the help text or not. */ + private boolean helpPrinted; + /** The new position of GnuCRYPTO provider if it is not already installed. */ + private int gnuCryptoProviderNdx = -2; + /** The new position of GNU Callbacks provider if it is not already installed. */ + private int gnuCallbacksNdx = -2; + + private Main() + { + super(); + } + + public static final void main(String[] args) + { + log.entering(Main.class.getName(), "main", args); //$NON-NLS-1$ + + Main tool = new Main(); + try + { + tool.setup(); + tool.start(args); + } + catch (SecurityException x) + { + log.throwing(Main.class.getName(), "main", x); //$NON-NLS-1$ + System.err.println(Messages.getString("Main.6") + x.getMessage()); //$NON-NLS-1$ + } + catch (Exception x) + { + log.throwing(Main.class.getName(), "main", x); //$NON-NLS-1$ + System.err.println(Messages.getString("Main.8") + x); //$NON-NLS-1$ + } + finally + { + tool.teardown(); + } + + log.exiting(Main.class.getName(), "main"); //$NON-NLS-1$ + // System.exit(0); + } + + // helper methods ----------------------------------------------------------- + + private void start(String[] args) throws Exception + { + log.entering(this.getClass().getName(), "start", args); //$NON-NLS-1$ + + if (args == null) + args = new String[0]; + + int limit = args.length; + log.finest("args.length=" + limit); //$NON-NLS-1$ + int i = 0; + String opt; + Command cmd; + while (i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + cmd = null; + if ("-genkey".equals(opt)) //$NON-NLS-1$ + cmd = new GenKeyCmd(); + else if ("-import".equals(opt)) //$NON-NLS-1$ + cmd = new ImportCmd(); + else if ("-selfcert".equals(opt)) //$NON-NLS-1$ + cmd = new SelfCertCmd(); + else if ("-identitydb".equals(opt)) //$NON-NLS-1$ + cmd = new IdentityDBCmd(); + else if ("-certreq".equals(opt)) //$NON-NLS-1$ + cmd = new CertReqCmd(); + else if ("-export".equals(opt)) //$NON-NLS-1$ + cmd = new ExportCmd(); + else if ("-list".equals(opt)) //$NON-NLS-1$ + cmd = new ListCmd(); + else if ("-printcert".equals(opt)) //$NON-NLS-1$ + cmd = new PrintCertCmd(); + else if ("-keyclone".equals(opt)) //$NON-NLS-1$ + cmd = new KeyCloneCmd(); + else if ("-storepasswd".equals(opt)) //$NON-NLS-1$ + cmd = new StorePasswdCmd(); + else if ("-keypasswd".equals(opt)) //$NON-NLS-1$ + cmd = new KeyPasswdCmd(); + else if ("-delete".equals(opt)) //$NON-NLS-1$ + cmd = new DeleteCmd(); + else if ("-help".equals(opt)) //$NON-NLS-1$ + { + printHelp(); + i++; + } + else + { + log.fine("Unknown command [" + opt + "] at index #" + i //$NON-NLS-1$ //$NON-NLS-2$ + + ". Arguments from that token onward will be ignored"); //$NON-NLS-1$ + break; + } + + if (cmd != null) + { + i = cmd.processArgs(args, i); + cmd.doCommand(); + } + } + + // the -help command is the default; i.e. + // keytool + // is equivalent to: + // keytool -help + if (i == 0) + printHelp(); + + if (i < limit) // more options than needed + log.fine("Last recognized argument is assumed at index #" + (i - 1) //$NON-NLS-1$ + + ". Remaining arguments (" + args[i] + "...) will be ignored"); //$NON-NLS-1$ //$NON-NLS-2$ + + log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$ + } + + private void setup() + { + log.entering(this.getClass().getName(), "setup"); //$NON-NLS-1$ + + gnuCryptoProviderNdx = ProviderUtil.addProvider(new GnuCrypto()); + gnuCallbacksNdx = ProviderUtil.addProvider(new GnuCallbacks()); + + log.exiting(this.getClass().getName(), "setup"); //$NON-NLS-1$ + } + + private void teardown() + { + log.entering(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + + // if we added our own providers remove them + if (gnuCryptoProviderNdx > 0) + ProviderUtil.removeProvider(Registry.GNU_CRYPTO); + + if (gnuCallbacksNdx > 0) + ProviderUtil.removeProvider("GNU-CALLBACKS"); //$NON-NLS-1$ + + log.exiting(this.getClass().getName(), "teardown"); //$NON-NLS-1$ + } + + private void printHelp() + { + if (helpPrinted) + return; + + HelpPrinter.printHelp(HELP_PATH); + helpPrinted = true; + } +} diff --git a/tools/gnu/classpath/tools/keytool/Messages.java b/tools/gnu/classpath/tools/keytool/Messages.java new file mode 100644 index 000000000..e3308e021 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/Messages.java @@ -0,0 +1,115 @@ +/* Messages.java -- I18N related helper class + 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 java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +/** + * An initially generated Eclipse helper class to ease the use of localized + * messages. + * <p> + * Enriched to handle localized message formats. + */ +class Messages +{ + private static final Logger log = Logger.getLogger(Messages.class.getName()); + private static final String BUNDLE_NAME = "gnu.classpath.tools.keytool.MessageBundle"; //$NON-NLS-1$ + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + private static final Map CACHED_FORMATS = new HashMap(5); + + private Messages() + { + super(); + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return constructMessage(key, null); + } + } + + public static String getFormattedString(String key, Object args) + { + MessageFormat mf = (MessageFormat) CACHED_FORMATS.get(key); + if (mf == null) + { + String formatString = getString(key); + if (formatString.startsWith("!")) + return constructMessage(key, args); + + mf = new MessageFormat(formatString); + CACHED_FORMATS.put(key, mf); + } + + // if the argument is not an array, then build one consisiting of the + // sole argument before passing it to the format() method + try + { + if (args instanceof Object[]) + return mf.format(args); + + return mf.format(new Object[] { args }); + } + catch (IllegalArgumentException x) + { + log.fine("Exception while rendering a message format keyed by [" + + key + "]: " + mf.toPattern()); + return constructMessage(mf.toPattern(), args); + } + } + + private static final String constructMessage(String m, Object args) + { + if (args == null) + return '!' + m + '!'; + + return '!' + m + '!' + String.valueOf(args) + '!'; + } +} diff --git a/tools/gnu/classpath/tools/keytool/PrintCertCmd.java b/tools/gnu/classpath/tools/keytool/PrintCertCmd.java new file mode 100644 index 000000000..9ba1d5970 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/PrintCertCmd.java @@ -0,0 +1,123 @@ +/* PrintCertCmd.java -- The printcert command handler of the keytool + 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 java.io.PrintWriter; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.logging.Logger; + +/** + * The <b>-printcert</b> keytool command handler is used to read a certificate + * from a designated file, and print its contents in a human-readable format. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-file FILE_NAME</dt> + * <dd>The fully qualified path of the file to read the certificate from. + * If this option is omitted, the tool will process STDIN. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class PrintCertCmd extends Command +{ + private static final Logger log = Logger.getLogger(PrintCertCmd.class.getName()); + private String _certFileName; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param pathName the fully qualified path name of the file to process. */ + public void setFile(String pathName) + { + this._certFileName = pathName; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); + if (opt == null || opt.length() == 0) + continue; + + if ("-file".equals(opt)) // -file FILE_NAME + _certFileName = args[++i]; + else if ("-v".equals(opt)) + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setInputStreamParam(_certFileName); + + log.finer("-printcert handler will use the following options:"); + log.finer(" -file=" + _certFileName); + log.finer(" -v=" + verbose); + } + + void start() throws CertificateException + { + log.entering(getClass().getName(), "start"); + + CertificateFactory x509Factory = CertificateFactory.getInstance(Main.X_509); + Certificate certificate = x509Factory.generateCertificate(inStream); + PrintWriter writer = new PrintWriter(System.out, true); + writer.println(); + printVerbose(certificate, writer); + + log.exiting(getClass().getName(), "start"); + } +} diff --git a/tools/gnu/classpath/tools/keytool/SelfCertCmd.java b/tools/gnu/classpath/tools/keytool/SelfCertCmd.java new file mode 100644 index 000000000..db7d45994 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/SelfCertCmd.java @@ -0,0 +1,371 @@ +/* SelfCertCmd.java -- The selfcert command handler of the keytool + 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.java.security.x509.X500DistinguishedName; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.logging.Logger; + +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.x500.X500Principal; + +/** + * The <b>-selfcert</b> keytool command handler is used to generate a self- + * signed X.509 version 1 certificate using key store credentials stored under a + * designated alias. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-alias ALIAS</dt> + * <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted + * Certificate</i>, in a key store is uniquely identified by a user-defined + * <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use + * when referring to an entry in the key store. Unless specified otherwise, + * a default value of <code>mykey</code> shall be used when this option is + * omitted from the command line. + * <p></dd> + * + * <dt>-sigalg ALGORITHM</dt> + * <dd>The canonical name of the digital signature algorithm to use for + * signing the certificate. If this option is omitted, a default value will + * be chosen based on the type of the private key associated with the + * designated <i>Alias</i>. If the private key is a <code>DSA</code> one, + * the value for the signature algorithm will be <code>SHA1withDSA</code>. + * If on the other hand the private key is an <code>RSA</code> one, then + * the tool will use <code>MD5withRSA</code> as the signature algorithm. + * <p></dd> + * + * <dt>-dname NAME</dt> + * <dd>Use this option to specify the <i>Distinguished Name</i> of the + * newly generated self-signed certificate. If this option is omitted, the + * existing <i>Distinguished Name</i> of the base certificate in the chain + * associated with the designated <i>Alias</i> will be used instead. + * <p> + * The syntax of a valid value for this option MUST follow RFC-2253 + * specifications. Namely the following components (with their accepted + * meaning) will be recognized. Note that the component name is case- + * insensitive: + * <dl> + * <dt>CN</dt> + * <dd>The Common Name; e.g. "host.domain.com"</dd> + * + * <dt>OU</dt> + * <dd>The Organizational Unit; e.g. "IT Department"</dd> + * + * <dt>O</dt> + * <dd>The Organization Name; e.g. "The Sample Company"</dd> + * + * <dt>L</dt> + * <dd>The Locality Name; e.g. "Sydney"</dd> + * + * <dt>ST</dt> + * <dd>The State Name; e.g. "New South Wales"</dd> + * + * <dt>C</dt> + * <dd>The 2-letter Country identifier; e.g. "AU"</dd> + * </dl> + * <p> + * When specified with a <code>-dname</code> option, each pair of component + * / value will be separated from the other with a comma. Each component + * and value pair MUST be separated by an equal sign. For example, the + * following is a valid DN value: + * <pre> + * CN=host.domain.com, O=The Sample Company, L=Sydney, ST=NSW, C=AU + * </pre> + * <p></dd> + * + * <dt>-validity DAY_COUNT</dt> + * + * <dt>-keypass PASSWORD</dt> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class SelfCertCmd extends Command +{ + private static final Logger log = Logger.getLogger(SelfCertCmd.class.getName()); + private String _alias; + private String _sigAlgorithm; + private String _dName; + private String _password; + private String _validityStr; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private X500DistinguishedName distinguishedName; + private int validityInDays; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param alias the alias to use. */ + public void setAlias(String alias) + { + this._alias = alias; + } + + /** + * @param algorithm the canonical name of the digital signature algorithm to + * use. + */ + public void setSigalg(String algorithm) + { + this._sigAlgorithm = algorithm; + } + + /** + * @param name the distiniguished name of both the issuer and subject (since + * we are dealing with a self-signed certificate) to use. + */ + public void setDname(String name) + { + this._dName = name; + } + + /** + * @param days the string representation of the number of days (a decimal, + * positive integer) to assign to the generated (self-signed) + * certificate. + */ + public void setValidity(String days) + { + this._validityStr = days; + } + + /** @param password the (private) key password to use. */ + public void setKeypass(String password) + { + this._password = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); + if (opt == null || opt.length() == 0) + continue; + + if ("-alias".equals(opt)) // -alias ALIAS + _alias = args[++i]; + else if ("-sigalg".equals(opt)) // -sigalg ALGORITHM + _sigAlgorithm = args[++i]; + else if ("-dname".equals(opt)) // -dname NAME + _dName = args[++i]; + else if ("-keypass".equals(opt)) // -keypass PASSWORD + _password = args[++i]; + else if ("-validity".equals(opt)) // -validity DAY_COUNT + _validityStr = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME + _providerClassName = args[++i]; + else if ("-v".equals(opt)) + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setAliasParam(_alias); + setKeyPasswordNoPrompt(_password); +// setDName(_dName); + setValidityParam(_validityStr); +// setSignatureAlgorithm(_sigAlgorithm); + + log.finer("-selfcert handler will use the following options:"); + log.finer(" -alias=" + alias); + log.finer(" -sigalg=" + _sigAlgorithm); + log.finer(" -dname=" + _dName); + log.finer(" -keypass=" + _password); + log.finer(" -validity=" + validityInDays); + log.finer(" -storetype=" + storeType); + log.finer(" -keystore=" + storeURL); + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); + log.finer(" -provider=" + provider); + log.finer(" -v=" + verbose); + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException, IOException, UnsupportedCallbackException, + InvalidKeyException, SignatureException, CertificateException + { + log.entering(getClass().getName(), "start"); + + // 1. get the key entry and certificate chain associated to alias + Key privateKey = getAliasPrivateKey(); + Certificate[] chain = store.getCertificateChain(alias); + + // 2. if the user has not supplied a DN use one from the certificate chain + X509Certificate bottomCertificate = (X509Certificate) chain[0]; + X500Principal defaultPrincipal = bottomCertificate.getIssuerX500Principal(); + setDName(_dName, defaultPrincipal); + + // 4. get alias's public key from certificate's SubjectPublicKeyInfo + PublicKey publicKey = bottomCertificate.getPublicKey(); + + // 5. issue the self-signed certificate + setSignatureAlgorithmParam(_sigAlgorithm, privateKey); + + byte[] derBytes = getSelfSignedCertificate(distinguishedName, + publicKey, + (PrivateKey) privateKey); + CertificateFactory x509Factory = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream bais = new ByteArrayInputStream(derBytes); + Certificate certificate = x509Factory.generateCertificate(bais); + + // 6. store it, w/ its private key, associating them to alias + chain = new Certificate[] { certificate }; + store.setKeyEntry(alias, privateKey, keyPasswordChars, chain); + + // 7. persist the key store + saveKeyStore(); + + log.exiting(getClass().getName(), "start"); + } + + // own methods -------------------------------------------------------------- + + private void setDName(String name, X500Principal defaultName) + { + if (name != null && name.trim().length() > 0) + name = name.trim(); + else + { + // If dname is supplied at the command line, it is used as the X.500 + // Distinguished Name for both the issuer and subject of the certificate. + // Otherwise, the X.500 Distinguished Name associated with alias (at the + // bottom of its existing certificate chain) is used. + name = defaultName.toString().trim(); + } + + distinguishedName = new X500DistinguishedName(name); + } +} diff --git a/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java b/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java new file mode 100644 index 000000000..1eb053c1c --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java @@ -0,0 +1,275 @@ +/* StorePasswdCmd.java -- The storepasswd command handler of the keytool + 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.SystemProperties; + +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.logging.Logger; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * The <b>-storepasswd</b> keytool command handler is used to change the + * password which protects the integrity of the key store. + * <p> + * Possible options for this command are: + * <p> + * <dl> + * <dt>-new PASSWORD</dt> + * <dd>The new, and different, password which will be used to protect the + * designated key store. + * <p></dd> + * + * <dt>-storetype STORE_TYP}</dt> + * <dd>Use this option to specify the type of the key store to use. The + * default value, if this option is omitted, is that of the property + * <code>keystore.type</code> in the security properties file, which is + * obtained by invoking the {@link java.security.KeyStore#getDefaultType()} + * static method. + * <p></dd> + * + * <dt>-keystore URL</dt> + * <dd>Use this option to specify the location of the key store to use. + * The default value is a file {@link java.net.URL} referencing the file + * named <code>.keystore</code> located in the path returned by the call to + * {@link java.lang.System#getProperty(String)} using <code>user.home</code> + * as argument. + * <p> + * If a URL was specified, but was found to be malformed --e.g. missing + * protocol element-- the tool will attempt to use the URL value as a file- + * name (with absolute or relative path-name) of a key store --as if the + * protocol was <code>file:</code>. + * <p></dd> + * + * <dt>-storepass PASSWORD</dt> + * <dd>Use this option to specify the password protecting the key store. If + * this option is omitted from the command line, you will be prompted to + * provide a password. + * <p></dd> + * + * <dt>-provider PROVIDER_CLASS_NAME</dt> + * <dd>A fully qualified class name of a Security Provider to add to the + * current list of Security Providers already installed in the JVM in-use. + * If a provider class is specified with this option, and was successfully + * added to the runtime --i.e. it was not already installed-- then the tool + * will attempt to removed this Security Provider before exiting. + * <p></dd> + * + * <dt>-v</dt> + * <dd>Use this option to enable more verbose output.</dd> + * </dl> + */ +class StorePasswdCmd extends Command +{ + private static final Logger log = Logger.getLogger(StorePasswdCmd.class.getName()); + private String _newPassword; + private String _ksType; + private String _ksURL; + private String _ksPassword; + private String _providerClassName; + private char[] newStorePasswordChars; + + // default 0-arguments constructor + + // public setters ----------------------------------------------------------- + + /** @param password the new key-store password to use. */ + public void setNew(String password) + { + this._newPassword = password; + } + + /** @param type the key-store type to use. */ + public void setStoretype(String type) + { + this._ksType = type; + } + + /** @param url the key-store URL to use. */ + public void setKeystore(String url) + { + this._ksURL = url; + } + + /** @param password the key-store password to use. */ + public void setStorepass(String password) + { + this._ksPassword = password; + } + + /** @param className a security provider fully qualified class name to use. */ + public void setProvider(String className) + { + this._providerClassName = className; + } + + // life-cycle methods ------------------------------------------------------- + + int processArgs(String[] args, int i) + { + int limit = args.length; + String opt; + while (++i < limit) + { + opt = args[i]; + log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$ + if (opt == null || opt.length() == 0) + continue; + + if ("-new".equals(opt)) // -new PASSWORD //$NON-NLS-1$ + _newPassword = args[++i]; + else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$ + _ksType = args[++i]; + else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$ + _ksURL = args[++i]; + else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$ + _ksPassword = args[++i]; + else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$ + _providerClassName = args[++i]; + else if ("-v".equals(opt)) //$NON-NLS-1$ + verbose = true; + else + break; + } + + return i; + } + + void setup() throws Exception + { + setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL); + setNewKeystorePassword(_newPassword); + + log.finer("-storepasswd handler will use the following options:"); //$NON-NLS-1$ + log.finer(" -new=" + String.valueOf(newStorePasswordChars)); //$NON-NLS-1$ + log.finer(" -storetype=" + storeType); //$NON-NLS-1$ + log.finer(" -keystore=" + storeURL); //$NON-NLS-1$ + log.finer(" -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$ + log.finer(" -provider=" + provider); //$NON-NLS-1$ + log.finer(" -v=" + verbose); //$NON-NLS-1$ + } + + void start() throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException + { + log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$ + + saveKeyStore(newStorePasswordChars); + + log.exiting(getClass().getName(), "start"); //$NON-NLS-1$ + } + + // own methods -------------------------------------------------------------- + + protected void setNewKeystorePassword(String password) throws IOException, + UnsupportedCallbackException + { + if (password != null) + newStorePasswordChars = password.toCharArray(); + else + { + boolean ok = false; + Callback[] prompts = new Callback[1]; + Callback[] errors = new Callback[1]; + for (int i = 0; i < 3; i++) + if (prompt4NewPassword(getCallbackHandler(), prompts, errors)) + { + ok = true; + break; + } + if (! ok) + throw new SecurityException(Messages.getString("StorePasswdCmd.19")); //$NON-NLS-1$ + } + } + + private boolean prompt4NewPassword(CallbackHandler handler, + Callback[] prompts, Callback[] errors) + throws IOException, UnsupportedCallbackException + { + // prompt user (1st time) to provide one + String p = Messages.getString("StorePasswdCmd.20"); //$NON-NLS-1$ + PasswordCallback pcb = new PasswordCallback(p, false); + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd1 = pcb.getPassword(); + pcb.clearPassword(); + String ls = SystemProperties.getProperty("line.separator"); //$NON-NLS-1$ + if (pwd1 == null || pwd1.length < 6) + { + String m = Messages.getString("StorePasswdCmd.21") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + if (Arrays.equals(storePasswordChars, pwd1)) + { + String m = Messages.getString("StorePasswdCmd.22") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + // prompt user (2nd time) for confirmation + pcb = new PasswordCallback(Messages.getString("StorePasswdCmd.23"), false); //$NON-NLS-1$ + prompts[0] = pcb; + handler.handle(prompts); + char[] pwd2 = pcb.getPassword(); + pcb.clearPassword(); + if (! Arrays.equals(pwd1, pwd2)) + { + String m = Messages.getString("StorePasswdCmd.24") + ls; //$NON-NLS-1$ + errors[0] = new TextOutputCallback(TextOutputCallback.ERROR, m); + handler.handle(errors); + return false; + } + + newStorePasswordChars = pwd2; + return true; + } +} diff --git a/tools/gnu/classpath/tools/keytool/keytool.txt b/tools/gnu/classpath/tools/keytool/keytool.txt new file mode 100644 index 000000000..15f9b96f9 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/keytool.txt @@ -0,0 +1,616 @@ +NAME + keytool - manage private keys and public certificates + +SYNOPSIS + keytool [COMMAND]... + +DESCRIPTION + A Java-based tool for managing both Key Entries as well as Trusted + Certificates. + + Multiple COMMANDs may be specified at once, each complete with its own + options. keytool will parse all the arguments, before processing, and + executing, each COMMAND. If an exception occurs while executing one + COMMAND keytool will abort. + + A COMMAND can be one of the followings: + + -genkey [OPTION]... + Generate a new Key Entry, eventually creating a new key store. + + -import [OPTION]... + Add, to a key store, Key Entries (private keys and certificate + chains authenticating the public keys) and Trusted Certificates + (3rd party certificates which can be used as Trust anchors when + building chains-of-trust). + + -selfcert [OPTION]... + Generate a new self-signed Trusted Certificate. + + -identitydb [OPTION]... + NOT IMPLEMENTED YET. + Import a JDK 1.1 style Identity Database. + + -certreq [OPTION]... + Issue a Certificate Signing Request (CSR) which can be then sent + to a Certification Authority (CA) to issue a certificate signed + (by the CA) and authenticating the Subject of the request. + + -export [OPTION]... + Export a Certificate from a key store. + + -list [OPTION]... + Print one or all Certificates in a key store to STDOUT. + + -printcert [OPTION]... + Print a human-readable form of a Certificate in a designated + file to STDOUT. + + -keyclone [OPTION]... + Clone a Key Entry in a key store. + + -storepasswd [OPTION]... + Change the password protecting a key store. + + -keypasswd [OPTION]... + Change the password protecting a Key Entry in a key store. + + -delete [OPTION]... + Delete a Key Entry or a Trusted Certificate from a key store. + + -help Display this text. + +OPTIONS COMMON TO MORE THAN ONE COMMAND + The following OPTIONs are used in more than one COMMAND. They are + described here to reduce redundancy. + + -alias ALIAS + Every entry, be it a Key Entry or a Trusted Certificate, in a + key store is uniquely identified by a user-defined Alias string. + Use this option to specify the Alias to use when referring to an + entry in the key store. Unless specified otherwise, a default + value of "mykey" (all lower case, without the enclosing quotes) + shall be used when this option is omitted from the command line. + + -keyalg ALGORITHM + Use this option to specify the canonical name of the key-pair + generation algorithm. The default value for this option is + "DSS" (a synonym for the Digital Signature Algorithm also known + as DSA). + + -keysize SIZE + Use this option to specify the number of bits of the shared + modulus (for both the public and private keys) to use when + generating new keys. A default value of 1024 will be used if + this option is omitted from the command line. + + -validity DAY_COUNT + Use this option to specify the number of days a newly generated + certificate will be valid for. The default value is 90 (days) + if this option is omitted from the command line. + + -storetype STORE_TYPE + Use this option to specify the type of the key store to use. + The default value, if this option is omitted, is that of the + property "keystore.type" in the security properties file, which + is obtained by invoking the static method call getDefaultType() + in java.security.KeyStore. + + -storepass PASSWORD + Use this option to specify the password protecting the key + store. If this option is omitted from the command line, you + will be prompted to provide a password. + + -keystore URL + Use this option to specify the location of the key store to use. + The default value is a file URL referencing the file named + ".keystore" (all lower case and without the enclosing quotes) + located in the path returned by the call to + java.lang.System#getProperty(String) using "user.home" as + argument. + + If a URL was specified, but was found to be malformed --e.g. + missing protocol element-- the tool will attempt to use the URL + value as a file-name (with absolute or relative path-name) of a + key store --as if the protocol was "file:". + + -provider PROVIDER_CLASS_NAME + A fully qualified class name of a Security Provider to add to + the current list of Security Providers already installed in the + JVM in-use. If a provider class is specified with this option, + and was successfully added to the runtime --i.e. it was not + already installed-- then the tool will attempt to remove this + Security Provider before exiting. + + -file FILE_NAME + Use this option to designate a file to use with a command. When + specified with this option, the value is expected to be the + fully qualified path of a file accessible by the File System. + Depending on the command, the file may be used as input or as + output. When this option is omitted from the command line, + STDIN will be used instead, as the source of input, and STDOUT + will be used instead as the output destination. + + -v Unless specified otherwise, use this option to enable more + verbose output. + +X.500 DISTINGUISHED NAME + A Distinguished Name (or DN) MUST be supplied with some of the COMMANDs + using a -dname option. The syntax of a valid value for this option MUST + follow RFC-2253 specifications. Namely the following components (with + their accepted meaning) will be recognized. Note that the component + name is case-insensitive: + + CN The Common Name; e.g. "host.domain.com" + OU The Organizational Unit; e.g. "IT Department" + O The Organization Name; e.g. "The Sample Company" + L The Locality Name; e.g. "Sydney" + ST The State Name; e.g. "New South Wales" + C The 2-letter Country identifier; e.g. "AU" + + When specified with a -dname option, each pair of component/value will + be separated from the other with a comma. Each component and value pair + MUST be separated by an equal sign. For example, the following is + a valid DN value: + + CN=host.domain.com, O=The Sample Company, L=Sydney, ST=NSW, C=AU + + If the Distinguished Name is required, and no valid default value can be + used, the tool will prompt you to enter the information through the + console. + +-genkey COMMAND + Generate a new key-pair (both private and public keys), and save these + credentials in the key store as a Key Entry, associated with the + designated (if was specified in the -alias option) or default (if the + -alias option is omitted) Alias. + + The private key material will be protected with a user-defined password + (see -keypass option). The public key on the other hand will be part + of a self-signed X.509 certificate, which will form a 1-element chain + and will be saved in the key store. + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keyalg ALGORITHM + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keysize KEY_SIZE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -sigalg ALGORITHM + The canonical name of the digital signature algorithm to use for + signing certificates. If this option is omitted, a default + value will be chosen based on the type of the key-pair; i.e. the + algorithm that ends up being used by the -keyalg option. If the + key-pair generation algorithm is "DSA", the value for the + signature algorithm will be "SHA1withDSA". If on the other hand + the key-pair generation algorithm is "RSA", then the tool will + use "MD5withRSA" as the signature algorithm. + + -dname NAME + This a mandatory value for the command. If no value is + specified --i.e. the -dname option is omitted-- the tool will + prompt you to enter a Distinguished Name to use as both the + Owner and Issuer of the generated self-signed certificate. + + (see X.500 DISTINGUISHED NAME) + + -keypass PASSWORD + Use this option to specify the password which the tool will use + to protect the newly created Key Entry. + + If this option is omitted, you will be prompted to provide a + password. + + -validity DAY_COUNT + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-import COMMAND + Read an X.509 certificate, or a PKCS#7 Certificate Reply from a + designated input source and incorporate the certificates into the key + store. + + If the Alias does not already exist in the key store, the tool treats + the certificate read from the input source as a new Trusted Certificate. + It then attempts to discover a chain-of-trust, starting from that + certificate and ending at another Trusted Certificate, already stored in + the key store. If the -trustcacerts option is present, an additional + key store, of type "JKS" named "cacerts", and assumed to be present in + ${JAVA_HOME}/lib/security will also be consulted if found --${JAVA_HOME} + refers to the location of an installed Java Runtime Environment (JRE). + If no chain-of-trust can be established, and unless the -noprompt option + has been specified, the certificate is printed to STDOUT and the user is + prompted for a confirmation. + + If Alias exists in the key store, the tool will treat the certificate(s) + read from the input source as a Certificate Reply, which can be a chain + of certificates, that eventually would replace the chain of certificates + associated with the Key Entry of that Alias. The substitution of the + certificates only occurs if a chain-of-trust can be established between + the bottom certificate of the chain read from the input file and the + Trusted Certificates already present in the key store. Again, if the + -trustcacerts option is specified, additional Trusted Certificates in + the same "cacerts" key store will be considered. If no chain-of-trust + can be established, the operation will abort. + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -file FILE_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keypass PASSWORD + Use this option to specify the password which the tool will use + to protect the Key Entry associated with the designated Alias, + when replacing this Alias' chain of certificates with that found + in the certificate reply. + + If this option is omitted, and the chain-of-trust for the + certificate reply has been established, the tool will first + attempt to unlock the Key Entry using the same password + protecting the key store. If this fails, you will then be + prompted to provide a password. + + -noprompt + Use this option to prevent the tool from prompting the user. + + -trustcacerts + Use this option to indicate to the tool that a key store, of + type "JKS", named "cacerts", and usually located in lib/security + in an installed Java Runtime Environment should be considered + when trying to establish chain-of-trusts. + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-selfcert COMMAND + Generate a self-signed X.509 version 1 certificate. The newly generated + certificate will form a chain of one element which will replace the + previous chain associated with the designated Alias (if -alias option + was specified), or the default Alias (if -alias option was omitted). + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -sigalg ALGORITHM + The canonical name of the digital signature algorithm to use for + signing the certificate. If this option is omitted, a default + value will be chosen based on the type of the private key + associated with the designated Alias. If the private key is a + "DSA" one, the value for the signature algorithm will be + "SHA1withDSA". If on the other hand the private key is an "RSA" + one, then the tool will use "MD5withRSA" as the signature + algorithm. + + -dname NAME + Use this option to specify the Distinguished Name of the newly + generated self-signed certificate. If this option is omitted, + the existing Distinguished Name of the base certificate in the + chain associated with the designated Alias will be used instead. + + (see X.500 DISTINGUISHED NAME) + + -validity DAY_COUNT + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keypass PASSWORD + Use this option to specify the password which the tool will use + to unlock the Key Entry associated with the designated Alias. + + If this option is omitted, the tool will first attempt to unlock + the Key Entry using the same password protecting the key store. + If this fails, you will then be prompted to provide a password. + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-identitydb COMMAND + NOT IMPLEMENTED YET. + + Import a JDK 1.1 style Identity Database. + + -file FILE_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-certreq COMMAND + Generate a PKCS#10 Certificate Signing Request (CSR) and writes it to + a designated output destination. The contents of the destination + should look something like the following: + + -----BEGIN NEW CERTIFICATE REQUEST----- + MIICYTCCAiECAQAwXzEUMBIGA1UEAwwLcnNuQGdudS5vcmcxGzAZBgNVBAoMElUg + Q29tcGFueTEPMA0GA1UEBwwGU3lkbmV5MQwwCgYDVQQIDANOU1cxCzAJBgNVBACC + ... + FCTlKlok8KwGuIVwNVOfQLRX+O5kAhQ/a4RTZme2L8PnpvgRwrf7Eg8D6w== + -----END NEW CERTIFICATE REQUEST----- + + IMPORTANT: Some documentation (e.g. RSA examples) claims that the + Attributes field, in the CSR is OPTIONAL while RFC-2986 implies the + opposite. This implementation considers this field, by default, as + OPTIONAL, unless the option -attributes is specified on the command + line. + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -sigalg ALGORITHM + The canonical name of the digital signature algorithm to use for + signing the certificate. If this option is omitted, a default + value will be chosen based on the type of the private key + associated with the designated Alias. If the private key is a + "DSA" one, the value for the signature algorithm will be + "SHA1withDSA". If on the other hand the private key is an "RSA" + one, then the tool will use "MD5withRSA" as the signature + algorithm. + + -file FILE_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keypass PASSWORD + Use this option to specify the password which the tool will use + to unlock the Key Entry associated with the designated Alias. + + If this option is omitted, the tool will first attempt to unlock + the Key Entry using the same password protecting the key store. + If this fails, you will then be prompted to provide a password. + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -attributes + Use this option to force the tool to encode a NULL DER value in + the CSR as the value of the Attributes field. + +-export COMMAND + Export a certificate stored in the key store to a designated output + destination, either in binary format (if the -v option is specified), + or in RFC-1421 compliant encoding (if the -rfc option is specified + instead). + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -file FILE_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -rfc Use RFC-1421 specifications when encoding the output. + + -v Output the certificate in binary DER encoding. This is the + default output format of the command if neither -rfc nor -v + options were detected on the command line. If both this option + and the -rfc option are detected on the command line, the tool + will opt for the RFC-1421 style encoding. + +-list COMMAND + Print one or all of the key store entries to STDOUT. Usually this + command will only print a fingerprint of the certificate, unless either + the -rfc or the -v option is specified. + + -alias ALIAS + If this option is omitted, the tool will print ALL the entries + found in the key store. + + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -rfc Use RFC-1421 specifications when encoding the output. + + -v Output the certificate in human-readable format. If both this + option and the -rfc option are detected on the command line, + the tool will opt for the human-readable form and will not + abort the command. + +-printcert COMMAND + Read a certificate from a designated input source and print it to STDOUT + in a human-readable form. + + -file FILE_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-keyclone COMMAND + Clone an existing Key Entry and store it under a new (different) Alias + protecting, its private key material with possibly a new password. + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -dest ALIAS + Use this option to specify the new Alias which will be used to + identify the cloned copy of the Key Entry. + + -keypass PASSWORD + Use this option to specify the password which the tool will use + to unlock the Key Entry associated with the designated Alias. + + If this option is omitted, the tool will first attempt to unlock + the Key Entry using the same password protecting the key store. + If this fails, you will then be prompted to provide a password. + + -new PASSWORD + Use this option to specify the password protecting the private + key material of the newly cloned copy of the Key Entry. + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-storepasswd COMMAND + Change the password protecting a key store. + + -new PASSWORD + The new, and different, password which will be used to protect + the designated key store. + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-keypasswd COMMAND + Change the password protecting the private key material of a designated + Key Entry. + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keypass PASSWORD + Use this option to specify the password which the tool will use + to unlock the Key Entry associated with the designated Alias. + + If this option is omitted, the tool will first attempt to unlock + the Key Entry using the same password protecting the key store. + If this fails, you will then be prompted to provide a password. + + -new PASSWORD + The new, and different, password which will be used to protect + the private key material of the designated Key Entry. + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +-delete COMMAND + Delete a designated key store entry. + + -alias ALIAS + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storetype STORE_TYPE + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -keystore URL + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -storepass PASSWORD + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -provider PROVIDER_CLASS_NAME + (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + + -v (see OPTIONS COMMON TO MORE THAN ONE COMMAND) + +REPORTING BUGS + Please report bugs at http://www.gnu.org/software/classpath/bugs.html + +COPYRIGHT + Copyright (C) 2006 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is + NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. diff --git a/tools/gnu/classpath/tools/keytool/package.html b/tools/gnu/classpath/tools/keytool/package.html new file mode 100644 index 000000000..c447b8d01 --- /dev/null +++ b/tools/gnu/classpath/tools/keytool/package.html @@ -0,0 +1,65 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.classpath.tools.keytool + +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. --> + +<html> +<head> +<title>GNU Classpath - gnu.classpath.tools.keytool</title> +</head> + +<body> +This package contains the classes that provide an implementation of the +Security Tool: <code>keytool</code>. The behaviour of these classes should +match that of the same tool provided in the RI version 1.4.2, except for the +following: + +<ul> + <li>The RI tool accepts -J<i>javaoption</i> options which it then passes to + the underlying JVM. This is because the RI tool acts as a <i>wrapper</i> + around the JVM launcher. + <p> + This implementation DOES NOT support these options. + </li> + + <li>The RI tool is capable of importing JDK-1.1 style <i>identities</i>. + <p> + This implementation does not offer this feature. + </li> +</ul> +</body> +</html> diff --git a/tools/keytool.sh.in b/tools/keytool.sh.in new file mode 100644 index 000000000..6c11dc407 --- /dev/null +++ b/tools/keytool.sh.in @@ -0,0 +1,63 @@ +#!/bin/sh + +## Copyright (C) 2006 Free Software Foundation, Inc. +## +## This file is a 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 of the License, 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; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, 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. +## +## +## A simple shell script to launch the GNU Classpath keytool tool. +## + +prefix=@prefix@ +tools_dir=@datadir@/@PACKAGE@ +tools_cp=${tools_dir}/tools.zip + +# find the java executable... +if [ -z "${JAVA}" ] ; then + if [ -n "${JAVA_HOME}" ] ; then + if [ -x "${JAVA_HOME}/jre/sh/java" ] ; then + JAVA="${JAVA_HOME}/jre/sh/java" + else + JAVA="${JAVA_HOME}/bin/java" + fi + else + JAVA=`which java 2> /dev/null ` + if [ -z "${JAVA}" ] ; then + JAVA=java + fi + fi +fi + +exec "${JAVA}" -Xbootclasspath/p:"${tools_cp}" gnu.classpath.tools.keytool.Main $@ |