Beispiel #1
0
  /**
   * Add a public key to the identity storage. Also call addIdentity to ensure that the identityName
   * for the key exists.
   *
   * @param keyName The name of the public key to be added.
   * @param keyType Type of the public key to be added.
   * @param publicKeyDer A blob of the public key DER to be added.
   * @throws SecurityException if a key with the keyName already exists.
   */
  public final void addKey(Name keyName, KeyType keyType, Blob publicKeyDer)
      throws SecurityException {
    if (keyName.size() == 0) return;

    checkAddKey(keyName);

    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    addIdentity(identityName);

    try {
      PreparedStatement statement =
          database_.prepareStatement(
              "INSERT INTO Key (identity_name, key_identifier, key_type, public_key) values (?, ?, ?, ?)");
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);
      statement.setInt(3, keyType.getNumericType());
      statement.setBytes(4, publicKeyDer.getImmutableArray());

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #2
0
  /**
   * 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 setDefaultKeyNameForIdentity(Name keyName, Name identityNameCheck)
      throws SecurityException {
    checkSetDefaultKeyNameForIdentity(keyName, identityNameCheck);

    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    try {
      // Reset the previous default Key.
      PreparedStatement statement =
          database_.prepareStatement(
              "UPDATE Key SET default_key=0 WHERE " + WHERE_setDefaultKeyNameForIdentity_reset);
      statement.setString(1, identityName.toUri());
      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }

      // Set the current default Key.
      statement =
          database_.prepareStatement(
              "UPDATE Key SET default_key=1 WHERE " + WHERE_setDefaultKeyNameForIdentity_set);
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);
      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
  @Before
  public void setUp() throws SecurityException {
    face_ = new Face();

    // For now, when setting face.setCommandSigningInfo, use a key chain with
    //   a default private key instead of the system default key chain. This
    //   is OK for now because NFD is configured to skip verification, so it
    //   ignores the system default key chain.
    // On a platform which supports it, it would be better to use the default
    //   KeyChain constructor.
    MemoryIdentityStorage identityStorage = new MemoryIdentityStorage();
    MemoryPrivateKeyStorage privateKeyStorage = new MemoryPrivateKeyStorage();
    KeyChain keyChain =
        new KeyChain(
            new IdentityManager(identityStorage, privateKeyStorage),
            new SelfVerifyPolicyManager(identityStorage));
    keyChain.setFace(face_);

    // Initialize the storage.
    Name keyName = new Name("/testname/DSK-123");
    Name certificateName =
        keyName
            .getSubName(0, keyName.size() - 1)
            .append("KEY")
            .append(keyName.get(-1))
            .append("ID-CERT")
            .append("0");
    identityStorage.addKey(keyName, KeyType.RSA, new Blob(DEFAULT_RSA_PUBLIC_KEY_DER, false));
    privateKeyStorage.setKeyPairForKeyName(
        keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER);

    face_.setCommandSigningInfo(keyChain, certificateName);
  }
Beispiel #4
0
  /**
   * Create an identity certificate for a public key supplied by the caller.
   *
   * @param certificatePrefix The name of public key to be signed.
   * @param publicKey The public key to be signed.
   * @param signerCertificateName The name of signing certificate.
   * @param notBefore The notBefore value in the validity field of the generated certificate.
   * @param notAfter The notAfter vallue in validity field of the generated certificate.
   * @return The generated identity certificate.
   */
  public final IdentityCertificate createIdentityCertificate(
      Name certificatePrefix,
      PublicKey publicKey,
      Name signerCertificateName,
      double notBefore,
      double notAfter)
      throws SecurityException {
    IdentityCertificate certificate = new IdentityCertificate();
    Name keyName = getKeyNameFromCertificatePrefix(certificatePrefix);

    Name certificateName = new Name(certificatePrefix);
    certificateName.append("ID-CERT").appendVersion((long) Common.getNowMilliseconds());

    certificate.setName(certificateName);
    certificate.setNotBefore(notBefore);
    certificate.setNotAfter(notAfter);
    certificate.setPublicKeyInfo(publicKey);
    certificate.addSubjectDescription(
        new CertificateSubjectDescription("2.5.4.41", keyName.toUri()));
    try {
      certificate.encode();
    } catch (DerEncodingException ex) {
      throw new SecurityException("DerDecodingException: " + ex);
    } catch (DerDecodingException ex) {
      throw new SecurityException("DerEncodingException: " + ex);
    }

    Sha256WithRsaSignature sha256Sig = new Sha256WithRsaSignature();

    KeyLocator keyLocator = new KeyLocator();
    keyLocator.setType(KeyLocatorType.KEYNAME);
    keyLocator.setKeyName(signerCertificateName);

    sha256Sig.setKeyLocator(keyLocator);

    certificate.setSignature(sha256Sig);

    SignedBlob unsignedData = certificate.wireEncode();

    IdentityCertificate signerCertificate;
    try {
      signerCertificate = getCertificate(signerCertificateName);
    } catch (DerDecodingException ex) {
      throw new SecurityException("DerDecodingException: " + ex);
    }
    Name signerkeyName = signerCertificate.getPublicKeyName();

    Blob sigBits = privateKeyStorage_.sign(unsignedData.signedBuf(), signerkeyName);

    sha256Sig.setSignature(sigBits);

    return certificate;
  }
Beispiel #5
0
  /**
   * Set the default identity. If the identityName does not exist, then clear the default identity
   * so that getDefaultIdentity() throws an exception.
   *
   * @param identityName The default identity name.
   */
  public final void setDefaultIdentity(Name identityName) throws SecurityException {
    try {
      // Reset the previous default identity.
      PreparedStatement statement =
          database_.prepareStatement(
              "UPDATE Identity SET default_identity=0 WHERE " + WHERE_setDefaultIdentity_reset);
      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }

      // Set the current default identity.
      statement =
          database_.prepareStatement(
              "UPDATE Identity SET default_identity=1 WHERE " + WHERE_setDefaultIdentity_set);
      statement.setString(1, identityName.toUri());
      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #6
0
  /**
   * Delete a certificate.
   *
   * @param certificateName The certificate name.
   */
  public void deleteCertificateInfo(Name certificateName) throws SecurityException {
    if (certificateName.size() == 0) return;

    try {
      PreparedStatement statement =
          database_.prepareStatement(
              "DELETE FROM Certificate WHERE " + WHERE_deleteCertificateInfo);
      statement.setString(1, certificateName.toUri());

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #7
0
  /**
   * Generate a self-signed certificate for a public key.
   *
   * @param keyName The name of the public key.
   * @return The generated certificate.
   */
  public IdentityCertificate selfSign(Name keyName) throws SecurityException {
    IdentityCertificate certificate = new IdentityCertificate();

    Blob keyBlob = identityStorage_.getKey(keyName);
    PublicKey publicKey = new PublicKey(keyBlob);

    Calendar calendar = Calendar.getInstance();
    double notBefore = (double) calendar.getTimeInMillis();
    calendar.add(Calendar.YEAR, 2);
    double notAfter = (double) calendar.getTimeInMillis();

    certificate.setNotBefore(notBefore);
    certificate.setNotAfter(notAfter);

    Name certificateName =
        keyName
            .getPrefix(-1)
            .append("KEY")
            .append(keyName.get(-1))
            .append("ID-CERT")
            .appendVersion((long) certificate.getNotBefore());
    certificate.setName(certificateName);

    certificate.setPublicKeyInfo(publicKey);
    certificate.addSubjectDescription(
        new CertificateSubjectDescription("2.5.4.41", keyName.toUri()));
    try {
      certificate.encode();
    } catch (DerEncodingException ex) {
      // We don't expect this to happen.
      Logger.getLogger(IdentityManager.class.getName()).log(Level.SEVERE, null, ex);
      return null;
    } catch (DerDecodingException ex) {
      // We don't expect this to happen.
      Logger.getLogger(IdentityManager.class.getName()).log(Level.SEVERE, null, ex);
      return null;
    }

    signByCertificate(certificate, certificate.getName());

    return certificate;
  }
  private static boolean isCorrectName(Name name) {
    int i = name.size() - 1;

    String idString = "ID-CERT";
    for (; i >= 0; i--) {
      if (name.get(i).toEscapedString().equals(idString)) break;
    }

    if (i < 0) return false;

    int keyIdx = 0;
    String keyString = "KEY";
    for (; keyIdx < name.size(); keyIdx++) {
      if (name.get(keyIdx).toEscapedString().equals(keyString)) break;
    }

    if (keyIdx >= name.size()) return false;

    return true;
  }
Beispiel #9
0
  /**
   * In table Key, set 'active' to isActive for the keyName.
   *
   * @param keyName The name of the key.
   * @param isActive The value for the 'active' field.
   */
  protected void updateKeyStatus(Name keyName, boolean isActive) throws SecurityException {
    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    try {
      PreparedStatement statement =
          database_.prepareStatement("UPDATE Key SET active=? WHERE " + WHERE_updateKeyStatus);
      statement.setInt(1, (isActive ? 1 : 0));
      statement.setString(2, identityName.toUri());
      statement.setString(3, keyId);

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #10
0
  /**
   * Check if the specified key already exists.
   *
   * @param keyName The name of the key.
   * @return true if the key exists, otherwise false.
   */
  public final boolean doesKeyExist(Name keyName) throws SecurityException {
    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    try {
      PreparedStatement statement = database_.prepareStatement(SELECT_doesKeyExist);
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);

      try {
        ResultSet result = statement.executeQuery();

        if (result.next()) return result.getInt(1) > 0;
        else return false;
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #11
0
  /**
   * Delete a public key and related certificates.
   *
   * @param keyName The key name.
   */
  public void deletePublicKeyInfo(Name keyName) throws SecurityException {
    if (keyName.size() == 0) return;

    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    try {
      PreparedStatement statement =
          database_.prepareStatement("DELETE FROM Certificate WHERE " + WHERE_deletePublicKeyInfo);
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }

      statement = database_.prepareStatement("DELETE FROM Key WHERE " + WHERE_deletePublicKeyInfo);
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #12
0
  private static Name getKeyNameFromCertificatePrefix(Name certificatePrefix)
      throws SecurityException {
    Name result = new Name();

    String keyString = "KEY";
    int i = 0;
    for (; i < certificatePrefix.size(); i++) {
      if (certificatePrefix.get(i).toEscapedString().equals(keyString)) break;
    }

    if (i >= certificatePrefix.size())
      throw new SecurityException("Identity Certificate Prefix does not have a KEY component");

    result.append(certificatePrefix.getSubName(0, i));
    result.append(certificatePrefix.getSubName(i + 1, certificatePrefix.size() - i - 1));

    return result;
  }
Beispiel #13
0
  /**
   * Get the public key DER blob from the identity storage.
   *
   * @param keyName The name of the requested public key.
   * @return The DER Blob. If not found, return a Blob with a null pointer.
   */
  public final Blob getKey(Name keyName) throws SecurityException {
    if (!doesKeyExist(keyName)) return new Blob();

    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    try {
      PreparedStatement statement = database_.prepareStatement(SELECT_getKey);
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);

      try {
        ResultSet result = statement.executeQuery();

        if (result.next()) return new Blob(result.getBytes("public_key"));
        else return new Blob();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #14
0
  /**
   * Return a new Signature object based on the signature algorithm of the public key with keyName
   * (derived from certificateName).
   *
   * @param certificateName The certificate name.
   * @param digestAlgorithm Set digestAlgorithm[0] to the signature algorithm's digest algorithm,
   *     e.g. DigestAlgorithm.SHA256.
   * @return A new object of the correct subclass of Signature.
   */
  private Signature makeSignatureByCertificate(
      Name certificateName, DigestAlgorithm[] digestAlgorithm) throws SecurityException {
    Name keyName = IdentityCertificate.certificateNameToPublicKeyName(certificateName);
    PublicKey publicKey = privateKeyStorage_.getPublicKey(keyName);
    KeyType keyType = publicKey.getKeyType();

    if (keyType == KeyType.RSA) {
      Sha256WithRsaSignature signature = new Sha256WithRsaSignature();
      digestAlgorithm[0] = DigestAlgorithm.SHA256;

      signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
      signature.getKeyLocator().setKeyName(certificateName.getPrefix(-1));

      return signature;
    } else if (keyType == KeyType.ECDSA) {
      Sha256WithEcdsaSignature signature = new Sha256WithEcdsaSignature();
      digestAlgorithm[0] = DigestAlgorithm.SHA256;

      signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
      signature.getKeyLocator().setKeyName(certificateName.getPrefix(-1));

      return signature;
    } else throw new SecurityException("Key type is not recognized");
  }
Beispiel #15
0
  /**
   * Get the default certificate name for the specified key.
   *
   * @param keyName The key name.
   * @return The default certificate name.
   * @throws SecurityException if the default certificate name for the key name is not set.
   */
  public final Name getDefaultCertificateNameForKey(Name keyName) throws SecurityException {
    String keyId = keyName.get(-1).toEscapedString();
    Name identityName = keyName.getPrefix(-1);

    try {
      PreparedStatement statement =
          database_.prepareStatement(SELECT_getDefaultCertificateNameForKey);
      statement.setString(1, identityName.toUri());
      statement.setString(2, keyId);

      try {
        ResultSet result = statement.executeQuery();

        if (result.next()) return new Name(result.getString("cert_name"));
        else
          throw new SecurityException(
              "BasicIdentityStorage.getDefaultCertificateNameForKey: The default certificate for the key name is not defined");
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #16
0
  /**
   * Check if the specified certificate already exists.
   *
   * @param certificateName The name of the certificate.
   * @return True if the certificate exists, otherwise false.
   */
  public final boolean doesCertificateExist(Name certificateName) throws SecurityException {
    try {
      PreparedStatement statement = database_.prepareStatement(SELECT_doesCertificateExist);
      statement.setString(1, certificateName.toUri());

      try {
        ResultSet result = statement.executeQuery();

        if (result.next()) return result.getInt(1) > 0;
        else return false;
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #17
0
  /**
   * Add a new identity. Do nothing if the identity already exists.
   *
   * @param identityName The identity name to be added.
   */
  public final void addIdentity(Name identityName) throws SecurityException {
    if (doesIdentityExist(identityName)) return;

    try {
      PreparedStatement statement =
          database_.prepareStatement("INSERT INTO Identity (identity_name) values (?)");
      statement.setString(1, identityName.toUri());

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #18
0
  /**
   * Add a certificate to the identity storage.
   *
   * @param certificate The certificate to be added. This makes a copy of the certificate.
   * @throws SecurityException if the certificate is already installed.
   */
  public final void addCertificate(IdentityCertificate certificate) throws SecurityException {
    checkAddCertificate(certificate);

    Name certificateName = certificate.getName();
    Name keyName = certificate.getPublicKeyName();

    // Insert the certificate.
    try {
      PreparedStatement statement =
          database_.prepareStatement(
              "INSERT INTO Certificate (cert_name, cert_issuer, identity_name, key_identifier, not_before, not_after, certificate_data) "
                  + "values (?, ?, ?, ?, datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?)");
      statement.setString(1, certificateName.toUri());

      Name signerName = KeyLocator.getFromSignature(certificate.getSignature()).getKeyName();
      statement.setString(2, signerName.toUri());

      String keyId = keyName.get(-1).toEscapedString();
      Name identity = keyName.getPrefix(-1);
      statement.setString(3, identity.toUri());
      statement.setString(4, keyId);

      // Convert from milliseconds to seconds since 1/1/1970.
      statement.setLong(5, (long) (Math.floor(certificate.getNotBefore() / 1000.0)));
      statement.setLong(6, (long) (Math.floor(certificate.getNotAfter() / 1000.0)));

      // wireEncode returns the cached encoding if available.
      statement.setBytes(7, certificate.wireEncode().getImmutableArray());

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #19
0
  /**
   * Get a certificate from the identity storage.
   *
   * @param certificateName The name of the requested certificate.
   * @param allowAny If false, only a valid certificate will be returned, otherwise validity is
   *     disregarded.
   * @return The requested certificate. If not found, return null.
   */
  public final IdentityCertificate getCertificate(Name certificateName, boolean allowAny)
      throws SecurityException {
    if (doesCertificateExist(certificateName)) {
      try {
        PreparedStatement statement;

        if (!allowAny) {
          throw new UnsupportedOperationException(
              "BasicIdentityStorage.getCertificate for !allowAny is not implemented");
          /*
          statement = database_.prepareStatement
            ("SELECT certificate_data FROM Certificate " +
             "WHERE cert_name=? AND not_before<datetime(?, 'unixepoch') AND not_after>datetime(?, 'unixepoch') and valid_flag=1");
          statement.setString(1, certificateName.toUri());
          sqlite3_bind_int64(statement, 2, (sqlite3_int64)floor(ndn_getNowMilliseconds() / 1000.0));
          sqlite3_bind_int64(statement, 3, (sqlite3_int64)floor(ndn_getNowMilliseconds() / 1000.0));
          */
        } else {
          statement = database_.prepareStatement(SELECT_getCertificate);
          statement.setString(1, certificateName.toUri());
        }

        IdentityCertificate certificate = new IdentityCertificate();
        try {
          ResultSet result = statement.executeQuery();

          if (result.next()) {
            try {
              certificate.wireDecode(new Blob(result.getBytes("certificate_data")));
            } catch (EncodingException ex) {
              throw new SecurityException(
                  "BasicIdentityStorage: Error decoding certificate data: " + ex);
            }
          }
        } finally {
          statement.close();
        }

        return certificate;
      } catch (SQLException exception) {
        throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
      }
    } else return new IdentityCertificate();
  }
Beispiel #20
0
  /**
   * Get the default key name for the specified identity.
   *
   * @param identityName The identity name.
   * @return The default key name.
   * @throws SecurityException if the default key name for the identity is not set.
   */
  public final Name getDefaultKeyNameForIdentity(Name identityName) throws SecurityException {
    try {
      PreparedStatement statement = database_.prepareStatement(SELECT_getDefaultKeyNameForIdentity);
      statement.setString(1, identityName.toUri());

      try {
        ResultSet result = statement.executeQuery();

        if (result.next()) return new Name(identityName).append(result.getString("key_identifier"));
        else
          throw new SecurityException(
              "BasicIdentityStorage.getDefaultKeyNameForIdentity: The default key for the identity is not defined");
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #21
0
  /**
   * Append all the key names of a particular identity to the nameList.
   *
   * @param identityName The identity name to search for.
   * @param nameList Append result names to nameList.
   * @param isDefault If true, add only the default key name. If false, add only the non-default key
   *     names.
   */
  public void getAllKeyNamesOfIdentity(Name identityName, ArrayList nameList, boolean isDefault)
      throws SecurityException {
    try {
      String sql =
          isDefault
              ? SELECT_getAllKeyNamesOfIdentity_default_true
              : SELECT_getAllKeyNamesOfIdentity_default_false;
      PreparedStatement statement = database_.prepareStatement(sql);
      statement.setString(1, identityName.toUri());

      try {
        ResultSet result = statement.executeQuery();

        while (result.next())
          nameList.add(new Name(identityName).append(result.getString("key_identifier")));
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #22
0
  /**
   * Delete an identity and related public keys and certificates.
   *
   * @param identityName The identity name.
   */
  public void deleteIdentityInfo(Name identityName) throws SecurityException {
    String identity = identityName.toUri();

    try {
      PreparedStatement statement =
          database_.prepareStatement("DELETE FROM Certificate WHERE " + WHERE_deleteIdentityInfo);
      statement.setString(1, identity);

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }

      statement = database_.prepareStatement("DELETE FROM Key WHERE " + WHERE_deleteIdentityInfo);
      statement.setString(1, identity);

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }

      statement =
          database_.prepareStatement("DELETE FROM Identity WHERE " + WHERE_deleteIdentityInfo);
      statement.setString(1, identity);

      try {
        statement.executeUpdate();
      } finally {
        statement.close();
      }
    } catch (SQLException exception) {
      throw new SecurityException("BasicIdentityStorage: SQLite error: " + exception);
    }
  }
Beispiel #23
0
  /**
   * Prepare an unsigned identity certificate.
   *
   * @param keyName The key name, e.g., `/{identity_name}/ksk-123456`.
   * @param publicKey The public key to sign.
   * @param signingIdentity The signing identity.
   * @param notBefore See IdentityCertificate.
   * @param notAfter See IdentityCertificate.
   * @param subjectDescription A list of CertificateSubjectDescription. See IdentityCertificate. If
   *     null or empty, this adds a an ATTRIBUTE_NAME based on the keyName.
   * @param certPrefix The prefix before the `KEY` component. If null, this infers the certificate
   *     name according to the relation between the signingIdentity and the subject identity. If the
   *     signingIdentity is a prefix of the subject identity, `KEY` will be inserted after the
   *     signingIdentity, otherwise `KEY` is inserted after subject identity (i.e., before
   *     `ksk-...`).
   * @return The unsigned IdentityCertificate, or null if the inputs are invalid.
   */
  public final IdentityCertificate prepareUnsignedIdentityCertificate(
      Name keyName,
      PublicKey publicKey,
      Name signingIdentity,
      double notBefore,
      double notAfter,
      List subjectDescription,
      Name certPrefix)
      throws SecurityException {
    if (keyName.size() < 1) return null;

    String tempKeyIdPrefix = keyName.get(-1).toEscapedString();
    if (tempKeyIdPrefix.length() < 4) return null;
    String keyIdPrefix = tempKeyIdPrefix.substring(0, 4);
    if (!keyIdPrefix.equals("ksk-") && !keyIdPrefix.equals("dsk-")) return null;

    IdentityCertificate certificate = new IdentityCertificate();
    Name certName = new Name();

    if (certPrefix == null) {
      // No certificate prefix hint, so infer the prefix.
      if (signingIdentity.match(keyName))
        certName
            .append(signingIdentity)
            .append("KEY")
            .append(keyName.getSubName(signingIdentity.size()))
            .append("ID-CERT")
            .appendVersion((long) Common.getNowMilliseconds());
      else
        certName
            .append(keyName.getPrefix(-1))
            .append("KEY")
            .append(keyName.get(-1))
            .append("ID-CERT")
            .appendVersion((long) Common.getNowMilliseconds());
    } else {
      // A cert prefix hint is supplied, so determine the cert name.
      if (certPrefix.match(keyName) && !certPrefix.equals(keyName))
        certName
            .append(certPrefix)
            .append("KEY")
            .append(keyName.getSubName(certPrefix.size()))
            .append("ID-CERT")
            .appendVersion((long) Common.getNowMilliseconds());
      else return null;
    }

    certificate.setName(certName);
    certificate.setNotBefore(notBefore);
    certificate.setNotAfter(notAfter);
    certificate.setPublicKeyInfo(publicKey);

    if (subjectDescription == null || subjectDescription.isEmpty())
      certificate.addSubjectDescription(
          new CertificateSubjectDescription("2.5.4.41", keyName.getPrefix(-1).toUri()));
    else {
      for (int i = 0; i < subjectDescription.size(); ++i)
        certificate.addSubjectDescription(
            (CertificateSubjectDescription) subjectDescription.get(i));
    }

    try {
      certificate.encode();
    } catch (DerEncodingException ex) {
      throw new SecurityException("DerEncodingException: " + ex);
    } catch (DerDecodingException ex) {
      throw new SecurityException("DerDecodingException: " + ex);
    }

    return certificate;
  }
  /**
   * Get the public key name from the full certificate name.
   *
   * @param certificateName The full certificate name.
   * @return The related public key name.
   */
  public static Name certificateNameToPublicKeyName(Name certificateName) {
    String idString = "ID-CERT";
    boolean foundIdString = false;
    int idCertComponentIndex = certificateName.size() - 1;
    for (; idCertComponentIndex + 1 > 0; --idCertComponentIndex) {
      if (certificateName.get(idCertComponentIndex).toEscapedString().equals(idString)) {
        foundIdString = true;
        break;
      }
    }

    if (!foundIdString)
      throw new Error("Incorrect identity certificate name " + certificateName.toUri());

    Name tempName = certificateName.getSubName(0, idCertComponentIndex);
    String keyString = "KEY";
    boolean foundKeyString = false;
    int keyComponentIndex = 0;
    for (; keyComponentIndex < tempName.size(); keyComponentIndex++) {
      if (tempName.get(keyComponentIndex).toEscapedString().equals(keyString)) {
        foundKeyString = true;
        break;
      }
    }

    if (!foundKeyString)
      throw new Error("Incorrect identity certificate name " + certificateName.toUri());

    return tempName
        .getSubName(0, keyComponentIndex)
        .append(
            tempName.getSubName(keyComponentIndex + 1, tempName.size() - keyComponentIndex - 1));
  }