Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertImporter.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertImporter.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertImporter.java (revision 0) @@ -0,0 +1,323 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.tools.keytool; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; + +/** + * Class for importing certificates - adding to trusted certificates or + * Certificate Signing Request (CSR) replies from certificate authorirties + * (CAs). CSR reply can be a single X.509 certificate or a PKCS#7-formatted + * certificate chain containing X.509 certificates. X.509 v1, v2 and v3 + * certificates are supported. Certificates to import can be in binary (DER) or + * printable (PEM) encoding format. + */ +public class CertImporter { + + /** + * Reads an X.509 certificate or a PKCS#7 formatted certificate chain from + * the file specified in param and puts it into the entry identified by the + * supplied alias. If the input file is not specified, the certificates are + * read from the standard input. + * + * @param param + * @throws KeytoolException + * @throws IOException + * @throws CertPathBuilderException + * @throws UnrecoverableKeyException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws FileNotFoundException + * @throws NoSuchProviderException + * @throws KeyStoreException + */ + static void importCert(KeytoolParameters param) + throws FileNotFoundException, CertificateException, + NoSuchAlgorithmException, UnrecoverableKeyException, + CertPathBuilderException, IOException, KeytoolException, + NoSuchProviderException, KeyStoreException { + + String alias = param.getAlias(); + KeyStore keyStore = param.getKeyStore(); + boolean contains = keyStore.containsAlias(alias); + + // if the alias already exists, try to import the certificate as + // a cert reply + if (contains + && keyStore.entryInstanceOf(alias, + KeyStore.PrivateKeyEntry.class)) { + // read the certificates + Collection certCollection = CertReader.readCerts( + param.getFileName(), false, param.getProvider()); + + importReply(param, certCollection); + } else if (!contains) { // import a trusted certificate + // read the certificate + Collection trustedCert = CertReader.readCerts( + param.getFileName(), true, param.getProvider()); + + importTrusted(param, trustedCert.iterator().next()); + } else {// if the existing entry is not a private key entry + throw new KeytoolException( + "Failed to import the certificate. \nAlias <" + alias + + "> already exists and is not a private key entry"); + } + } + + /** + * Imports a Certificate Signing Request (CSR) reply - single X.509 + * certificate or PKCS#7 formatted certificate chain, consisting of X.509 + * certificates. + * + * @param param + * @throws FileNotFoundException + * @throws CertificateException + * @throws IOException + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws UnrecoverableKeyException + * @throws CertPathBuilderException + * @throws InvalidAlgorithmParameterException + * @throws KeytoolException + * @throws NoSuchProviderException + */ + private static void importReply(KeytoolParameters param, + Collection certCollection) + throws FileNotFoundException, CertificateException, IOException, + KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException, CertPathBuilderException, + KeytoolException, NoSuchProviderException { + if (certCollection.size() == 1) { + importSingleX509Reply(param, certCollection.iterator().next()); + } else if (certCollection.size() > 0) { + importCertChain(param, certCollection); + } + } + + /** + * Imports a single X.509 certificate Certificate Signing Request (CSR) + * reply. + * + * @param param + * @param newCert + * @throws CertificateException + * @throws FileNotFoundException + * @throws IOException + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws UnrecoverableKeyException + * @throws CertPathBuilderException + * @throws KeytoolException + * @throws NoSuchProviderException + */ + + private static void importSingleX509Reply(KeytoolParameters param, + X509Certificate newCert) throws CertificateException, + FileNotFoundException, IOException, KeyStoreException, + NoSuchAlgorithmException, UnrecoverableKeyException, + CertPathBuilderException, KeytoolException, NoSuchProviderException { + + String alias = param.getAlias(); + KeyStore keyStore = param.getKeyStore(); + + // the certificate to be replaced with certificate reply. + X509Certificate csrCert = (X509Certificate) keyStore + .getCertificate(alias); + // quit if public keys of the imported certificate and csrCert don't + // match + PublicKey publicKey = csrCert.getPublicKey(); + if (!Arrays.equals(publicKey.getEncoded(), newCert.getPublicKey() + .getEncoded())) { + throw new KeytoolException("Public keys don't match."); + } + // quit if the certificates are identical + if (newCert.equals(csrCert)) { + throw new KeytoolException("Certificate reply is identical to the " + + "certificate in keystore"); + } + + // save the private key to put it in a newly created entry + PrivateKey privateKey; + try { + privateKey = (PrivateKey) keyStore + .getKey(alias, param.getKeyPass()); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException( + "Cannot find the algorithm to recover the key. ", e); + } + + X509Certificate[] newChain = CertChainVerifier.buildFullCertPath(param, + newCert); + + // changing the certificate chain // + // remove the entry with old certificate chain + keyStore.deleteEntry(alias); + + // set the new certificate chain + keyStore.setKeyEntry(alias, privateKey, param.getKeyPass(), newChain); + param.setNeedSaveKS(true); + System.out + .println("Certificate reply is successfully installed into the keystore."); + } + + /** + * Imports an X.509 certificate into a trusted certificate entry. + * + * @param param + * @throws KeytoolException + * @throws IOException + * @throws CertPathBuilderException + * @throws CertificateException + * @throws NoSuchAlgorithmException + * @throws KeyStoreException + * @throws NoSuchProviderException + */ + private static void importTrusted(KeytoolParameters param, + X509Certificate newCert) throws NoSuchAlgorithmException, + CertificateException, CertPathBuilderException, IOException, + KeytoolException, KeyStoreException, NoSuchProviderException { + String alias = param.getAlias(); + KeyStore keyStore = param.getKeyStore(); + + // should the certificate be added to the store or not + boolean addIt = false; + + // if "-noprompt" option has been specified, don't make + // additional checks. + if (param.isNoPrompt()) { + addIt = true; + } else { + // search for an equal certificate in the keystore + String equalCertName = keyStore.getCertificateAlias(newCert); + + if (equalCertName != null) { + // if an equal certificate exists in the store + System.out.println("The certificate already exists in the " + + "keystore under alias <" + equalCertName + ">"); + // ask if a duplicating certificate should be added to the + // store + addIt = ArgumentsParser.getConfirmation( + "Do you still want to add it? [no] ", false); + } else { + try { + if (CertChainVerifier.buildCertPath(param, newCert) != null) { + addIt = true; + } + } catch (Exception e) { + // if the certificate chain cannot be built + // print it and ask if it should be trusted or not. + KeyStoreCertPrinter.printX509CertDetailed(newCert, param + .getProvider()); + addIt = ArgumentsParser.getConfirmation( + "Trust this certificate? [no] ", false); + } + } + } + if (addIt) { + keyStore.setCertificateEntry(alias, newCert); + param.setNeedSaveKS(true); + System.out.println("The certificate is added to the keystore\n"); + } else { + System.out + .println("The certificate is not added to the keystore\n"); + } + } + + /** + * Imports a PKCS#7-formatted certificate chain as a CSR reply. The + * certificte chain is firstly ordered. After that top-level certificate of + * the chain is checked to be a trusted one. If it is not a trusted + * certificate, the user is asked if the certificate should be trusted. If + * the certificate is considered to be trusted, old certificate chain, + * associated with param.getAlias() is replaced with the new one. + * Certificates can be in DER or PEM format. + * + * @param param + * @param newCerts + * @throws Exception + * @throws NoSuchAlgorithmException + * @throws KeytoolException + * @throws KeyStoreException + * @throws IOException + * @throws UnrecoverableKeyException + * @throws NoSuchProviderException + * @throws CertificateException + */ + private static void importCertChain(KeytoolParameters param, + Collection newCerts) + throws NoSuchAlgorithmException, KeytoolException, + KeyStoreException, IOException, UnrecoverableKeyException, + NoSuchProviderException, CertificateException { + + String alias = param.getAlias(); + KeyStore keyStore = param.getKeyStore(); + // get the public key of the certiciate, associated with alias, + // to import reply to. + PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey(); + // order the certificate chain + X509Certificate[] orderedChain = CertChainVerifier.orderChain(newCerts, + publicKey); + // get the top-level certificate in the chain + X509Certificate lastInChain = orderedChain[orderedChain.length - 1]; + + // should the chain be added to the keystore or not + boolean needAddChain; + // try to build a chain of trust beginning with the top certificate + needAddChain = CertChainVerifier.isTrusted(param, lastInChain); + + if (!needAddChain) { + // If couldn't build full cert path for some reason, + // ask user if the certificate should be trusted. + System.out.println("Top-level certificate in the reply chain:\n"); + KeyStoreCertPrinter.printX509CertDetailed(lastInChain, param + .getProvider()); + needAddChain = ArgumentsParser.getConfirmation("... is not trusted.\n" + + "Do you still want to install the reply? [no]: ", false); + + if (!needAddChain) { + System.out.println("The certificate reply is " + "not " + + "installed into the keystore."); + return; + } + } + + // replacing old certificate chain with the new one + char[] keyPassword = param.getKeyPass(); + PrivateKey privateKey = (PrivateKey) keyStore + .getKey(alias, keyPassword); + keyStore.deleteEntry(alias); + keyStore.setKeyEntry(alias, privateKey, keyPassword, orderedChain); + param.setNeedSaveKS(true); + System.out.println("The certificate reply is " + "successfully " + + "installed into the keystore."); + } + +} Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertReader.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertReader.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertReader.java (revision 0) @@ -0,0 +1,186 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.tools.keytool; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.NoSuchProviderException; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Collection; +import java.util.Collections; + +/** + * Class for reading an X.509 certificate or an X.509 certificate chain from the + * file or standard input. + */ +public class CertReader { + // certificate factory to read certificates and CRLs + private static CertificateFactory certFactory; + // time to wait for user to input the data. + // need this for user to have time to paste the certificate to stdin. + private static long sleepPeriod; + + + /** + * Reads an X.509 certificate or a certificate chain from the file with the + * given name or from stdin if the fileName is null and generates a + * collection of Certifices. + * + * @param fileName + * @param readOnlyFirst + * @param providerName + * @return + * @throws NoSuchProviderException + * @throws CertificateException + * @throws KeytoolException + * @throws IOException + */ + static Collection readCerts(String fileName, boolean readOnlyFirst, + String providerName) throws CertificateException, + NoSuchProviderException, IOException, KeytoolException { + + InputStream input = getInputStream(fileName); + CertificateFactory factory = getCertificateFactory(providerName); + if (input == System.in){ + System.out.println("Please, input certificate(s)..."); + } + try { + // let the user paste the certificates or CRLs, if read from stdin. + // If reading from file, don't sleep. + Thread.sleep(sleepPeriod); + } catch (InterruptedException e) { + // do nothing + } + + // if the file is empty or nothing was entered + // FIXME: remove available. Try to read and catch exception? + if (input.available() <= 0) { + throw new KeytoolException("Empty input"); + } + + Collection certCollection; + try { + // if only the first certificate is requested, return a + // single-element Collection + if (readOnlyFirst) { + certCollection = Collections.singleton(factory + .generateCertificate(input)); + } else { + certCollection = factory.generateCertificates(input); + } + if (input != System.in) { + input.close(); + } + return certCollection; + } catch (CertificateException e) { + throw new CertificateException( + "Failed to generate a certificate from the input. ", e); + } + } + + /** + * Reads CRLs from the file with given name and generates a collection of + * CRLs. + * + * @param fileName + * @param providerName + * @return + * @throws NoSuchProviderException + * @throws CertificateException + * @throws KeytoolException + * @throws IOException + * @throws CRLException + * + */ + static Collection readCRLs(String fileName, String providerName) + throws CertificateException, NoSuchProviderException, IOException, + KeytoolException, CRLException { + + InputStream input = getInputStream(fileName); + CertificateFactory factory = getCertificateFactory(providerName); + if (input == System.in){ + System.out.println("Please, input CRL(s)..."); + } + try { + // let the user paste the certificates or CRLs, if read from stdin. + // If reading from file, don't sleep. + Thread.sleep(sleepPeriod); + } catch (InterruptedException e) { + // do nothing + } + + // if the file is empty or nothing was entered + // FIXME: remove available. Try to read and catch exception? + if (input.available() <= 0) { + throw new KeytoolException("Empty input"); + } + + try { + Collection crlCollection = factory.generateCRLs(input); + if (input != System.in) { + input.close(); + } + return crlCollection; + } catch (CRLException e) { + throw new CRLException("Failed to generate a CRL from the input. ", + e); + } + } + + + // Returns an input stream - FileInputStream or System.in. + private static InputStream getInputStream(String fileName) + throws FileNotFoundException { + if (fileName != null) { + sleepPeriod = 0; + // use the file if its name is specified + return new FileInputStream(fileName); + } else {// if the file name is not given, use stdin + sleepPeriod = 3000; + return System.in; + } + } + + // Sets certFactory if it is still not set and returns it + private static CertificateFactory getCertificateFactory(String providerName) + throws CertificateException, NoSuchProviderException { + if (certFactory == null) { + try { + if (providerName == null) { + certFactory = CertificateFactory.getInstance("X.509"); + } else { + certFactory = CertificateFactory.getInstance("X.509", + providerName); + } + } catch (CertificateException e) { + throw new CertificateException( + "This type of certificate is not " + + "available from the provider. ", e); + } catch (NoSuchProviderException e) { + throw (NoSuchProviderException) new NoSuchProviderException( + "The provider " + providerName + + " is not found in the environment.") + .initCause(e); + } + } + return certFactory; + } +} Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/ArgumentsParser.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/ArgumentsParser.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/ArgumentsParser.java (working copy) @@ -16,7 +16,15 @@ package org.apache.harmony.tools.keytool; +import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; /** * The class to interact with the user - parse the program arguments, ask for @@ -25,7 +33,26 @@ */ class ArgumentsParser { - // / options names to compare to /// + // used to get additional data prompted + static InputStreamReader in = new InputStreamReader(System.in); + + // buffer for the data read + static char[] readData = new char[256]; + + // number of symbols read + static int charsRead; + + // minimum password length permitted + static int minPwdLength = 6; + + // maximum number of attempts to set the password + static int maxNrOfAttempts = 3; + + // length of the "\r\n" which is added to the end of the line, + // when ENTER is pressed. + private static int newLineLength = 2; + + // options names to compare to // // commands final static String sGenkey = "-genkey"; @@ -41,11 +68,9 @@ final static String sCertreq = "-certreq"; - final static String sSign = "-sign"; - final static String sCheck = "-checkcrl"; - final static String sAdd = "-addtocrl"; + final static String sConvert = "-convert"; final static String sVerify = "-verify"; @@ -97,11 +122,9 @@ final static String sNew = "-new"; final static String sIssuerAlias = "-issuer"; - + final static String sIssuerPass = "-issuerpass"; - - final static String sCertstore = "-certstore"; - + final static String sSecretkey = "-secretkey"; final static String sX509Version = "-x509version"; @@ -110,18 +133,37 @@ final static String sDestAlias = "-dest"; - final static String sCRLstore = "-crlstore"; + final static String sCRLfile = "-crlfile"; + + final static String sCA = "-ca"; + + final static String sConvStorePath = "-convkeystore"; + + final static String sConvStorePass = "-convstorepass"; + + final static String sConvStoreType = "-convtype"; + final static String sConvKeyEntries = "-convkeys"; + + final static String sCAcertsPath = "-cacerts"; + + final static String sCAcertsPass = "-cacertspass"; + /** * The method finds known options in args which is usually taken from * command line and sets the corresponding fields of the returned * KeytoolParameters object to given values. * - * @throws KeytoolException + * @param args - + * String array to parse. + * @return null if args is null or zero-sized, one of the elements of args + * is null or empty, an unknown option is found or an expected + * option value is not given or not of an expected type. * @throws IOException */ - static KeytoolParameters parseArgs(String[] args) throws KeytoolException, - IOException { + + static KeytoolParameters parseArgs(String[] args) + throws NumberFormatException, KeytoolException, IOException { if (args == null || args.length == 0) { return null; } @@ -164,8 +206,8 @@ param.setCommand(Command.CHECK); continue; } - if (args[i].compareToIgnoreCase(sAdd) == 0) { - param.setCommand(Command.ADD); + if (args[i].compareToIgnoreCase(sConvert) == 0) { + param.setCommand(Command.CONVERT); continue; } if (args[i].compareToIgnoreCase(sVerify) == 0) { @@ -230,10 +272,6 @@ param.setIssuerAlias(args[++i]); continue; } - if (args[i].compareToIgnoreCase(sCertstore) == 0) { - param.setStorePath(args[++i]); - continue; - } if (args[i].compareToIgnoreCase(sStorepass) == 0) { param.setStorePass(args[++i].toCharArray()); continue; @@ -246,8 +284,8 @@ param.setIssuerPass(args[++i].toCharArray()); continue; } - if (args[i].compareToIgnoreCase(sCRLstore) == 0) { - param.setCrlStore(args[++i]); + if (args[i].compareToIgnoreCase(sCRLfile) == 0) { + param.setCrlFile(args[++i]); continue; } if (args[i].compareToIgnoreCase(sDestAlias) == 0) { @@ -258,6 +296,26 @@ param.setNewPasswd(args[++i].toCharArray()); continue; } + if (args[i].compareToIgnoreCase(sConvStorePath) == 0) { + param.setConvertedKeyStorePath(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sConvStoreType) == 0) { + param.setConvertedKeyStoreType(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sConvStorePass) == 0) { + param.setConvertedKeyStorePass(args[++i].toCharArray()); + continue; + } + if (args[i].compareToIgnoreCase(sCAcertsPath) == 0) { + param.setCacertsPath(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sCAcertsPass) == 0) { + param.setCacertsPass(args[++i].toCharArray()); + continue; + } if (args[i].compareToIgnoreCase(sKeysize) == 0) { param.setKeySize((new Integer(args[++i])).intValue()); @@ -314,6 +372,14 @@ param.setSecretKey(true); continue; } + if (args[i].compareToIgnoreCase(sCA) == 0) { + param.setCA(true); + continue; + } + if (args[i].compareToIgnoreCase(sConvKeyEntries) == 0) { + param.setConvertKeyEntries(true); + continue; + } System.out.println("Illegal option: " + args[i]); return null; @@ -322,7 +388,6 @@ // ignore the last option if its value is not provided } - // set flag to use certstore, not keystore. Command cmd = param.getCommand(); // check whether -v and -rfc options are used separately with -list. @@ -337,14 +402,358 @@ return param; } - // TODO: if the store password has not been entered, prompt for it + // if the store password has not been entered, prompt for it + if (param.getStorePass() == null) { + // get path to the store + String storePath = (param.getStorePath() != null) ? param + .getStorePath() : KeytoolParameters.defaultKeystorePath; + // get store password + String prompt = "Enter keystore password: "; + System.out.print(prompt); + charsRead = in.read(readData); + char[] storePass; + File storeFile = new File(storePath); + if (storeFile.isDirectory()) { + throw new KeytoolException("The keystore path " + storePath + + " points to a directory."); + } + // Allow short passwords to unlock existing stores and + // disallow passwords shorter than minPwdLength symbols for new + // ones. + // Check whether the file exists + if (!storeFile.exists()) { + // check of password length and additional prompts for + // password are made here + storePass = promptLongerPassword(prompt); + } else { + // if the store exists don't check the length + storePass = new char[charsRead - newLineLength];// remove "\r\n" + System.arraycopy(readData, 0, storePass, 0, charsRead + - newLineLength); + } + if (storePass.length != 0) { + param.setStorePass(storePass); + } else { + param.setStorePass(null); + } + } + return param; } /** * Checks if the needed values are set and, if not, prompts for them. + * + * This method must be called after the keystore is loaded. + * + * @param param + * @return + * @throws KeytoolException + * @throws UnrecoverableKeyException + * @throws NoSuchAlgorithmException + * @throws IOException + * @throws KeyStoreException + * @throws NoSuchProviderException + * @throws CertificateException */ - static void getAdditionalParameters(KeytoolParameters param) { - // TODO + static void getAdditionalParameters(KeytoolParameters param) + throws KeytoolException, IOException, KeyStoreException, + UnrecoverableKeyException, NoSuchAlgorithmException, + CertificateException, NoSuchProviderException { + // this method must be called after the keystore is loaded. + KeyStore keyStore = param.getKeyStore(); + + // set the alias to "mykey" if it's not set up + Command command = param.getCommand(); + if (param.getAlias() == null + && (command == Command.KEYCLONE || command == Command.EXPORT + || command == Command.CERTREQ + || command == Command.GENKEY + || command == Command.SELFCERT + || command == Command.IMPORT || command == Command.KEYPASSWD)) { + param.setAlias("mykey"); + } + String alias = param.getAlias(); + + // check if the alias exists + if (command == Command.CERTREQ || command == Command.DELETE + || command == Command.EXPORT || command == Command.KEYCLONE + || command == Command.KEYPASSWD || command == Command.SELFCERT + || (command == Command.LIST && param.getAlias() != null)) { + if (!keyStore.containsAlias(param.getAlias())) { + throw new KeytoolException("Alias <" + alias + + "> doesn't exist"); + } + } else if (command == Command.GENKEY){ + if (keyStore.containsAlias(param.getAlias())) { + throw new KeytoolException("Key(s) not generated, alias <" + + alias + "> already exists."); + } + } + + // if the key password has not been entered and the password is required + // to get the key (it is not a password for a newly created entry) + if (param.getKeyPass() == null + && (command == Command.KEYCLONE || command == Command.EXPORT + || command == Command.CERTREQ + || command == Command.KEYPASSWD + || command == Command.SELFCERT + // if keystore contains alias, import of a certificate reply + // is considered, otherwise password is unnecessary. + || (command == Command.IMPORT && keyStore.containsAlias(alias)))) { + param.setKeyPass(tryStorePassAsKeyPass(keyStore, alias, param + .getStorePass())); + } + + switch (command) { + case KEYCLONE: + // prompt for a destination alias, if one is not specified + if (param.getDestAlias() == null) { + System.out.print("Enter destination alias name: "); + charsRead = in.read(readData); + if (charsRead <= newLineLength) { + throw new KeytoolException( + "Must specify destination alias"); + } else { + param.setDestAlias(new String(readData).substring(0, + charsRead - newLineLength)); + } + } + // if the password for a newly created entry is not specified, + // ask for it. + if (param.getNewPasswd() == null) { + param.setNewPasswd(getNewPassword(alias, param + .getDestAlias(), param.getKeyPass())); + } + break; + case DELETE: + // prompt for an alias to delete, if one is not specified + if (alias == null) { + System.out.print("Enter alias name: "); + charsRead = in.read(readData); + if (charsRead <= newLineLength) { + throw new KeytoolException("Must specify alias"); + } else { + param.setAlias(new String(readData).substring(0, + charsRead - newLineLength)); + } + } + break; + case STOREPASSWD: + case KEYPASSWD: + String prompt; + String promptReenter; + // prompt for a new password, if it is not specified + if (command == Command.KEYPASSWD) { + prompt = "Enter new key password for <" + alias + ">: "; + promptReenter = "Re-enter new keystore password for <" + + alias + ">: "; + } else { // if param.getCommand() == Command.STOREPASSWD + // prompt for a new store password, if it is not specified + prompt = "Enter new keystore password: "; + promptReenter = "Re-enter new keystore password: "; + } + + // if the new password is not entered + if (param.getNewPasswd() == null) { + System.out.print(prompt); + charsRead = in.read(readData); + char[] password = promptLongerPassword(prompt); + System.out.print(promptReenter); + charsRead = in.read(readData); + if (charsRead == password.length + newLineLength) { + for (int i = 0; i < password.length; i++) { + if (readData[i] != password[i]) { + throw new KeytoolException( + "Passwords do not match"); + } + } + param.setNewPasswd(password); + } else { + throw new KeytoolException("Passwords do not match"); + } + // if entered a short password in the command line + } else if (param.getNewPasswd().length < minPwdLength) { + throw new KeytoolException("The password must be at least " + + minPwdLength + " characters"); + } + + break; + case LIST: + if (alias != null) { + // This check is not where the same thing for other + // commands done, because (alias != null) check is + // necessary. + if (keyStore.entryInstanceOf(alias, + KeyStore.SecretKeyEntry.class) + && param.getKeyPass() == null) { + param.setKeyPass(tryStorePassAsKeyPass(keyStore, alias, + param.getStorePass())); + } + } + break; + }// switch (param.getCommand()) + } + + /** + * The method should be called only after the password was entered and put into + * readData. charsRead also shouldn't be changed after the password was + * entered and before the method is called. If charsRead is less than + * minPwdLength + newLineLength, the method just copies the password from + * readData into a newly created char array; otherwise it prompts for a + * longer password for maxNrOfAttempts times. + * + * @param prompt + * @return new password of length equal or longer than minPwdLength + * @throws IOException + * @throws KeytoolException + */ + private static char[] promptLongerPassword(String prompt) + throws IOException, KeytoolException { + int cntAttempts = 0; + while (charsRead < minPwdLength + newLineLength) { + System.out.println("The password must be at least " + minPwdLength + + " characters"); + System.out.print(prompt); + charsRead = in.read(readData); + ++cntAttempts; + if (cntAttempts >= maxNrOfAttempts) { + throw new KeytoolException("Too many failures. " + + "Please, try again later."); + } + } + char[] password = new char[charsRead - newLineLength]; + System.arraycopy(readData, 0, password, 0, charsRead - newLineLength); + return password; + } + + /** + * Does all work to get from the user a password for a newly created (cloned + * or generated) key. + * + * @param - + * srcAlias is the alias of the entry to clone, or if it is null, + * the keystore password will be prompted to use. + * @param - + * destAlias is the alias of the newly created entry. + * @param - + * srcPass is the password to be used with a new entry if the + * user doesn't enter a new one. + * + * @return - char array representing the password for the entry. It can be + * equal to the keystore password or the password of a cloned key. + */ + private static char[] getNewPassword(String srcAlias, String destAlias, + char[] srcPass) throws IOException, KeytoolException { + if (destAlias == null) { + return null; + } + String prompt = "Enter key password for <" + destAlias + ">: "; + System.out.print(prompt); + if (srcAlias == null) { + System.out.print("(Press RETURN if same as for keystore) "); + } else { + System.out.print("(Press RETURN if same as for <" + srcAlias + + ">) "); + } + charsRead = in.read(readData); + char[] destPass; + // if RETURN was pressed + if (charsRead <= newLineLength) { + destPass = new char[srcPass.length]; + System.arraycopy(srcPass, 0, destPass, 0, srcPass.length); + } else {// if some password was entered + destPass = promptLongerPassword(prompt); + } + return destPass; + } + + /** + * Prints a promt. Reads what the user enters. If the user has entered + * 'y'/"yes" or 'n'/"no" (case insensitively) the method returns + * respectively true or false. Depending on acceptAnother parameter the + * method can return false if anything except 'y' or "yes" is entered, or it + * can prompt for a correct answer. If only ENTER is pressed false is + * returned. + * + * @param promt - + * text printed to ask the user for a confirmation + * @param acceptAnother - + * if set to true, the method returns true if and only if the + * user enters 'y' or "yes"; if set to false prompts to reenter + * the answer from user until 'y'/"yes" or 'n'/"no" is entered. + * @return true if the user confirms the request, false - otherwise. + * @throws IOException + */ + static boolean getConfirmation(String promt, boolean acceptAnother) + throws IOException, KeytoolException { + int counter = 0; + while (counter++ < 100) { + System.out.print(promt); + charsRead = in.read(readData); + // if pressed ENTER return the default value + if (charsRead == newLineLength) { + return false; + } + // confirm, if the user enters 'y' or "yes" + if ((charsRead == newLineLength + 1 && (readData[0] == 'y' || readData[0] == 'Y')) + || (charsRead == newLineLength + 3 && "yes" + .equalsIgnoreCase(new String(readData).substring(0, + 3)))) { + return true; + } else if (acceptAnother) { + return false; + } else { + // if entered 'n' or "no" + if (readData[0] == 'n' + || readData[0] == 'N' + && ((charsRead == newLineLength + 1) || (charsRead == newLineLength + 2 + && readData[0] == 'o' || readData[0] == 'O'))) { + return false; + } else { + System.out.println("Wrong answer, please, try again"); + } + } + } + throw new KeytoolException("Too many failures. "); + } + + // method tries to get the key, associated with alias, using the storePass, + // if it can be recovered using the password storePass is returned, + // otherwise - the password is prompted for. Another attempt to recover the + // key with entered password. If it is ok, it is returned, otherwise + // UnrecoverableKeyException is thrown. + private static char[] tryStorePassAsKeyPass(KeyStore keyStore, + String alias, char[] storePass) throws KeyStoreException, + IOException, UnrecoverableKeyException, NoSuchAlgorithmException { + try { + // try to get a key with keystore password + // if succeed set key password same as that for keystore + keyStore.getKey(alias, storePass); + + // will not come here if exception is thrown + return storePass; + } catch (UnrecoverableKeyException e) { + // if key password is not equal to store password, ask for it. + System.out.print("Enter key password for <" + alias + ">: "); + charsRead = in.read(readData); + char[] keyPass = new char[charsRead - newLineLength]; + System + .arraycopy(readData, 0, keyPass, 0, charsRead + - newLineLength); + // if the new password is incorrect an axception will be thrown + try { + keyStore.getKey(alias, keyPass); + } catch (NoSuchAlgorithmException nsae) { + throw new NoSuchAlgorithmException( + "Cannot find the algorithm to recover the key. ", e); + } + return keyPass; + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException( + "Cannot find the algorithm to recover the key. ", e); + } + } + } Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/Main.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/Main.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/Main.java (working copy) @@ -49,6 +49,16 @@ case KEYPASSWD: EntryManager.keyPasswd(param); break; + case IMPORT: + CertImporter.importCert(param); + break; + case CHECK: + CRLManager.checkRevoked(param); + break; + case HELP: + HelpPrinter.printHelp(); + break; + // TODO: calls for other options. } } @@ -63,7 +73,7 @@ KeytoolParameters param = ArgumentsParser.parseArgs(args); if (param == null) { - System.out.println("Help message is printed here"); + HelpPrinter.printHelp(); System.exit(-1); } @@ -78,11 +88,9 @@ throw new KeytoolException( "Must specify store password to work with this command."); } - // load the keystore - KeyStoreLoaderSaver.loadStore(param); // prompt for additional parameters if some of the expected // ones have not been specified. - //ArgumentsParser.getAdditionalParameters(param); + ArgumentsParser.getAdditionalParameters(param); } // print the warning if store password is not set @@ -97,10 +105,6 @@ doWork(param); if (param.isNeedSaveKS()) { - // if the program should output additional information, do it - if (param.isVerbose()) { - System.out.println("[Saving " + param.getStorePath() + "]"); - } // save the store KeyStoreLoaderSaver.saveStore(param); } Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertExporter.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertExporter.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertExporter.java (working copy) @@ -23,7 +23,10 @@ import java.io.OutputStream; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.harmony.luni.util.Base64; @@ -36,17 +39,20 @@ /** * Reads an X.509 certificate associated with alias and prints it into the - * given file. alias and the file name are supplied in param. If the file + * given file. alias and the file name are supplied in param. if The file * name is not given, the certificate is printed to stdout. * * @param param * @throws KeyStoreException - * @throws CertificateEncodingException * @throws IOException * @throws KeytoolException + * @throws NoSuchProviderException + * @throws CertificateException + * @throws NoSuchAlgorithmException */ static void exportCert(KeytoolParameters param) throws KeyStoreException, - CertificateEncodingException, IOException, KeytoolException { + IOException, KeytoolException, NoSuchAlgorithmException, + CertificateException, NoSuchProviderException { KeyStore keyStore = param.getKeyStore(); String alias = param.getAlias(); if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreCertPrinter.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreCertPrinter.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreCertPrinter.java (working copy) @@ -16,7 +16,8 @@ package org.apache.harmony.tools.keytool; -import java.io.UnsupportedEncodingException; +import java.io.FileNotFoundException; +import java.io.IOException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; @@ -26,9 +27,12 @@ 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.util.Collection; import java.util.Collections; import java.util.Enumeration; +import java.util.Iterator; import org.apache.harmony.luni.util.Base64; @@ -46,16 +50,18 @@ * * @param param * @throws KeyStoreException - * @throws CertificateEncodingException * @throws NoSuchAlgorithmException * @throws NoSuchProviderException - * @throws UnsupportedEncodingException * @throws UnrecoverableKeyException + * @throws KeytoolException + * @throws IOException + * @throws FileNotFoundException + * @throws CertificateException */ static void list(KeytoolParameters param) throws KeyStoreException, - CertificateEncodingException, NoSuchAlgorithmException, - NoSuchProviderException, UnsupportedEncodingException, - UnrecoverableKeyException { + NoSuchAlgorithmException, NoSuchProviderException, + UnrecoverableKeyException, CertificateException, + FileNotFoundException, IOException, KeytoolException { Enumeration aliases; KeyStore keyStore = param.getKeyStore(); String alias = param.getAlias(); @@ -71,8 +77,9 @@ System.out.println("Type of keystore: " + keyStore.getType()); System.out.println("Keystore provider name: " + keyStore.getProvider().getName()); - System.out.println("\nThe keystore contains " + keyStore.size() - + " entries \n"); + int keyStoreSize = keyStore.size(); + System.out.println("\nThe keystore contains " + keyStoreSize + + ((keyStoreSize == 1) ? " entry \n" : " entries \n")); } String provider = param.getProvider(); @@ -198,7 +205,6 @@ // If the key is explicitly asked to be printed // by setting the alias, print it out, otherwise - do // nothing. - System.out.println(); if (alias != null) { char[] keyPass; if ((keyPass = param.getKeyPass()) != null) { @@ -294,20 +300,39 @@ * Reads an X.509 certificate from the file specified in param and prints it * in a human-readable format. If param.getFileName() returns null, the * certificate is read from the standard input. The input data is awaited - * for some time. If the data is not entered, an exception is thrown. + * for 3 seconds. If the data is not entered, an exception is thrown. * * @param param + * @throws KeytoolException + * @throws IOException + * @throws CertificateException + * @throws FileNotFoundException + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException */ - static void printCert(KeytoolParameters param) { - // TODO - throw new RuntimeException("The method is not implemented yet."); + static void printCert(KeytoolParameters param) + throws FileNotFoundException, CertificateException, IOException, + KeytoolException, NoSuchAlgorithmException, NoSuchProviderException { + // get the certificate(s) from the file + Collection certCollection = CertReader.readCerts(param.getFileName(), + false, param.getProvider()); + Iterator certIter = certCollection.iterator(); + int counter = 1; + + // print the datailed info on all certificates + while (certIter.hasNext()) { + X509Certificate cert = (X509Certificate) certIter.next(); + System.out.println("\nCertificate[" + counter + "]:"); + printX509CertDetailed(cert, param.getProvider()); + ++counter; + } } // Formats byte array as a String looking like "0A:1B:C3:D4:....:E5". private static String formatBytes(byte[] bytes) { int i; // The method is expected to format mostly message digest results and - // ? length of the String repesenting a SHA1 digest printed in + // åhe length of the String repesenting a SHA1 digest printed in // the way: "0A:1B:C3:D4:....:E5" is the biggest and is 59. StringBuffer buffer = new StringBuffer(59); int length; @@ -315,14 +340,12 @@ for (i = 0; i < bytes.length - 1; i++) { // TODO: change when String.format(..) method is implemented. // buffer.append(String.format("%02X", bytes[i]) + ":"); - // / currentByte = Integer.toHexString(bytes[i]).toUpperCase(); if ((length = currentByte.length()) > 1) { buffer.append(currentByte.substring(length - 2) + ":"); } else { buffer.append("0" + currentByte + ":"); } - // / } // The last byte doesn't need ":" after it ("...:E5:6F") // TODO: change in the same way to (String.format(..)) Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeytoolParameters.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeytoolParameters.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeytoolParameters.java (working copy) @@ -17,11 +17,17 @@ package org.apache.harmony.tools.keytool; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; /** - * The class encapsulates paramaters for Keytool most of which are ususally - * given in command line. + * The class encapsulates paramaters for Keytool most of which are ususally given + * in command line. */ public class KeytoolParameters { /** @@ -32,13 +38,14 @@ .getProperty("user.home") + File.separator + ".keystore"; - /** - * Location of cacerts file, containing the certificates from root - * certificate authorities (usually self-signed). - */ - public static final String cacertsPath = System.getProperty("java.home") + // Default location of cacerts file + private static final String defaultCacertsPath = System.getProperty("java.home") + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts"; + + // Default password for cacerts keystore + private static final char[] defaultCacertsPass = { 'c', 'h', 'a', 'n', 'g', + 'e', 'i', 't' }; // the keystore to work with private KeyStore keyStore; @@ -74,8 +81,8 @@ // generated certificate can be signed with) private String issuerAlias; - // certstore to keep a CRL in - private String crlStore; + // file with CRLs + private String crlFile; // used in keyclone. Shows the destination alias to copy key pair to private String destAlias; @@ -121,15 +128,37 @@ // should a secret key or a key pair be generated private boolean isSecretKey; + + // should the generated certificate ba a CA certificate or not + private boolean isCA; - // true if the store worked with is a keystore, false - if a certstore - private boolean isKeyStore = true; - + // path to the keystore to convert the current keystore to + private String convertedKeyStorePath; + + // type of the keystore to convert the current keystore to + private String convertedKeyStoreType; + + // password to the keystore to convert the current keystore to + private char [] convertedKeyStorePass; + + // should the key entries be converted or not + private boolean convertKeyEntries; + + // location of cacerts file + private String cacertsPath; + + // password for cacerts keystore + private char [] cacertsPass; + + // cacerts keystore containing the certificates from root + // certificate authorities (usually self-signed) + private KeyStore cacerts; + // command to perform private Command command = Command.HELP; - + /** - * The method sets the fields to default values. If there is no default + * The method sets the fields to default values. If there is not a default * value the field is set to null. */ void setDefault() { @@ -154,17 +183,38 @@ verbose = false; isSecretKey = false; issuerAlias = null; + issuerPass = null; X509version = 3; certSerialNr = 0; - isKeyStore = true; + isCA = false; + convertedKeyStorePath = null; + convertedKeyStoreType = null; + convertedKeyStorePass = null; + convertKeyEntries = false; + cacertsPath = null; + cacertsPass = null; + cacerts = null; + crlFile = null; command = Command.HELP; } // getters and setters down here. /** * @return Returns the keystore to work with. + * @throws KeytoolException + * @throws IOException + * @throws NoSuchProviderException + * @throws KeyStoreException + * @throws FileNotFoundException + * @throws CertificateException + * @throws NoSuchAlgorithmException */ - KeyStore getKeyStore() { + KeyStore getKeyStore() throws NoSuchAlgorithmException, + CertificateException, FileNotFoundException, KeyStoreException, + NoSuchProviderException, IOException, KeytoolException { + if (keyStore == null){ + KeyStoreLoaderSaver.loadStore(this); + } return keyStore; } @@ -224,18 +274,18 @@ } /** - * @return Returns the certstore path to keep CRLs in. + * @return Returns path to a file with CRLs. */ - String getCrlStore() { - return crlStore; + String getCrlFile() { + return crlFile; } /** - * @param crlStore - * certstore path to keep CRLs in. + * @param crlFile + * path to a file with CRLs. */ - public void setCrlStore(String crlStore) { - this.crlStore = crlStore; + public void setCrlFile(String crlStore) { + this.crlFile = crlStore; } /** @@ -314,23 +364,6 @@ } /** - * @return Returns true if the store to work with is a keystore, false - if - * a certstore - */ - boolean isKeyStore() { - return isKeyStore; - } - - /** - * @param isKeyStore - * set true if the store worked with is a keystore, false - if a - * certstore - */ - void setIsKeyStore(boolean isKeyStore) { - this.isKeyStore = isKeyStore; - } - - /** * @return Returns the algorithm name to get instance of KeyPairGenerator, * KeyFactory, etc. */ @@ -476,6 +509,23 @@ } /** + * @return true if the generated certificate should be a CA certificate, + * false - otherwise + */ + boolean isCA() { + return isCA; + } + + /** + * @param isCA + * set true if the generated certificate should be a CA + * certificate, false - otherwise + */ + public void setCA(boolean isCA) { + this.isCA = isCA; + } + + /** * @return Returns the digital signature algorithm */ String getSigAlg() { @@ -604,5 +654,133 @@ this.storePath = storePath; } + /** + * @return password for the keystore to convert the current keystore to + */ + char [] getConvertedKeyStorePass() { + return convertedKeyStorePass; + } + + /** + * @param password + * for the keystore to convert the current keystore to + */ + public void setConvertedKeyStorePass(char [] convertedKeyStorePass) { + this.convertedKeyStorePass = convertedKeyStorePass; + } + + /** + * @return path to the keystore to convert the current keystore to + */ + String getConvertedKeyStorePath() { + return convertedKeyStorePath; + } + + /** + * @param path + * to the keystore to convert the current keystore to + */ + public void setConvertedKeyStorePath(String convertedKeyStorePath) { + this.convertedKeyStorePath = convertedKeyStorePath; + } + + /** + * @return type of the keystore to convert the current keystore to + */ + String getConvertedKeyStoreType() { + return convertedKeyStoreType; + } + + /** + * @param type + * of the keystore to convert the current keystore to + */ + public void setConvertedKeyStoreType(String convertedKeyStoreType) { + this.convertedKeyStoreType = convertedKeyStoreType; + } + + /** + * @return true if key entries should be converted, false - if not + */ + boolean isConvertKeyEntries() { + return convertKeyEntries; + } + + /** + * @param set + * true if key entries should be converted, false - if not + */ + public void setConvertKeyEntries(boolean convertKeyEnties) { + this.convertKeyEntries = convertKeyEnties; + } + + /** + * @return Returns the location of cacerts file, containing the certificates + * from root certificate authorities (usually self-signed). + */ + String getCacertsPath() { + if (cacertsPath != null) { + return cacertsPath; + } else { + return defaultCacertsPath; + } + } + + /** + * @param the + * location of cacerts file, containing the certificates from + * root certificate authorities (usually self-signed). + */ + public void setCacertsPath(String cacertsPath) { + this.cacertsPath = cacertsPath; + } + + /** + * @return password for cacerts keystore + */ + char[] getCacertsPass() { + if (cacertsPass != null) { + return cacertsPass; + } else { + return defaultCacertsPass; + } + } + + /** + * @param password + * for cacerts keystore + */ + public void setCacertsPass(char[] cacertsPass) { + this.cacertsPass = cacertsPass; + } + + /** + * @return cacerts keystore containing the certificates from root + * certificate authorities (usually self-signed) + * @throws KeytoolException + * @throws IOException + * @throws NoSuchProviderException + * @throws CertificateException + * @throws NoSuchAlgorithmException + * @throws KeyStoreException + * @throws FileNotFoundException + */ + KeyStore getCacerts() throws FileNotFoundException, KeyStoreException, + NoSuchAlgorithmException, CertificateException, + NoSuchProviderException, IOException, KeytoolException { + if (cacerts == null) { + cacerts = KeyStoreLoaderSaver.loadStore(getCacertsPath(), + storeType, getCacertsPass(), provider); + } + return cacerts; + } + + /** + * @param cacerts + * keystore containing the certificates from root certificate + * authorities (usually self-signed) + */ + void setCacerts(KeyStore cacerts) { + this.cacerts = cacerts; + } } - Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/CRLManager.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/CRLManager.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CRLManager.java (revision 0) @@ -0,0 +1,99 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.tools.keytool; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.X509CRL; +import java.security.cert.X509CRLEntry; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Iterator; + +/** + * Class for managing Certificate Revocation Lists (CRLs). + */ +public class CRLManager { + /** + * Checks if the certificate given in the file is contained in the CRL which + * is stored in the certstore. If the file name is not given, stdin is used. + * File with CRL and the checked certificate file are specified in param. + * + * @param param + * @throws KeytoolException + * @throws IOException + * @throws CRLException + * @throws NoSuchProviderException + * @throws CertificateException + * @throws FileNotFoundException + * @throws NoSuchAlgorithmException + */ + static void checkRevoked(KeytoolParameters param) throws FileNotFoundException, + CertificateException, NoSuchProviderException, CRLException, + IOException, KeytoolException, NoSuchAlgorithmException { + + String providerName = param.getProvider(); + // firstly, get CRLs from the file + Collection crls = CertReader.readCRLs(param.getCrlFile(), providerName); + // quit, if couldn't read anything + if (crls.isEmpty()) { + throw new CRLException("Failed to generate a CRL from the input. "); + } + + // secondly, get certificates from another file + Collection certs = CertReader.readCerts(param.getFileName(), false, + param.getProvider()); + if (certs.isEmpty()) { + throw new CertificateException( + "Failed to generate a certificate from the input. "); + } + + boolean foundRevoked = false; + + // search in the CRLs for revocations of the certificates + Iterator crlIter = crls.iterator(); + while (crlIter.hasNext()) { + X509CRL crl = (X509CRL) crlIter.next(); + Iterator certIter = certs.iterator(); + while (certIter.hasNext()){ + X509Certificate cert = (X509Certificate)certIter.next(); + X509CRLEntry entry = crl.getRevokedCertificate(cert); + if (entry != null) { + System.out.println("The certificate ..."); + KeyStoreCertPrinter.printX509CertDetailed(cert, providerName); + System.out.println("... is revoked on " + + entry.getRevocationDate() + "\n"); + foundRevoked = true; + continue; + } + } + } + + if (certs.size() == 1 && !foundRevoked){ + System.out.println("The certificate ..."); + KeyStoreCertPrinter.printX509CertDetailed((X509Certificate) certs + .iterator().next(), providerName); + System.out.println("... is not found in CRLs given"); + } else if (!foundRevoked){ + System.out.println("The certificates are not found in CRLs given"); + } + } +} Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/EntryManager.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/EntryManager.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/EntryManager.java (working copy) @@ -16,12 +16,16 @@ package org.apache.harmony.tools.keytool; +import java.io.FileNotFoundException; +import java.io.IOException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; +import java.security.cert.CertificateException; /** * Class for managing keystore entries - cloning, deleting, changing entry @@ -38,10 +42,15 @@ * @throws NoSuchAlgorithmException * @throws KeyStoreException * @throws KeytoolException + * @throws IOException + * @throws NoSuchProviderException + * @throws FileNotFoundException + * @throws CertificateException */ static void keyClone(KeytoolParameters param) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, - KeytoolException { + KeytoolException, CertificateException, FileNotFoundException, + NoSuchProviderException, IOException { KeyStore keyStore = param.getKeyStore(); String alias = param.getAlias(); Key srcKey; @@ -67,8 +76,17 @@ * * @param param * @throws KeyStoreException + * @throws KeytoolException + * @throws IOException + * @throws NoSuchProviderException + * @throws FileNotFoundException + * @throws CertificateException + * @throws NoSuchAlgorithmException */ - static void delete(KeytoolParameters param) throws KeyStoreException { + static void delete(KeytoolParameters param) throws KeyStoreException, + NoSuchAlgorithmException, CertificateException, + FileNotFoundException, NoSuchProviderException, IOException, + KeytoolException { param.getKeyStore().deleteEntry(param.getAlias()); param.setNeedSaveKS(true); } @@ -80,9 +98,16 @@ * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws UnrecoverableKeyException + * @throws KeytoolException + * @throws IOException + * @throws NoSuchProviderException + * @throws FileNotFoundException + * @throws CertificateException */ static void keyPasswd(KeytoolParameters param) throws KeyStoreException, - NoSuchAlgorithmException, UnrecoverableKeyException { + NoSuchAlgorithmException, UnrecoverableKeyException, + CertificateException, FileNotFoundException, + NoSuchProviderException, IOException, KeytoolException { KeyStore keyStore = param.getKeyStore(); String alias = param.getAlias(); Key key; Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertChainVerifier.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertChainVerifier.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertChainVerifier.java (revision 0) @@ -0,0 +1,590 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.tools.keytool; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertPathBuilderResult; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.CertificateException; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +/** + * A class for checking X.509 certifcicates and building and verifying X.509 + * certificate chains. + */ +public class CertChainVerifier { + private final static String strFailed = "Failed to build a certificate chain from reply.\n"; + + /** + * A cerificate chain is built by looking up the certificate of the issuer + * of the current certificate. If a sertificate is self-signed it is assumed + * to be the root CA. After that the certificates are searched in the lists + * of revoked certificates. Certificate signatures are checked and + * certificate path is built in the same way as in import operation. If an + * error occurs the flow is not stopped but an attempt to continue is made. + * The results of the verification are printed to stdout. + */ + static void verifyChain(KeytoolParameters param){ + // TODO + throw new RuntimeException("Method is not implemented yet."); + } + + /** + * Builds a certificate chain from the given X509Certificate newCert to a + * self-signed root CA whose certificate is contained in the keystore or + * cacerts file (if "-trustcacerts" option is specified). + * + * @param param - + * specifies the keystore, provider name and other options (such + * as "-trustcacerts"). + * @param newCert - + * certificate to start the chain + * @return the chain as an array of X509Certificate-s. If the chain cannot + * be built for some reason an exception is thrown. + * @throws KeyStoreException + * @throws FileNotFoundException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws IOException + * @throws KeytoolException + * @throws NoSuchProviderException + * @throws CertPathBuilderException + */ + static X509Certificate[] buildFullCertPath(KeytoolParameters param, + X509Certificate newCert) throws KeyStoreException, + FileNotFoundException, NoSuchAlgorithmException, + CertificateException, IOException, KeytoolException, + CertPathBuilderException, NoSuchProviderException { + + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(newCert); + + Collection[] trustedSeparated = separateTrusted(param); + Set selfSignedTAs = (Set) trustedSeparated[0]; + Collection trustedCerts = trustedSeparated[1]; + Collection selfSignedTAsCerts = trustedSeparated[2]; + + CertPathBuilderResult bldResult = buildCertPath(param.getProvider(), + newCert, selfSignedTAs, trustedCerts); + + // The validation of the certificate path. + // According to black-box testing, RI keytool doesn't perform + // the certificate path validation procedure. Therefore this + // implementation also doesn't validate the certificate chain + // The path validation procedure can be done in future as an + // extension of the current functionality. + + // The imported certificate should be included in the chain; + // The root CA is not included in it. + X509Certificate[] newChainNoCA = (X509Certificate[]) bldResult + .getCertPath().getCertificates() + .toArray(new X509Certificate[0]); + + // get the subject of the root CA which will be the last element of the + // chain + X500Principal caSubject = newChainNoCA[newChainNoCA.length - 1] + .getIssuerX500Principal(); + + // set the search parameter for the root CA + selector.setCertificate(null); + selector.setSubject(caSubject); + + // add all root CAs to the CertStore to search through them + CollectionCertStoreParameters rootCAs = new CollectionCertStoreParameters( + selfSignedTAsCerts); + CertStore rootCAsCertStore; + try { + rootCAsCertStore = CertStore.getInstance("Collection", rootCAs); + } catch (Exception e) { + throw new KeytoolException(strFailed, e); + } + + // find certificate to add to the end of the certificate chain + X509Certificate rootCA; + try { + rootCA = (X509Certificate) rootCAsCertStore.getCertificates( + selector).iterator().next(); + } catch (CertStoreException e) { + throw new KeytoolException(strFailed, e); + } + // create a new array of certificates with the root CA in it. + X509Certificate[] newChain = new X509Certificate[newChainNoCA.length + 1]; + System.arraycopy(newChainNoCA, 0, newChain, 0, newChainNoCA.length); + newChain[newChain.length - 1] = rootCA; + + return newChain; + } + + /** + * Build a certificte chain up to the trust anchor, based on trusted + * certificates contained in the keystore and possibly cacerts file (if + * param.isTrustCACerts() returns true). + * + * @param param + * @param newCert + * @return + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws KeyStoreException + * @throws CertPathBuilderException + * @throws IOException + * @throws KeytoolException + * @throws NoSuchProviderException + */ + static CertPathBuilderResult buildCertPath(KeytoolParameters param, + X509Certificate newCert) throws NoSuchAlgorithmException, + CertificateException, KeyStoreException, CertPathBuilderException, + IOException, KeytoolException, NoSuchProviderException { + Collection[] trustedSeparated = separateTrusted(param); + Set selfSignedTAs = (Set) trustedSeparated[0]; + Collection trustedCerts = trustedSeparated[1]; + + return buildCertPath(param.getProvider(), newCert, selfSignedTAs, + trustedCerts); + } + + + // Build a certificte chain up to the self-signed trust anchor, based on + // trusted certificates given. + // + // @param provider + // @param newCert + // is a certificate to build chain for. + // @param selfSignedTAs + // are used as trust anchors. + // @param trustedCerts + // elements of trustedCerts are used as chain links It can be + // null if no intermediate certifictaes allowed. + private static CertPathBuilderResult buildCertPath(String provider, + X509Certificate newCert, Set selfSignedTAs, Collection trustedCerts) + throws NoSuchAlgorithmException, CertificateException, IOException, + KeyStoreException, CertPathBuilderException, KeytoolException, + NoSuchProviderException { + + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(newCert); + + String strPKIX = "PKIX"; + String strNoSelfSigned = "Possibly, keystore doesn't " + + "contain any self-signed (root CA) trusted certificates. "; + + // this parameter will be used to generate the certificate chain + PKIXBuilderParameters builderParam = null; + try { + // set the search parameters with selector + // and TrustAnchors with selfSignedTAs + builderParam = new PKIXBuilderParameters(selfSignedTAs, selector); + } catch (InvalidAlgorithmParameterException e) { + throw new KeytoolException(strFailed + strNoSelfSigned, e); + } + + if (trustedCerts != null) { + CollectionCertStoreParameters trustedCertsCCSParams = + new CollectionCertStoreParameters(trustedCerts); + CertStore trustedCertStore; + try { + trustedCertStore = CertStore.getInstance("Collection", + trustedCertsCCSParams); + } catch (Exception e) { + throw new KeytoolException(strFailed, e); + } + + // add certificates to use as chain links + builderParam.addCertStore(trustedCertStore); + } + + // disable the revocation checking + builderParam.setRevocationEnabled(false); + + CertPathBuilder cpBuilder; + try { + if (provider == null) { + cpBuilder = CertPathBuilder.getInstance(strPKIX); + } else { + cpBuilder = CertPathBuilder.getInstance(strPKIX, provider); + } + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException("The algorithm " + strPKIX + + " is not available.", e); + } catch (NoSuchProviderException e) { + throw (NoSuchProviderException) new NoSuchProviderException( + "The provider " + provider + + " is not found in the environment.").initCause(e); + } + + CertPathBuilderResult bldResult = null; + try { + // the actual building of the certificate chain is done here + bldResult = cpBuilder.build(builderParam); + } catch (CertPathBuilderException e) { + throw new CertPathBuilderException(strFailed, e); + } catch (InvalidAlgorithmParameterException e) { + throw new KeytoolException(strFailed + strNoSelfSigned, e); + } + + return bldResult; + } + + + // Separates the trusted certificates from keystore (and cacerts file if + // "-trustcacerts" option is specified) into self-signed certificate + // authority certificates and non-self-signed certifcates. + // @return - Returns an array of Collection-s. + // The first element of the array is Set - self-signed CAs. + // The second - ArrayList of non-self-signed trusted certifcates. + // The third - ArrayList of self-signed certificates which correspond to + // TrustAnchors containied in the first element of the array. + private static Collection[] separateTrusted(KeytoolParameters param) + throws KeyStoreException, FileNotFoundException, IOException, + NoSuchAlgorithmException, CertificateException, KeytoolException, + NoSuchProviderException { + // Are there any trusted certificates in the keyStore? + boolean trustedExistInKS = false; + // Is "-trustcacerts" option specified? + boolean trustCaCerts = param.isTrustCACerts(); + String strNoTrusted = "Possibly, keystore doesn't " + + "contain any trusted certificates. "; + + // This one is temporary. Used just to get trusted certificates + // from keyStore. + PKIXBuilderParameters keyStoreBuilderParam = null; + X509CertSelector selector = new X509CertSelector(); + + // After getting the trusted certificates, they will be sorted into + // self-signed (they are considered to be CAs) and interim trusted + // certs. + + // self-signed trust anchors. The CertPath is ok if it finishes + // on such trust anchor. + Set selfSignedTAs = null; + // certificates of selfSignedTAs + Collection selfSignedTAsCerts = null; + // trusted certiifcates which can be the chain links of the CertPath + Collection trustedCerts = null; + try { + keyStoreBuilderParam = new PKIXBuilderParameters(param + .getKeyStore(), selector); + + // won't come here if exception is thrown + trustedExistInKS = true; + } catch (InvalidAlgorithmParameterException e) { + // if "-trustcacerts" option is NOT specified + if (!trustCaCerts) { + throw new KeytoolException(strFailed + strNoTrusted); + } + } + + // if there are trusted certificates in the keyStore + if (keyStoreBuilderParam != null) { + // trustAnchorsSet is a set of trusted certificates + // contained in keyStore + Set trustAnchorsSet = keyStoreBuilderParam.getTrustAnchors(); + int halfSize = trustAnchorsSet.size() / 2; + selfSignedTAs = new HashSet(halfSize); + selfSignedTAsCerts = new ArrayList(halfSize); + trustedCerts = new ArrayList(halfSize); + + Iterator trustAnchorsIter = trustAnchorsSet.iterator(); + while (trustAnchorsIter.hasNext()) { + TrustAnchor ta = (TrustAnchor) trustAnchorsIter.next(); + X509Certificate trCert = ta.getTrustedCert(); + // if the trusted certificate is self-signed, + // add it to the selfSignedTAs. + if (Arrays.equals( + trCert.getSubjectX500Principal().getEncoded(), trCert + .getIssuerX500Principal().getEncoded())) { + selfSignedTAs.add(ta); + selfSignedTAsCerts.add(trCert); + } else {// otherwise just add it to the list of + // trusted certs + trustedCerts.add(trCert); + } + } + } + + // if "-trustcacerts" is specified, add CAs from cacerts + if (trustCaCerts) { + KeyStore cacertsFile = null; + try { + cacertsFile = param.getCacerts(); + } catch (Exception e) { + if (trustedExistInKS) { + // if there are trusted certificates in keyStore, + // just print the notification + System.err.println(e.getMessage()); + } else {// otherwise quit + throw new KeytoolException(strFailed, e); + } + } + + // if cacerts loaded + if (cacertsFile != null) { + PKIXBuilderParameters cacertsBuilderParam = null; + try { + cacertsBuilderParam = new PKIXBuilderParameters( + cacertsFile, selector); + } catch (InvalidAlgorithmParameterException e) { + if (!trustedExistInKS) { + throw new KeytoolException(strFailed + strNoTrusted); + } else { + // if there are trusted certificates in keyStore, + // just return what have now + return new Collection[] { selfSignedTAs, trustedCerts, + selfSignedTAsCerts }; + } + } + + Set cacertsCAs = cacertsBuilderParam + .getTrustAnchors(); + + // if there are no trusted certificates in the + // keyStore + if (!trustedExistInKS) { + Set trustAnchorsSet = cacertsBuilderParam.getTrustAnchors(); + int size = trustAnchorsSet.size(); + // usually only self-signed CAs are in the + // cacerts file, so selfSignedTAs is of the + // same size as trustAnchorsSet. + selfSignedTAs = new HashSet(size); + selfSignedTAsCerts = new HashSet(size); + trustedCerts = new ArrayList(); + } + + Iterator cacertsCAsIter = cacertsCAs.iterator(); + while (cacertsCAsIter.hasNext()) { + TrustAnchor ta = (TrustAnchor) cacertsCAsIter.next(); + X509Certificate trCert = ta.getTrustedCert(); + // if the trusted certificate is self-signed, + // add it to the selfSignedTAs. + if (Arrays.equals(trCert.getSubjectX500Principal() + .getEncoded(), trCert.getIssuerX500Principal() + .getEncoded())) { + selfSignedTAs.add(ta); + selfSignedTAsCerts.add(trCert); + } else {// otherwise just add it to the list of + // trusted certs + trustedCerts.add(trCert); + } + } + }// if (cacertsFile != null)... + }// if (trustCacerts)... + return new Collection[] { selfSignedTAs, trustedCerts, + selfSignedTAsCerts }; + } + + /** + * Orders a collection of certificates into a certificate chain beginning + * with the certificate which has public key equal to aliasPubKey. + * + * @throws KeytoolException + */ + static X509Certificate[] orderChain(Collection certs, + PublicKey aliasPubKey) throws KeytoolException { + + String strOrderFailed = "Failed to order the certiticate chain."; + + // add certificates to the certstore to ease the search + CollectionCertStoreParameters chainCCSParams = new CollectionCertStoreParameters( + certs); + CertStore certStore; + try { + certStore = CertStore.getInstance("Collection", chainCCSParams); + } catch (Exception e) { + throw new KeytoolException(strOrderFailed, e); + } + + // set up selector to search the certificates + X509CertSelector selector = new X509CertSelector(); + // try to find the first certificate in the chain + selector.setSubjectPublicKey(aliasPubKey); + + // current certificate + X509Certificate current = null; + try { + current = (X509Certificate) certStore.getCertificates(selector) + .iterator().next(); + } catch (CertStoreException e) { + // do nothing + } catch (NoSuchElementException e) { + // do nothing + } + + if (current == null) { + throw new KeytoolException( + "Failed to find the requested public key " + + "in certificate reply."); + } + // number of certificates in collection + int colSize = certs.size(); + // new chain to return + X509Certificate[] ordered = new X509Certificate[colSize]; + ordered[0] = current; + selector.setSubjectPublicKey((PublicKey) null); + + // counter of ordered certificates + // it will be incremented later + int orderedCnt = 0; + + if (!Arrays.equals(current.getSubjectX500Principal().getEncoded(), + current.getIssuerX500Principal().getEncoded())) { + // orderedCnt = 1, because the first certificate is already in + // the resulting array + for (orderedCnt = 1; orderedCnt < colSize; orderedCnt++) { + // set new filter + selector.setSubject(current.getIssuerX500Principal()); + try { + // get issuer's certificate + current = (X509Certificate) certStore.getCertificates( + selector).iterator().next(); + } catch (CertStoreException e) { + throw new KeytoolException(strOrderFailed, e); + } catch (NoSuchElementException e) { + break; + } + + if (Arrays.equals(current.getSubjectX500Principal() + .getEncoded(), current.getIssuerX500Principal() + .getEncoded())) { + // if self-signed, save it and quit. It is the last. + ordered[orderedCnt] = current; + break; + } else { + // add current certificate to the chain and continue + ordered[orderedCnt] = current; + } + } + } + + // If the certificate collection contains certificates which + // are not a part of the chain. + // ++orderedCnt is used because 'break's don't let the + // variable be incremented when it should be. + if (++orderedCnt < colSize) { + X509Certificate[] orderedShort = new X509Certificate[orderedCnt]; + System.arraycopy(ordered, 0, orderedShort, 0, orderedCnt); + return orderedShort; + } + + return ordered; + } + + // orders a chain without a starting element given + static X509Certificate[] orderChain(Collection certs) + throws KeytoolException { + + int certsLen = certs.size(); + int startPos = -1; + + List certsList = new ArrayList(certs); + + // searching for the first element of the chain + for (int i = 0; i < certsLen; i++) { + X509Certificate curCert = certsList.get(i); + for (int j = 0; j < certsLen; j++) { + if (j != i) { + // if the subject is the issuer of another cert, it is not + // the first in the chain. + if (Arrays.equals(curCert.getSubjectX500Principal() + .getEncoded(), certsList.get(j) + .getIssuerX500Principal().getEncoded())) { + break; + } + } + // If the certificate's subject is not found to be an issuer to + // any other cert in this chain, then it is the first element. + if (j == certsLen - 1) { + startPos = i; + break; + } + } + // don't search any more, if the first element is found. + if (startPos > -1) { + break; + } + } + + return orderChain(certsList, certsList.get(startPos).getPublicKey()); + } + + + /** + * Checks if the X509Certificate cert is contained as a trusted certificate + * entry in keystore and possibly cacerts file (if "-trustcacerts" option is + * specified). + * + * @param param + * @param cert + * @return true if the certificate is trused, false - otherwise. + * @throws FileNotFoundException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws IOException + * @throws KeytoolException + * @throws KeyStoreException + * @throws NoSuchProviderException + */ + static boolean isTrusted(KeytoolParameters param, X509Certificate cert) + throws FileNotFoundException, NoSuchAlgorithmException, + CertificateException, IOException, KeytoolException, + KeyStoreException, NoSuchProviderException { + // check the main keyStore + KeyStore keyStore = param.getKeyStore(); + String alias = keyStore.getCertificateAlias(cert); + if (alias != null) { + if (keyStore.isCertificateEntry(alias)) { + return true; + } + } + + if (!param.isTrustCACerts()) { + return false; + } else {// check cacerts file + KeyStore cacerts = param.getCacerts(); + alias = cacerts.getCertificateAlias(cert); + if (alias != null) { + if (cacerts.isCertificateEntry(alias)) { + return true; + } + } + return false; + } + } +} Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/HelpPrinter.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/HelpPrinter.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/HelpPrinter.java (revision 0) @@ -0,0 +1,30 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.tools.keytool; + +/** + * Class for printing help messages . + */ +public class HelpPrinter { + /** + * Prints the help message. + */ + static void printHelp() { + // TODO + System.out.println("Help message here"); + } +} Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreLoaderSaver.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreLoaderSaver.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreLoaderSaver.java (working copy) @@ -16,6 +16,7 @@ package org.apache.harmony.tools.keytool; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -31,7 +32,6 @@ * Class for loading keystores, saving ang changing the main keystore password. */ public class KeyStoreLoaderSaver { - /** * Creates an instance of class KeyStore and loads a keystore to it. * param.getStorePass() is used to check the integrity of the keystore. If @@ -98,16 +98,16 @@ KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, NoSuchProviderException, KeytoolException { - FileInputStream fis; + BufferedInputStream bis; // if the path is given, make a FileInputStream on it if (path != null) { File ksFile = new File(path); if (ksFile.length() == 0) { throw new KeytoolException("Keystore file exists but is empty"); } - fis = new FileInputStream(ksFile); + bis = new BufferedInputStream(new FileInputStream(ksFile)); } else { // if the path is not given, a new keystore will be created - fis = null; + bis = null; } // Set the store type to default if it is not given. @@ -131,7 +131,7 @@ try { // try to load the keystore - keyStore.load(fis, storePass); + keyStore.load(bis, storePass); } catch (NoSuchAlgorithmException e) { throw new NoSuchAlgorithmException( "Failed to find the algorithm to check the keystore integrity", @@ -147,27 +147,54 @@ } /** - * Saves the keystore to the file and protects its integrity with password. + * Saves the main keystore to the file and protects its integrity with + * password. * * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws IOException + * @throws KeytoolException + * @throws NoSuchProviderException */ static void saveStore(KeytoolParameters param) throws KeyStoreException, - NoSuchAlgorithmException, CertificateException, IOException { + NoSuchAlgorithmException, CertificateException, IOException, + NoSuchProviderException, KeytoolException { + saveStore(param.getKeyStore(), param.getStorePath(), param + .getStorePass(), param.isVerbose()); + } + + /** + * Saves a keystore to the file and protects its integrity with password. + * + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws IOException + */ + static void saveStore(KeyStore keyStore, String storePath, + char[] storePass, boolean isVerbose) + throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException { // TODO: store not only to a file? + + // if the program should output additional information, do it + if (isVerbose) { + System.out.println("[Saving " + storePath + "]"); + } + // if the path to the store is not set, use the default value - if (param.getStorePath() == null) { - param.setStorePath(KeytoolParameters.defaultKeystorePath); + if (storePath == null) { + storePath = KeytoolParameters.defaultKeystorePath; } - File ksFile = new File(param.getStorePath()); + + File ksFile = new File(storePath); // the file will be created if and only if one with the same name // doesn't exist ksFile.createNewFile(); - FileOutputStream fos = new FileOutputStream(param.getStorePath()); + FileOutputStream fos = new FileOutputStream(ksFile); try { - param.getKeyStore().store(fos, param.getStorePass()); + keyStore.store(fos, storePass); } catch (NoSuchAlgorithmException e) { throw new NoSuchAlgorithmException( "Failed to find the algorithm to check the keystore integrity", @@ -180,7 +207,7 @@ .initCause(e); } } - + /** * Changes the keystore password to the new one. * @@ -189,5 +216,4 @@ static void storePasswd(KeytoolParameters param) { param.setStorePass(param.getNewPasswd()); } - } Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/Command.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/Command.java (revision 425692) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/Command.java (working copy) @@ -37,8 +37,8 @@ CERTREQ, // check a CRL CHECK, - // add a certificate to a CRL - ADD, + // convert keystore to another format + CONVERT, // verify a certificate chain VERIFY, // copy a key entry into a newly created one