blob: aaf1d92eedc9f763f56192e46a51dff41326e1f8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
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;
/**
* Class for printing to stdout the contents of the keystore or a single
* certificate. The certificate can be in the keystore (list(..) method) or not
* (printCert(..) method).
*/
public class KeyStoreCertPrinter {
/**
* Prints the contents of the entry associated with the alias given in
* param. If no alias is specified, the contents of the entire keystore are
* printed.
*
* @param param
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws UnrecoverableKeyException
* @throws IOException
* @throws FileNotFoundException
* @throws CertificateException
*/
static void list(KeytoolParameters param) throws KeyStoreException,
NoSuchAlgorithmException, NoSuchProviderException,
UnrecoverableKeyException, CertificateException,
FileNotFoundException, IOException {
Enumeration aliases;
KeyStore keyStore = param.getKeyStore();
String alias = param.getAlias();
if (alias != null) {
// if the alias is specified, make a single-element
// enumeration of it
aliases = Collections.enumeration(Collections.singleton(alias));
} else {// if the alias is not given,
// get all aliases
aliases = keyStore.aliases();
// print the keystore info
System.out.println("Type of keystore: " + keyStore.getType());
System.out.println("Keystore provider name: "
+ keyStore.getProvider().getName());
int keyStoreSize = keyStore.size();
System.out.println("\nThe keystore contains " + keyStoreSize
+ ((keyStoreSize == 1) ? " entry \n" : " entries \n"));
}
String mdProvider = (param.getMdProvider() != null) ? param
.getMdProvider() : param.getProvider();
while (aliases.hasMoreElements()) {
String currentAlias = (String) aliases.nextElement();
String creationDate = keyStore.getCreationDate(currentAlias)
.toString();
// true if the keystore entry is a TrustedCertificateEntry
boolean trustedEntry = false;
// true if the keystore entry is a SecretKeyEntry
boolean secretKeyEntry = false;
// get the type of the entry to print it out
String entryType = "Key entry";
if (keyStore.entryInstanceOf(currentAlias,
KeyStore.TrustedCertificateEntry.class)) {
entryType = "Trusted certificate entry";
trustedEntry = true;
} else if (keyStore.entryInstanceOf(currentAlias,
KeyStore.PrivateKeyEntry.class)) {
entryType = "Private key entry";
} else if (keyStore.entryInstanceOf(currentAlias,
KeyStore.SecretKeyEntry.class)) {
entryType = "Secret key entry";
secretKeyEntry = true;
}
// get the certificate associated with the currentAlias
X509Certificate x509cert = ((X509Certificate) keyStore
.getCertificate(currentAlias));
// if -v or -rfc options are specified
if (param.isVerbose() || param.isRfc()) {
// print detailed info about the _entry_
System.out.println("Alias name: " + currentAlias);
System.out.println("Date of creation: " + creationDate);
System.out.println("Type of the entry: " + entryType);
if (!secretKeyEntry) {
Certificate[] certChain = keyStore
.getCertificateChain(currentAlias);
if (!trustedEntry) {
System.out.println("Certificate chain length: "
+ certChain.length);
}
// if -v option was given, print the detailed info about
// the certificate
if (param.isVerbose()) {
// print out the first certificate
System.out.println("Certificate[1]:");
printX509CertDetailed(x509cert, mdProvider);
if (!trustedEntry) {
for (int i = 1; i < certChain.length; i++) {
System.out.println("Certificate[" + (i + 1)
+ "]:");
printX509CertDetailed(
(X509Certificate) certChain[i],
mdProvider);
}
}
}
// if -rfc option is given, print the certificate in Base64
// printable format
else {
// print out the first certificate
System.out.println("Certificate[1]:");
System.out.println("-----BEGIN CERTIFICATE-----");
System.out.println(Base64.encode(x509cert.getEncoded(),
"ISO-8859-1"));
System.out.println("-----END CERTIFICATE-----");
if (!trustedEntry) {
for (int i = 1; i < certChain.length; i++) {
System.out.println("Certificate[" + (i + 1)
+ "]:");
System.out
.println("-----BEGIN CERTIFICATE-----");
System.out.println(Base64.encode(certChain[i]
.getEncoded(), "ISO-8859-1"));
System.out.println("-----END CERTIFICATE-----");
}
}
}
} else {
// if the key is explicitly asked to be printed
// by setting the alias, print it out, otherwise - do
// nothing.
if (alias != null) {
// TODO: ask for password if not set, when read from
// stdin is OK.
char[] keyPass;
if ((keyPass = param.getKeyPass()) != null) {
Key key = keyStore.getKey(currentAlias, keyPass);
System.out.println("Algorithm: "
+ key.getAlgorithm() + "\nFormat: "
+ key.getFormat());
System.out.println("Key: "
+ formatBytes(key.getEncoded()));
} else {
System.out.println("If you want to print the key, "
+ "please set the entry password using "
+ "\"-keypass\" option");
}
}
}
System.out.println("\n*******************************"
+ "*******************************\n");
} else {// if neither -v nor -rfc options specified
String commaSpc = ", ";
System.out.print(currentAlias + commaSpc + creationDate
+ commaSpc + entryType);
if (!secretKeyEntry) {
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
// nothing.
if (alias != null) {
char[] keyPass;
if ((keyPass = param.getKeyPass()) != null) {
Key key = keyStore.getKey(currentAlias, keyPass);
System.out.println(key.getAlgorithm() + ", "
+ key.getFormat() + ", "
+ formatBytes(key.getEncoded()));
} else {
System.out.println("If you want to print the key, "
+ "please set the entry password using "
+ "\"-keypass\" option");
}
}
}
}
}
}
/**
* Prints the detailed description of a certificate in a human-readable
* format: its owner and issuer, serial number, validity period and
* fingerprints. providerName is needed to generate MessageDigest. If it is
* null, a default one is used.
*
* @param x509cert
* @param providerName
* @throws CertificateEncodingException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
static void printX509CertDetailed(X509Certificate x509cert,
String providerName) throws CertificateEncodingException,
NoSuchAlgorithmException, NoSuchProviderException {
System.out.println("Owner: " + x509cert.getSubjectX500Principal());
System.out.println("Issuer: " + x509cert.getIssuerX500Principal());
System.out.println("Serial number: "
+ Integer.toHexString(x509cert.getSerialNumber().intValue()));
System.out.println("Validity period \n\t from: "
+ x509cert.getNotBefore() + "\n\t until: "
+ x509cert.getNotAfter());
// print certificate fingerprints (MD5 and SHA1).
byte[] encodedCert;
try {
encodedCert = x509cert.getEncoded();
} catch (CertificateEncodingException e) {
throw new CertificateEncodingException(
"Failed to encode the certificate", e);
}
String strMD5 = "MD5";
String strSHA1 = "SHA1";
System.out.print("Certificate fingerprints: " + "\n\t " + strMD5
+ ": ");
printMD(encodedCert, strMD5, providerName);
System.out.print("\t " + strSHA1 + ": ");
printMD(encodedCert, strSHA1, providerName);
}
// Prints out the message digest of the encoding using the given algorithm
// and provider. Provider can be null.
private static void printMD(byte[] encoding, String mdAlgorithm,
String providerName) throws NoSuchAlgorithmException,
NoSuchProviderException {
byte[] digest;
// if provider name is given, use it when getting
// an instance of MessageDigest.
try {
if (providerName != null) {
digest = MessageDigest.getInstance(mdAlgorithm, providerName)
.digest(encoding);
} else {
digest = MessageDigest.getInstance(mdAlgorithm)
.digest(encoding);
}
} catch (NoSuchAlgorithmException e) {
throw new NoSuchAlgorithmException("The algorithm " + mdAlgorithm
+ " is not found in the environment.", e);
} catch (NoSuchProviderException e) {
throw (NoSuchProviderException) new NoSuchProviderException(
"The provider " + providerName
+ " is not found in the environment.").initCause(e);
}
// print out in the way: "0A:1B:C3:D4:..."
System.out.println(formatBytes(digest));
}
/**
* 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 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)
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, certProvider);
Iterator certIter = certCollection.iterator();
int counter = 1;
// print the detailed info on all certificates
while (certIter.hasNext()) {
X509Certificate cert = (X509Certificate) certIter.next();
System.out.println("\nCertificate[" + counter + "]:");
printX509CertDetailed(cert, mdProvider);
++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
// the length of the String representing 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;
String currentByte;
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(..))
currentByte = Integer.toHexString(bytes[i]).toUpperCase();
if ((length = currentByte.length()) > 1) {
buffer.append(currentByte.substring(length - 2));
} else {
buffer.append("0" + currentByte);
}
return new String(buffer);
}
}