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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeytoolParameters.java (working copy) @@ -59,9 +59,27 @@ // type of the store. Default type is set in java.security file. private String storeType = KeyStore.getDefaultType(); - // the name of the provider to use + // the name of the provider to use if specific provider is not given private String provider; + // the name of the provider to work with certificates + private String certProvider; + + // the name of the provider to work with keys + private String keyProvider; + + // the name of the provider to work with message digests + private String mdProvider; + + // the name of the provider to work with signatures + private String sigProvider; + + // the name of the provider to work with keystore + private String ksProvider; + + // the name of the provider to work with keystore to convert to + private String convKsProvider; + // alias to access an entry in keystore private String alias; @@ -154,6 +172,9 @@ // certificate authorities (usually self-signed) private KeyStore cacerts; + // topic to print help on + private String helpTopic; + // command to perform private Command command = Command.HELP; @@ -167,6 +188,13 @@ storePath = null; storeType = KeyStore.getDefaultType(); provider = null; + certProvider = null; + keyProvider = null; + mdProvider = null; + sigProvider = null; + ksProvider = null; + convKsProvider = null; + helpTopic = null; storePass = null; alias = null; keyAlg = null; @@ -460,7 +488,8 @@ } /** - * @return Returns the name of the provider to use + * @return Returns the name of the provider to use if specific provider is + * not given */ String getProvider() { return provider; @@ -468,13 +497,106 @@ /** * @param provider - * the name of the provider to use + * the name of the provider to use if specific provider is not + * given */ public void setProvider(String provider) { this.provider = provider; } /** + * @return the name of the provider to work with certificates + */ + String getCertProvider() { + return certProvider; + } + + /** + * @param certProvider + * the name of the provider to work with certificates + */ + public void setCertProvider(String certProvider) { + this.certProvider = certProvider; + } + + /** + * @return the name of the provider to work with keystore to convert the + * main keystore to + */ + String getConvKsProvider() { + return convKsProvider; + } + + /** + * @param convKsProvider + * the name of the provider to work with keystore to convert the + * main keystore to + */ + public void setConvKsProvider(String convKsProvider) { + this.convKsProvider = convKsProvider; + } + + /** + * @return the name of the provider to work with keys + */ + String getKeyProvider() { + return keyProvider; + } + + /** + * @param keyProvider + * the name of the provider to work with keys + */ + public void setKeyProvider(String keyProvider) { + this.keyProvider = keyProvider; + } + + /** + * @return the name of the provider to work with keystore + */ + String getKsProvider() { + return ksProvider; + } + + /** + * @param ksProvider + * the name of the provider to work with keystore + */ + public void setKsProvider(String ksProvider) { + this.ksProvider = ksProvider; + } + + /** + * @return the name of the provider to work with message digests + */ + String getMdProvider() { + return mdProvider; + } + + /** + * @param mdProvider + * the name of the provider to work with message digests + */ + public void setMdProvider(String mdProvider) { + this.mdProvider = mdProvider; + } + + /** + * @return the name of the provider to work with signatures + */ + String getSigProvider() { + return sigProvider; + } + + /** + * @param sigProvider + * the name of the provider to work with signatures + */ + public void setSigProvider(String sigProvider) { + this.sigProvider = sigProvider; + } + + /** * @return Returns true if the certificate should be printed or exported in * printable format, false - if in binary format */ @@ -662,8 +784,8 @@ } /** - * @param password - * for the keystore to convert the current keystore to + * @param convertedKeyStorePass + * password for the keystore to convert the current keystore to */ public void setConvertedKeyStorePass(char [] convertedKeyStorePass) { this.convertedKeyStorePass = convertedKeyStorePass; @@ -677,8 +799,8 @@ } /** - * @param path - * to the keystore to convert the current keystore to + * @param convertedKeyStorePath + * path to the keystore to convert the current keystore to */ public void setConvertedKeyStorePath(String convertedKeyStorePath) { this.convertedKeyStorePath = convertedKeyStorePath; @@ -692,8 +814,8 @@ } /** - * @param type - * of the keystore to convert the current keystore to + * @param convertedKeyStoreType + * type of the keystore to convert the current keystore to */ public void setConvertedKeyStoreType(String convertedKeyStoreType) { this.convertedKeyStoreType = convertedKeyStoreType; @@ -707,8 +829,8 @@ } /** - * @param set - * true if key entries should be converted, false - if not + * @param convertKeyEnties + * set true if key entries should be converted, false - if not */ public void setConvertKeyEntries(boolean convertKeyEnties) { this.convertKeyEntries = convertKeyEnties; @@ -727,8 +849,8 @@ } /** - * @param the - * location of cacerts file, containing the certificates from + * @param cacertsPath + * the location of cacerts file, containing the certificates from * root certificate authorities (usually self-signed). */ public void setCacertsPath(String cacertsPath) { @@ -747,8 +869,8 @@ } /** - * @param password - * for cacerts keystore + * @param cacertsPass + * password for cacerts keystore */ public void setCacertsPass(char[] cacertsPass) { this.cacertsPass = cacertsPass; @@ -769,8 +891,9 @@ NoSuchAlgorithmException, CertificateException, NoSuchProviderException, IOException, KeytoolException { if (cacerts == null) { + String keyStoreProv = (ksProvider != null) ? ksProvider : provider; cacerts = KeyStoreLoaderSaver.loadStore(getCacertsPath(), - storeType, getCacertsPass(), provider); + storeType, getCacertsPass(), keyStoreProv); } return cacerts; } @@ -783,4 +906,18 @@ void setCacerts(KeyStore cacerts) { this.cacerts = cacerts; } + + /** + * @return topic to print help on + */ + String getHelpTopic() { + return helpTopic; } + /** + * @param helpTopic + * topic to print help on + */ + public void setHelpTopic(String helpTopic) { + this.helpTopic = helpTopic; + } +} 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CRLManager.java (working copy) @@ -34,9 +34,10 @@ 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. + * is stored in the file. If the file name is not given, stdin is used. * File with CRL and the checked certificate file are specified in param. * + * @return true if found at least one revoked certifiacte * @param param * @throws KeytoolException * @throws IOException @@ -46,13 +47,18 @@ * @throws FileNotFoundException * @throws NoSuchAlgorithmException */ - static void checkRevoked(KeytoolParameters param) throws FileNotFoundException, - CertificateException, NoSuchProviderException, CRLException, - IOException, KeytoolException, NoSuchAlgorithmException { + static boolean checkRevoked(KeytoolParameters param) + throws FileNotFoundException, CertificateException, + NoSuchProviderException, CRLException, IOException, + KeytoolException, NoSuchAlgorithmException { - String providerName = param.getProvider(); + String provider = param.getProvider(); + String certProvider = (param.getCertProvider() != null) ? param + .getCertProvider() : provider; + String mdProvider = (param.getMdProvider() != null) ? param + .getMdProvider() : provider; // firstly, get CRLs from the file - Collection crls = CertReader.readCRLs(param.getCrlFile(), providerName); + Collection crls = CertReader.readCRLs(param.getCrlFile(), certProvider); // quit, if couldn't read anything if (crls.isEmpty()) { throw new CRLException("Failed to generate a CRL from the input. "); @@ -67,33 +73,34 @@ } 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); + 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); + KeyStoreCertPrinter.printX509CertDetailed(cert, mdProvider); System.out.println("... is revoked on " + entry.getRevocationDate() + "\n"); foundRevoked = true; continue; - } + } } } - - if (certs.size() == 1 && !foundRevoked){ + + if (certs.size() == 1 && !foundRevoked) { System.out.println("The certificate ..."); KeyStoreCertPrinter.printX509CertDetailed((X509Certificate) certs - .iterator().next(), providerName); + .iterator().next(), mdProvider); System.out.println("... is not found in CRLs given"); - } else if (!foundRevoked){ + } else if (!foundRevoked) { System.out.println("The certificates are not found in CRLs given"); } + return foundRevoked; } } 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertImporter.java (working copy) @@ -68,6 +68,8 @@ String alias = param.getAlias(); KeyStore keyStore = param.getKeyStore(); boolean contains = keyStore.containsAlias(alias); + String certProvider = (param.getCertProvider() != null) ? param + .getCertProvider() : param.getProvider(); // if the alias already exists, try to import the certificate as // a cert reply @@ -76,13 +78,13 @@ KeyStore.PrivateKeyEntry.class)) { // read the certificates Collection certCollection = CertReader.readCerts( - param.getFileName(), false, param.getProvider()); + param.getFileName(), false, certProvider); importReply(param, certCollection); } else if (!contains) { // import a trusted certificate // read the certificate Collection trustedCert = CertReader.readCerts( - param.getFileName(), true, param.getProvider()); + param.getFileName(), true, certProvider); importTrusted(param, trustedCert.iterator().next()); } else {// if the existing entry is not a private key entry @@ -234,8 +236,10 @@ } 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()); + String mdProvider = (param.getMdProvider() != null) ? param + .getMdProvider() : param.getProvider(); + KeyStoreCertPrinter.printX509CertDetailed(newCert, + mdProvider); addIt = ArgumentsParser.getConfirmation( "Trust this certificate? [no] ", false); } @@ -297,11 +301,15 @@ // 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); - + String mdProvider = (param.getMdProvider() != null) ? param + .getMdProvider() : param.getProvider(); + KeyStoreCertPrinter.printX509CertDetailed(lastInChain, mdProvider); + 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."); 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/ArgumentsParser.java (working copy) @@ -91,6 +91,18 @@ final static String sProvider = "-provider"; + final static String sCertProvider = "-certprovider"; + + final static String sKeyProvider = "-keyprovider"; + + final static String sMdProvider = "-mdprovider"; + + final static String sSigProvider = "-sigprovider"; + + final static String sKsProvider = "-ksprovider"; + + final static String sConvKsProvider = "-convprovider"; + final static String sStorepass = "-storepass"; final static String sAlias = "-alias"; @@ -232,6 +244,9 @@ } if (args[i].compareToIgnoreCase(sHelp) == 0) { param.setCommand(Command.HELP); + if (args.length == i + 2){ + param.setHelpTopic(args[++i]); + } continue; } @@ -248,6 +263,30 @@ param.setProvider(args[++i]); continue; } + if (args[i].compareToIgnoreCase(sCertProvider) == 0) { + param.setCertProvider(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sKeyProvider) == 0) { + param.setKeyProvider(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sMdProvider) == 0) { + param.setMdProvider(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sSigProvider) == 0) { + param.setSigProvider(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sKsProvider) == 0) { + param.setKsProvider(args[++i]); + continue; + } + if (args[i].compareToIgnoreCase(sConvKsProvider) == 0) { + param.setConvKsProvider(args[++i]); + continue; + } if (args[i].compareToIgnoreCase(sAlias) == 0) { param.setAlias(args[++i]); continue; 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/Main.java (working copy) @@ -37,6 +37,9 @@ case LIST: KeyStoreCertPrinter.list(param); break; + case PRINTCERT: + KeyStoreCertPrinter.printCert(param); + break; case KEYCLONE: EntryManager.keyClone(param); break; @@ -55,10 +58,15 @@ case CHECK: CRLManager.checkRevoked(param); break; + case VERIFY: + CertChainVerifier.verifyChain(param); + break; + case CERTREQ: + CSRGenerator.certReq(param); + break; case HELP: - HelpPrinter.printHelp(); + HelpPrinter.printHelp(); break; - // TODO: calls for other options. } } @@ -81,8 +89,8 @@ // all commands except printcert and help work with a store if (command != Command.PRINTCERT && command != Command.HELP) { - // all commands that work with store except list and export - // need store password to with keystore. + // all commands that work with store except list and export + // need store password to with keystore. if (param.getStorePass() == null && command != Command.LIST && command != Command.EXPORT) { throw new KeytoolException( @@ -91,15 +99,15 @@ // prompt for additional parameters if some of the expected // ones have not been specified. ArgumentsParser.getAdditionalParameters(param); - } - // print the warning if store password is not set - if (param.getStorePass() == null) { - System.out - .println("\nWARNING!!!\nThe integrity of the keystore data " - + "has NOT been checked!\n" - + "To check it you must provide your keystore password!\n"); - } + // print the warning if store password is not set + if (param.getStorePass() == null) { + System.out.println("\nWARNING!!!\nThe integrity " + + "of the keystore data has NOT been checked!\n" + + "To check it you must provide" + + " your keystore password!\n"); + } + } // the work is being done here doWork(param); 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreCertPrinter.java (working copy) @@ -82,7 +82,8 @@ + ((keyStoreSize == 1) ? " entry \n" : " entries \n")); } - String provider = param.getProvider(); + String mdProvider = (param.getMdProvider() != null) ? param + .getMdProvider() : param.getProvider(); while (aliases.hasMoreElements()) { String currentAlias = (String) aliases.nextElement(); @@ -134,14 +135,14 @@ if (param.isVerbose()) { // print out the first certificate System.out.println("Certificate[1]:"); - printX509CertDetailed(x509cert, provider); + printX509CertDetailed(x509cert, mdProvider); if (!trustedEntry) { for (int i = 1; i < certChain.length; i++) { System.out.println("Certificate[" + (i + 1) + "]:"); printX509CertDetailed( (X509Certificate) certChain[i], - provider); + mdProvider); } } } @@ -197,10 +198,11 @@ String commaSpc = ", "; System.out.print(currentAlias + commaSpc + creationDate + commaSpc + entryType); - + if (!secretKeyEntry) { - System.out.print(commaSpc + "\nCertificate fingerprint (MD5): "); - printMD(x509cert.getEncoded(), "MD5", provider); + System.out.print(commaSpc + + "\nCertificate fingerprint (MD5): "); + printMD(x509cert.getEncoded(), "MD5", mdProvider); } else { // If the key is explicitly asked to be printed // by setting the alias, print it out, otherwise - do @@ -313,9 +315,15 @@ static void printCert(KeytoolParameters param) throws FileNotFoundException, CertificateException, IOException, KeytoolException, NoSuchAlgorithmException, NoSuchProviderException { + + String provider = param.getProvider(); + String certProvider = (param.getCertProvider() != null) ? param + .getCertProvider() : provider; + String mdProvider = (param.getMdProvider() != null) ? param + .getMdProvider() : provider; // get the certificate(s) from the file Collection certCollection = CertReader.readCerts(param.getFileName(), - false, param.getProvider()); + false, certProvider); Iterator certIter = certCollection.iterator(); int counter = 1; @@ -323,7 +331,7 @@ while (certIter.hasNext()) { X509Certificate cert = (X509Certificate) certIter.next(); System.out.println("\nCertificate[" + counter + "]:"); - printX509CertDetailed(cert, param.getProvider()); + printX509CertDetailed(cert, mdProvider); ++counter; } } 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/HelpPrinter.java (working copy) @@ -20,11 +20,302 @@ * Class for printing help messages . */ public class HelpPrinter { + private static StringBuffer message; + + final static String certReq = "-certreq"; + final static String checkCRL = "-checkcrl"; + final static String convert = "-convert"; + final static String delete = "-delete"; + final static String export = "-export"; + final static String genKey = "-genkey"; + final static String help = "-help"; + final static String sImport = "-import"; + final static String keyClone = "-keyclone"; + final static String keyPasswd = "-keypasswd"; + final static String list = "-list"; + final static String printCert = "-printcert"; + final static String selfCert = "-selfcert"; + final static String storePasswd = "-storepasswd"; + final static String verify = "-verify"; + + final static String keyStore = " {-keystore }"; + final static String storeType = " {-storetype }"; + final static String keyPass = " {-keypass }"; + final static String oldKeyPass = " {-keypass }"; + final static String storePass = " {-storepass }"; + final static String provider = " {-provider }"; + final static String certReqFile = " {-file }"; + final static String certFile = " {-file }"; + final static String keyAlg = " {-keyalg }"; + final static String sigAlg = " {-sigalg }"; + final static String keySize = " {-keysize }"; + final static String alias = " {-alias }"; + final static String dName = " {-dname }"; + final static String validity = " {-validity }"; + final static String verbose = " {-v}"; + final static String verboseOrRfc = " {-rfc | -v}"; + final static String javaOption = " {-J}"; + final static String crlFile = " {-crlfile }"; + final static String convKeyStore = " {-convkeystore }"; + final static String convStoreType = " {-convtype }"; + final static String convStorePass = " {-convstorepass }"; + final static String convKeys = " {-convkeys}"; + final static String ca = " {-ca}"; + final static String secretKey = " {-secretkey}"; + final static String trustCAcerts = " {-trustcacerts}"; + final static String noPrompt = " {-noprompt}"; + final static String cacerts = " {-cacerts }"; + final static String cacertsPass = " {-cacertspass }"; + final static String x509version = " {-x509version }"; + final static String dest = " {-dest }"; + final static String sNew = " {-new }"; + final static String issuer = " {-issuer }"; + final static String issuerPass = " {-issuerpass }"; + final static String serialNum = " {-certserial }"; + final static String newLine = "\n"; + final static String doubleNewLine = "\n\n"; + final static String ksTypePassVProvCacerts = newLine + keyStore + storeType + + newLine + storePass + verbose + provider + newLine + cacerts + + cacertsPass + doubleNewLine; + /** * Prints the help message. */ static void printHelp() { - // TODO - System.out.println("Help message here"); + if (message == null) { + message = new StringBuffer(); + String tab = "\t"; + String doubleTab = "\t\t"; + String tripleTab = "\t\t\t"; + message.append("\nKeytool usage:\n"); + message.append("keytool {-} {-}" + + " {}... -J\n\n"); + message.append("Known commands:\n"); + message.append(tab + certReq + doubleTab + + "Generate certificate request\n"); + message.append(tab + checkCRL + doubleTab + + "Check certificates revocation status\n"); + message.append(tab + convert + doubleTab + + "Convert keystore to another format\n"); + message.append(tab + delete + tripleTab + + "Remove entry from keystore\n"); + message.append(tab + export + tripleTab + + "Export certificate to a file or stdout\n"); + message.append(tab + genKey + tripleTab + + "Secret key or key pair generation\n"); + message.append(tab + help + tripleTab + + "This help message or help on a command\n"); + message.append(tab + sImport + tripleTab + + "Import a certificate (chain) or a CSR reply\n"); + message.append(tab + keyClone + doubleTab + + "Duplicate a key entry\n"); + message.append(tab + keyPasswd + doubleTab + + "Change key password\n"); + message.append(tab + printCert + doubleTab + + "Print to stdout a certificate from file\n"); + message.append(tab + selfCert + doubleTab + + "Generate a self-signed certificate " + + "with existing key\n"); + message.append(tab + storePasswd + doubleTab + + "Change keystore password\n"); + message.append(tab + verify + tripleTab + + "Verify a certificate chain\n"); + + message.append("\nHelp usage:\n"); + message.append("keytool -help {}\n"); + message.append("E.g.:\t keytool -help genkey\n"); + } + System.out.println(message); } -} + + static void topicHelp(String topic) { + StringBuffer topicMsg = new StringBuffer(); + if (topic.equalsIgnoreCase("certreq")) { + topicMsg.append(" Generates a Certificate Signing Request " + + "(CSR). The request is generated\n"); + topicMsg.append(" based on data taken from keystore entry " + + "associated with alias given.\n"); + topicMsg.append(" The certificate request " + + "is printed to a file, if its name is supplied\n"); + topicMsg.append(" or otherwise printed to stdout.\n"); + topicMsg.append("\ncertreq Usage:\n"); + topicMsg.append(certReq + alias + certReqFile + newLine + sigAlg + + keyPass + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("checkcrl")) { + topicMsg.append(" Checks if the certificate given in the file " + + "is contained in the CRL which\n"); + topicMsg.append(" is stored in the CRL file. If the file " + + "name is not given, stdin is used.\n"); + topicMsg.append("\ncheckcrl Usage:\n"); + topicMsg.append(checkCRL + certFile + crlFile + + ksTypePassVProvCacerts); + } else if (topic.equalsIgnoreCase("convert")) { + topicMsg.append(" Converts keystore to another format.\n" + + " If \"-convkeys\" option has been specified, " + + "an attempt to convert\n key entries is performed." + + " Only entries with password equal to \n" + + " keystore password are converted.\n"); + topicMsg.append("\nconvert Usage:\n"); + topicMsg.append(convert + convStoreType + convKeyStore + newLine + + convStorePass + convKeys + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("delete")) { + topicMsg.append(" Removes from the keystore the entry " + + "associated with alias.\n"); + topicMsg.append("\ndelete Usage:\n"); + topicMsg.append(delete + alias + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("export")) { + topicMsg.append(" Reads an X.509 certificate associated with " + + "alias and prints it into the\n"); + topicMsg.append(" given file. If The file"); + topicMsg.append(" name is not given, the certificate is printed\n" + + " to stdout.\n"); + topicMsg.append("\nexport Usage:\n"); + topicMsg.append(export + verboseOrRfc + alias + certFile + + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("genkey")) { + topicMsg.append(" Generates a key pair or a secret key." + + " Key pair is composed of a private\n"); + topicMsg.append(" and a public key. Wraps the public key " + + "into a self-signed X.509\n"); + topicMsg.append(" (v1, v2, v3) certificate and puts the " + + "certificate into a single-element\n"); + topicMsg.append(" certificate chain or signs the certificate " + + "with private key from another\n"); + topicMsg.append(" key entry and adds its chain to the newly " + + "generated certificate . After\n"); + topicMsg.append(" that adds to the keystore a new " + + "entry containing the generated\n"); + topicMsg.append(" private key and the chain. If a secret key is " + + "generated it is put into a\n"); + topicMsg + .append(" secret key entry, with null certificate chain.\n"); + topicMsg + .append(" If \"-ca\" option is specified, generated certificate\n"); + topicMsg + .append(" will can be used for signing another certifictes.\n"); + topicMsg + .append(" If \"-secretkey\" option is specified, a secret key will.\n"); + topicMsg + .append(" be generated instead of key pair and a certificate which\n"); + topicMsg.append(" are generated by default. \n"); + + topicMsg.append("\ngenkey usage\n"); + topicMsg.append(genKey + alias + keyAlg + newLine + keySize + + sigAlg + newLine + validity + dName + newLine + + x509version + ca + serialNum + newLine + secretKey + + keyPass + newLine + issuer + issuerPass + + ksTypePassVProvCacerts); + } else if (topic.equalsIgnoreCase("help")) { + printHelp(); + } else if (topic.equalsIgnoreCase("import")) { + topicMsg.append(" Reads an X.509 certificate or a PKCS#7 " + + "formatted certificate chain from\n"); + topicMsg.append(" the file specified in param and puts it " + + "into the entry identified by the\n"); + topicMsg.append(" supplied alias. If the input file is " + + "not specified, the certificates are\n"); + topicMsg.append(" read from the standard input.\n"); + topicMsg.append("\nimport Usage:\n"); + topicMsg.append(sImport + alias + certFile + newLine + noPrompt + + trustCAcerts + newLine + keyPass + cacerts + newLine + + cacertsPass + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("keyclone")) { + topicMsg.append(" Copies the key and the certificate " + + "chain (if any) from the keystore entry\n"); + topicMsg.append(" identified by given alias into a newly " + + "created one with given destination.\n"); + topicMsg.append("\nkeyclone Usage:\n"); + topicMsg.append(keyClone + alias + dest + newLine + sNew + keyPass + + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("keypasswd")) { + topicMsg.append(" Changes the key password to the new one.\n"); + topicMsg.append("\nkeypasswd Usage:\n"); + topicMsg.append(keyPasswd + alias + oldKeyPass + newLine + sNew + + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("list")) { + topicMsg.append(" Prints the contents of the entry associated " + + "with the alias given. \n"); + topicMsg.append(" If no alias is specified, the contents of " + + "the entire keystore are printed.\n"); + topicMsg.append("\nlist Usage:\n"); + topicMsg.append(list + verboseOrRfc + alias + + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("printcert")) { + topicMsg.append(" Prints the detailed description of a " + + "certificate in a human-readable\n"); + topicMsg.append(" format: its owner and issuer, serial number, " + + "validity period and\n"); + topicMsg.append(" fingerprints.\n"); + topicMsg.append("\nprintcert Usage:\n"); + topicMsg.append(printCert + verbose + certFile + doubleNewLine); + + } else if (topic.equalsIgnoreCase("selfcert")) { + topicMsg.append(" Generates an X.509 (v1, v2, v3) self-signed " + + "certificate using a key pair\n"); + topicMsg.append(" associated with alias. " + + "If X.500 Distinguished Name is supplied it is \n"); + topicMsg.append(" used as both subject and issuer of the" + + "certificate. Otherwise the\n"); + topicMsg.append(" distinguished name associated with alias is" + + " used. Signature algorithm,\n"); + topicMsg.append(" validity period and certificate serial" + + " number are taken from command line if \n"); + topicMsg.append(" defined there or " + + "from the keystore entry identified by alias.\n"); + topicMsg + .append(" If \"-ca\" option is specified, generated certificate\n"); + topicMsg + .append(" will can be used for signing another certifictes.\n"); + topicMsg + .append(" If \"-secretkey\" option is specified, a secret key will.\n"); + topicMsg + .append(" be generated instead of key pair and a certificate which\n"); + topicMsg.append(" are generated by default. \n"); + topicMsg.append("\nselfcert Usage:\n"); + topicMsg.append(selfCert + alias + dName + newLine + validity + + sigAlg + newLine + keyPass + ca + serialNum + + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("storepasswd")) { + topicMsg.append(" Changes the keystore password to the new one.\n"); + topicMsg.append("\nstorepasswd Usage:\n"); + topicMsg.append(storePasswd + sNew + ksTypePassVProvCacerts); + + } else if (topic.equalsIgnoreCase("verify")) { + topicMsg.append(" A cerificate chain is built by looking up " + + "the certificate of the issuer\n"); + topicMsg.append(" of the current certificate. If a sertificate " + + "is self-signed it is assumed\n"); + topicMsg.append(" to be the root CA. After that the certificates " + + "are searched in the lists\n"); + topicMsg.append(" of revoked certificates. Certificate signatures " + + "are checked and\n"); + topicMsg.append(" certificate path is built in the same way as in " + + "import operation. If an\n"); + topicMsg.append(" error occurs the flow is not stopped but an " + + "attempt to continue is made.\n"); + topicMsg.append(" The results of the verification are" + + " printed to stdout.\n"); + topicMsg.append("\nverify Usage:\n"); + topicMsg.append(verify + certFile + crlFile + newLine + + trustCAcerts + cacerts + newLine + cacertsPass + + ksTypePassVProvCacerts); + + } else { + System.out.println("The option with name <" + topic + + "> is unknown."); + printHelp(); + return; + } + System.out.println(topicMsg); + } + +} 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CertChainVerifier.java (working copy) @@ -24,11 +24,13 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PublicKey; +import java.security.SignatureException; 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.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; @@ -38,6 +40,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -51,7 +54,8 @@ * certificate chains. */ public class CertChainVerifier { - private final static String strFailed = "Failed to build a certificate chain from reply.\n"; + 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 @@ -61,12 +65,147 @@ * 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. + * + * @param param + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws FileNotFoundException + * @throws CertificateException + * @throws IOException + * @throws KeytoolException + * @throws KeyStoreException */ - static void verifyChain(KeytoolParameters param){ - // TODO - throw new RuntimeException("Method is not implemented yet."); + static void verifyChain(KeytoolParameters param) + throws NoSuchAlgorithmException, NoSuchProviderException, + FileNotFoundException, CertificateException, IOException, + KeytoolException, KeyStoreException { + + try { + if (param.getCrlFile() != null) { + CRLManager.checkRevoked(param); + } else { + System.out + .println("Certificates revocation status is not checked, " + + "CRL file name is not supplied."); + } + } catch (Exception e) { + System.out.println(e); + System.out.println("Failed to check revocation status."); + } + + String provider = param.getProvider(); + String certProvider = (param.getCertProvider() != null) ? param + .getCertProvider() : provider; + String sigProvider = (param.getSigProvider() != null) ? param + .getSigProvider() : provider; + String mdProvider = (param.getMdProvider() != null) ? param + .getMdProvider() : provider; + + // Don't catch exceptions here, because if exception is + // thrown here, there is no need to proceed. + Collection certs = CertReader.readCerts(param + .getFileName(), false, certProvider); + X509Certificate[] ordered = orderChain(certs); + + try { + for (int i = 0; i < ordered.length - 1; i++) { + checkSignature(ordered[i], ordered[i + 1].getPublicKey(), + sigProvider, mdProvider); + } + // check the signature of the last element of the ordered chain + boolean lastSignChecked = findIssuerAndCheckSignature(param + .getKeyStore(), ordered[ordered.length - 1], sigProvider, + mdProvider); + // if haven't found issuer's certificate in main keystore + if (!lastSignChecked) { + if (param.isTrustCACerts()) { + // make the search and check again + lastSignChecked = findIssuerAndCheckSignature(param + .getCacerts(), ordered[ordered.length - 1], + sigProvider, mdProvider); + } + + if (!lastSignChecked) { + System.out + .println("Failed to find the issuer's certificate."); + System.out + .println("Failed to check the signature of certificate:"); + KeyStoreCertPrinter.printX509CertDetailed( + ordered[ordered.length - 1], mdProvider); + } + } + } catch (Exception e) { + System.out.println(e); + System.out.println("Signature check failed."); + } + + try { + buildCertPath(param, ordered[0]); + + // won't come here if exception is thrown + System.out.println("Certificate path is built successfully."); + } catch (Exception e) { + // Exception's own message contains strFailed string, + // but its cause can be more informative here. + System.out.println(e.getCause()); + System.out.println("Failed to build a certificate path."); + } + System.out.println("Verification complete."); } - + + // Checks the signature, prints the result. Returns true if + // signature verification process succeeds (no exceptions or + // SignatureException thrown) + private static boolean checkSignature(X509Certificate cert, + PublicKey pubKey, String sigProvider, String mdProvider) + throws CertificateEncodingException, NoSuchAlgorithmException, + NoSuchProviderException { + try { + if (sigProvider == null) { + cert.verify(pubKey); + } else { + cert.verify(pubKey, sigProvider); + } + } catch (SignatureException e) { + System.out.println("The signature is incorrect for certificate: "); + KeyStoreCertPrinter.printX509CertDetailed(cert, mdProvider); + } catch (Exception e) { + System.out.println(e); + System.out + .println("Signature verification failed for certificate: "); + KeyStoreCertPrinter.printX509CertDetailed(cert, mdProvider); + return false; + } + return true; + } + + // Searches for cert issuer's certificate in keyStore and checks if + // cert was signed using the private key corresponding to public key + // wrapped into the found certificate. + private static boolean findIssuerAndCheckSignature(KeyStore keyStore, + X509Certificate cert, String sigProvider, String mdProvider) + throws KeyStoreException, CertificateEncodingException, + NoSuchAlgorithmException, NoSuchProviderException { + + Enumeration keyStoreAliases = keyStore.aliases(); + while (keyStoreAliases.hasMoreElements()) { + // get a certificate from keyStore + X509Certificate nextKScert = (X509Certificate) keyStore + .getCertificate((String) keyStoreAliases.nextElement()); + if (nextKScert == null) { + continue; + } + if (Arrays.equals(cert.getIssuerX500Principal().getEncoded(), + nextKScert.getSubjectX500Principal().getEncoded())) { + checkSignature(cert, keyStore.getCertificate( + (String) keyStoreAliases.nextElement()).getPublicKey(), + sigProvider, mdProvider); + return true; + } + } + return false; + } + /** * Builds a certificate chain from the given X509Certificate newCert to a * self-signed root CA whose certificate is contained in the keystore or @@ -102,9 +241,12 @@ Collection trustedCerts = trustedSeparated[1]; Collection selfSignedTAsCerts = trustedSeparated[2]; - CertPathBuilderResult bldResult = buildCertPath(param.getProvider(), - newCert, selfSignedTAs, trustedCerts); + String certProvider = (param.getCertProvider() != null) ? param + .getCertProvider() : param.getProvider(); + CertPathBuilderResult bldResult = buildCertPath(certProvider, 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 @@ -177,15 +319,16 @@ Set selfSignedTAs = (Set) trustedSeparated[0]; Collection trustedCerts = trustedSeparated[1]; - return buildCertPath(param.getProvider(), newCert, selfSignedTAs, - trustedCerts); + String certProvider = (param.getCertProvider() != null) ? param + .getCertProvider() : param.getProvider(); + + return buildCertPath(certProvider, newCert, selfSignedTAs, trustedCerts); } - // Build a certificte chain up to the self-signed trust anchor, based on // trusted certificates given. // - // @param provider + // @param certProvider // @param newCert // is a certificate to build chain for. // @param selfSignedTAs @@ -193,7 +336,7 @@ // @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, + private static CertPathBuilderResult buildCertPath(String certProvider, X509Certificate newCert, Set selfSignedTAs, Collection trustedCerts) throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, CertPathBuilderException, KeytoolException, @@ -218,7 +361,7 @@ if (trustedCerts != null) { CollectionCertStoreParameters trustedCertsCCSParams = - new CollectionCertStoreParameters(trustedCerts); + new CollectionCertStoreParameters(trustedCerts); CertStore trustedCertStore; try { trustedCertStore = CertStore.getInstance("Collection", @@ -236,17 +379,17 @@ CertPathBuilder cpBuilder; try { - if (provider == null) { + if (certProvider == null) { cpBuilder = CertPathBuilder.getInstance(strPKIX); } else { - cpBuilder = CertPathBuilder.getInstance(strPKIX, provider); + cpBuilder = CertPathBuilder.getInstance(strPKIX, certProvider); } } catch (NoSuchAlgorithmException e) { throw new NoSuchAlgorithmException("The algorithm " + strPKIX + " is not available.", e); } catch (NoSuchProviderException e) { throw (NoSuchProviderException) new NoSuchProviderException( - "The provider " + provider + "The certProvider " + certProvider + " is not found in the environment.").initCause(e); } @@ -263,7 +406,6 @@ 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. @@ -339,7 +481,7 @@ } } } - + // if "-trustcacerts" is specified, add CAs from cacerts if (trustCaCerts) { KeyStore cacertsFile = null; @@ -505,7 +647,7 @@ return ordered; } - + // orders a chain without a starting element given static X509Certificate[] orderChain(Collection certs) throws KeytoolException { @@ -543,8 +685,7 @@ 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 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/EntryManager.java (working copy) @@ -33,8 +33,8 @@ */ public class EntryManager { /** - * Copies the private key and the certificate chain from the keystore entry - * identifiad by given alias into a newly created one with given destination + * Copies the key and the certificate chain (if any) from the keystore entry + * identified by given alias into a newly created one with given destination * alias. alias and destination alias are specified in param. * * @param param 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 428231) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/KeyStoreLoaderSaver.java (working copy) @@ -59,15 +59,17 @@ if (param.getStorePath() == null) { param.setStorePath(KeytoolParameters.defaultKeystorePath); } + String ksProvider = (param.getKsProvider() != null) ? param + .getKsProvider() : param.getProvider(); KeyStore keyStore; if (new File(param.getStorePath()).exists()) { // load an existing store keyStore = loadStore(param.getStorePath(), param.getStoreType(), - param.getStorePass(), param.getProvider()); + param.getStorePass(), ksProvider); } else { // create a new store if it doesn't exist keyStore = loadStore(null, param.getStoreType(), param - .getStorePass(), param.getProvider()); + .getStorePass(), ksProvider); param.setNeedSaveKS(true); } param.setKeyStore(keyStore); Index: modules/tools/src/main/java/org/apache/harmony/tools/keytool/CSRGenerator.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/keytool/CSRGenerator.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/keytool/CSRGenerator.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.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.InvalidKeyException; +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.Signature; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Vector; + +import org.apache.harmony.luni.util.Base64; +import org.apache.harmony.security.pkcs10.CertificationRequest; +import org.apache.harmony.security.pkcs10.CertificationRequestInfo; +import org.apache.harmony.security.x501.Name; +import org.apache.harmony.security.x509.AlgorithmIdentifier; +import org.apache.harmony.security.x509.SubjectPublicKeyInfo; + +/** + * Class for generating X.509 Certificate Signing Requests (CSRs). The generated + * certificate request is printed to a file, if its name is supplied in param, + * or otherwise printed to stdout. + */ +public class CSRGenerator { + /** + * Generates a Certificate Signing Request (CSR). The request is generated + * based on data taken from keystore entry associated with alias given in + * parameter param. The certificate request is printed to a file, if its + * name is supplied in param, or otherwise printed to stdout. + * + * @param param + * @throws KeyStoreException + * @throws UnrecoverableKeyException + * @throws NoSuchAlgorithmException + * @throws IOException + * @throws InvalidKeyException + * @throws SignatureException + * @throws NoSuchProviderException + * @throws KeytoolException + * @throws CertificateException + */ + static void certReq(KeytoolParameters param) throws KeyStoreException, + NoSuchAlgorithmException, UnrecoverableKeyException, IOException, + InvalidKeyException, SignatureException, NoSuchProviderException, + KeytoolException, CertificateException { + + KeyStore keyStore = param.getKeyStore(); + String alias = param.getAlias(); + + if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { + throw new KeytoolException( + "Failed to generate a certificate request. \n" + "Entry <" + + alias + "> is not a private key entry. "); + } + + // get the existing certificate and keys associated with the alias + X509Certificate cert = (X509Certificate) keyStore.getCertificate(param + .getAlias()); + PrivateKey privateKey; + try { + privateKey = (PrivateKey) keyStore.getKey(param.getAlias(), param + .getKeyPass()); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException( + "Cannot find the algorithm to recover the key. ", e); + } + PublicKey publicKey = cert.getPublicKey(); + + Name distinguishedName; + try { + distinguishedName = new Name(cert.getSubjectDN().getName()); + } catch (IOException e) { + throw (IOException) new IOException( + "Failed to generate a distinguished name. ").initCause(e); + } + + SubjectPublicKeyInfo subjectPublicKeyInfo = null; + try { + subjectPublicKeyInfo = (SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1 + .decode(publicKey.getEncoded()); + } catch (IOException e) { + throw (IOException) new IOException( + "Failed to decode SubjectPublicKeyInfo. ").initCause(e); + } + + // generate CertificationRequestInfo based on data taken from + // the existing certificate. + CertificationRequestInfo certReqInfo = new CertificationRequestInfo( + cert.getVersion(), distinguishedName, subjectPublicKeyInfo, + // attributes + new Vector()); + byte[] infoEncoding = certReqInfo.getEncoded(); + + // generate the signature + String sigAlgName = (param.getSigAlg() != null) ? param.getSigAlg() + : cert.getSigAlgName(); + + Signature sig; + String sigProvider = (param.getSigProvider() != null) ? param + .getSigProvider() : param.getProvider(); + try { + sig = (sigProvider != null) ? Signature.getInstance(sigAlgName, + sigProvider) : Signature.getInstance(sigAlgName); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException("The algorithm " + sigAlgName + + " is not found in the environment.", e); + } catch (NoSuchProviderException e) { + throw (NoSuchProviderException) new NoSuchProviderException( + "The provider " + sigProvider + + " is not found in the environment.").initCause(e); + } + + try { + sig.initSign(privateKey); + } catch (InvalidKeyException e) { + throw new InvalidKeyException( + "The private key used to generate the signature is invalid.", + e); + } + + byte[] signatureValue; + try { + sig.update(infoEncoding, 0, infoEncoding.length); + signatureValue = sig.sign(); + } catch (SignatureException e) { + throw new SignatureException("Failed to sign the certificate. ", e); + } + + // generating the request + CertificationRequest certReq = new CertificationRequest(certReqInfo, + new AlgorithmIdentifier(cert.getSigAlgOID()), signatureValue); + byte[] certReqEncoding = certReq.getEncoded(); + + OutputStream output; + // if no file name is given, output to System.out + String fileName = param.getFileName(); + if (fileName == null) { + output = System.out; + } else { // output to a file if the name is supplied + File file = new File(fileName); + // the file will be created if it doesn't already exist. + // If it already exists and is not a file, then an IOException will + // be thrown. + file.createNewFile(); + + output = new BufferedOutputStream(new FileOutputStream(file)); + } + + output.write("-----BEGIN NEW CERTIFICATE REQUEST-----\n".getBytes()); + output.write(Base64.encode(certReqEncoding, "ISO-8859-1").getBytes()); + output.write("\n-----END NEW CERTIFICATE REQUEST-----\n".getBytes()); + output.flush(); + + if (param.isVerbose() && fileName != null) { + System.out.println("The certificate request is stored in file <" + + fileName + ">."); + } + } +} +