All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.named_data.jndn.security.KeyChain Maven / Gradle / Ivy

/**
 * Copyright (C) 2014-2019 Regents of the University of California.
 * @author: Jeff Thompson 
 * @author: From code in ndn-cxx by Yingdi Yu 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program.  If not, see .
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

package net.named_data.jndn.security;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.named_data.jndn.ContentType;
import net.named_data.jndn.Data;
import net.named_data.jndn.DigestSha256Signature;
import net.named_data.jndn.Face;
import net.named_data.jndn.HmacWithSha256Signature;
import net.named_data.jndn.Interest;
import net.named_data.jndn.KeyLocator;
import net.named_data.jndn.KeyLocatorType;
import net.named_data.jndn.Name;
import net.named_data.jndn.OnData;
import net.named_data.jndn.OnTimeout;
import net.named_data.jndn.Sha256WithEcdsaSignature;
import net.named_data.jndn.Sha256WithRsaSignature;
import net.named_data.jndn.Signature;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.encoding.WireFormat;
import net.named_data.jndn.encoding.der.DerDecodingException;
import net.named_data.jndn.security.SigningInfo.SignerType;
import net.named_data.jndn.security.certificate.IdentityCertificate;
import net.named_data.jndn.security.certificate.PublicKey;
import net.named_data.jndn.security.identity.BasicIdentityStorage;
import net.named_data.jndn.security.identity.IdentityManager;
import net.named_data.jndn.security.pib.Pib;
import net.named_data.jndn.security.pib.PibIdentity;
import net.named_data.jndn.security.pib.PibImpl;
import net.named_data.jndn.security.pib.PibKey;
import net.named_data.jndn.security.pib.PibMemory;
import net.named_data.jndn.security.pib.PibSqlite3;
import net.named_data.jndn.security.policy.NoVerifyPolicyManager;
import net.named_data.jndn.security.policy.PolicyManager;
import net.named_data.jndn.security.tpm.Tpm;
import net.named_data.jndn.security.tpm.TpmBackEnd;
import net.named_data.jndn.security.tpm.TpmBackEndFile;
import net.named_data.jndn.security.tpm.TpmBackEndMemory;
import net.named_data.jndn.security.v2.CertificateV2;
import net.named_data.jndn.util.Blob;
import net.named_data.jndn.util.Common;
import net.named_data.jndn.util.ConfigFile;
import net.named_data.jndn.util.SignedBlob;

/**
 * KeyChain is the main class of the security library.
 *
 * The KeyChain class provides a set of interfaces to the security library such
 * as identity management, policy configuration and packet signing and
 * verification.
 * @note This class is an experimental feature.  See the API docs for more
 * detail at
 * http://named-data.net/doc/ndn-ccl-api/key-chain.html .
 */
public class KeyChain {
  /**
   * A KeyChain.Error extends Exception and represents an error in KeyChain
   * processing.
   * Note that even though this is called "Error" to be consistent with the
   * other libraries, it extends the Java Exception class, not Error.
   */
  public static class Error extends Exception {
    public Error(String message)
    {
      super(message);
    }
  }

  /**
   * A KeyChain.InvalidSigningInfoError extends KeyChain.Error to indicate
   * that the supplied SigningInfo is invalid.
   */
  public static class InvalidSigningInfoError extends KeyChain.Error {
    public InvalidSigningInfoError(String message)
    {
      super(message);
    }
  }

  /**
   * A KeyChain.LocatorMismatchError extends KeyChain.Error to indicate that
   * the supplied TPM locator does not match the locator stored in the PIB.
   */
  public static class LocatorMismatchError extends KeyChain.Error {
    public LocatorMismatchError(String message)
    {
      super(message);
    }
  }

  public interface MakePibImpl {
    PibImpl makePibImpl(String location) throws PibImpl.Error;
  }

  public interface MakeTpmBackEnd {
    TpmBackEnd makeTpmBackEnd(String location);
  }

  /**
   * Create a KeyChain to use the PIB and TPM defined by the given locators.
   * This creates a security v2 KeyChain that uses CertificateV2, Pib, Tpm and
   * Validator (instead of v1 Certificate, IdentityStorage, PrivateKeyStorage
   * and PolicyManager).
   * @param pibLocator The PIB locator, e.g., "pib-sqlite3:/example/dir".
   * @param tpmLocator The TPM locator, e.g., "tpm-memory:".
   * @param allowReset If true, the PIB will be reset when the supplied
   * tpmLocator mismatches the one in the PIB.
   * @throws KeyChain.LocatorMismatchError if the supplied TPM locator does not
   * match the locator stored in the PIB.
   */
  public KeyChain(String pibLocator, String tpmLocator, boolean allowReset)
    throws KeyChain.Error, PibImpl.Error, SecurityException, IOException
  {
    isSecurityV1_ = false;
    construct(pibLocator, tpmLocator, allowReset);
  }

  /**
   * Create a KeyChain to use the PIB and TPM defined by the given locators.
   * Don't allow resetting the PIB when the supplied tpmLocator mismatches the
   * one in the PIB.
   * This creates a security v2 KeyChain that uses CertificateV2, Pib, Tpm and
   * Validator (instead of v1 Certificate, IdentityStorage, PrivateKeyStorage
   * and PolicyManager).
   * @param pibLocator The PIB locator, e.g., "pib-sqlite3:/example/dir".
   * @param tpmLocator The TPM locator, e.g., "tpm-memory:".
   * @throws KeyChain.LocatorMismatchError if the supplied TPM locator does not
   * match the locator stored in the PIB.
   */
  public KeyChain(String pibLocator, String tpmLocator)
    throws KeyChain.Error, PibImpl.Error, SecurityException, IOException
  {
    isSecurityV1_ = false;
    construct(pibLocator, tpmLocator, false);
  }

  /**
   * Create a security v2 KeyChain with explicitly-created PIB and TPM objects,
   * and that still uses the v1 PolicyManager.
   * @param pibImpl An explicitly-created PIB object of a subclass of PibImpl.
   * @param tpmBackEnd An explicitly-created TPM object of a subclass of
   * TpmBackEnd.
   * @param policyManager An object of a subclass of a security v1 PolicyManager.
   */
  public KeyChain
    (PibImpl pibImpl, TpmBackEnd tpmBackEnd, PolicyManager policyManager)
    throws PibImpl.Error
  {
    isSecurityV1_ = false;
    policyManager_ = policyManager;

    pib_ = new Pib("", "", pibImpl);
    tpm_ = new Tpm("", "", tpmBackEnd);
  }

  /**
   * Create a security v2 KeyChain with explicitly-created PIB and TPM objects.
   * This sets the policy manager to a security v1 NoVerifyPolicyManager.
   * @param pibImpl An explicitly-created PIB object of a subclass of PibImpl.
   * @param tpmBackEnd An explicitly-created TPM object of a subclass of
   * TpmBackEnd.
   */
  public KeyChain(PibImpl pibImpl, TpmBackEnd tpmBackEnd)
    throws PibImpl.Error
  {
    isSecurityV1_ = false;
    policyManager_ = new NoVerifyPolicyManager();

    pib_ = new Pib("", "", pibImpl);
    tpm_ = new Tpm("", "", tpmBackEnd);
  }

  /**
   * Create a new security v1 KeyChain with the given IdentityManager and
   * PolicyManager. For security v2, use KeyChain(pibLocator, tpmLocator) or the
   * default constructor if your .ndn folder is already initialized for v2.
   * @param identityManager An object of a subclass of IdentityManager.
   * @param policyManager An object of a subclass of PolicyManager.
   */
  public KeyChain
    (IdentityManager identityManager, PolicyManager policyManager)
  {
    isSecurityV1_ = true;

    identityManager_ = identityManager;
    policyManager_ = policyManager;
  }

  /**
   * Create a new security v1 KeyChain with the given IdentityManager and a
   * NoVerifyPolicyManager. For security v2, use KeyChain(pibLocator, tpmLocator)
   * or the default constructor if your .ndn folder is already initialized for v2.
   * @param identityManager An object of a subclass of IdentityManager.
   */
  public KeyChain(IdentityManager identityManager)
  {
    isSecurityV1_ = true;

    identityManager_ = identityManager;
    policyManager_ = new NoVerifyPolicyManager();
  }

  /**
   * Create a KeyChain with the default PIB and TPM, which are
   * platform-dependent and can be overridden system-wide or individually by the
   * user. This creates a security v2 KeyChain that uses CertificateV2, Pib, Tpm
   * and Validator. However, if the default security v1 database file still
   * exists, and the default security v2 database file does not yet exists,then
   * assume that the system is running an older NFD and create a security v1
   * KeyChain with the default IdentityManager and a NoVerifyPolicyManager.
   * Note: To create a security v2 KeyChain on android, you must use the
   * KeyChain constructor to provide AndroidSqlite3Pib and TpmBackEndFile
   * objects which are initialized with the explicit directory for the Android
   * filesDir.
   */
  public KeyChain()
    throws SecurityException, KeyChain.Error, PibImpl.Error, IOException
  {
    isSecurityV1_ = false;

    if (BasicIdentityStorage.getDefaultDatabaseFilePath().exists() &&
        !PibSqlite3.getDefaultDatabaseFilePath().exists()) {
      // The security v1 SQLite file still exists and the security v2 does not yet.
      isSecurityV1_ = true;
      identityManager_ = new IdentityManager();
      policyManager_ = new NoVerifyPolicyManager();

      return;
    }

    construct("", "", true);
  }

  public final Pib
  getPib()
  {
    if (isSecurityV1_)
      throw new AssertionError("getPib is not supported for security v1");

    return pib_;
  }

  public final Tpm
  getTpm()
  {
    if (isSecurityV1_)
      throw new AssertionError("getTpm is not supported for security v1");

    return tpm_;
  }

  /**
   * Get the flag set by the constructor if this is a security v1 or v2 KeyChain.
   * @return True if this is a security v1 KeyChain, false if this is a security
   * v2 KeyChain.
   */
  public final boolean
  getIsSecurityV1() { return isSecurityV1_; }

  // Identity management

  /**
   * Create a security V2 identity for identityName. This method will check if
   * the identity exists in PIB and whether the identity has a default key and
   * default certificate. If the identity does not exist, this method will
   * create the identity in PIB. If the identity's default key does not exist,
   * this method will create a key pair and set it as the identity's default
   * key. If the key's default certificate is missing, this method will create a
   * self-signed certificate for the key. If identityName did not exist and no
   * default identity was selected before, the created identity will be set as
   * the default identity.
   * @param identityName The name of the identity.
   * @param params The key parameters if a key needs to be generated for the
   *   identity.
   * @return The created PibIdentity instance.
   */
  public final PibIdentity
  createIdentityV2(Name identityName, KeyParams params)
    throws PibImpl.Error, Pib.Error, Tpm.Error, TpmBackEnd.Error, Error
  {
    PibIdentity id = pib_.addIdentity_(identityName);

    PibKey key;
    try {
      key = id.getDefaultKey();
    }
    catch (Pib.Error ex) {
      key = createKey(id, params);
    }

    try {
      key.getDefaultCertificate();
    }
    catch (Pib.Error ex) {
      Logger.getLogger(this.getClass().getName()).log
        (Level.INFO, "No default cert for " + key.getName() +
         ", requesting self-signing");
      selfSign(key);
    }

    return id;
  }

