001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017:
018: package org.apache.harmony.tools.keytool;
019:
020: import java.io.FileNotFoundException;
021: import java.io.IOException;
022: import java.security.Key;
023: import java.security.KeyStore;
024: import java.security.KeyStoreException;
025: import java.security.MessageDigest;
026: import java.security.NoSuchAlgorithmException;
027: import java.security.NoSuchProviderException;
028: import java.security.UnrecoverableKeyException;
029: import java.security.cert.Certificate;
030: import java.security.cert.CertificateEncodingException;
031: import java.security.cert.CertificateException;
032: import java.security.cert.X509Certificate;
033: import java.util.Collection;
034: import java.util.Collections;
035: import java.util.Enumeration;
036: import java.util.Iterator;
037:
038: import org.apache.harmony.luni.util.Base64;
039:
040: /**
041: * Class for printing to stdout the contents of the keystore or a single
042: * certificate. The certificate can be in the keystore (list(..) method) or not
043: * (printCert(..) method).
044: */
045: public class KeyStoreCertPrinter {
046:
047: /**
048: * Prints the contents of the entry associated with the alias given in
049: * param. If no alias is specified, the contents of the entire keystore are
050: * printed.
051: *
052: * @param param
053: * @throws KeyStoreException
054: * @throws NoSuchAlgorithmException
055: * @throws NoSuchProviderException
056: * @throws UnrecoverableKeyException
057: * @throws IOException
058: * @throws FileNotFoundException
059: * @throws CertificateException
060: */
061: static void list(KeytoolParameters param) throws KeyStoreException,
062: NoSuchAlgorithmException, NoSuchProviderException,
063: UnrecoverableKeyException, CertificateException,
064: FileNotFoundException, IOException {
065: Enumeration aliases;
066: KeyStore keyStore = param.getKeyStore();
067: String alias = param.getAlias();
068:
069: if (alias != null) {
070: // if the alias is specified, make a single-element
071: // enumeration of it
072: aliases = Collections.enumeration(Collections
073: .singleton(alias));
074: } else {// if the alias is not given,
075: // get all aliases
076: aliases = keyStore.aliases();
077: // print the keystore info
078: System.out.println("Type of keystore: "
079: + keyStore.getType());
080: System.out.println("Keystore provider name: "
081: + keyStore.getProvider().getName());
082: int keyStoreSize = keyStore.size();
083: System.out.println("\nThe keystore contains "
084: + keyStoreSize
085: + ((keyStoreSize == 1) ? " entry \n"
086: : " entries \n"));
087: }
088:
089: String mdProvider = (param.getMdProvider() != null) ? param
090: .getMdProvider() : param.getProvider();
091:
092: while (aliases.hasMoreElements()) {
093: String currentAlias = (String) aliases.nextElement();
094: String creationDate = keyStore
095: .getCreationDate(currentAlias).toString();
096:
097: // true if the keystore entry is a TrustedCertificateEntry
098: boolean trustedEntry = false;
099: // true if the keystore entry is a SecretKeyEntry
100: boolean secretKeyEntry = false;
101:
102: // get the type of the entry to print it out
103: String entryType = "Key entry";
104: if (keyStore.entryInstanceOf(currentAlias,
105: KeyStore.TrustedCertificateEntry.class)) {
106: entryType = "Trusted certificate entry";
107: trustedEntry = true;
108: } else if (keyStore.entryInstanceOf(currentAlias,
109: KeyStore.PrivateKeyEntry.class)) {
110: entryType = "Private key entry";
111: } else if (keyStore.entryInstanceOf(currentAlias,
112: KeyStore.SecretKeyEntry.class)) {
113: entryType = "Secret key entry";
114: secretKeyEntry = true;
115: }
116:
117: // get the certificate associated with the currentAlias
118: X509Certificate x509cert = ((X509Certificate) keyStore
119: .getCertificate(currentAlias));
120:
121: // if -v or -rfc options are specified
122: if (param.isVerbose() || param.isRfc()) {
123: // print detailed info about the _entry_
124: System.out.println("Alias name: " + currentAlias);
125: System.out.println("Date of creation: " + creationDate);
126: System.out.println("Type of the entry: " + entryType);
127:
128: if (!secretKeyEntry) {
129: Certificate[] certChain = keyStore
130: .getCertificateChain(currentAlias);
131:
132: if (!trustedEntry) {
133: System.out.println("Certificate chain length: "
134: + certChain.length);
135: }
136:
137: // if -v option was given, print the detailed info about
138: // the certificate
139: if (param.isVerbose()) {
140: // print out the first certificate
141: System.out.println("Certificate[1]:");
142: printX509CertDetailed(x509cert, mdProvider);
143: if (!trustedEntry) {
144: for (int i = 1; i < certChain.length; i++) {
145: System.out.println("Certificate["
146: + (i + 1) + "]:");
147: printX509CertDetailed(
148: (X509Certificate) certChain[i],
149: mdProvider);
150: }
151: }
152: }
153: // if -rfc option is given, print the certificate in Base64
154: // printable format
155: else {
156: // print out the first certificate
157: System.out.println("Certificate[1]:");
158: System.out
159: .println("-----BEGIN CERTIFICATE-----");
160: System.out.println(Base64.encode(x509cert
161: .getEncoded(), "ISO-8859-1"));
162: System.out.println("-----END CERTIFICATE-----");
163:
164: if (!trustedEntry) {
165: for (int i = 1; i < certChain.length; i++) {
166: System.out.println("Certificate["
167: + (i + 1) + "]:");
168: System.out
169: .println("-----BEGIN CERTIFICATE-----");
170: System.out.println(Base64.encode(
171: certChain[i].getEncoded(),
172: "ISO-8859-1"));
173: System.out
174: .println("-----END CERTIFICATE-----");
175: }
176: }
177: }
178: } else {
179: // if the key is explicitly asked to be printed
180: // by setting the alias, print it out, otherwise - do
181: // nothing.
182: if (alias != null) {
183: // TODO: ask for password if not set, when read from
184: // stdin is OK.
185: char[] keyPass;
186: if ((keyPass = param.getKeyPass()) != null) {
187: Key key = keyStore.getKey(currentAlias,
188: keyPass);
189: System.out.println("Algorithm: "
190: + key.getAlgorithm() + "\nFormat: "
191: + key.getFormat());
192: System.out.println("Key: "
193: + formatBytes(key.getEncoded()));
194: } else {
195: System.out
196: .println("If you want to print the key, "
197: + "please set the entry password using "
198: + "\"-keypass\" option");
199: }
200:
201: }
202: }
203: System.out.println("\n*******************************"
204: + "*******************************\n");
205:
206: } else {// if neither -v nor -rfc options specified
207: String commaSpc = ", ";
208: System.out.print(currentAlias + commaSpc + creationDate
209: + commaSpc + entryType);
210:
211: if (!secretKeyEntry) {
212: System.out.print(commaSpc
213: + "\nCertificate fingerprint (MD5): ");
214: printMD(x509cert.getEncoded(), "MD5", mdProvider);
215: } else {
216: // If the key is explicitly asked to be printed
217: // by setting the alias, print it out, otherwise - do
218: // nothing.
219: if (alias != null) {
220: char[] keyPass;
221: if ((keyPass = param.getKeyPass()) != null) {
222: Key key = keyStore.getKey(currentAlias,
223: keyPass);
224: System.out.println(key.getAlgorithm()
225: + ", " + key.getFormat() + ", "
226: + formatBytes(key.getEncoded()));
227: } else {
228: System.out
229: .println("If you want to print the key, "
230: + "please set the entry password using "
231: + "\"-keypass\" option");
232: }
233: }
234: }
235: }
236: }
237:
238: }
239:
240: /**
241: * Prints the detailed description of a certificate in a human-readable
242: * format: its owner and issuer, serial number, validity period and
243: * fingerprints. providerName is needed to generate MessageDigest. If it is
244: * null, a default one is used.
245: *
246: * @param x509cert
247: * @param providerName
248: * @throws CertificateEncodingException
249: * @throws NoSuchAlgorithmException
250: * @throws NoSuchProviderException
251: */
252: static void printX509CertDetailed(X509Certificate x509cert,
253: String providerName) throws CertificateEncodingException,
254: NoSuchAlgorithmException, NoSuchProviderException {
255: System.out.println("Owner: "
256: + x509cert.getSubjectX500Principal());
257: System.out.println("Issuer: "
258: + x509cert.getIssuerX500Principal());
259: System.out.println("Serial number: "
260: + Integer.toHexString(x509cert.getSerialNumber()
261: .intValue()));
262: System.out.println("Validity period \n\t from: "
263: + x509cert.getNotBefore() + "\n\t until: "
264: + x509cert.getNotAfter());
265:
266: // print certificate fingerprints (MD5 and SHA1).
267: byte[] encodedCert;
268: try {
269: encodedCert = x509cert.getEncoded();
270: } catch (CertificateEncodingException e) {
271: throw new CertificateEncodingException(
272: "Failed to encode the certificate", e);
273: }
274:
275: String strMD5 = "MD5";
276: String strSHA1 = "SHA1";
277:
278: System.out.print("Certificate fingerprints: " + "\n\t "
279: + strMD5 + ": ");
280: printMD(encodedCert, strMD5, providerName);
281:
282: System.out.print("\t " + strSHA1 + ": ");
283: printMD(encodedCert, strSHA1, providerName);
284: }
285:
286: // Prints out the message digest of the encoding using the given algorithm
287: // and provider. Provider can be null.
288: private static void printMD(byte[] encoding, String mdAlgorithm,
289: String providerName) throws NoSuchAlgorithmException,
290: NoSuchProviderException {
291: byte[] digest;
292: // if provider name is given, use it when getting
293: // an instance of MessageDigest.
294: try {
295: if (providerName != null) {
296: digest = MessageDigest.getInstance(mdAlgorithm,
297: providerName).digest(encoding);
298: } else {
299: digest = MessageDigest.getInstance(mdAlgorithm).digest(
300: encoding);
301: }
302: } catch (NoSuchAlgorithmException e) {
303: throw new NoSuchAlgorithmException(
304: "The algorithm " + mdAlgorithm
305: + " is not found in the environment.", e);
306: } catch (NoSuchProviderException e) {
307: throw (NoSuchProviderException) new NoSuchProviderException(
308: "The provider " + providerName
309: + " is not found in the environment.")
310: .initCause(e);
311: }
312:
313: // print out in the way: "0A:1B:C3:D4:..."
314: System.out.println(formatBytes(digest));
315: }
316:
317: /**
318: * Reads an X.509 certificate from the file specified in param and prints it
319: * in a human-readable format. If param.getFileName() returns null, the
320: * certificate is read from the standard input. The input data is awaited
321: * for 3 seconds. If the data is not entered, an exception is thrown.
322: *
323: * @param param
324: * @throws KeytoolException
325: * @throws IOException
326: * @throws CertificateException
327: * @throws FileNotFoundException
328: * @throws NoSuchAlgorithmException
329: * @throws NoSuchProviderException
330: */
331: static void printCert(KeytoolParameters param)
332: throws FileNotFoundException, CertificateException,
333: IOException, KeytoolException, NoSuchAlgorithmException,
334: NoSuchProviderException {
335:
336: String provider = param.getProvider();
337: String certProvider = (param.getCertProvider() != null) ? param
338: .getCertProvider() : provider;
339: String mdProvider = (param.getMdProvider() != null) ? param
340: .getMdProvider() : provider;
341: // get the certificate(s) from the file
342: Collection certCollection = CertReader.readCerts(param
343: .getFileName(), false, certProvider);
344: Iterator certIter = certCollection.iterator();
345: int counter = 1;
346:
347: // print the detailed info on all certificates
348: while (certIter.hasNext()) {
349: X509Certificate cert = (X509Certificate) certIter.next();
350: System.out.println("\nCertificate[" + counter + "]:");
351: printX509CertDetailed(cert, mdProvider);
352: ++counter;
353: }
354: }
355:
356: // Formats byte array as a String looking like "0A:1B:C3:D4:....:E5".
357: private static String formatBytes(byte[] bytes) {
358: int i;
359: // The method is expected to format mostly message digest results and
360: // the length of the String representing a SHA1 digest printed in
361: // the way: "0A:1B:C3:D4:....:E5" is the biggest and is 59.
362: StringBuffer buffer = new StringBuffer(59);
363: int length;
364: String currentByte;
365: for (i = 0; i < bytes.length - 1; i++) {
366: // TODO: change when String.format(..) method is implemented.
367: // buffer.append(String.format("%02X", bytes[i]) + ":");
368: currentByte = Integer.toHexString(bytes[i]).toUpperCase();
369: if ((length = currentByte.length()) > 1) {
370: buffer.append(currentByte.substring(length - 2) + ":");
371: } else {
372: buffer.append("0" + currentByte + ":");
373: }
374: }
375: // The last byte doesn't need ":" after it ("...:E5:6F")
376: // TODO: change in the same way to (String.format(..))
377: currentByte = Integer.toHexString(bytes[i]).toUpperCase();
378: if ((length = currentByte.length()) > 1) {
379: buffer.append(currentByte.substring(length - 2));
380: } else {
381: buffer.append("0" + currentByte);
382: }
383: return new String(buffer);
384: }
385: }
|