/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008-2013 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library 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
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.util.logging.Level;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.ccnx.ccn.config.ConfigurationException;
import org.ccnx.ccn.config.SystemConfiguration;
import org.ccnx.ccn.impl.CCNFlowControl;
import org.ccnx.ccn.impl.CCNFlowControl.SaveType;
import org.ccnx.ccn.impl.security.keys.BasicKeyManager;
import org.ccnx.ccn.impl.security.keys.PublicKeyCache;
import org.ccnx.ccn.impl.security.keys.SecureKeyCache;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.impl.support.Tuple;
import org.ccnx.ccn.io.CCNReader;
import org.ccnx.ccn.io.ErrorStateException;
import org.ccnx.ccn.io.content.PublicKeyObject;
import org.ccnx.ccn.profiles.SegmentationProfile;
import org.ccnx.ccn.profiles.VersioningProfile;
import org.ccnx.ccn.profiles.repo.RepositoryControl;
import org.ccnx.ccn.profiles.security.KeyProfile;
import org.ccnx.ccn.profiles.security.access.AccessControlManager;
import org.ccnx.ccn.protocol.CCNTime;
import org.ccnx.ccn.protocol.Component;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.ContentObject;
import org.ccnx.ccn.protocol.ContentObject.SimpleVerifier;
import org.ccnx.ccn.protocol.KeyLocator;
import org.ccnx.ccn.protocol.KeyLocator.KeyLocatorType;
import org.ccnx.ccn.protocol.KeyName;
import org.ccnx.ccn.protocol.PublisherPublicKeyDigest;
import org.ccnx.ccn.protocol.SignedInfo.ContentType;
/**
* Top-level interface for managing our own keys, as well as maintaining an address book containing
* the keys of others (which will be used by the TrustManager). Also handles loading of the BouncyCastle
* provider, which we need for many things. Very minimal interface now, expect to evolve extensively.
*/
public abstract class KeyManager {
/**
* Canary value, indicates we want to override any other key locator available.
*/
protected static final KeyLocator SELF_SIGNED_KEY_LOCATOR = new KeyLocator();
/**
* Currently default to SHA-256. Only thing that associates a specific digest algorithm
* with a version of the CCN protocol is the calculation of the vestigial content digest component
* of ContentName used in Interest matching, and publisher digests. Changing the latter
* is handled by backwards-compatible changes to the protocol encoding. All other digests are
* stored prefaced with an algorithm identifier, to allow them to be modified.
* We expect the protocol default digest algorithm to move to SHA3 when defined.
*/
public static final String DEFAULT_DIGEST_ALGORITHM = "SHA-256";
public static final Provider PROVIDER = getBcProvider();
/**
* The default KeyManager for this user/VM pair. The KeyManager will eventually have access
* to significant cached state, and so a single one should be shared by as many processes
* within the same trust domain as possible. We might make multiple KeyManagers representing
* different "users" for testing purposes.
*/
protected static KeyManager _defaultKeyManager = null;
/**
* A default verifier to use, relative to these key caches and all. Move to TrustManager eventually.
*/
protected ContentVerifier _verifier = null;
/**
* Accessor to retrieve default key manager instance, or create it if necessary.
* @return the KeyManager
* @throws ConfigurationException if there is a problem with the user or system configuration that
* requires intervention to resolve, or we have a significant problem starting up the key manager.
*/
public static synchronized KeyManager getDefaultKeyManager() {
// could print a stack trace
if (Log.isLoggable(Log.FAC_KEYS, Level.FINER)) {
Log.finer(Log.FAC_KEYS, "NOTICE: retrieving default key manager. Do you really want to do this?");
try {
throw new ConfigurationException("THIS IS NOT AN ERROR: tracking stack trace to find use of default key manager.");
} catch (ConfigurationException e) {
Log.logStackTrace(Level.FINER, e);
}
}
if (null != _defaultKeyManager)
return _defaultKeyManager;
try {
return createDefaultKeyManager();
} catch (IOException io) {
Log.warning("IOException attempting to get KeyManager: " + io.getClass().getName() + ":" + io.getMessage());
Log.warningStackTrace(io);
throw new RuntimeException("Error in system configuration. Cannot get KeyManager.",io);
} catch (InvalidKeyException io) {
Log.warning("InvalidKeyException attempting to get KeyManager: " + io.getClass().getName() + ":" + io.getMessage());
Log.warningStackTrace(io);
throw new RuntimeException("Error in system configuration. Cannot get KeyManager.",io);
} catch (ConfigurationException e) {
Log.warning("Configuration exception attempting to get KeyManager: " + e.getMessage());
Log.warningStackTrace(e);
throw new RuntimeException("Error in system configuration. Cannot get KeyManager.",e);
}
}
/**
* Clean up state left around by the default key manager and remove it.
* For now that just means shutting down the network manager started by it
*/
public static synchronized void closeDefaultKeyManager() {
if (null != _defaultKeyManager) {
_defaultKeyManager.close();
_defaultKeyManager = null;
}
}
/**
* Create the default key manager.
* @return the key manager
* @throws ConfigurationException if there is a problem with the user or system configuration
* that requires intervention to fix
* @throws IOException if there is an operational problem loading data or initializing the key store
* @throws ConfigurationException
*/
protected static synchronized KeyManager createDefaultKeyManager() throws InvalidKeyException, IOException, ConfigurationException {
if (null == _defaultKeyManager) {
_defaultKeyManager = new BasicKeyManager();
_defaultKeyManager.initialize();
}
return _defaultKeyManager;
}
/**
* Set the default key manager to one of our choice. If you do this, be careful on
* calling close().
*/
public static synchronized void setDefaultKeyManager(KeyManager keyManager) {
if (null == keyManager) {
Log.warning("Setting default key manager to NULL. Default user key manager will be loaded on next request for default key manager.");
}
closeDefaultKeyManager();
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "Setting default key manager: new KeyManager {0}", keyManager.getClass().getName());
}
_defaultKeyManager = keyManager;
}
/**
* Load the BouncyCastle and other necessary providers, should be called once for initialization.
* Currently this is done by CCNHandle.
*/
private static Provider getBcProvider() {
// first try and get it, in case some other code has already created it.
Provider p = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
// it's not yet known to the Security class, so create it.
if (p == null) {
p = new BouncyCastleProvider();
Security.addProvider(p);
}
return p;
}
/**
* Subclasses can override with fancier verification behavior; again move to TrustManager eventually
*/
public synchronized ContentVerifier getDefaultVerifier() {
if (null == _verifier) {
_verifier = new SimpleVerifier(null, this);
}
return _verifier;
}
/**
* Close any connections we have to the network. Ideally prepare to
* reopen them when they are next needed.
*/
public void close() {
synchronized (KeyManager.class) {
if (_defaultKeyManager == this) {
_defaultKeyManager = null;
}
}
}
/**
* Allows subclasses to specialize key manager initialization.
* @throws ConfigurationException
* @throws IOException
*/
public abstract void initialize() throws InvalidKeyException, IOException, ConfigurationException;
public abstract boolean initialized();
public abstract void clearSavedConfigurationState() throws FileNotFoundException, IOException;
/**
* Get our default key ID.
* @return the digest of our default key
*/
public abstract PublisherPublicKeyDigest getDefaultKeyID();
public boolean isOurDefaultKey(PublisherPublicKeyDigest keyID) {
if (getDefaultKeyID().equals(keyID))
return true;
return false;
}
/**
* Access our collected store of public keys.
* @return our PublicKeyCache
*/
public abstract PublicKeyCache getPublicKeyCache();
/**
* Access our store of private keys and other secret key
* material that we have retrieved.
* @return our SecureKeyCache
*/
public abstract SecureKeyCache getSecureKeyCache();
public abstract void saveSecureKeyCache() throws FileNotFoundException, IOException;
public abstract void saveConfigurationState() throws FileNotFoundException,
IOException;
/**
* Not sure that this is the best idea, but others want to bootstrap on
* our configuration data store to stash their own config data. Return
* location as a URI as it might be a namespace rather than a directory.
*/
public abstract URI getConfigurationDataURI();
/**
* Get our default private key.
* @return our default private key
*/
public abstract Key getDefaultSigningKey();
/**
* Get our default public key.
* @return our default public key
*/
public abstract PublicKey getDefaultPublicKey();
/**
* Return the key's content name for a given key id, given
* a specified prefix and version.
* The default key name is the publisher ID itself,
* under the user's key collection.
* @param keyID[] publisher ID
* @return content name
*/
public ContentName getDefaultKeyName(ContentName keyPrefix, PublisherPublicKeyDigest keyID, CCNTime keyVersion) {
if (null == keyPrefix) {
keyPrefix = getDefaultKeyNamePrefix();
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "Got default key name prefix: {0}", keyPrefix);
}
}
ContentName keyName = KeyProfile.keyName(keyPrefix, keyID);
if (keyVersion == null)
return keyName;
return new ContentName(keyName, keyVersion);
}
/**
* Get the key-manager determined default key name for a key. Might include
* a version, might allow caller to save with generated version.
*/
public abstract ContentName getDefaultKeyName(PublisherPublicKeyDigest keyID);
/**
* Allow subclasses to override default publishing location.
*/
public abstract ContentName getDefaultKeyNamePrefix();
/**
* Gets the preferred key locator for this signing key.
* @param publisherKeyID the key whose locator we want to retrieve,
* if null retrieves the key locator for our default key
* @return the current preferred key locator for that key
*/
public abstract KeyLocator getKeyLocator(PublisherPublicKeyDigest publisherKeyID);
/**
* Get our current preferred key locator for this signing key. Uses
* getKeyLocator(PublisherPublicKeyDigest).
*/
public abstract KeyLocator getKeyLocator(Key signingKey);
/**
* Get the key locator for our default key. Same as getKeyLocator(null)
*/
public KeyLocator getDefaultKeyLocator() {
return getKeyLocator(getDefaultKeyID());
}
public abstract boolean haveStoredKeyLocator(PublisherPublicKeyDigest keyID);
public abstract KeyLocator getStoredKeyLocator(PublisherPublicKeyDigest keyID);
public abstract void clearStoredKeyLocator(PublisherPublicKeyDigest keyID);
/**
* Remember the key locator to use for a given key. Use
* this to publish this key in the future if not overridden by method
* calls. If no key locator stored for this key, and no override
* given, compute a KEY type key locator if this key has not been
* published, and the name given to it when published if it has.
* @param publisherKeyID the key whose locator to set; if null sets it for our
* default key
* @param keyLocator the new key locator for this key; overrides any previous value.
* If null, erases previous value and defaults will be used.
*/
public abstract void setKeyLocator(PublisherPublicKeyDigest publisherKeyID, KeyLocator keyLocator);
/**
* Get a KEY type key locator for a particular public key.
* @param publisherKeyID the key whose locator we want to retrieve
* @return the key locator
* @throws IOException
*/
public KeyLocator getKeyTypeKeyLocator(PublisherPublicKeyDigest publisherKeyID) {
PublicKey theKey = getPublicKey(publisherKeyID);
if (null == theKey) {
return null;
}
return new KeyLocator(theKey);
}
/**
* Get the public key associated with a given publisher
* @param publisher the digest of the desired key
* @return the key, or null if no such key known to our cache
* @throws IOException
*/
public abstract PublicKey getPublicKey(PublisherPublicKeyDigest publisher);
/**
* Get the publisher key digest associated with one of our signing keys
* @param signingKey key whose publisher data we want
* @return the digest of the corresponding public key
*/
public abstract PublisherPublicKeyDigest getPublisherKeyID(Key signingKey);
/**
* Get the private key associated with a given publisher
* @param publisherKeyID the public key digest of the desired key
* @return the key, or null if no such key known to our cache
*/
public abstract Key getSigningKey(PublisherPublicKeyDigest publisherKeyID);
/**
* Get all of our private keys, used for cache loading.
* @return an array of our currently available private keys
*/
public abstract Key[] getSigningKeys();
/**
* Get the public key digest of all our signing keys -- essentially our available identities.
*/
public abstract PublisherPublicKeyDigest [] getAvailableIdentities();
/**
* Get any timestamp associate with this key.
* @param keyID
* @return
*/
public abstract CCNTime getKeyVersion(PublisherPublicKeyDigest keyID);
/**
* Get the verification key for a given publisher, going to the network to retrieve it if necessary.
* @param publisherKeyID the digest of the keys we want
* @param keyLocator the key locator to tell us where to retrieve the key from
* @param timeout how long to try to retrieve the key
* @return the key
* @throws IOException if we run into an error attempting to read the key
*/
public abstract Key getVerificationKey(
PublisherPublicKeyDigest publisherKeyID, KeyLocator keyLocator,
long timeout) throws IOException;
/**
* Get the verification key for a given publisher, going to the network to retrieve it if necessary.
* Uses the SystemConfiguration.EXTRA_LONG_TIMEOUT to be aggressive and reexpress.
* @param publisherKeyID the digest of the keys we want
* @param keyLocator the key locator to tell us where to retrieve the key from
* @return the key
* @throws IOException if we run into an error attempting to read the key
*/
public Key getVerificationKey(
PublisherPublicKeyDigest publisherKeyID, KeyLocator keyLocator) throws IOException {
return getVerificationKey(publisherKeyID, keyLocator, SystemConfiguration.EXTRA_LONG_TIMEOUT);
}
/**
* Get the public key for a given publisher as it was explicitly published,
* going to the network to retrieve it if necessary. If the key was not
* published as a KEY content item (was in our keystore, or was in a KEY
* type of key locator), this wil not retrieve anything.
* @param publisherKeyID the digest of the keys we want
* @param keyLocator the key locator to tell us where to retrieve the key from
* @param timeout how long to try to retrieve the key
* @return the key
* @throws IOException if we run into an error attempting to read the key
*/
public abstract PublicKeyObject getPublicKeyObject(
PublisherPublicKeyDigest desiredKeyID, KeyLocator locator, long timeout) throws IOException;
/**
* Allow subclasses to specialize key publication, if any.
* @param defaultPrefix our default namespace, if we know
* one for this environment. If null, take user defaults.
* @throws ConfigurationException
*/
public PublicKeyObject publishDefaultKey(ContentName keyName)
throws IOException, InvalidKeyException {
if (!initialized()) {
throw new IOException("KeyServer: cannot publish keys, have not yet initialized KeyManager!");
}
return publishKey(keyName, getDefaultKeyID(), null, null);
}
/**
* Publish a key at a certain name, signed by a specified identity (our
* default, if null). Usually used to
* publish our own keys, but can specify other keys we have in our cache.
*
* This publishes our key to our own internal key server, from where it can be retrieved
* as long as this KeyManager is running. It does not put it on the wire until someone
* requests it.
* Implementation Note: This code is used in CCNHandle initialization, and as such it
* cannot use a CCNHandle or any of the standard network operations without introducing
* a circular dependency. The code is very low-level and should only be modified with
* great caution.
*
* @param keyName content name of the public key
* @param keyToPublish public key digest of key to publish, if null publish our default key
* @param handle handle for ccn
* @throws IOException
* @throws InvalidKeyException
*/
public PublicKeyObject publishKey(ContentName keyName,
PublisherPublicKeyDigest keyToPublish,
PublisherPublicKeyDigest signingKeyID,
KeyLocator signingKeyLocator) throws InvalidKeyException, IOException {
if (null == keyToPublish) {
keyToPublish = getDefaultKeyID();
}
PublicKey theKey = getPublicKey(keyToPublish);
if (null == theKey) {
Log.warning("Cannot publish key {0} to name {1}, do not have public key in cache.", keyToPublish, keyName);
return null;
}
return publishKey(keyName, theKey, signingKeyID, signingKeyLocator, true);
}
/**
* Publish my public key to a local key server run in this JVM, as a self-signed key
* record. We do this by default if we don't have any credentials for this key; this
* just allows the caller to explicitly request this behavior even if we do have
* credentials.
* TODO need mechanism for controlling whether this ends up in the key locator...
* @param keyName content name of the public key
* @param keyToPublish public key digest of key to publish and to sign with
* @param handle handle for ccn
* @throws IOException
* @throws InvalidKeyException
*/
public PublicKeyObject publishSelfSignedKey(ContentName keyName,
PublisherPublicKeyDigest keyToPublish,
boolean learnKeyLocator) throws InvalidKeyException, IOException {
if (null == keyToPublish) {
keyToPublish = getDefaultKeyID();
}
PublicKey theKey = getPublicKey(keyToPublish);
if (null == theKey) {
Log.warning("Cannot publish key {0} to name {1}, do not have public key in cache.", keyToPublish, keyName);
return null;
}
return publishKey(keyName, theKey, keyToPublish, SELF_SIGNED_KEY_LOCATOR, learnKeyLocator);
}
/**
* Publish a key at a certain name, signed by our default identity. Usually used to
* publish our own keys, but can specify other keys we have in our cache.
*
* This publishes our key to our own internal key server, from where it can be retrieved
* as long as this KeyManager is running. It does not put it on the wire until someone
* requests it.
* Implementation Note: This code is used in CCNHandle initialization, and as such it
* cannot use a CCNHandle or any of the standard network operations without introducing
* a circular dependency. The code is very low-level and should only be modified with
* great caution.
*
* @param keyName the name under which the key should be published. For the moment, keys are
* unversioned.
* @param keyToPublish can be null, in which case we publish our own default public key
* @param signingKeyID key to sign with, if we wish to override default
* @param signingKeyLocator locator to use, if we wish to override default; if null, one will
* be computed
* @param learnKeyLocator do we remember the key locator used as the default for this signing key
* @throws InvalidKeyException
* @throws IOException
* @throws ConfigurationException
*/
public abstract PublicKeyObject publishKey(ContentName keyName,
PublicKey keyToPublish,
PublisherPublicKeyDigest signingKeyID,
KeyLocator signingKeyLocator,
boolean learnKeyLocator) throws InvalidKeyException, IOException;
/**
* Publish a key at a certain name, ensuring that it is stored in a repository. Will throw an
* exception if no repository available. Usually used to publish our own keys, but can specify
* any key known to our key cache.
* @param keyName Name under which to publish the key. Currently added under existing version, or version
* included in keyName.
* @param keyToPublish can be null, in which case we publish our own default public key.
* @param handle the handle to use for network requests
* @throws InvalidKeyException
* @throws IOException
*/
public abstract PublicKeyObject publishKeyToRepository(ContentName keyName,
PublisherPublicKeyDigest keyToPublish,
long timeToWaitForPreexisting)
throws InvalidKeyException, IOException;
/**
* Publish one of our keys to a repository, if it isn't already there, and ensure
* that it's self-signed regardless of what credentials we have for it (this
* is the default behavior if we have no credentials for the key. Throws an exception
* if no repository is available
* @param keyName Name under which to publish the key. Currently added under existing version, or version
* included in keyName.
* @param theKey the public key to publish, if we happen to have it; otherwise it will be retrieved
* from cache based on keyToPublish.
* @param keyToPublish can be null, in which case we publish our own default public key.
* @param handle the handle to use for network requests
* @throws InvalidKeyException
* @throws IOException
*/
public abstract PublicKeyObject publishSelfSignedKeyToRepository(ContentName keyName,
PublicKey theKey,
PublisherPublicKeyDigest keyToPublish,
long timeToWaitForPreexisting)
throws InvalidKeyException, IOException;
/**
* Publish our default key to a repository at its default location.
* @param handle the handle used for network requests
* @throws InvalidKeyException
* @throws IOException
*/
public PublicKeyObject publishKeyToRepository() throws InvalidKeyException, IOException {
return publishKeyToRepository(null, null);
}
public PublicKeyObject publishKeyToRepository(ContentName keyName, PublisherPublicKeyDigest keyToPublish)
throws InvalidKeyException, IOException {
return publishKeyToRepository(keyName, keyToPublish, SystemConfiguration.SHORT_TIMEOUT);
}
/**
* Publish a public key to repository, if it isn't already there.
* @param keyName content name of the public key to publish under (adds a version)
* @param keyToPublish the key to publish
* @param handle the handle to use to publish it with
* @return the published information about this key, whether we published it or someone else had
* @throws IOException
*/
public static PublicKeyObject publishKeyToRepository(
ContentName keyName,
PublicKey keyToPublish,
PublisherPublicKeyDigest signingKeyID,
KeyLocator signingKeyLocator,
CCNHandle handle) throws IOException {
return publishKeyToRepository(keyName, keyToPublish, signingKeyID, signingKeyLocator,
SystemConfiguration.SHORT_TIMEOUT, false, handle);
}
/**
* Publish a public key to repository, if it isn't already there.
* @param keyName content name of the public key to publish under (adds a version)
* @param keyToPublish the key to publish
* @param signingKeyID the key to sign with
* @param signingKeyLocator the key locator to use
* @param timeToWaitForPreexisting how long to wait to see if it has already been published
* (avoid re-publishing). If 0, we don't even try to find preexisting content.
* @param requirePublisherMatch check to see if we match the specified publisher. Key locator
* match too complex to check, make caller do that one.
* @param handle the handle to use to publish it with
* @return the published information about this key, whether we published it or someone else had
* @throws IOException
*/
public static PublicKeyObject publishKeyToRepository(
ContentName keyName,
PublicKey keyToPublish,
PublisherPublicKeyDigest signingKeyID,
KeyLocator signingKeyLocator,
long timeToWaitForPreexisting,
boolean requirePublisherMatch,
CCNHandle handle) throws IOException {
// To avoid repeating work, we first see if this content is available on the network, then
// if it's in a repository. That's because if it's not in a repository, we need to know if
// it's on the network, and this way we save doing that work twice (as the repo-checking code
// also needs to know if it's on the network).
PublisherPublicKeyDigest keyDigest = new PublisherPublicKeyDigest(keyToPublish);
// Returns immediately if timeToWaitForPreexisting is 0.
ContentObject availableContent =
CCNReader.isVersionedContentAvailable(keyName, ContentType.KEY, keyDigest.digest(),
(requirePublisherMatch ? signingKeyID : null), null, timeToWaitForPreexisting, handle);
// If we want it self-signed...
if ((SELF_SIGNED_KEY_LOCATOR == signingKeyLocator) && (null != availableContent)) {
// do mean == here....
// have already verified that keyDigest is the digest of the content of availableContent
if (!PublicKeyObject.isSelfSigned(SegmentationProfile.segmentRoot(availableContent.name()),
keyDigest, availableContent.signedInfo().getKeyLocator())) {
// it would be perfect, but it's not self-signed
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "Found our key published under desired name {0}, but not self-signed as required - key locator is {1}.",
availableContent.name(), availableContent.signedInfo().getKeyLocator());
}
availableContent = null;
}
}
if (null != availableContent) {
// Make sure the key is in our repository
PublicKeyObject pko = new PublicKeyObject(availableContent, handle);
RepositoryControl.localRepoSync(handle, pko);
return pko;
} else {
// We need to write this content ourselves, nobody else has it. We know we really want to
// write it, no point in checking again to see if it's there.
PublicKeyObject publishedKey =
publishKey(keyName, keyToPublish, signingKeyID, signingKeyLocator,
null, SaveType.REPOSITORY, handle, handle.keyManager());
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "Published key {0} from scratch as content {1}.", publishedKey.getVersionedName(),
Component.printURI(publishedKey.getContentDigest()));
}
return publishedKey;
}
}
/**
* Note: this is the lowest level interface to key publication; there are many higher-level
* interfaces that are probably what you want. This needs to be public to get across
* package constraints.
* Publish a signed record for this key. We've already decided we need to publish,
* and how; no more checks are made to see if the key already exists.
*
* @param keyName the key's content name. Will add a version when saving if it doesn't
* have one already. If it does have a version, will use that one (see below for effect
* of version on the key locator). (Note that this is not
* standard behavior for savable network content, which needs its version explicitly
* set.)
* @param keyToPublish the public key to publish
* @param keyID the publisher id
* @param signingKeyID the key id of the key pair to sign with
* @param signingKeyLocator the key locator to use if we save this key (if it is not already published).
* If not specified, we look for the default locator for the signing key. If there is none,
* and we are signing with the same key we are publishing, we build a
* self-referential key locator, using the name passed in (versioned or not).
* @param flowController flow controller to use. If non-null, saveType is ignored.
* @param saveType -- if we don't want to hand in a special-purpose flow controller, set saveType to RAW
* or REPO to get standard publishing behavior.
* @param handle the handle to use if we haven't specified a flow controller. Makes a flow controller
* of the type specified by saveType.
* @param keyManager the key manager to use to pull additional signing information (default keys
* and locators if not specified). If null, taken from handle. Also publish key added to its cache.
* @return the published information about this key, whether we published it or someone else had
* @throws IOException
*/
public static PublicKeyObject publishKey(
ContentName keyName, PublicKey keyToPublish,
PublisherPublicKeyDigest signingKeyID, KeyLocator signingKeyLocator,
CCNFlowControl flowController,
SaveType saveType,
CCNHandle handle,
KeyManager keyManager)
throws IOException {
if ((null == keyManager) && (null != handle)) {
keyManager = handle.keyManager();
}
if ((null == keyManager) || ((null == flowController) && (null == handle)) ||
((null == flowController) && (null == saveType))) {
// TODO DKS not quite right type...
throw new ErrorStateException("Must provide a flow controller or a handle and a save type, and a key manager!");
}
// Now, finally; it's not published, so make an object to write it
// with. We've already tried to pull it, so don't try here. Will
// set publisher info below.
// Need a key locator to stick in data entry for
// locator. Could use key itself, but then would have
// key both in the content for this item and in the
// key locator, which is redundant. Use naming form
// that allows for self-referential key names -- the
// CCN equivalent of a "self-signed cert". Means that
// we will refer to only the base key name and the publisher ID.
if (null == signingKeyID) {
signingKeyID = keyManager.getDefaultKeyID();
}
// Here is where we get tricky. We might really want the key to be of a particular
// version. In general, as we use the network objects to write versioned versioned stuff,
// we might not be able to take the last component of a name, if versioned, as the version
// to use to save -- might really want <name>/<version1>/<version2>. So unless we want to
// make that impossible to achieve, we need to not have the network objects take the
// name <name>/<version1> and save to <version1> (though they read from <version1> just
// fine given the same). You always want to save to a new version, unless someone tells you
// something different from the outside.
// Come up with a contorted option. If you want to publish <version>/<version> stuff, you
// need to pass in the second version...
CCNTime keyVersion = null; // do we force a version?
Tuple<ContentName, byte []> nameAndVersion = VersioningProfile.cutTerminalVersion(keyName);
if (null != nameAndVersion.second()) {
keyVersion = VersioningProfile.getVersionComponentAsTimestamp(nameAndVersion.second());
} else {
keyVersion = new CCNTime(); // so we can use it in locator
}
// Set key locator if not specified, include version for self-signed.
// Really do want == here
if ((null == signingKeyLocator) || (SELF_SIGNED_KEY_LOCATOR == signingKeyLocator)) {
KeyLocator existingLocator = keyManager.getKeyLocator(signingKeyID);
// If we've asked for this to be self-signed, or we have made the default KEY
// type key locator, make this a self-signed key.
if ((SELF_SIGNED_KEY_LOCATOR == signingKeyLocator) ||
(existingLocator.type() == KeyLocatorType.KEY)) {
PublisherPublicKeyDigest keyDigest = new PublisherPublicKeyDigest(keyToPublish);
if (signingKeyID.equals(keyDigest)) {
// Make a self-referential key locator. Include the version, in case we are not using the key ID in the name.
// People wanting versionless key locators need to construct their own.
existingLocator = new KeyLocator(
new KeyName(new ContentName(nameAndVersion.first(), keyVersion), signingKeyID));
if (Log.isLoggable(Log.FAC_KEYS, Level.FINER)) {
Log.finer(Log.FAC_KEYS, "Overriding constructed key locator of type KEY, making self-referential locator {0}", existingLocator);
}
}
}
signingKeyLocator = existingLocator;
}
PublicKeyObject keyObject = null;
if (null != flowController) {
// If a flow controller was specified, use that
keyObject = new PublicKeyObject(nameAndVersion.first(), keyToPublish,
signingKeyID, signingKeyLocator, flowController);
} else {
// No flow controller given, use specified saveType.
keyObject = new PublicKeyObject(nameAndVersion.first(), keyToPublish, saveType,
signingKeyID, signingKeyLocator, handle);
}
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "publishKey: key not previously published, making new key object {0} with version {1} displayed as {2}",
keyObject.getVersionedName(), keyVersion,
((null != nameAndVersion.second()) ? Component.printURI(nameAndVersion.second()) : "<no version>"));
}
// Eventually may want to find something already published and link to it, but be simple here.
if (!keyObject.save(keyVersion)) {
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "Not saving key when we thought we needed to: desired key value {0}, have key value {1}, " +
keyToPublish, new PublisherPublicKeyDigest(keyObject.publicKey()));
}
} else {
if (Log.isLoggable(Log.FAC_KEYS, Level.INFO)) {
Log.info(Log.FAC_KEYS, "Published key {0} to name {1} with key locator {2}; ephemeral digest {3}.",
keyToPublish, keyObject.getVersionedName(), signingKeyLocator,
Component.printURI(keyObject.getFirstDigest()));
}
}
keyManager.getPublicKeyCache().remember(keyObject);
return keyObject;
}
/**
* Right now KeyServers are hidden in our subclasses.... this makes it hard to expose
* control of filter registration. This is a bad attempt at an API for that, it should
* change. Don't make it abstract as subclasses may not need it.
* @throws IOException
*/
public void respondToKeyRequests(ContentName keyPrefix) throws IOException {}
/**
* Handle access control manager cache.
* @param contentName
* @return
*/
public abstract AccessControlManager getAccessControlManagerForName(ContentName contentName);
public abstract void rememberAccessControlManager(AccessControlManager acm);
}