  /**
   * Create a security V2 identity for identityName. This method will check if
   * the identity exists in PIB and whether the identity has a default key and
   * default certificate. If the identity does not exist, this method will
   * create the identity in PIB. If the identity's default key does not exist,
   * this method will create a key pair using getDefaultKeyParams() and set it
   * as the identity's default key. If the key's default certificate is missing,
   * this method will create a self-signed certificate for the key. If
   * identityName did not exist and no default identity was selected before, the
   * created identity will be set as the default identity.
   * @param identityName The name of the identity.
   * @return The created Identity instance.
   */
  public final PibIdentity
  createIdentityV2(Name identityName)
    throws PibImpl.Error, Pib.Error, Tpm.Error, TpmBackEnd.Error, Error
  {
    return createIdentityV2(identityName, getDefaultKeyParams());
  }

  /**
   * Delete the identity. After this operation, the identity is invalid.
   * @param identity The identity to delete.
   */
  public final void
  deleteIdentity(PibIdentity identity) throws PibImpl.Error, TpmBackEnd.Error
  {
    Name identityName = identity.getName();

    ArrayList keyNames = identity.getKeys_().getKeyNames();
    for (Name keyName : keyNames)
      tpm_.deleteKey_(keyName);

    pib_.removeIdentity_(identityName);
    // TODO: Mark identity as invalid.
  }

  /**
   * Set the identity as the default identity.
   * @param identity The identity to make the default.
   */
  public final void
  setDefaultIdentity(PibIdentity identity) throws PibImpl.Error, Pib.Error
  {
    pib_.setDefaultIdentity_(identity.getName());
  }

  // Key management

  /**
   * Create a key for the identity according to params. If the identity had no
   * default key selected, the created key will be set as the default for this
   * identity. This method will also create a self-signed certificate for the
   * created key.
   * @param identity A valid PibIdentity object.
   * @param params The key parameters if a key needs to be generated for the
   * identity.
   * @return The new PibKey.
   */
  public final PibKey
  createKey(PibIdentity identity, KeyParams params)
    throws Tpm.Error, TpmBackEnd.Error, PibImpl.Error, Pib.Error, KeyChain.Error
  {
    // Create the key in the TPM.
    Name keyName = tpm_.createKey_(identity.getName(), params);

    // Set up the key info in the PIB.
    Blob publicKey = tpm_.getPublicKey(keyName);
    PibKey key = identity.addKey_(publicKey.buf(), keyName);

    Logger.getLogger(this.getClass().getName()).log
      (Level.INFO,
       "Requesting self-signing for newly created key " + key.getName().toUri());
    selfSign(key);

    return key;
  }

  /**
   * Create a key for the identity according to getDefaultKeyParams(). If the
   * identity had no default key selected, the created key will be set as the
   * default for this identity. This method will also create a self-signed
   * certificate for the created key.
   * @param identity A valid PibIdentity object.
   * @return The new PibKey.
   */
  public final PibKey
  createKey(PibIdentity identity)
    throws Tpm.Error, TpmBackEnd.Error, PibImpl.Error, Pib.Error, KeyChain.Error
  {
    return createKey(identity, getDefaultKeyParams());
  }

  /**
   * Delete the given key of the given identity. The key becomes invalid.
   * @param identity A valid PibIdentity object.
   * @param key The key to delete.
   * @throws IllegalArgumentException If the key does not belong to the identity.
   */
  public final void
  deleteKey(PibIdentity identity, PibKey key)
    throws PibImpl.Error, TpmBackEnd.Error
  {
    Name keyName = key.getName();
    if (!identity.getName().equals(key.getIdentityName()))
      throw new IllegalArgumentException("Identity `" +
        identity.getName().toUri() + "` does not match key `" +
        keyName.toUri() + "`");

    identity.removeKey_(keyName);
    tpm_.deleteKey_(keyName);
  }

  /**
   * Set the key as the default key of identity.
   * @param identity A valid PibIdentity object.
   * @param key The key to become the default.
   * @throws IllegalArgumentException If the key does not belong to the identity.
   */
  public final void
  setDefaultKey(PibIdentity identity, PibKey key) throws Pib.Error, PibImpl.Error
  {
    if (!identity.getName().equals(key.getIdentityName()))
      throw new IllegalArgumentException("Identity `" + identity.getName().toUri() +
        "` does not match key `" + key.getName().toUri() + "`");

    identity.setDefaultKey_(key.getName());
  }

  // Certificate management

  /**
   * Add a certificate for the key. If the key had no default certificate
   * selected, the added certificate will be set as the default certificate for
   * this key.
   * @param key A valid PibKey object.
   * @param certificate The certificate to add. This copies the object.
   * @note This method overwrites a certificate with the same name, without
   * considering the implicit digest.
   * @throws IllegalArgumentException If the key does not match the certificate.
   */
  public final void
  addCertificate(PibKey key, CertificateV2 certificate)
    throws CertificateV2.Error, PibImpl.Error
  {
    if (!key.getName().equals(certificate.getKeyName()) ||
        !certificate.getContent().equals(key.getPublicKey()))
      throw new IllegalArgumentException("Key `" + key.getName().toUri() +
        "` does not match certificate `" + certificate.getKeyName().toUri() + "`");

    key.addCertificate_(certificate);
  }

  /**
   * Delete the certificate with the given name from the given key.
   * If the certificate does not exist, this does nothing.
   * @param key A valid PibKey object.
   * @param certificateName The name of the certificate to delete.
   * @throws IllegalArgumentException If certificateName does not follow
   * certificate naming conventions.
   */
  public final void
  deleteCertificate(PibKey key, Name certificateName) throws PibImpl.Error
  {
    if (!CertificateV2.isValidName(certificateName))
      throw new IllegalArgumentException("Wrong certificate name `" +
        certificateName.toUri() + "`");

    key.removeCertificate_(certificateName);
  }

  /**
   * Set the certificate as the default certificate of the key. The certificate
   * will be added to the key, potentially overriding an existing certificate if
   * it has the same name (without considering implicit digest).
   * @param key A valid PibKey object.
   * @param certificate The certificate to become the default. This copies the
   * object.
   */
  public final void
  setDefaultCertificate(PibKey key, CertificateV2 certificate)
    throws PibImpl.Error, CertificateV2.Error, Pib.Error
  {
    // This replaces the certificate it it exists.
    addCertificate(key, certificate);
    key.setDefaultCertificate_(certificate.getName());
  }

  // Signing

  /**
   * Wire encode the Data object, sign it according to the supplied signing
   * parameters, and set its signature.
   * @param data The Data object to be signed. This replaces its Signature
   * object based on the type of key and other info in the SigningInfo params,
   * and updates the wireEncoding.
   * @param params The signing parameters.
   * @param wireFormat A WireFormat object used to encode the input.
   * @throws KeyChain.Error if signing fails.
   * @throws KeyChain.InvalidSigningInfoError if params is invalid, or if the
   * identity, key or certificate specified in params does not exist.
   */
  public final void
  sign(Data data, SigningInfo params, WireFormat wireFormat)
    throws TpmBackEnd.Error, PibImpl.Error, KeyChain.Error
  {
    Name[] keyName = new Name[1];
    Signature signatureInfo = prepareSignatureInfo(params, keyName);

    data.setSignature(signatureInfo);

    // Encode once to get the signed portion.
    SignedBlob encoding = data.wireEncode(wireFormat);

    Blob signatureBytes = sign
      (encoding.signedBuf(), keyName[0], params.getDigestAlgorithm());
    data.getSignature().setSignature(signatureBytes);

    // Encode again to include the signature.
    data.wireEncode(wireFormat);
  }

  /**
   * Wire encode the Data object, sign it according to the supplied signing
   * parameters, and set its signature.
   * Use the default WireFormat.getDefaultWireFormat().
   * @param data The Data object to be signed. This replaces its Signature
   * object based on the type of key and other info in the SigningInfo params,
   * and updates the wireEncoding.
   * @param params The signing parameters.
   * @throws KeyChain.Error if signing fails.
   * @throws KeyChain.InvalidSigningInfoError if params is invalid, or if the
   * identity, key or certificate specified in params does not exist.
   */
  public final void
  sign(Data data, SigningInfo params)
    throws TpmBackEnd.Error, PibImpl.Error, KeyChain.Error
  {
    sign(data, params, WireFormat.getDefaultWireFormat());
  }

  /**
   * Wire encode the Data object, sign it with the default key of the default
   * identity, and set its signature.
   * If this is a security v1 KeyChain then use the IdentityManager to get the
   * default identity. Otherwise use the PIB.
   * @param data The Data object to be signed. This replaces its Signature
   * object based on the type of key of the default identity, and updates the
   * wireEncoding.
   * @param wireFormat A WireFormat object used to encode the input.
   */
  public final void
  sign(Data data, WireFormat wireFormat)
    throws SecurityException, TpmBackEnd.Error, PibImpl.Error, KeyChain.Error
  {
    if (isSecurityV1_) {
      identityManager_.signByCertificate
        (data, prepareDefaultCertificateName(), wireFormat);
      return;
    }

    sign(data, defaultSigningInfo_, wireFormat);
  }

  /**
   * Wire encode the Data object, sign it with the default key of the default
   * identity, and set its signature.
   * If this is a security v1 KeyChain then use the IdentityManager to get the
   * default identity. Otherwise use the PIB.
   * Use the default WireFormat.getDefaultWireFormat().
   * @param data The Data object to be signed. This replaces its Signature
   * object based on the type of key of the default identity, and updates the
   * wireEncoding.
   */
  public final void
  sign(Data data)
    throws SecurityException, TpmBackEnd.Error, PibImpl.Error, KeyChain.Error
  {
    sign(data, WireFormat.getDefaultWireFormat());
  }

  /**
   * Sign the Interest according to the supplied signing parameters. Append a
   * SignatureInfo to the Interest name, sign the encoded name components and
   * append a final name component with the signature bits.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param params The signing parameters.
   * @param wireFormat A WireFormat object used to encode the input and encode
   * the appended components.
   * @throws KeyChain.Error if signing fails.
   * @throws KeyChain.InvalidSigningInfoError if params is invalid, or if the
   * identity, key or certificate specified in params does not exist.
   */
  public final void
  sign(Interest interest, SigningInfo params, WireFormat wireFormat)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error
  {
    Name[] keyName = new Name[1];
    Signature signatureInfo = prepareSignatureInfo(params, keyName);

    // Append the encoded SignatureInfo.
    interest.getName().append(wireFormat.encodeSignatureInfo(signatureInfo));

    // Append an empty signature so that the "signedPortion" is correct.
    interest.getName().append(new Name.Component());
    // Encode once to get the signed portion, and sign.
    SignedBlob encoding = interest.wireEncode(wireFormat);
    Blob signatureBytes = sign
      (encoding.signedBuf(), keyName[0], params.getDigestAlgorithm());
    signatureInfo.setSignature(signatureBytes);

    // Remove the empty signature and append the real one.
    interest.setName(interest.getName().getPrefix(-1).append
      (wireFormat.encodeSignatureValue(signatureInfo)));
  }

