//============================================================================== // tribble/security/KeyReader.java //------------------------------------------------------------------------------ package tribble.security; // System imports import java.io.BufferedReader; import java.io.FileReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.Reader; import java.lang.String; import java.lang.StringBuffer; import java.lang.System; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; // Local imports import tribble.crypto.AESCipher; import tribble.crypto.SymmetricCipher; /******************************************************************************* * Public/private encryption key reader. * Provides the capability of reading public or private encryption keys contained * in XML text files that were generated by class {@link GenKeyPair}. * *

* Note that this is not a full-fledged XML parser, but rather an extemely * simplified scanner that assumes that the XML input text is in a particular * format. Input XML text that deviates too far from the assumed format will * not be parsed correctly. * * @version $Revision: 1.3 $ $Date: 2003/03/18 03:28:09 $ * @since 2003-03-14 * @author * David R. Tribble, * david@tribble.com. *
* Copyright ©2003 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source code * provided that the original copyright and authorship notices remain * intact. * * @see #main * @see GenKeyPair */ public class KeyReader { // Identification /** Source revision information. */ static final String REV = "@(#)tribble/security/KeyReader.java $Revision: 1.3+ $ $Date: 2003/03/18 03:28:09 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constants // (None) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public static methods /*************************************************************************** * Read one or more XML keyfiles and display information about the public or * private encryption keys. * *

* Usage *

    *    java tribble.security.KeyReader [option...] keyfile.xml ...
    * 
* *

* Options: *

*

*

