| /* GnuKeyring.java -- KeyStore adapter for a pair of private and public Keyrings |
| Copyright (C) 2003, 2006 Free Software Foundation, Inc. |
| |
| This file is a part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or (at |
| your option) any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 |
| USA |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.javax.crypto.jce.keyring; |
| |
| import gnu.java.security.Configuration; |
| import gnu.java.security.Registry; |
| import gnu.javax.crypto.keyring.GnuPrivateKeyring; |
| import gnu.javax.crypto.keyring.GnuPublicKeyring; |
| import gnu.javax.crypto.keyring.IKeyring; |
| import gnu.javax.crypto.keyring.IPrivateKeyring; |
| import gnu.javax.crypto.keyring.IPublicKeyring; |
| import gnu.javax.crypto.keyring.MalformedKeyringException; |
| import gnu.javax.crypto.keyring.PrimitiveEntry; |
| |
| import java.io.BufferedInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.security.Key; |
| import java.security.KeyStoreException; |
| import java.security.KeyStoreSpi; |
| import java.security.PrivateKey; |
| import java.security.PublicKey; |
| import java.security.UnrecoverableKeyException; |
| import java.security.cert.Certificate; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| import java.util.logging.Logger; |
| |
| import javax.crypto.SecretKey; |
| |
| /** |
| * An <i>Adapter</i> over a pair of one private, and one public keyrings to |
| * emulate the keystore operations. |
| */ |
| public class GnuKeyring |
| extends KeyStoreSpi |
| { |
| private static final Logger log = Logger.getLogger(GnuKeyring.class.getName()); |
| private static final String NOT_LOADED = "not loaded"; |
| |
| /** TRUE if the keystore is loaded; FALSE otherwise. */ |
| private boolean loaded; |
| /** our underlying private keyring. */ |
| private IPrivateKeyring privateKR; |
| /** our underlying public keyring. */ |
| private IPublicKeyring publicKR; |
| |
| // default 0-arguments constructor |
| |
| public Enumeration engineAliases() |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineAliases"); |
| ensureLoaded(); |
| Enumeration result; |
| if (privateKR == null) |
| result = Collections.enumeration(Collections.EMPTY_SET); |
| else |
| { |
| Set aliases = new HashSet(); |
| for (Enumeration e = privateKR.aliases(); e.hasMoreElements();) |
| { |
| String alias = (String) e.nextElement(); |
| if (alias != null) |
| { |
| alias = alias.trim(); |
| if (alias.length() > 0) |
| { |
| if (Configuration.DEBUG) |
| log.fine("Adding alias (from private keyring): " + alias); |
| aliases.add(alias); |
| } |
| } |
| } |
| for (Enumeration e = publicKR.aliases(); e.hasMoreElements();) |
| { |
| String alias = (String) e.nextElement(); |
| if (alias != null) |
| { |
| alias = alias.trim(); |
| if (alias.length() > 0) |
| { |
| if (Configuration.DEBUG) |
| log.fine("Adding alias (from public keyring): " + alias); |
| aliases.add(alias); |
| } |
| } |
| } |
| if (Configuration.DEBUG) |
| log.fine("Will enumerate: " + aliases); |
| result = Collections.enumeration(aliases); |
| } |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineAliases"); |
| return result; |
| } |
| |
| public boolean engineContainsAlias(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineContainsAlias", alias); |
| ensureLoaded(); |
| boolean inPrivateKR = privateKR.containsAlias(alias); |
| if (Configuration.DEBUG) |
| log.fine("inPrivateKR=" + inPrivateKR); |
| boolean inPublicKR = publicKR.containsAlias(alias); |
| if (Configuration.DEBUG) |
| log.fine("inPublicKR=" + inPublicKR); |
| boolean result = inPrivateKR || inPublicKR; |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineContainsAlias", |
| Boolean.valueOf(result)); |
| return result; |
| } |
| |
| public void engineDeleteEntry(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineDeleteEntry", alias); |
| ensureLoaded(); |
| if (privateKR.containsAlias(alias)) |
| privateKR.remove(alias); |
| else if (publicKR.containsAlias(alias)) |
| publicKR.remove(alias); |
| else if (Configuration.DEBUG) |
| log.fine("Unknwon alias: " + alias); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineDeleteEntry"); |
| } |
| |
| public Certificate engineGetCertificate(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineGetCertificate", alias); |
| ensureLoaded(); |
| Certificate result = publicKR.getCertificate(alias); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineGetCertificate", result); |
| return result; |
| } |
| |
| public String engineGetCertificateAlias(Certificate cert) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineGetCertificateAlias", cert); |
| ensureLoaded(); |
| String result = null; |
| for (Enumeration aliases = publicKR.aliases(); aliases.hasMoreElements();) |
| { |
| String alias = (String) aliases.nextElement(); |
| Certificate cert2 = publicKR.getCertificate(alias); |
| if (cert.equals(cert2)) |
| { |
| result = alias; |
| break; |
| } |
| } |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineGetCertificateAlias", result); |
| return result; |
| } |
| |
| public void engineSetCertificateEntry(String alias, Certificate cert) |
| throws KeyStoreException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineSetCertificateEntry", |
| new Object[] { alias, cert }); |
| ensureLoaded(); |
| if (privateKR.containsAlias(alias)) |
| throw new KeyStoreException("Alias [" + alias |
| + "] already exists and DOES NOT identify a " |
| + "Trusted Certificate Entry"); |
| if (publicKR.containsCertificate(alias)) |
| { |
| if (Configuration.DEBUG) |
| log.fine("Public keyring already contains Alias [" + alias |
| + "]. Will remove it"); |
| publicKR.remove(alias); |
| } |
| publicKR.putCertificate(alias, cert); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineSetCertificateEntry"); |
| } |
| |
| public Certificate[] engineGetCertificateChain(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineGetCertificateChain", alias); |
| ensureLoaded(); |
| Certificate[] result = privateKR.getCertPath(alias); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineGetCertificateChain", result); |
| return result; |
| } |
| |
| public Date engineGetCreationDate(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineGetCreationDate", alias); |
| ensureLoaded(); |
| Date result = getCreationDate(alias, privateKR); |
| if (result == null) |
| result = getCreationDate(alias, publicKR); |
| |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineGetCreationDate", result); |
| return result; |
| } |
| |
| public Key engineGetKey(String alias, char[] password) |
| throws UnrecoverableKeyException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineGetKey", alias); |
| ensureLoaded(); |
| Key result = null; |
| if (password == null) |
| { |
| if (privateKR.containsPublicKey(alias)) |
| result = privateKR.getPublicKey(alias); |
| } |
| else if (privateKR.containsPrivateKey(alias)) |
| result = privateKR.getPrivateKey(alias, password); |
| |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineGetKey", |
| result == null ? "null" : result.getClass().getName()); |
| return result; |
| } |
| |
| public void engineSetKeyEntry(String alias, Key key, char[] password, |
| Certificate[] chain) |
| throws KeyStoreException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineSetKeyEntry", |
| new Object[] { alias, key.getClass().getName(), chain }); |
| ensureLoaded(); |
| if (publicKR.containsAlias(alias)) |
| throw new KeyStoreException("Alias [" + alias |
| + "] already exists and DOES NOT identify a " |
| + "Key Entry"); |
| if (key instanceof PublicKey) |
| { |
| privateKR.remove(alias); |
| PublicKey pk = (PublicKey) key; |
| privateKR.putPublicKey(alias, pk); |
| } |
| else |
| { |
| if (! (key instanceof PrivateKey) && ! (key instanceof SecretKey)) |
| throw new KeyStoreException("cannot store keys of type " |
| + key.getClass().getName()); |
| privateKR.remove(alias); |
| privateKR.putCertPath(alias, chain); |
| if (Configuration.DEBUG) |
| log.fine("About to put private key in keyring..."); |
| privateKR.putPrivateKey(alias, key, password); |
| } |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineSetKeyEntry"); |
| } |
| |
| public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) |
| throws KeyStoreException |
| { |
| KeyStoreException x = new KeyStoreException("method not supported"); |
| if (Configuration.DEBUG) |
| log.throwing(this.getClass().getName(), "engineSetKeyEntry(3)", x); |
| throw x; |
| } |
| |
| public boolean engineIsCertificateEntry(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineIsCertificateEntry", alias); |
| ensureLoaded(); |
| boolean result = publicKR.containsCertificate(alias); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineIsCertificateEntry", |
| Boolean.valueOf(result)); |
| return result; |
| } |
| |
| public boolean engineIsKeyEntry(String alias) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineIsKeyEntry", alias); |
| ensureLoaded(); |
| boolean result = privateKR.containsPublicKey(alias) |
| || privateKR.containsPrivateKey(alias); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineIsKeyEntry", |
| Boolean.valueOf(result)); |
| return result; |
| } |
| |
| public void engineLoad(InputStream in, char[] password) throws IOException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineLoad"); |
| if (in != null) |
| { |
| if (! in.markSupported()) |
| in = new BufferedInputStream(in); |
| |
| loadPrivateKeyring(in, password); |
| loadPublicKeyring(in, password); |
| } |
| else |
| createNewKeyrings(); |
| |
| loaded = true; |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineLoad"); |
| } |
| |
| public void engineStore(OutputStream out, char[] password) throws IOException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineStore"); |
| ensureLoaded(); |
| HashMap attr = new HashMap(); |
| attr.put(IKeyring.KEYRING_DATA_OUT, out); |
| attr.put(IKeyring.KEYRING_PASSWORD, password); |
| |
| privateKR.store(attr); |
| publicKR.store(attr); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineStore"); |
| } |
| |
| public int engineSize() |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "engineSize"); |
| int result = 0; |
| for (Enumeration e = engineAliases(); e.hasMoreElements(); result++) |
| e.nextElement(); |
| |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "engineSize", Integer.valueOf(result)); |
| return result; |
| } |
| |
| /** |
| * Ensure that the underlying keyring pair is loaded. Throw an exception if it |
| * isn't; otherwise returns silently. |
| * |
| * @throws IllegalStateException if the keyring is not loaded. |
| */ |
| private void ensureLoaded() |
| { |
| if (! loaded) |
| throw new IllegalStateException(NOT_LOADED); |
| } |
| |
| /** |
| * Load the private keyring from the designated input stream. |
| * |
| * @param in the input stream to process. |
| * @param password the password protecting the keyring. |
| * @throws MalformedKeyringException if the keyring is not a private one. |
| * @throws IOException if an I/O related exception occurs during the process. |
| */ |
| private void loadPrivateKeyring(InputStream in, char[] password) |
| throws MalformedKeyringException, IOException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "loadPrivateKeyring"); |
| in.mark(5); |
| for (int i = 0; i < 4; i++) |
| if (in.read() != Registry.GKR_MAGIC[i]) |
| throw new MalformedKeyringException("incorrect magic"); |
| |
| int usage = in.read(); |
| in.reset(); |
| if (usage != GnuPrivateKeyring.USAGE) |
| throw new MalformedKeyringException( |
| "Was expecting a private keyring but got a wrong USAGE: " |
| + Integer.toBinaryString(usage)); |
| HashMap attr = new HashMap(); |
| attr.put(IKeyring.KEYRING_DATA_IN, in); |
| attr.put(IKeyring.KEYRING_PASSWORD, password); |
| privateKR = new GnuPrivateKeyring(); |
| privateKR.load(attr); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "loadPrivateKeyring"); |
| } |
| |
| /** |
| * Load the public keyring from the designated input stream. |
| * |
| * @param in the input stream to process. |
| * @param password the password protecting the keyring. |
| * @throws MalformedKeyringException if the keyring is not a public one. |
| * @throws IOException if an I/O related exception occurs during the process. |
| */ |
| private void loadPublicKeyring(InputStream in, char[] password) |
| throws MalformedKeyringException, IOException |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "loadPublicKeyring"); |
| in.mark(5); |
| for (int i = 0; i < 4; i++) |
| if (in.read() != Registry.GKR_MAGIC[i]) |
| throw new MalformedKeyringException("incorrect magic"); |
| |
| int usage = in.read(); |
| in.reset(); |
| if (usage != GnuPublicKeyring.USAGE) |
| throw new MalformedKeyringException( |
| "Was expecting a public keyring but got a wrong USAGE: " |
| + Integer.toBinaryString(usage)); |
| HashMap attr = new HashMap(); |
| attr.put(IKeyring.KEYRING_DATA_IN, in); |
| attr.put(IKeyring.KEYRING_PASSWORD, password); |
| publicKR = new GnuPublicKeyring(); |
| publicKR.load(attr); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "loadPublicKeyring"); |
| } |
| |
| /** |
| * Return the creation date of a named alias in a designated keyring. |
| * |
| * @param alias the alias to look for. |
| * @param keyring the keyring to search. |
| * @return the creattion date of the entry named <code>alias</code>. Return |
| * <code>null</code> if <code>alias</code> was not found in |
| * <code>keyring</code>. |
| */ |
| private Date getCreationDate(String alias, IKeyring keyring) |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "getCreationDate", |
| new Object[] { alias, keyring }); |
| Date result = null; |
| if (keyring != null) |
| for (Iterator it = keyring.get(alias).iterator(); it.hasNext();) |
| { |
| Object o = it.next(); |
| if (o instanceof PrimitiveEntry) |
| { |
| result = ((PrimitiveEntry) o).getCreationDate(); |
| break; |
| } |
| } |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "getCreationDate", result); |
| return result; |
| } |
| |
| /** Create empty keyrings. */ |
| private void createNewKeyrings() |
| { |
| if (Configuration.DEBUG) |
| log.entering(this.getClass().getName(), "createNewKeyrings"); |
| privateKR = new GnuPrivateKeyring("HMAC-SHA-1", 20, "AES", "OFB", 16); |
| publicKR = new GnuPublicKeyring("HMAC-SHA-1", 20); |
| if (Configuration.DEBUG) |
| log.exiting(this.getClass().getName(), "createNewKeyrings"); |
| } |
| } |