  /**
   * Sign the Interest according to the supplied signing parameters. Append a
   * SignatureInfo to the Interest name, sign the encoded name components and
   * append a final name component with the signature bits.
   * Use the default WireFormat.getDefaultWireFormat().
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param params The signing parameters.
   * @throws KeyChain.Error if signing fails.
   * @throws KeyChain.InvalidSigningInfoError if params is invalid, or if the
   * identity, key or certificate specified in params does not exist.
   */
  public final void
  sign(Interest interest, SigningInfo params)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error
  {
    sign(interest, params, WireFormat.getDefaultWireFormat());
  }

  /**
   * Sign the Interest with the default key of the default identity. Append a
   * SignatureInfo to the Interest name, sign the encoded name components and
   * append a final name component with the signature bits.
   * If this is a security v1 KeyChain then use the IdentityManager to get the
   * default identity. Otherwise use the PIB.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param wireFormat A WireFormat object used to encode the input and encode
   * the appended components.
   */
  public final void
  sign(Interest interest, WireFormat wireFormat)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error, SecurityException
  {
    if (isSecurityV1_) {
      identityManager_.signInterestByCertificate
        (interest, prepareDefaultCertificateName(), wireFormat);
      return;
    }

    sign(interest, defaultSigningInfo_, wireFormat);
  }

  /**
   * Sign the Interest with the default key of the default identity. Append a
   * SignatureInfo to the Interest name, sign the encoded name components and
   * append a final name component with the signature bits.
   * Use the default WireFormat.getDefaultWireFormat().
   * If this is a security v1 KeyChain then use the IdentityManager to get the
   * default identity. Otherwise use the PIB.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   */
  public final void
  sign(Interest interest)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error, SecurityException
  {
    sign(interest, WireFormat.getDefaultWireFormat());
  }

  /**
   * Sign the byte buffer according to the supplied signing parameters.
   * @param buffer The byte buffer to be signed.
   * @param params The signing parameters. If params refers to an identity, this
   * selects the default key of the identity. If params refers to a key or
   * certificate, this selects the corresponding key.
   * @return The signature Blob, or an isNull Blob if params.getDigestAlgorithm()
   * is unrecognized.
   */
  public final Blob
  sign(ByteBuffer buffer, SigningInfo params)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error
  {
    Name[] keyName = new Name[1];
    Signature signatureInfo = prepareSignatureInfo(params, keyName);

    return sign(buffer, keyName[0], params.getDigestAlgorithm());
  }

  /**
   * Sign the byte buffer using the default key of the default identity.
   * @param buffer The byte buffer to be signed.
   * @return The signature Blob.
   */
  public final Blob
  sign(ByteBuffer buffer)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error
  {
    return sign(buffer, defaultSigningInfo_);
  }

  /**
   * Generate a self-signed certificate for the public key and add it to the
   * PIB. This creates the certificate name from the key name by appending
   * "self" and a version based on the current time. If no default certificate
   * for the key has been set, then set the certificate as the default for the
   * key.
   * @param key The PibKey with the key name and public key.
   * @param wireFormat A WireFormat object used to encode the certificate.
   * @return The new certificate.
   */
  public final CertificateV2
  selfSign(PibKey key, WireFormat wireFormat)
    throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error
  {
    CertificateV2 certificate = new CertificateV2();

    // Set the name.
    double now = Common.getNowMilliseconds();
    Name certificateName = new Name(key.getName());
    certificateName.append("self").appendVersion((long)now);
    certificate.setName(certificateName);

    // Set the MetaInfo.
    certificate.getMetaInfo().setType(ContentType.KEY);
    // Set a one-hour freshness period.
    certificate.getMetaInfo().setFreshnessPeriod(3600 * 1000.0);

    // Set the content.
    certificate.setContent(key.getPublicKey());

    // Set the signature-info.
    SigningInfo signingInfo = new SigningInfo(key);
    // Set a 20-year validity period.
    signingInfo.setValidityPeriod
      (new ValidityPeriod(now, now + 20 * 365 * 24 * 3600 * 1000.0));

    sign(certificate, signingInfo, wireFormat);

    try {
      key.addCertificate_(certificate);
    } catch (CertificateV2.Error ex) {
      // We don't expect this since we just created the certificate.
      throw new Error("Error encoding certificate: " + ex);
    }
    return certificate;
  }


  /**
   * Generate a self-signed certificate for the public key and add it to the
   * PIB. This creates the certificate name from the key name by appending
   * "self" and a version based on the current time. If no default certificate
   * for the key has been set, then set the certificate as the default for the
   * key.
   * Use the default WireFormat.getDefaultWireFormat() to encode the certificate.
   * @param key The PibKey with the key name and public key.
   * @return The new certificate.
   */
  public final CertificateV2
  selfSign(PibKey key) throws PibImpl.Error, KeyChain.Error, TpmBackEnd.Error
  {
    return selfSign(key, WireFormat.getDefaultWireFormat());
  }

  // Import and export

  /**
   * Export a certificate and its corresponding private key in a SafeBag.
   * @param certificate The certificate to export. This gets the key from the
   * TPM using certificate.getKeyName().
   * @param password The password for encrypting the private key, which should
   * have characters in the range of 1 to 127. If the password is supplied, use
   * it to put a PKCS #8 EncryptedPrivateKeyInfo in the SafeBag. If the password
   * is null, put an unencrypted PKCS #8 PrivateKeyInfo in the SafeBag.
   * @return A SafeBag carrying the certificate and private key.
   * @throws KeyChain.Error certificate.getKeyName() key does not exist, if the
   * password is null and the TPM does not support exporting an unencrypted
   * private key, or for other errors exporting the private key.
   */
  public final SafeBag
  exportSafeBag(CertificateV2 certificate, ByteBuffer password)
    throws KeyChain.Error
  {
    Name keyName = certificate.getKeyName();

    Blob encryptedKey;
    try {
      encryptedKey = tpm_.exportPrivateKey_(keyName, password);
    } catch (Throwable ex) {
      throw new KeyChain.Error("Failed to export private key `" +
        keyName.toUri() + "`: " + ex);
    }

    return new SafeBag(certificate, encryptedKey);
  }

  /**
   * Export a certificate and its corresponding private key in a SafeBag, with a
   * null password which exports an unencrypted PKCS #8 PrivateKeyInfo.
   * @param certificate The certificate to export. This gets the key from the
   * TPM using certificate.getKeyName().
   * @return A SafeBag carrying the certificate and private key.
   * @throws KeyChain.Error certificate.getKeyName() key does not exist, if the
   * TPM does not support exporting an unencrypted private key, or for other
   * errors exporting the private key.
   */
  public final SafeBag
  exportSafeBag(CertificateV2 certificate)
    throws KeyChain.Error
  {
    return exportSafeBag(certificate, null);
  }

  /**
   * Import a certificate and its corresponding private key encapsulated in a
   * SafeBag. If the certificate and key are imported properly, the default
   * setting will be updated as if a new key and certificate is added into this
   * KeyChain.
   * @param safeBag The SafeBag containing the certificate and private key. This
   * copies the values from the SafeBag.
   * @param password The password for decrypting the private key, which should
   * have characters in the range of 1 to 127. If the password is supplied, use
   * it to decrypt the PKCS #8 EncryptedPrivateKeyInfo. If the password is null,
   * import an unencrypted PKCS #8 PrivateKeyInfo.
   * @throws KeyChain.Error if the private key cannot be imported, or if a
   * public key or private key of the same name already exists, or if a
   * certificate of the same name already exists.
   */
  public final void
  importSafeBag(SafeBag safeBag, ByteBuffer password)
    throws KeyChain.Error, CertificateV2.Error, TpmBackEnd.Error, PibImpl.Error,
      Pib.Error
  {
    CertificateV2 certificate = new CertificateV2(safeBag.getCertificate());
    Name identity = certificate.getIdentity();
    Name keyName = certificate.getKeyName();
    Blob publicKeyBits = certificate.getPublicKey();

    if (tpm_.hasKey(keyName))
      throw new KeyChain.Error("Private key `" + keyName.toUri() +
        "` already exists");

    try {
      PibIdentity existingId = pib_.getIdentity(identity);
      existingId.getKey(keyName);
      throw new KeyChain.Error("Public key `" + keyName.toUri() +
        "` already exists");
    } catch (Pib.Error ex) {
      // Either the identity or the key doesn't exist, so OK to import.
    }

    try {
      tpm_.importPrivateKey_(keyName, safeBag.getPrivateKeyBag().buf(), password);
    } catch (Exception ex) {
      throw new KeyChain.Error("Failed to import private key `" +
        keyName.toUri() + "`: " + ex);
    }

    // Check the consistency of the private key and certificate.
    Blob content = new Blob(new int[] {0x01, 0x02, 0x03, 0x04});
    Blob signatureBits;
    try {
      signatureBits = tpm_.sign(content.buf(), keyName, DigestAlgorithm.SHA256);
    } catch (Exception ex) {
      tpm_.deleteKey_(keyName);
      throw new KeyChain.Error("Invalid private key `" + keyName.toUri() + "`");
    }

    PublicKey publicKey;
    try {
      publicKey = new PublicKey(publicKeyBits);
    } catch (UnrecognizedKeyFormatException ex) {
      // Promote to KeyChain.Error.
      tpm_.deleteKey_(keyName);
      throw new KeyChain.Error("Error decoding public key " + ex);
    }

    if (!VerificationHelpers.verifySignature(content, signatureBits, publicKey)) {
      tpm_.deleteKey_(keyName);
      throw new KeyChain.Error("Certificate `" + certificate.getName().toUri() +
        "` and private key `" + keyName.toUri() + "` do not match");
    }

    // The consistency is verified. Add to the PIB.
    PibIdentity id = pib_.addIdentity_(identity);
    PibKey key = id.addKey_(certificate.getPublicKey().buf(), keyName);
    key.addCertificate_(certificate);
  }

  /**
   * Import a certificate and its corresponding private key encapsulated in a
   * SafeBag, with a null password which imports an unencrypted PKCS #8
   * PrivateKeyInfo. If the certificate and key are imported properly, the
   * default setting will be updated as if a new key and certificate is added
   * into this KeyChain.
   * @param safeBag The SafeBag containing the certificate and private key. This
   * copies the values from the SafeBag.
   * @throws KeyChain.Error if the private key cannot be imported, or if a
   * public key or private key of the same name already exists, or if a
   * certificate of the same name already exists.
   */
  public final void
  importSafeBag(SafeBag safeBag)
    throws KeyChain.Error, CertificateV2.Error, TpmBackEnd.Error, PibImpl.Error,
      Pib.Error
  {
    importSafeBag(safeBag, null);
  }

  // PIB & TPM backend registry