-p phrase *
Unprotect private keys by decrypting them with a passphrase. * By default, private keys are not decrypted. *
* * @since 1.1, 2003-03-14 */ public static void main(String[] args) throws IOException, GeneralSecurityException { String passphrase = null; int i; // Get options for (i = 0; i < args.length && args[i].charAt(0) == '-'; i++) { if (args[i].equals("-p")) passphrase = args[i++]; else i = args.length+1; } // Check usage if (i+1 >= args.length) { System.out.println("Read encryption keys from one or more " + "keyfiles."); System.out.println(); System.out.println("usage: java " + KeyReader.class.getName() + " [option...] keyfile.xml ..."); System.out.println(); System.out.println("Options:"); System.out.println(" -p phrase Passphrase used to decrypt " + "private keys."); System.exit(255); } // Open/read the key input files for ( ; i < args.length; i++) { // Open/read the next key input file if (i > 0) System.out.println(); readKeyFile(args[i], passphrase); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private static methods /*************************************************************************** * Read a single XML keyfile and display information about the public or * private encryption key. * * @param fname * Name of the file containing a XML formatted key. * * @param passphrase * Passphrase to use to decrypt private keys. * * @since 1.4, 2003-03-27 (2003-03-17) */ private static void readKeyFile(String fname, String passphrase) throws IOException, GeneralSecurityException { BufferedReader in; KeyReader reader; Key key; // Open/read the key input file if (fname.equals("-")) { // Read from standard input in = new BufferedReader(new InputStreamReader(System.in)); } else { // Open/read the named input file System.out.println(fname); in = new BufferedReader(new FileReader(fname)); } // Read a key from the input file reader = new KeyReader(in); key = reader.readKey(passphrase); if (key == null) { // Empty key file System.out.println("Empty keyfile"); } else { String type; // Display the key information type = (key instanceof PublicKey ? "Public" : "Private"); System.out.println(type + " key:"); //System.out.println(" class: " + key.getClass().getName()); System.out.println(" algorithm: " + key.getAlgorithm()); System.out.println(" format: " + key.getFormat()); System.out.println(" created: " + reader.m_created); if (key instanceof PrivateKey) System.out.println(" protected: " + reader.m_protected); System.out.println(" encoding: " + reader.m_encoding); } // Clean up if (!fname.equals("-")) reader.close(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private variables /** Keyfile input stream, from which encryption keys are read. */ private BufferedReader m_in; /** Type of key {@link #m_key} ("public" or "private"). */ private String m_type; /** Date that {@link #m_key} was created. */ private String m_created; /** Key algorithm used to create {@link #m_key}. */ private String m_alg; /** Encoding format that {@link #m_key} was stored in the keyfile. */ private String m_format; /** Binary encoding that {@link #m_key} was stored in the keyfile. */ private String m_encoding; /** Key that was last read from the keyfile {@link #m_in}. */ private Key m_key; /** (Priveate) key is encrypted with a passphrase (protected). */ private String m_protected; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constructors /*************************************************************************** * Constructor. * * @param in * Input stream, from which XML encryption keys are to be read. * * @since 1.1, 2003-03-14 */ public KeyReader(Reader in) { // Establish the underlying input stream if (in instanceof BufferedReader) m_in = (BufferedReader) in; else m_in = new BufferedReader(in); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*************************************************************************** * Close the input stream. * Closes the underlying XML input stream for this encryption key reader. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2003-03-14 */ public synchronized void close() throws IOException { // Check input stream if (m_in == null) return; // Close and disassociate the input stream m_in.close(); m_in = null; } /*************************************************************************** * Read a public or private encryption key from the XML input stream of this * key reader. * *

* Note that this is not a full-fledged XML parser, but rather an extemely * simplified scanner that assumes that the XML input text is in a particular * format. Input XML text that deviates too far from the assumed format will * not be parsed correctly. * * @return * A public or private encryption key, or null if the end of the input stream * was reached before finding a key. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2003-03-14 */ public synchronized Key readKey() throws IOException, GeneralSecurityException { // Read an unprotected (unencrypted) key return (readKey(null)); } /*************************************************************************** * Read a public or private encryption key from the XML input stream of this * key reader. * *

* Note that this is not a full-fledged XML parser, but rather an extemely * simplified scanner that assumes that the XML input text is in a particular * format. Input XML text that deviates too far from the assumed format will * not be parsed correctly. * * @param passphrase * Passphrase to use to decrypt private keys. * * @return * A public or private encryption key, or null if the end of the input stream * was reached before finding a key. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.4, 2003-03-27 */ public synchronized Key readKey(String passphrase) throws IOException, GeneralSecurityException { String type = "?"; String created = "?"; String algo = "?"; String format = "?"; String protect = "no"; String encoding = "?"; String size = "?"; String keyData; StringBuffer keyLine = new StringBuffer(800); byte[] enc; int len; KeyFactory keyFact; KeySpec spec; Key key; // Read formatted text lines for (;;) { String line; // Read a key as a formatted text line line = readLine(); // Check the text line if (line == null) break; if (line.startsWith(" 0; ) k[i] = (byte) 0x00; // Decrypt the encoded key System.out.println("% enc.len=" + enc.length); d = cipher.doAllFinal(enc, 0, enc.length); System.out.println("% d.len=" + d.length + ", [0..3]=" + Integer.toHexString(d[0] & 0xFF) + "." + Integer.toHexString(d[1] & 0xFF) + ", " + Integer.toHexString(d[2] & 0xFF) + "." + Integer.toHexString(d[3] & 0xFF)); +++*/ // Verify the decrypted sentinel byte if (d[0] != (byte) 0xAA || d[1] != (byte) 0x55) throw new GeneralSecurityException("Wrong passphrase"); // Extract the decrypted encoded key len = ((d[2] & 0xFF) << 8) + (d[3] & 0xFF); len = d.length/0x10000*0x10000 + len; enc = new byte[len-4]; System.out.println("% e.len=" + enc.length); System.arraycopy(d, 4, enc, 0, len-4); for (int i = d.length; i-- > 0; ) d[i] = (byte) 0x00; } // Convert encoded bytes into a key object keyFact = KeyFactory.getInstance(algo); if (format.equalsIgnoreCase("X.509") || format.equalsIgnoreCase("X509")) spec = new X509EncodedKeySpec(enc); else if (format.equalsIgnoreCase("PKCS#8") || format.equalsIgnoreCase("PKCS8")) spec = new PKCS8EncodedKeySpec(enc); else throw new NoSuchAlgorithmException("Unsupported key format: " + format); if (type.equals("public")) key = keyFact.generatePublic(spec); else if (type.equals("private")) key = keyFact.generatePrivate(spec); else throw new NoSuchAlgorithmException("Unsupported key type: " + type); // Done m_type = type; m_created = created; m_alg = algo; m_format = format; m_protected = protect; m_encoding = encoding; m_key = key; return (key); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private constructors /*************************************************************************** * Default constructor. * Do not used this constructor. * * @since 1.1, 2003-03-14 */ private KeyReader() { // Do nothing } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private methods /*************************************************************************** * Read a non-blank line from the XML input stream of this key reader. * Also cleans up the contents of the line by removing spaces and quote * marks. * * @return * A string containing the contents of a non-blank text line, or null if the * end of the input stream was reached before finding a key. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2003-03-14 */ private String readLine() throws IOException { String line; // Check input stream if (m_in == null) throw new IOException("Null input stream"); // Read a non-blank text line do { int i; int j; int len; char quote; StringBuffer buf; // Read a text line from the input stream line = m_in.readLine(); // Check for end of file if (line == null) return (null); // Clean up the XML text line line = line.trim(); len = line.length(); buf = new StringBuffer(80); quote = '\0'; for (i = 0, j = 0; i < len; i++) { char ch; // Get the next character from the XML text line ch = line.charAt(i); // Handle quotes and whitespace if (ch == '\'' && quote == '\0') { quote = ch; continue; } else if (ch == '"' && quote == '\0') { quote = ch; continue; } else if (ch == quote && quote != '\0') { quote = '\0'; continue; } else if ((ch == ' ' || ch == '\t') && quote == '\0') continue; if (ch == '>' && i == len-1) continue; // Insert the cleaned char buf.append(ch); } // Replace the XML text line with its cleaned-up version if (j != len) line = buf.toString(); } while (line.length() == 0); // Done return (line); } } // End KeyReader.java