  /**
   * Add to the PIB factories map where scheme is the key and makePibImpl is the
   * value. If your application has its own PIB implementations, this must be
   * called before creating a KeyChain instance which uses your PIB scheme.
   * @param scheme The PIB scheme.
   * @param makePibImpl An interface with makePibImpl which takes the PIB
   * location and returns a new PibImpl instance.
   */
  public static void
  registerPibBackend(String scheme, MakePibImpl makePibImpl)
  {
    getPibFactories().put(scheme, makePibImpl);
  }

  /**
   * Add to the TPM factories map where scheme is the key and makeTpmBackEnd is
   * the value. If your application has its own TPM implementations, this must
   * be called before creating a KeyChain instance which uses your TPM scheme.
   * @param scheme The TPM scheme.
   * @param makeTpmBackEnd An interface with makeTpmBackEnd which takes the TPM
   * location and returns a new TpmBackEnd instance.
   */
  public static void
  registerTpmBackend(String scheme, MakeTpmBackEnd makeTpmBackEnd)
  {
    getTpmFactories().put(scheme, makeTpmBackEnd);
  }

  // Security v1 methods

  /*****************************************
   *          Identity Management          *
   *****************************************/

  /**
   * Create a security v1 identity by creating a pair of Key-Signing-Key (KSK)
   * for this identity and a self-signed certificate of the KSK. If a key pair or
   * certificate for the identity already exists, use it.
   * @param identityName The name of the identity.
   * @param params The key parameters if a key needs to be generated for the
   * identity.
   * @return The name of the default certificate of the identity.
   * @throws SecurityException if the identity has already been created.
   */
  public final Name
  createIdentityAndCertificate(Name identityName, KeyParams params)
    throws SecurityException
  {
    return identityManager_.createIdentityAndCertificate(identityName, params);
  }

  /**
   * Create a security v1 identity by creating a pair of Key-Signing-Key (KSK)
   * for this identity and a self-signed certificate of the KSK. Use
   * getDefaultKeyParams() to create the key if needed. If a key pair or
   * certificate for the identity already exists, use it.
   * @param identityName The name of the identity.
   * @return The name of the default certificate of the identity.
   * @throws SecurityException if the identity has already been created.
   */
  public final Name
  createIdentityAndCertificate(Name identityName) throws SecurityException
  {
    return createIdentityAndCertificate(identityName, getDefaultKeyParams());
  }

  /**
   * Create a security v1 identity by creating a pair of Key-Signing-Key (KSK)
   * for this identity and a self-signed certificate of the KSK.
   * @deprecated Use createIdentityAndCertificate which returns the
   * certificate name instead of the key name.
   * @param identityName The name of the identity.
   * @param params The key parameters if a key needs to be generated for the
   * identity.
   * @return The key name of the auto-generated KSK of the identity.
   * @throws SecurityException if the identity has already been created.
   */
  public final Name
  createIdentity(Name identityName, KeyParams params) throws SecurityException
  {
    return IdentityCertificate.certificateNameToPublicKeyName
      (createIdentityAndCertificate(identityName, params));
  }

  /**
   * Create a security v1 identity by creating a pair of Key-Signing-Key (KSK)
   * for this identity and a self-signed certificate of the KSK. Use
   * getDefaultKeyParams() to create the key if needed.
   * @deprecated Use createIdentityAndCertificate which returns the
   * certificate name instead of the key name.
   * @param identityName The name of the identity.
   * @return The key name of the auto-generated KSK of the identity.
   * @throws SecurityException if the identity has already been created.
   */
  public final Name
  createIdentity(Name identityName) throws SecurityException
  {
    return IdentityCertificate.certificateNameToPublicKeyName
      (createIdentityAndCertificate(identityName));
  }

  /**
   * Delete the identity from the public and private key storage. If the
   * identity to be deleted is the current default system default, this will not
   * delete the identity and will return immediately.
   * @param identityName The name of the identity.
   */
  public final void
  deleteIdentity(Name identityName) throws SecurityException
  {
    if (!isSecurityV1_) {
      try {
        deleteIdentity(pib_.getIdentity(identityName));
      } catch (Pib.Error ex) {
      } catch (PibImpl.Error ex) {
      } catch (TpmBackEnd.Error ex) {
      }
      return;
    }

    identityManager_.deleteIdentity(identityName);
  }

  /**
   * Get the default identity.
   * @return The name of default identity.
   * @throws SecurityException if the default identity is not set.
   */
  public final Name
  getDefaultIdentity() throws SecurityException
  {
    if (!isSecurityV1_) {
      try {
        return pib_.getDefaultIdentity().getName();
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in getDefaultIdentity: " + ex);
      } catch (Pib.Error ex) {
        throw new SecurityException("Error in getDefaultIdentity: " + ex);
      }
    }

    return identityManager_.getDefaultIdentity();
  }

  /**
   * Get the default certificate name of the default identity.
   * @return The requested certificate name.
   * @throws SecurityException if the default identity is not set or the default
   * key name for the identity is not set or the default certificate name for
   * the key name is not set.
   */
  public final Name
  getDefaultCertificateName() throws SecurityException
  {
    if (!isSecurityV1_) {
      try {
        return pib_.getDefaultIdentity().getDefaultKey().getDefaultCertificate()
                .getName();
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in getDefaultCertificate: " + ex);
      } catch (Pib.Error ex) {
        throw new SecurityException("Error in getDefaultCertificate: " + ex);
      }
    }

    return identityManager_.getDefaultCertificateName();
  }

  /**
   * Generate a pair of RSA keys for the specified identity.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @param keySize The size of the key.
   * @return The generated key name.
   */
  public final Name
  generateRSAKeyPair
    (Name identityName, boolean isKsk, int keySize) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateRSAKeyPair is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateRSAKeyPair(identityName, isKsk, keySize);
  }

  /**
   * Generate a pair of RSA keys for the specified identity and default keySize
   * 2048.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @return The generated key name.
   */
  public final Name
  generateRSAKeyPair(Name identityName, boolean isKsk) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateRSAKeyPair is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateRSAKeyPair(identityName, isKsk);
  }

  /**
   * Generate a pair of RSA keys for the specified identity for a
   * Data-Signing-Key and default keySize 2048.
   * @param identityName The name of the identity.
   * @return The generated key name.
   */
  public final Name
  generateRSAKeyPair(Name identityName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateRSAKeyPair is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateRSAKeyPair(identityName);
  }

  /**
   * Generate a pair of ECDSA keys for the specified identity.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @param keySize The size of the key.
   * @return The generated key name.
   */
  public final Name
  generateEcdsaKeyPair
    (Name identityName, boolean isKsk, int keySize) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateEcdsaKeyPair is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateEcdsaKeyPair(identityName, isKsk, keySize);
  }

  /**
   * Generate a pair of ECDSA keys for the specified identity and default keySize
   * 256.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @return The generated key name.
   */
  public final Name
  generateEcdsaKeyPair(Name identityName, boolean isKsk) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateEcdsaKeyPair is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateEcdsaKeyPair(identityName, isKsk);
  }

  /**
   * Generate a pair of ECDSA keys for the specified identity for a
   * Data-Signing-Key and default keySize 256.
   * @param identityName The name of the identity.
   * @return The generated key name.
   */
  public final Name
  generateEcdsaKeyPair(Name identityName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateEcdsaKeyPair is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateEcdsaKeyPair(identityName);
  }

  /**
   * Set a key as the default key of an identity. The identity name is inferred
   * from keyName.
   * @param keyName The name of the key.
   * @param identityNameCheck The identity name to check that the keyName
   * contains the same identity name. If an empty name, it is ignored.
   */
  public final void
  setDefaultKeyForIdentity(Name keyName, Name identityNameCheck) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("setDefaultKeyForIdentity is not supported for security v2. Use getPib() methods.");

    identityManager_.setDefaultKeyForIdentity(keyName, identityNameCheck);
  }

  /**
   * Set a key as the default key of an identity. The identity name is inferred
   * from keyName.
   * @param keyName The name of the key.
   */
  public final void
  setDefaultKeyForIdentity(Name keyName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("setDefaultKeyForIdentity is not supported for security v2. Use getPib() methods.");

    identityManager_.setDefaultKeyForIdentity(keyName);
  }

  /**
   * Generate a pair of RSA keys for the specified identity and set it as the
   * default key for the identity.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @param keySize The size of the key.
   * @return The generated key name.
   */
  public final Name
  generateRSAKeyPairAsDefault
    (Name identityName, boolean isKsk, int keySize) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateRSAKeyPairAsDefault is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateRSAKeyPairAsDefault(identityName, isKsk, keySize);
  }

  /**
   * Generate a pair of RSA keys for the specified identity and set it as the
   * default key for the identity, using the default keySize 2048.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @return The generated key name.
   */
  public final Name
  generateRSAKeyPairAsDefault(Name identityName, boolean isKsk) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateRSAKeyPairAsDefault is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateRSAKeyPairAsDefault(identityName, isKsk);
  }

  /**
   * Generate a pair of RSA keys for the specified identity and set it as
   * default key for the identity for a Data-Signing-Key and using the default
   * keySize 2048.
   * @param identityName The name of the identity.
   * @return The generated key name.
   */
  public final Name
  generateRSAKeyPairAsDefault(Name identityName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateRSAKeyPairAsDefault is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateRSAKeyPairAsDefault(identityName);
  }

  /**
   * Generate a pair of ECDSA keys for the specified identity and set it as
   * default key for the identity.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @param keySize The size of the key.
   * @return The generated key name.
   */
  public final Name
  generateEcdsaKeyPairAsDefault
    (Name identityName, boolean isKsk, int keySize) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateEcdsaKeyPairAsDefault is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateEcdsaKeyPairAsDefault(identityName, isKsk, keySize);
  }

  /**
   * Generate a pair of ECDSA keys for the specified identity and set it as
   * default key for the identity, using the default keySize 256.
   * @param identityName The name of the identity.
   * @param isKsk true for generating a Key-Signing-Key (KSK), false for a Data-Signing-Key (KSK).
   * @return The generated key name.
   */
  public final Name
  generateEcdsaKeyPairAsDefault(Name identityName, boolean isKsk) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateEcdsaKeyPairAsDefault is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateEcdsaKeyPairAsDefault(identityName, isKsk);
  }

  /**
   * Generate a pair of ECDSA keys for the specified identity and set it as
   * default key for the identity for a Data-Signing-Key and using the default
   * keySize 256.
   * @param identityName The name of the identity.
   * @return The generated key name.
   */
  public final Name
  generateEcdsaKeyPairAsDefault(Name identityName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("generateEcdsaKeyPairAsDefault is not supported for security v2. Use createIdentityV2.");

    return identityManager_.generateEcdsaKeyPairAsDefault(identityName);
  }

  /**
   * Create a public key signing request.
   * @param keyName The name of the key.
   * @return The signing request data.
   * @throws SecurityException if the keyName is not found.
   */
  public final Blob
  createSigningRequest(Name keyName) throws SecurityException
  {
    if (!isSecurityV1_) {
      try {
        return pib_.getIdentity(PibKey.extractIdentityFromKeyName(keyName))
                .getKey(keyName).getPublicKey();
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in getKey: " + ex);
      } catch (Pib.Error ex) {
        throw new SecurityException("Error in getKey: " + ex);
      }
    }

    return identityManager_.getPublicKey(keyName).getKeyDer();
  }

  /**
   * Install an identity certificate into the public key identity storage.
   * @param certificate The certificate to to added.
   */
  public final void
  installIdentityCertificate(IdentityCertificate certificate) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("installIdentityCertificate is not supported for security v2. Use getPib() methods.");

    identityManager_.addCertificate(certificate);
  }

  /**
   * Set the certificate as the default for its corresponding key.
   * @param certificate The certificate.
   */
  public final void
  setDefaultCertificateForKey(IdentityCertificate certificate) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("setDefaultCertificateForKey is not supported for security v2. Use getPib() methods.");

    identityManager_.setDefaultCertificateForKey(certificate);
  }

  /**
   * Get a certificate with the specified name.
   * @param certificateName The name of the requested certificate.
   * @return The requested certificate.
   */
  public final IdentityCertificate
  getCertificate(Name certificateName) throws SecurityException, DerDecodingException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("getCertificate is not supported for security v2. Use getPib() methods.");

    return identityManager_.getCertificate(certificateName);
  }

  /**
   * @deprecated Use getCertificate.
   */
  public final IdentityCertificate
  getIdentityCertificate(Name certificateName) throws SecurityException, DerDecodingException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("getIdentityCertificate is not supported for security v2. Use getPib() methods.");

    return identityManager_.getCertificate(certificateName);
  }

  /**
   * Revoke a key.
   * @param keyName The name of the key that will be revoked.
   */
  public final void
  revokeKey(Name keyName)
  {
    //TODO: Implement
  }

  /**
   * Revoke a certificate.
   * @param certificateName The name of the certificate that will be revoked.
   */
  public final void
  revokeCertificate(Name certificateName)
  {
    //TODO: Implement
  }

  /**
   * Get the identity manager given to or created by the constructor.
   * @return The identity manager.
   */
  public final IdentityManager
  getIdentityManager()
  {
    if (!isSecurityV1_)
      throw new AssertionError
        ("getIdentityManager is not supported for security v2");

    return identityManager_;
  }


  /*****************************************
   *              Sign/Verify              *
   *****************************************/

  /**
   * Wire encode the Data object, sign it and set its signature.
   * @param data The Data object to be signed.  This updates its signature and
   * key locator field and wireEncoding.
   * @param certificateName The certificate name of the key to use for signing.
   * @param wireFormat A WireFormat object used to encode the input.
   */
  public final void
  sign(Data data, Name certificateName, WireFormat wireFormat) throws SecurityException
  {
    if (!isSecurityV1_) {
      SigningInfo signingInfo = new SigningInfo();
      signingInfo.setSigningCertificateName(certificateName);
      try {
        sign(data, signingInfo, wireFormat);
      } catch (TpmBackEnd.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      }
      return;
    }

    identityManager_.signByCertificate(data, certificateName, wireFormat);
  }

  /**
   * Wire encode the Data object, sign it and set its signature.
   * Use the default WireFormat.getDefaultWireFormat().
   * @param data The Data object to be signed.  This updates its signature and
   * key locator field and wireEncoding.
   * @param certificateName The certificate name of the key to use for signing.
   */
  public final void
  sign(Data data, Name certificateName) throws SecurityException
  {
    sign(data, certificateName, WireFormat.getDefaultWireFormat());
  }

  /**
   * Append a SignatureInfo to the Interest name, sign the name components and
   * append a final name component with the signature bits.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param certificateName The certificate name of the key to use for signing.
   * @param wireFormat A WireFormat object used to encode the input.
   */
  public final void
  sign(Interest interest, Name certificateName, WireFormat wireFormat) throws SecurityException
  {
    if (!isSecurityV1_) {
      SigningInfo signingInfo = new SigningInfo();
      signingInfo.setSigningCertificateName(certificateName);
      try {
        sign(interest, signingInfo, wireFormat);
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (TpmBackEnd.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      }
      return;
    }

    identityManager_.signInterestByCertificate
      (interest, certificateName, wireFormat);
  }

  /**
   * Append a SignatureInfo to the Interest name, sign the name components and
   * append a final name component with the signature bits.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param certificateName The certificate name of the key to use for signing.
   */
  public final void
  sign(Interest interest, Name certificateName) throws SecurityException
  {
    sign(interest, certificateName, WireFormat.getDefaultWireFormat());
  }

  /**
   * Sign the byte buffer using a certificate name and return a Signature object.
   * @param buffer The byte array to be signed.
   * @param certificateName The certificate name used to get the signing key and which will be put into KeyLocator.
   * @return The Signature.
   */
  public Signature
  sign(ByteBuffer buffer, Name certificateName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("sign(buffer, certificateName) is not supported for security v2. Use sign with SigningInfo.");

    return identityManager_.signByCertificate(buffer, certificateName);
  }

  /**
   * Wire encode the Data object, sign it and set its signature.
   * @param data The Data object to be signed.  This updates its signature and
   * key locator field and wireEncoding.
   * @param identityName The identity name for the key to use for signing.
   * If empty, infer the signing identity from the data packet name.
   * @param wireFormat A WireFormat object used to encode the input. If omitted, use WireFormat getDefaultWireFormat().
   */
  public final void
  signByIdentity
    (Data data, Name identityName, WireFormat wireFormat) throws SecurityException
  {
    if (!isSecurityV1_) {
      SigningInfo signingInfo = new SigningInfo();
      signingInfo.setSigningIdentity(identityName);
      try {
        sign(data, signingInfo, wireFormat);
      } catch (TpmBackEnd.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      }
      return;
    }

    Name signingCertificateName;

    if (identityName.size() == 0) {
      Name inferredIdentity = policyManager_.inferSigningIdentity(data.getName());
      if (inferredIdentity.size() == 0)
        signingCertificateName = identityManager_.getDefaultCertificateName();
      else
        signingCertificateName =
          identityManager_.getDefaultCertificateNameForIdentity(inferredIdentity);
    }
    else
      signingCertificateName =
        identityManager_.getDefaultCertificateNameForIdentity(identityName);

    if (signingCertificateName.size() == 0)
      throw new SecurityException("No qualified certificate name found!");

    if (!policyManager_.checkSigningPolicy(data.getName(), signingCertificateName))
      throw new SecurityException
        ("Signing Cert name does not comply with signing policy");

    identityManager_.signByCertificate(data, signingCertificateName, wireFormat);
  }

  /**
   * Wire encode the Data object, sign it and set its signature.
   * @param data The Data object to be signed.  This updates its signature and
   * key locator field and wireEncoding.
   * Use the default WireFormat.getDefaultWireFormat().
   * @param identityName The identity name for the key to use for signing.
   * If empty, infer the signing identity from the data packet name.
   */
  public final void
  signByIdentity(Data data, Name identityName) throws SecurityException
  {
    signByIdentity(data, identityName, WireFormat.getDefaultWireFormat());
  }

  /**
   * Wire encode the Data object, sign it and set its signature.
   * @param data The Data object to be signed.  This updates its signature and
   * key locator field and wireEncoding.
   * Infer the signing identity from the data packet name.
   * Use the default WireFormat.getDefaultWireFormat().
   */
  public final void
  signByIdentity(Data data) throws SecurityException
  {
    signByIdentity(data, new Name(), WireFormat.getDefaultWireFormat());
  }

  /**
   * Sign the byte buffer using an identity name and return a Signature object.
   * @param buffer The byte array to be signed.
   * @param identityName The identity name.
   * @return The Signature.
   */
  public Signature
  signByIdentity(ByteBuffer buffer, Name identityName) throws SecurityException
  {
    if (!isSecurityV1_)
      throw new SecurityException
        ("signByIdentity(buffer, identityName) is not supported for security v2. Use sign with SigningInfo.");

    Name signingCertificateName =
      identityManager_.getDefaultCertificateNameForIdentity(identityName);

    if (signingCertificateName.size() == 0)
      throw new SecurityException("No qualified certificate name found!");

    return identityManager_.signByCertificate(buffer, signingCertificateName);
  }

  /**
   * Wire encode the Data object, digest it and set its SignatureInfo to
   * a DigestSha256.
   * @param data The Data object to be signed. This updates its signature and
   * wireEncoding.
   * @param wireFormat A WireFormat object used to encode the input.
   */
  public final void
  signWithSha256(Data data, WireFormat wireFormat) throws SecurityException
  {
    if (!isSecurityV1_) {
      SigningInfo signingInfo = new SigningInfo();
      signingInfo.setSha256Signing();
      try {
        sign(data, signingInfo, wireFormat);
      } catch (TpmBackEnd.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      }
      return;
    }

    identityManager_.signWithSha256(data, wireFormat);
  }

  /**
   * Wire encode the Data object, digest it and set its SignatureInfo to
   * a DigestSha256.
   * @param data The Data object to be signed. This updates its signature and
   * wireEncoding.
   */
  public final void
  signWithSha256(Data data) throws SecurityException
  {
    signWithSha256(data, WireFormat.getDefaultWireFormat());
  }

  /**
   * Append a SignatureInfo for DigestSha256 to the Interest name, digest the
   * name components and append a final name component with the signature bits
   * (which is the digest).
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param wireFormat A WireFormat object used to encode the input.
   */
  public final void
  signWithSha256(Interest interest, WireFormat wireFormat) throws SecurityException
  {
    if (!isSecurityV1_) {
      SigningInfo signingInfo = new SigningInfo();
      signingInfo.setSha256Signing();
      try {
        sign(interest, signingInfo, wireFormat);
      } catch (PibImpl.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      } catch (TpmBackEnd.Error ex) {
        throw new SecurityException("Error in sign: " + ex);
      }
      return;
    }

    identityManager_.signInterestWithSha256(interest, wireFormat);
  }

  /**
   * Append a SignatureInfo for DigestSha256 to the Interest name, digest the
   * name components and append a final name component with the signature bits
   * (which is the digest).
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   */
  public final void
  signWithSha256(Interest interest) throws SecurityException
  {
    signWithSha256(interest, WireFormat.getDefaultWireFormat());
  }

  public final void
  verifyData
    (Data data, OnVerified onVerified, OnDataValidationFailed onValidationFailed,
     int stepCount) throws SecurityException
  {
    Logger.getLogger(this.getClass().getName()).log
      (Level.INFO, "Enter Verify");

    if (policyManager_.requireVerify(data)) {
      ValidationRequest nextStep = policyManager_.checkVerificationPolicy
        (data, stepCount, onVerified, onValidationFailed);
      if (nextStep != null) {
        VerifyCallbacks callbacks = new VerifyCallbacks
          (nextStep, nextStep.retry_, onValidationFailed, data);
        try {
          face_.expressInterest(nextStep.interest_, callbacks, callbacks);
        }
        catch (IOException ex) {
          try {
            onValidationFailed.onDataValidationFailed
              (data, "Error calling expressInterest " + ex);
          } catch (Throwable exception) {
            logger_.log(Level.SEVERE, "Error in onDataValidationFailed", exception);
          }
        }
      }
    }
    else if (policyManager_.skipVerifyAndTrust(data)) {
      try {
        onVerified.onVerified(data);
      } catch (Throwable ex) {
        logger_.log(Level.SEVERE, "Error in onVerified", ex);
      }
    }
    else {
      try {
        onValidationFailed.onDataValidationFailed
          (data,
           "The packet has no verify rule but skipVerifyAndTrust is false");
      } catch (Throwable ex) {
        logger_.log(Level.SEVERE, "Error in onDataValidationFailed", ex);
      }
    }
  }

  /**
   * Check the signature on the Data object and call either onVerify.onVerify or
   * onValidationFailed.onDataValidationFailed.
   * We use callback functions because verify may fetch information to check the
   * signature.
   * @param data The Data object with the signature to check. It is an error if
   * data does not have a wireEncoding.
   * To set the wireEncoding, you can call data.wireDecode.
   * @param onVerified If the signature is verified, this calls
   * onVerified.onVerified(data).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   * @param onValidationFailed If the signature check fails, this calls
   * onValidationFailed.onDataValidationFailed(data, reason).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   */
  public final void
  verifyData
    (Data data, OnVerified onVerified, OnDataValidationFailed onValidationFailed)
    throws SecurityException
  {
    verifyData(data, onVerified, onValidationFailed, 0);
  }

  /**
   * Check the signature on the Data object and call either onVerify.onVerify or
   * onVerifyFailed.onVerifyFailed.
   * We use callback functions because verify may fetch information to check the
   * signature.
   * @deprecated Use verifyData with OnDataValidationFailed.
   * @param data The Data object with the signature to check. It is an error if
   * data does not have a wireEncoding.
   * To set the wireEncoding, you can call data.wireDecode.
   * @param onVerified If the signature is verified, this calls
   * onVerified.onVerified(data).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   * @param onVerifyFailed If the signature check fails, this calls
   * onVerifyFailed.onVerifyFailed(data).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   */
  public final void
  verifyData
    (Data data, OnVerified onVerified, final OnVerifyFailed onVerifyFailed)
    throws SecurityException
  {
    // Wrap the onVerifyFailed in an OnDataValidationFailed.
    verifyData
      (data, onVerified,
       new OnDataValidationFailed() {
         public void onDataValidationFailed(Data localData, String reason) {
           onVerifyFailed.onVerifyFailed(localData);
         }
       });
  }

  public final void
  verifyInterest
    (Interest interest, OnVerifiedInterest onVerified,
     OnInterestValidationFailed onValidationFailed, int stepCount)
    throws SecurityException
  {
    Logger.getLogger(this.getClass().getName()).log
      (Level.INFO, "Enter Verify");

    if (policyManager_.requireVerify(interest)) {
      ValidationRequest nextStep = policyManager_.checkVerificationPolicy
        (interest, stepCount, onVerified, onValidationFailed);
      if (nextStep != null) {
        VerifyCallbacksForVerifyInterest callbacks = new VerifyCallbacksForVerifyInterest
          (nextStep, nextStep.retry_, onValidationFailed, interest);
        try {
          face_.expressInterest(nextStep.interest_, callbacks, callbacks);
        }
        catch (IOException ex) {
          try {
            onValidationFailed.onInterestValidationFailed
              (interest, "Error calling expressInterest " + ex);
          } catch (Throwable exception) {
            logger_.log(Level.SEVERE, "Error in onInterestValidationFailed", exception);
          }
        }
      }
    }
    else if (policyManager_.skipVerifyAndTrust(interest)) {
      try {
        onVerified.onVerifiedInterest(interest);
      } catch (Throwable ex) {
        logger_.log(Level.SEVERE, "Error in onVerifiedInterest", ex);
      }
    }
    else {
      try {
        onValidationFailed.onInterestValidationFailed
          (interest,
           "The packet has no verify rule but skipVerifyAndTrust is false");
      } catch (Throwable ex) {
        logger_.log(Level.SEVERE, "Error in onInterestValidationFailed", ex);
      }
    }
  }

  /**
   * Check the signature on the signed interest and call either
   * onVerify.onVerifiedInterest or
   * onValidationFailed.onInterestValidationFailed. We
   * use callback functions because verify may fetch information to check the
   * signature.
   * @param interest The interest with the signature to check.
   * @param onVerified If the signature is verified, this calls
   * onVerified.onVerifiedInterest(interest).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   * @param onValidationFailed If the signature check fails, this calls
   * onValidationFailed.onInterestValidationFailed(interest, reason).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   */
  public final void
  verifyInterest
    (Interest interest, OnVerifiedInterest onVerified,
     OnInterestValidationFailed onValidationFailed) throws SecurityException
  {
    verifyInterest(interest, onVerified, onValidationFailed, 0);
  }

  /**
   * Check the signature on the signed interest and call either
   * onVerify.onVerifiedInterest or onVerifyFailed.onVerifyInterestFailed. We
   * use callback functions because verify may fetch information to check the
   * signature.
   * @deprecated Use verifyInterest with OnInterestValidationFailed.
   * @param interest The interest with the signature to check.
   * @param onVerified If the signature is verified, this calls
   * onVerified.onVerifiedInterest(interest).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   * @param onVerifyFailed If the signature check fails, this calls
   * onVerifyFailed.onVerifyInterestFailed(interest).
   * NOTE: The library will log any exceptions thrown by this callback, but for
   * better error handling the callback should catch and properly handle any
   * exceptions.
   */
  public final void
  verifyInterest
    (Interest interest, OnVerifiedInterest onVerified,
     final OnVerifyInterestFailed onVerifyFailed) throws SecurityException
  {
    // Wrap the onVerifyFailed in an OnInterestValidationFailed.
    verifyInterest
      (interest, onVerified,
       new OnInterestValidationFailed() {
         public void onInterestValidationFailed
           (Interest localInterest, String reason) {
           onVerifyFailed.onVerifyInterestFailed(localInterest);
         }
       });
  }

  /**
   * Set the Face which will be used to fetch required certificates.
   * @param face The Face object.
   */
  public final void
  setFace(Face face) { face_ = face; }

  /**
   * Wire encode the data packet, compute an HmacWithSha256 and update the
   * signature value.
   * @note This method is an experimental feature. The API may change.
   * @param data The Data object to be signed. This updates its signature.
   * @param key The key for the HmacWithSha256.
   * @param wireFormat A WireFormat object used to encode the data packet.
   */
  public static void
  signWithHmacWithSha256(Data data, Blob key, WireFormat wireFormat)
  {
    // Encode once to get the signed portion.
    SignedBlob encoding = data.wireEncode(wireFormat);
    byte[] signatureBytes = Common.computeHmacWithSha256
      (key.getImmutableArray(), encoding.signedBuf());
    data.getSignature().setSignature(new Blob(signatureBytes, false));
  }

  /**
   * Wire encode the data packet, compute an HmacWithSha256 and update the
   * signature value.
   * Use the default WireFormat.getDefaultWireFormat().
   * @note This method is an experimental feature. The API may change.
   * @param data The Data object to be signed. This updates its signature.
   * @param key The key for the HmacWithSha256.
   */
  public static void
  signWithHmacWithSha256(Data data, Blob key)
  {
    signWithHmacWithSha256(data, key, WireFormat.getDefaultWireFormat());
  }

  /**
   * Append a SignatureInfo to the Interest name, compute an HmacWithSha256
   * signature for the name components and append a final name component with
   * the signature bits.
   * @note This method is an experimental feature. The API may change.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param key The key for the HmacWithSha256.
   * @param keyName The name of the key for the KeyLocator in the SignatureInfo.
   * @param wireFormat A WireFormat object used to encode the input.
   */
  public static void
  signWithHmacWithSha256
    (Interest interest, Blob key, Name keyName, WireFormat wireFormat)
  {
    HmacWithSha256Signature signature = new HmacWithSha256Signature();
    signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
    signature.getKeyLocator().setKeyName(keyName);

    // Append the encoded SignatureInfo.
    interest.getName().append(wireFormat.encodeSignatureInfo(signature));
    // Append an empty signature so that the "signedPortion" is correct.
    interest.getName().append(new Name.Component());

    // Encode once to get the signed portion.
    SignedBlob encoding = interest.wireEncode(wireFormat);
    byte[] signatureBytes = Common.computeHmacWithSha256
      (key.getImmutableArray(), encoding.signedBuf());
    signature.setSignature(new Blob(signatureBytes, false));

    // Remove the empty signature and append the real one.
    interest.setName(interest.getName().getPrefix(-1).append
      (wireFormat.encodeSignatureValue(signature)));
  }

  /**
   * Append a SignatureInfo to the Interest name, compute an HmacWithSha256
   * signature for the name components and append a final name component with
   * the signature bits.
   * Use the default WireFormat.getDefaultWireFormat().
   * @note This method is an experimental feature. The API may change.
   * @param interest The Interest object to be signed. This appends name
   * components of SignatureInfo and the signature bits.
   * @param key The key for the HmacWithSha256.
   * @param keyName The name of the key for the KeyLocator in the SignatureInfo.
   */
  public static void
  signWithHmacWithSha256(Interest interest, Blob key, Name keyName)
  {
    signWithHmacWithSha256
      (interest, key, keyName, WireFormat.getDefaultWireFormat());
  }

  /**
   * Compute a new HmacWithSha256 for the data packet and verify it against the
   * signature value.
   * @note This method is an experimental feature. The API may change.
   * @param data The Data packet to verify.
   * @param key The key for the HmacWithSha256.
   * @param wireFormat A WireFormat object used to encode the data packet.
   * @return True if the signature verifies, otherwise false.
   */
  public static boolean
  verifyDataWithHmacWithSha256(Data data, Blob key, WireFormat wireFormat)
  {
    // wireEncode returns the cached encoding if available.
    SignedBlob encoding = data.wireEncode(wireFormat);
    byte[] newSignatureBytes = Common.computeHmacWithSha256
      (key.getImmutableArray(), encoding.signedBuf());

    return ByteBuffer.wrap(newSignatureBytes).equals
      (data.getSignature().getSignature().buf());
  }

  /**
   * Compute a new HmacWithSha256 for the data packet and verify it against the
   * signature value.
   * Use the default WireFormat.getDefaultWireFormat().
   * @note This method is an experimental feature. The API may change.
   * @param data The Data packet to verify.
   * @param key The key for the HmacWithSha256.
   * @return True if the signature verifies, otherwise false.
   */
  public static boolean
  verifyDataWithHmacWithSha256(Data data, Blob key)
  {
    return verifyDataWithHmacWithSha256
      (data, key, WireFormat.getDefaultWireFormat());
  }

  /**
   * Compute a new HmacWithSha256 for all but the final name component and
   * verify it against the signature value in the final name component.
   * @note This method is an experimental feature. The API may change.
   * @param interest The Interest object to verify.
   * @param key The key for the HmacWithSha256.
   * @param wireFormat A WireFormat object used to encode the input.
   * @return True if the signature verifies, otherwise false.
   */
  public static boolean
  verifyInterestWithHmacWithSha256
    (Interest interest, Blob key, WireFormat wireFormat)
  {
    Signature signature;
    try {
      // Decode the last two name components of the signed interest.
      signature = wireFormat.decodeSignatureInfoAndValue
        (interest.getName().get(-2).getValue().buf(),
         interest.getName().get(-1).getValue().buf());
    } catch (EncodingException ex) {
      // Treat a decoding error as a verification failure.
      return false;
    }

    // wireEncode returns the cached encoding if available.
    SignedBlob encoding = interest.wireEncode(wireFormat);
    byte[] newSignatureBytes = Common.computeHmacWithSha256
      (key.getImmutableArray(), encoding.signedBuf());

    return ByteBuffer.wrap(newSignatureBytes).equals
      (signature.getSignature().buf());
  }

  /**
   * Compute a new HmacWithSha256 for all but the final name component and
   * verify it against the signature value in the final name component.
   * Use the default WireFormat.getDefaultWireFormat().
   * @note This method is an experimental feature. The API may change.
   * @param interest The Interest object to verify.
   * @param key The key for the HmacWithSha256.
   * @return True if the signature verifies, otherwise false.
   */
  public static boolean
  verifyInterestWithHmacWithSha256(Interest interest, Blob key)
  {
    return verifyInterestWithHmacWithSha256
      (interest, key, WireFormat.getDefaultWireFormat());
  }

  public static KeyParams
  getDefaultKeyParams() { return defaultKeyParams_; }

  /**
   * @deprecated Use getDefaultKeyParams().
   */
  public static final RsaKeyParams DEFAULT_KEY_PARAMS = new RsaKeyParams();

  // Private security v2 methods

  /**
   * Do the work of the constructor.
   */
  private void
  construct(String pibLocator, String tpmLocator, boolean allowReset)
    throws KeyChain.Error, PibImpl.Error, SecurityException, IOException
  {
    ConfigFile config = new ConfigFile();
    if (pibLocator.equals(""))
      pibLocator = getDefaultPibLocator(config);
    if (tpmLocator.equals(""))
      tpmLocator = getDefaultTpmLocator(config);

    // PIB locator.
    String[] pibScheme = new String[1];
    String[] pibLocation = new String[1];
    parseAndCheckPibLocator(pibLocator, pibScheme, pibLocation);
    String canonicalPibLocator = pibScheme[0] + ":" + pibLocation[0];

    // Create the PIB.
    pib_ = createPib(canonicalPibLocator);
    String oldTpmLocator = "";
    try {
      oldTpmLocator = pib_.getTpmLocator();
    }
    catch (Pib.Error ex) {
      // The TPM locator is not set in the PIB yet.
    }

    // TPM locator.
    String[] tpmScheme = new String[1];
    String[] tpmLocation = new String[1];
    parseAndCheckTpmLocator(tpmLocator, tpmScheme, tpmLocation);
    String canonicalTpmLocator = tpmScheme[0] + ":" + tpmLocation[0];

    if (canonicalPibLocator.equals(getDefaultPibLocator(config))) {
      // The default PIB must use the default TPM.
      if (!oldTpmLocator.equals("") &&
          !oldTpmLocator.equals(getDefaultTpmLocator(config))) {
        pib_.reset_();
        canonicalTpmLocator = getDefaultTpmLocator(config);
      }
    }
    else {
      // Check the consistency of the non-default PIB.
      if (!oldTpmLocator.equals("") &&
          !oldTpmLocator.equals(canonicalTpmLocator)) {
        if (allowReset)
          pib_.reset_();
        else
          throw new LocatorMismatchError
            ("The supplied TPM locator does not match the TPM locator in the PIB: " +
             oldTpmLocator + " != " + canonicalTpmLocator);
      }
    }

    // Note that a key mismatch may still happen if the TPM locator is initially
    // set to a wrong one or if the PIB was shared by more than one TPM before.
    // This is due to the old PIB not having TPM info. The new PIB should not
    // have this problem.
    tpm_ = createTpm(canonicalTpmLocator);
    pib_.setTpmLocator(canonicalTpmLocator);

    // Provide a default NoVerifyPolicyManager, assuming the application will
    // use a Validator.
    policyManager_ = new NoVerifyPolicyManager();
  }

  /**
   * Get the PIB factories map. On the first call, this initializes the map with
   * factories for standard PibImpl implementations.
   * @return A map where the key is the scheme string and the value is the
   * object implementing makePibImpl.
   */
  private static HashMap
  getPibFactories()
  {
    if (pibFactories_ == null) {
      pibFactories_ = new HashMap();

      // Add the standard factories.
      pibFactories_.put(PibSqlite3.getScheme(), new MakePibImpl() {
        public PibImpl makePibImpl(String location) throws PibImpl.Error {
          return new PibSqlite3(location);
        }
      });
      pibFactories_.put(PibMemory.getScheme(), new MakePibImpl() {
        public PibImpl makePibImpl(String location) throws PibImpl.Error {
          return new PibMemory();
        }
      });
    }

    return pibFactories_;
  }

  /**
   * Get the TPM factories map. On the first call, this initializes the map with
   * factories for standard TpmBackEnd implementations.
   * @return A map where the key is the scheme string and the value is the
   * object implementing makeTpmBackEnd.
   */
  private static HashMap
  getTpmFactories()
  {
    if (tpmFactories_ == null) {
      tpmFactories_ = new HashMap();

      // Add the standard factories.
      tpmFactories_.put(TpmBackEndFile.getScheme(), new MakeTpmBackEnd() {
        public TpmBackEnd makeTpmBackEnd(String location) {
          return new TpmBackEndFile(location);
        }
      });
      tpmFactories_.put(TpmBackEndMemory.getScheme(), new MakeTpmBackEnd() {
        public TpmBackEnd makeTpmBackEnd(String location) {
          return new TpmBackEndMemory();
        }
      });
    }

    return tpmFactories_;
  }

  /**
   * Parse the uri and set the scheme and location.
   * @param uri The URI to parse.
   * @param scheme Set scheme[0] to the scheme.
   * @param location Set location[0] to the location.
   */
  private static void
  parseLocatorUri(String uri, String[] scheme, String[] location)
  {
    int iColon = uri.indexOf(':');
    if (iColon >= 0) {
      scheme[0] = uri.substring(0, iColon);
      location[0] = uri.substring(iColon + 1);
    }
    else {
      scheme[0] = uri;
      location[0] = "";
    }
  }

  /**
   * Parse the pibLocator and set the pibScheme and pibLocation.
   * @param pibLocator The PIB locator to parse.
   * @param pibScheme Set pibScheme[0] to the PIB scheme.
   * @param pibLocation Set pibLocation[0] to the PIB location.
   */
  private static void
  parseAndCheckPibLocator
    (String pibLocator, String[] pibScheme, String[] pibLocation)
    throws KeyChain.Error
  {
    parseLocatorUri(pibLocator, pibScheme, pibLocation);

    if (pibScheme[0].equals(""))
      pibScheme[0] = getDefaultPibScheme();

    if (!getPibFactories().containsKey(pibScheme[0]))
      throw new KeyChain.Error("PIB scheme `" + pibScheme[0] +
        "` is not supported");
  }

  /**
   * Parse the tpmLocator and set the tpmScheme and tpmLocation.
   * @param tpmLocator The TPM locator to parse.
   * @param tpmScheme Set tpmScheme[0] to the TPM scheme.
   * @param tpmLocation Set tpmLocation[0] to the TPM location.
   */
  private static void
  parseAndCheckTpmLocator
    (String tpmLocator, String[] tpmScheme, String[] tpmLocation)
    throws SecurityException, Error
  {
    parseLocatorUri(tpmLocator, tpmScheme, tpmLocation);

    if (tpmScheme[0].equals(""))
      tpmScheme[0] = getDefaultTpmScheme();

    if (!getTpmFactories().containsKey(tpmScheme[0]))
      throw new KeyChain.Error("TPM scheme `" + tpmScheme[0] +
        "` is not supported");
  }

  private static String
  getDefaultPibScheme() { return PibSqlite3.getScheme(); }

  private static String
  getDefaultTpmScheme() throws SecurityException
  {
    if (Common.platformIsOSX())
      throw new SecurityException
        ("TpmBackEndOsx is not implemented yet. You must use tpm-file.");

    return TpmBackEndFile.getScheme();
  }

  /**
   * Create a Pib according to the pibLocator
   * @param pibLocator The PIB locator, e.g., "pib-sqlite3:/example/dir".
   * @return A new Pib object.
   */
  private static Pib
  createPib(String pibLocator) throws KeyChain.Error, PibImpl.Error
  {
    String[] pibScheme = new String[1];
    String[] pibLocation = new String[1];
    parseAndCheckPibLocator(pibLocator, pibScheme, pibLocation);
    MakePibImpl pibFactory = getPibFactories().get(pibScheme[0]);
    return new Pib
      (pibScheme[0], pibLocation[0], pibFactory.makePibImpl(pibLocation[0]));
  }

  /**
   * Create a Tpm according to the tpmLocator
   * @param tpmLocator The TPM locator, e.g., "tpm-memory:".
   * @return A new Tpm object.
   */
  private static Tpm
  createTpm(String tpmLocator) throws SecurityException, KeyChain.Error
  {
    String[] tpmScheme = new String[1];
    String[] tpmLocation = new String[1];
    parseAndCheckTpmLocator(tpmLocator, tpmScheme, tpmLocation);
    MakeTpmBackEnd tpmFactory = getTpmFactories().get(tpmScheme[0]);
    return new Tpm
      (tpmScheme[0], tpmLocation[0], tpmFactory.makeTpmBackEnd(tpmLocation[0]));
  }

  private static String
  getDefaultPibLocator(ConfigFile config) throws Error
  {
    if (defaultPibLocator_ != null)
      return defaultPibLocator_;

    String clientPib = System.getenv("NDN_CLIENT_PIB");
    if (clientPib != null && clientPib != "")
      defaultPibLocator_ = clientPib;
    else
      defaultPibLocator_ = config.get("pib", getDefaultPibScheme() + ":");

    String[] pibScheme = new String[1];
    String[] pibLocation = new String[1];
    parseAndCheckPibLocator(defaultPibLocator_, pibScheme, pibLocation);
    defaultPibLocator_ = pibScheme[0] + ":" + pibLocation[0];

    return defaultPibLocator_;
  }

  private static String
  getDefaultTpmLocator(ConfigFile config) throws SecurityException, Error
  {
    if (defaultTpmLocator_ != null)
      return defaultTpmLocator_;

    String clientTpm = System.getenv("NDN_CLIENT_TPM");
    if (clientTpm != null && clientTpm != "")
      defaultTpmLocator_ = clientTpm;
    else
      defaultTpmLocator_ = config.get("tpm", getDefaultTpmScheme() + ":");

    String[] tpmScheme = new String[1];
    String[] tpmLocation = new String[1];
    parseAndCheckTpmLocator(defaultTpmLocator_, tpmScheme, tpmLocation);
    defaultTpmLocator_ = tpmScheme[0] + ":" + tpmLocation[0];

    return defaultTpmLocator_;
  }

  /**
   * Prepare a Signature object according to signingInfo and get the signing key
   * name.
   * @param params The signing parameters.
   * @param keyName Set keyName[0] to the signing key name.
   * @return A new Signature object with the SignatureInfo.
   * @throws InvalidSigningInfoError when the requested signing method cannot be
   * satisfied.
   */
  private Signature
  prepareSignatureInfo(SigningInfo params, Name[] keyName)
    throws PibImpl.Error, InvalidSigningInfoError, KeyChain.Error
  {
    PibIdentity identity = null;
    PibKey key = null;

    if (params.getSignerType() == SignerType.NULL) {
      try {
        identity = pib_.getDefaultIdentity();
      }
      catch (Pib.Error ex) {
        // There is no default identity, so use sha256 for signing.
        keyName[0] = SigningInfo.getDigestSha256Identity();
        return new DigestSha256Signature();
      }
    }
    else if (params.getSignerType() == SignerType.ID) {
      identity = params.getPibIdentity();
      if (identity == null) {
        try {
          identity = pib_.getIdentity(params.getSignerName());
        }
        catch (Pib.Error ex) {
          throw new InvalidSigningInfoError
            ("Signing identity `" + params.getSignerName().toUri() +
             "` does not exist");
        }
      }
    }
    else if (params.getSignerType() == SignerType.KEY) {
      key = params.getPibKey();
      if (key == null) {
        Name identityName = PibKey.extractIdentityFromKeyName
          (params.getSignerName());

        try {
          identity = pib_.getIdentity(identityName);
          key = identity.getKey(params.getSignerName());
          // We will use the PIB key instance, so reset the identity.
          identity = null;
        }
        catch (Pib.Error ex) {
          throw new InvalidSigningInfoError
            ("Signing key `" + params.getSignerName().toUri() +
             "` does not exist");
        }
      }
    }
    else if (params.getSignerType() == SignerType.CERT) {
      Name identityName = CertificateV2.extractIdentityFromCertName
        (params.getSignerName());

      try {
        identity = pib_.getIdentity(identityName);
        key = identity.getKey
          (CertificateV2.extractKeyNameFromCertName(params.getSignerName()));
      }
      catch (Pib.Error ex) {
        throw new InvalidSigningInfoError
          ("Signing certificate `" + params.getSignerName().toUri() +
           "` does not exist");
      }
    }
    else if (params.getSignerType() == SignerType.SHA256) {
      keyName[0] = SigningInfo.getDigestSha256Identity();
      return new DigestSha256Signature();
    }
    else
      // We don't expect this to happen.
      throw new InvalidSigningInfoError("Unrecognized signer type");

    if (identity == null && key == null)
      throw new InvalidSigningInfoError("Cannot determine signing parameters");

    if (identity != null && key == null) {
      try {
        key = identity.getDefaultKey();
      }
      catch (Pib.Error ex) {
        throw new InvalidSigningInfoError
          ("Signing identity `" + identity.getName().toUri() +
           "` does not have default certificate");
      }
    }

    Signature signatureInfo;

    if (key.getKeyType() == KeyType.RSA &&
        params.getDigestAlgorithm() == DigestAlgorithm.SHA256)
      signatureInfo = new Sha256WithRsaSignature();
    else if (key.getKeyType() == KeyType.EC &&
             params.getDigestAlgorithm() == DigestAlgorithm.SHA256)
      signatureInfo = new Sha256WithEcdsaSignature();
    else
      throw new KeyChain.Error("Unsupported key type");

    if (params.getValidityPeriod().hasPeriod() &&
        ValidityPeriod.canGetFromSignature(signatureInfo))
      // Set the ValidityPeriod from the SigningInfo params.
      ValidityPeriod.getFromSignature(signatureInfo).setPeriod
        (params.getValidityPeriod().getNotBefore(),
         params.getValidityPeriod().getNotAfter());

    KeyLocator keyLocator = KeyLocator.getFromSignature(signatureInfo);
    keyLocator.setType(KeyLocatorType.KEYNAME);
    keyLocator.setKeyName(key.getName());

    keyName[0] = key.getName();
    return signatureInfo;
  }

  /**
   * Sign the byte array using the key with name keyName.
   * @param buffer The byte buffer to be signed.
   * @param keyName The name of the key.
   * @param digestAlgorithm The digest algorithm.
   * @return The signature Blob, or an isNull Blob if the key does not exist, or
   * for an unrecognized digestAlgorithm.
   */
  private Blob
  sign(ByteBuffer buffer, Name keyName, DigestAlgorithm digestAlgorithm)
    throws TpmBackEnd.Error
  {
    if (keyName.equals(SigningInfo.getDigestSha256Identity()))
      return new Blob(Common.digestSha256(buffer));

    return tpm_.sign(buffer, keyName, digestAlgorithm);
  }

  // Private security v1 methods

  /**
   * A VerifyCallbacks is used for callbacks from verifyData.
   */
  private class VerifyCallbacks implements OnData, OnTimeout {
    public VerifyCallbacks
      (ValidationRequest nextStep, int retry,
       OnDataValidationFailed onValidationFailed, Data originalData)
    {
      nextStep_ = nextStep;
      retry_ = retry;
      onValidationFailed_ = onValidationFailed;
      originalData_ = originalData;
    }

    public final void onData(Interest interest, Data data)
    {
      try {
        // Try to verify the certificate (data) according to the parameters in
        //   nextStep.
        verifyData
          (data, nextStep_.onVerified_, nextStep_.onValidationFailed_,
           nextStep_.stepCount_);
      } catch (SecurityException ex) {
        Logger.getLogger(KeyChain.class.getName()).log(Level.SEVERE, null, ex);
      }
    }

    public final void onTimeout(Interest interest)
    {
      if (retry_ > 0) {
        // Issue the same expressInterest as in verifyData except decrement
        //   retry.
        VerifyCallbacks callbacks = new VerifyCallbacks
          (nextStep_, retry_ - 1, onValidationFailed_, originalData_);
        try {
          face_.expressInterest(interest, callbacks, callbacks);
        }
        catch (IOException ex) {
          try {
            onValidationFailed_.onDataValidationFailed
              (originalData_,
               "Error in expressInterest to retry after timeout for fetching " +
               interest.getName().toUri() + ": " + ex);
          } catch (Throwable exception) {
            logger_.log(Level.SEVERE, "Error in onDataValidationFailed", exception);
          }
        }
      }
      else {
        try {
          onValidationFailed_.onDataValidationFailed
            (originalData_,
               "The retry count is zero after timeout for fetching " +
               interest.getName().toUri());
        } catch (Throwable ex) {
          logger_.log(Level.SEVERE, "Error in onDataValidationFailed", ex);
        }
      }
    }

    private final ValidationRequest nextStep_;
    private final int retry_;
    private final OnDataValidationFailed onValidationFailed_;
    private final Data originalData_;
  }

  /**
   * A VerifyCallbacksForVerifyInterest is used for callbacks from verifyInterest.
   * This is the same as VerifyCallbacks, but we call
   * onValidationFailed.onInterestValidationFailed(originalInterest, reason) if
   * we have too many retries.
   */
  private class VerifyCallbacksForVerifyInterest implements OnData, OnTimeout {
    public VerifyCallbacksForVerifyInterest
      (ValidationRequest nextStep, int retry,
       OnInterestValidationFailed onValidationFailed, Interest originalInterest)
    {
      nextStep_ = nextStep;
      retry_ = retry;
      onValidationFailed_ = onValidationFailed;
      originalInterest_ = originalInterest;
    }

    public final void onData(Interest interest, Data data)
    {
      try {
        // Try to verify the certificate (data) according to the parameters in
        //   nextStep.
        verifyData
          (data, nextStep_.onVerified_, nextStep_.onValidationFailed_,
           nextStep_.stepCount_);
      } catch (SecurityException ex) {
        Logger.getLogger(KeyChain.class.getName()).log(Level.SEVERE, null, ex);
      }
    }

    public final void onTimeout(Interest interest)
    {
      if (retry_ > 0) {
        // Issue the same expressInterest as in verifyData except decrement
        //   retry.
        VerifyCallbacksForVerifyInterest callbacks = new VerifyCallbacksForVerifyInterest
          (nextStep_, retry_ - 1, onValidationFailed_, originalInterest_);
        try {
          face_.expressInterest(interest, callbacks, callbacks);
        }
        catch (IOException ex) {
          try {
            onValidationFailed_.onInterestValidationFailed
              (originalInterest_,
               "Error in expressInterest to retry after timeout for fetching " +
               interest.getName().toUri() + ": " + ex);
          } catch (Throwable exception) {
            logger_.log(Level.SEVERE, "Error in onInterestValidationFailed", exception);
          }
        }
      }
      else {
        try {
          onValidationFailed_.onInterestValidationFailed
            (originalInterest_,
               "The retry count is zero after timeout for fetching " +
               interest.getName().toUri());
        } catch (Throwable ex) {
          logger_.log(Level.SEVERE, "Error in onInterestValidationFailed", ex);
        }
      }
    }

    private final ValidationRequest nextStep_;
    private final int retry_;
    private final OnInterestValidationFailed onValidationFailed_;
    private final Interest originalInterest_;
  }

  /**
   * Get the default certificate from the identity storage and return its name.
   * If there is no default identity or default certificate, then create one.
   * @return The default certificate name.
   */
  private Name
  prepareDefaultCertificateName() throws SecurityException
  {
    IdentityCertificate signingCertificate =
      identityManager_.getDefaultCertificate();
    if (signingCertificate == null) {
      setDefaultCertificate();
      signingCertificate = identityManager_.getDefaultCertificate();
    }

    return signingCertificate.getName();
  }

  /**
   * Create the default certificate if it is not initialized. If there is no
   * default identity yet, creating a new tmp-identity.
   */
  private void
  setDefaultCertificate() throws SecurityException
  {
    if (identityManager_.getDefaultCertificate() == null) {
      Name defaultIdentity;
      try {
        defaultIdentity = identityManager_.getDefaultIdentity();
      } catch (SecurityException e) {
        // Create a default identity name.
        ByteBuffer randomComponent = ByteBuffer.allocate(4);
        Common.getRandom().nextBytes(randomComponent.array());
        defaultIdentity = new Name().append("tmp-identity")
          .append(new Blob(randomComponent, false));
      }

      createIdentityAndCertificate(defaultIdentity);
      identityManager_.setDefaultIdentity(defaultIdentity);
    }
  }

  private boolean isSecurityV1_;

  private IdentityManager identityManager_; // for security v1
  private PolicyManager policyManager_;     // for security v1
  private Face face_ = null; // for security v1

  private Pib pib_;
  private Tpm tpm_;

  private static String defaultPibLocator_ = null;
  private static String defaultTpmLocator_ = null;
  private static HashMap pibFactories_ = null;
  private static HashMap tpmFactories_ = null;
  private static final SigningInfo defaultSigningInfo_ = new SigningInfo();
  private static final KeyParams defaultKeyParams_ = new RsaKeyParams();

  private static final Logger logger_ = Logger.getLogger(KeyChain.class.getName());
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy