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

org.xipki.security.pkcs11.P11Slot Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2013 - 2020 Lijun Liao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xipki.security.pkcs11;

import iaik.pkcs.pkcs11.wrapper.Functions;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.security.EdECConstants;
import org.xipki.security.HashAlgo;
import org.xipki.security.X509Cert;
import org.xipki.security.pkcs11.P11ModuleConf.P11MechanismFilter;
import org.xipki.security.util.AlgorithmUtil;
import org.xipki.security.util.DSAParameterCache;
import org.xipki.security.util.KeyUtil;
import org.xipki.security.util.X509Util;
import org.xipki.util.Hex;

import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAParameterSpec;
import java.security.spec.ECParameterSpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static org.xipki.util.Args.*;
import static org.xipki.util.StringUtil.concat;
import static org.xipki.util.StringUtil.toUtf8Bytes;

/**
 * PKCS#11 slot.
 *
 * @author Lijun Liao
 * @since 2.0.0
 */

public abstract class P11Slot implements Closeable {

  public static class P11SlotRefreshResult {

    private final Map identities = new HashMap<>();

    private final Map certificates = new HashMap<>();

    private final Set mechanisms = new HashSet<>();

    public P11SlotRefreshResult() {
    }

    public Map getIdentities() {
      return identities;
    }

    public Map getCertificates() {
      return certificates;
    }

    public Set getMechanisms() {
      return mechanisms;
    }

    public void addIdentity(P11Identity identity) {
      notNull(identity, "identity");
      this.identities.put(identity.getId().getKeyId(), identity);
    }

    public void addMechanism(long mechanism) {
      this.mechanisms.add(mechanism);
    }

    public void addCertificate(P11ObjectIdentifier objectId, X509Cert certificate) {
      notNull(objectId, "objectId");
      notNull(certificate, "certificate");
      this.certificates.put(objectId, certificate);
    }

    /**
     * Returns the certificate of the given identifier {@code id}.
     * @param id
     *          Identifier. Must not be {@code null}.
     * @return the certificate of the given identifier.
     */
    public X509Cert getCertForId(byte[] id) {
      for (P11ObjectIdentifier objId : certificates.keySet()) {
        if (objId.matchesId(id)) {
          return certificates.get(objId);
        }
      }
      return null;
    }

    /**
     * Returns the PKCS#11 label for certificate of the given {@code id}.
     * @param id
     *          Identifier. Must not be {@code null}.
     * @return the label.
     */
    public String getCertLabelForId(byte[] id) {
      for (P11ObjectIdentifier objId : certificates.keySet()) {
        if (objId.matchesId(id)) {
          return objId.getLabel();
        }
      }
      return null;
    }

  } // class P11SlotRefreshResult

  public static class P11NewObjectControl {

    private final byte[] id;

    private final String label;

    public P11NewObjectControl(byte[] id, String label) {
      this.id = id;
      this.label = notBlank(label, "label");
    }

    public byte[] getId() {
      return id;
    }

    public String getLabel() {
      return label;
    }

  } // class P11NewObjectControl

  public enum P11KeyUsage {
    DECRYPT,
    DERIVE,
    SIGN,
    SIGN_RECOVER,
    UNWRAP
  } // class P11KeyUsage

  public static class P11NewKeyControl extends P11NewObjectControl {

    private Boolean extractable;

    private Boolean sensitive;

    private Set usages;

    public P11NewKeyControl(byte[] id, String label) {
      super(id, label);
    }

    public Boolean getExtractable() {
      return extractable;
    }

    public void setExtractable(Boolean extractable) {
      this.extractable = extractable;
    }

    public Boolean getSensitive() {
      return sensitive;
    }

    public void setSensitive(Boolean sensitive) {
      this.sensitive = sensitive;
    }

    public Set getUsages() {
      if (usages == null) {
        usages = new HashSet<>();
      }
      return usages;
    }

    public void setUsages(Set usages) {
      this.usages = usages;
    }

  } // class P11NewKeyControl

  private static final Logger LOG = LoggerFactory.getLogger(P11Slot.class);

  protected final String moduleName;

  protected final P11SlotIdentifier slotId;

  private final boolean readOnly;

  private final SecureRandom random = new SecureRandom();

  private final ConcurrentHashMap identities =
      new ConcurrentHashMap<>();

  private final ConcurrentHashMap certificates =
      new ConcurrentHashMap<>();

  private final Set mechanisms = new HashSet<>();

  private final P11MechanismFilter mechanismFilter;

  protected P11Slot(String moduleName, P11SlotIdentifier slotId, boolean readOnly,
      P11MechanismFilter mechanismFilter)
          throws P11TokenException {
    this.mechanismFilter = notNull(mechanismFilter, "mechanismFilter");
    this.moduleName = notBlank(moduleName, "moduleName");
    this.slotId = notNull(slotId, "slotId");
    this.readOnly = readOnly;
  }

  /**
   * Returns the hex representation of the bytes.
   *
   * @param bytes
   *          Data to be encoded. Must not be {@code null}.
   * @return the hex representation of the bytes.
   */
  protected static String hex(byte[] bytes) {
    return Hex.encode(bytes);
  }

  /**
   * Returns the hex representation of the bytes.
   *
   * @param hex
   *          Data to be decoded. Must not be {@code null}.
   * @return the hex representation of the bytes.
   */
  protected static byte[] decodeHex(String hex) {
    return Hex.decode(hex);
  }

  public static String getDescription(byte[] keyId, char[] keyLabel) {
    return concat("id ", (keyId == null ? "null" : hex(keyId)), " and label ",
        (keyLabel == null ? "null" : new String(keyLabel)));
  }

  public static String getDescription(byte[] keyId, String keyLabel) {
    return concat("id ", (keyId == null ? "null" : hex(keyId)), " and label ", keyLabel);
  }

  /**
   * Updates the certificate associated with the given {@code objectId} with the given certificate
   * {@code newCert}.
   *
   * @param keyId
   *          Object identifier of the private key. Must not be {@code null}.
   * @param newCert
   *          Certificate to be added. Must not be {@code null}.
   * @throws CertificateException
   *         if process with certificate fails.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  protected abstract void updateCertificate0(P11ObjectIdentifier keyId, X509Cert newCert)
      throws P11TokenException, CertificateException;

  /**
   * Removes the key (private key, public key, secret key, and certificates) associated with
   * the given identifier {@code objectId}.
   *
   * @param identityId
   *          Identity identifier. Must not be {@code null}.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  protected abstract void removeIdentity0(P11IdentityId identityId)
      throws P11TokenException;

  /**
   * Adds the certificate to the PKCS#11 token under the given identifier {@code objectId}.
   *
   * @param cert
   *          Certificate to be added. Must not be {@code null}.
   * @param control
   *          Control of the object creation process. Must not be {@code null}.
   * @return the PKCS#11 identifier of the added certificate.
   * @throws CertificateException
   *         if process with certificate fails.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  protected abstract P11ObjectIdentifier addCert0(X509Cert cert, P11NewObjectControl control)
      throws P11TokenException, CertificateException;

  /**
   * Generates a secret key in the PKCS#11 token.
   *
   * @param keyType
   *          key type
   * @param keysize
   *          key size
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  protected abstract P11Identity generateSecretKey0(long keyType, int keysize,
      P11NewKeyControl control)
          throws P11TokenException;

  /**
   * Imports secret key object in the PKCS#11 token. The key itself will not be generated
   * within the PKCS#11 token.
   *
   * @param keyType
   *          key type.
   * @param keyValue
   *          Key value. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  protected abstract P11Identity importSecretKey0(long keyType, byte[] keyValue,
      P11NewKeyControl control)
          throws P11TokenException;

  /**
   * Generates a DSA keypair.
   *
   * @param p
   *          p of DSA. Must not be {@code null}.
   * @param q
   *          q of DSA. Must not be {@code null}.
   * @param g
   *          g of DSA. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  protected abstract P11Identity generateDSAKeypair0(BigInteger p, BigInteger q, BigInteger g,
      P11NewKeyControl control)
          throws P11TokenException;

  /**
   * Generates an EC Edwards keypair.
   *
   * @param curveId
   *         Object Identifier of the curve. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  protected abstract P11Identity generateECEdwardsKeypair0(ASN1ObjectIdentifier curveId,
      P11NewKeyControl control)
          throws P11TokenException;

  /**
   * Generates an EC Montgomery keypair.
   *
   * @param curveId
   *         Object Identifier of the curve. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  protected abstract P11Identity generateECMontgomeryKeypair0(ASN1ObjectIdentifier curveId,
      P11NewKeyControl control)
          throws P11TokenException;

  /**
   * Generates an EC keypair.
   *
   * @param curveId
   *         Object identifier of the EC curve. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  protected abstract P11Identity generateECKeypair0(ASN1ObjectIdentifier curveId,
      P11NewKeyControl control)
          throws P11TokenException;

  /**
   * Generates an SM2p256v1 keypair.
   *
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  protected abstract P11Identity generateSM2Keypair0(P11NewKeyControl control)
      throws P11TokenException;

  /**
   * Generates an RSA keypair.
   *
   * @param keysize
   *          key size in bit
   * @param publicExponent
   *          RSA public exponent. Could be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  protected abstract P11Identity generateRSAKeypair0(int keysize, BigInteger publicExponent,
      P11NewKeyControl control)
          throws P11TokenException;

  protected abstract P11SlotRefreshResult refresh0()
      throws P11TokenException;

  protected abstract void removeCerts0(P11ObjectIdentifier objectId)
      throws P11TokenException;

  @Override
  public abstract void close();

  /**
   * Remove objects.
   *
   * @param id
   *         Id of the objects to be deleted. At least one of id and label may not be {@code null}.
   * @param label
   *         Label of the objects to be deleted
   * @return how many objects have been deleted
   * @throws P11TokenException
   *           If PKCS#11 error happens.
   */
  public abstract int removeObjects(byte[] id, String label)
      throws P11TokenException;

  /**
   * Gets certificate with the given identifier {@code id}.
   * @param id
   *          Identifier of the certificate. Must not be {@code null}.
   * @return certificate with the given identifier.
   */
  public X509Cert getCertForId(byte[] id) {
    for (P11ObjectIdentifier objId : certificates.keySet()) {
      if (objId.matchesId(id)) {
        return certificates.get(objId);
      }
    }
    return null;
  } // method getCertForId

  /**
   * Gets certificate with the given identifier {@code id}.
   * @param objectId
   *          Identifier of the certificate. Must not be {@code null}.
   * @return certificate with the given identifier.
   */
  public X509Cert getCert(P11ObjectIdentifier objectId) {
    return certificates.get(objectId);
  }

  private void updateCaCertsOfIdentities() {
    for (P11Identity identity : identities.values()) {
      updateCaCertsOfIdentity(identity);
    }
  }

  private void updateCaCertsOfIdentity(P11Identity identity) {
    X509Cert[] certchain = identity.certificateChain();
    if (certchain == null || certchain.length == 0) {
      return;
    }

    X509Cert[] newCertchain = buildCertPath(certchain[0]);
    if (!Arrays.equals(certchain, newCertchain)) {
      try {
        identity.setCertificates(newCertchain);
      } catch (P11TokenException ex) {
        LOG.warn("could not set certificates for identity {}", identity.getId());
      }
    }
  } // method updateCaCertsOfIdentity

  private X509Cert[] buildCertPath(X509Cert cert) {
    List certs = new LinkedList<>();
    X509Cert cur = cert;
    while (cur != null) {
      certs.add(cur);
      cur = getIssuerForCert(cur);
    }
    return certs.toArray(new X509Cert[0]);
  } // method buildCertPath

  private X509Cert getIssuerForCert(X509Cert cert) {
    try {
      if (cert.isSelfSigned()) {
        return null;
      }

      for (X509Cert cert2 : certificates.values()) {
        if (cert2 == cert) {
          continue;
        }

        if (X509Util.issues(cert2, cert)) {
          return cert2;
        }
      }
    } catch (CertificateEncodingException ex) {
      LOG.warn("invalid encoding of certificate {}", ex.getMessage());
    }
    return null;
  } // method getIssuerForCert

  public void refresh()
      throws P11TokenException {
    P11SlotRefreshResult res = refresh0(); // CHECKSTYLE:SKIP

    mechanisms.clear();
    certificates.clear();
    identities.clear();

    List ignoreMechs = new ArrayList<>();

    for (Long mech : res.getMechanisms()) {
      if (mechanismFilter.isMechanismPermitted(slotId, mech)) {
        mechanisms.add(mech);
      } else {
        ignoreMechs.add(mech);
      }
    }
    certificates.putAll(res.getCertificates());
    identities.putAll(res.getIdentities());

    updateCaCertsOfIdentities();

    if (LOG.isInfoEnabled()) {
      StringBuilder sb = new StringBuilder();
      sb.append("initialized module ").append(moduleName).append(", slot ").append(slotId);

      sb.append("\nsupported mechanisms:\n");
      List sortedMechs = new ArrayList<>(mechanisms);
      Collections.sort(sortedMechs);
      for (Long mech : sortedMechs) {
        sb.append("\t").append(Functions.getMechanismDescription(mech)).append("\n");
      }

      sb.append("\nsupported by device but ignored mechanisms:\n");
      if (ignoreMechs.isEmpty()) {
        sb.append("\tNONE\n");
      } else {
        Collections.sort(ignoreMechs);
        for (Long mech : ignoreMechs) {
          sb.append("\t").append(Functions.getMechanismDescription(mech)).append("\n");
        }
      }

      List ids = getSortedObjectIds(certificates.keySet());
      sb.append(ids.size()).append(" certificates:\n");
      for (P11ObjectIdentifier objectId : ids) {
        X509Cert entity = certificates.get(objectId);
        sb.append("\t").append(objectId);
        sb.append(", subject='").append(entity.getSubjectRfc4519Text()).append("'\n");
      }

      ids = getSortedObjectIds(identities.keySet());
      sb.append(ids.size()).append(" identities:\n");
      for (P11ObjectIdentifier objectId : ids) {
        P11Identity identity = identities.get(objectId);
        sb.append("\t").append(objectId);

        PublicKey publicKey = identity.getPublicKey();
        if (publicKey != null) {
          String algo = getAlgorithmDesc(publicKey);
          sb.append(", algo=").append(algo);
          if (identity.getCertificate() != null) {
            String subject = identity.getCertificate().getSubjectRfc4519Text();
            sb.append(", subject='").append(subject).append("'");
          }
        } else {
          sb.append(", algo=");
        }
        sb.append("\n");
      }

      LOG.info(sb.toString());
    }
  } // method refresh

  protected void addIdentity(P11Identity identity)
      throws P11DuplicateEntityException {
    if (!slotId.equals(identity.getId().getSlotId())) {
      throw new IllegalArgumentException("invalid identity");
    }

    P11ObjectIdentifier keyId = identity.getId().getKeyId();
    if (hasIdentity(keyId)) {
      throw new P11DuplicateEntityException(slotId, keyId);
    }

    identities.put(keyId, identity);
    updateCaCertsOfIdentity(identity);
  } // method addIdentity

  public boolean hasIdentity(P11ObjectIdentifier keyId) {
    return identities.containsKey(keyId);
  }

  public Set getMechanisms() {
    return Collections.unmodifiableSet(mechanisms);
  }

  public boolean supportsMechanism(long mechanism) {
    return mechanisms.contains(mechanism);
  }

  public void assertMechanismSupported(long mechanism)
      throws P11UnsupportedMechanismException {
    if (!mechanisms.contains(mechanism)) {
      throw new P11UnsupportedMechanismException(mechanism, slotId);
    }
  }

  public Set getIdentityKeyIds() {
    return Collections.unmodifiableSet(identities.keySet());
  }

  public Set getCertIds() {
    return Collections.unmodifiableSet(certificates.keySet());
  }

  public String getModuleName() {
    return moduleName;
  }

  public P11SlotIdentifier getSlotId() {
    return slotId;
  }

  public boolean isReadOnly() {
    return readOnly;
  }

  public P11Identity getIdentity(P11ObjectIdentifier keyId)
      throws P11UnknownEntityException {
    P11Identity ident = identities.get(keyId);
    if (ident == null) {
      throw new P11UnknownEntityException(slotId, keyId);
    }
    return ident;
  }

  protected void assertNoIdentityAndCert(byte[] id, String label)
      throws P11DuplicateEntityException {
    if (id == null && label == null) {
      return;
    }

    Set objectIds = new HashSet<>(identities.keySet());
    objectIds.addAll(certificates.keySet());

    for (P11ObjectIdentifier objectId : objectIds) {
      boolean matchId = id != null && objectId.matchesId(id);
      boolean matchLabel = label != null && label.equals(objectId.getLabel());

      if (matchId || matchLabel) {
        StringBuilder sb = new StringBuilder("Identity or Certificate with ");
        if (matchId) {
          sb.append("id=0x").append(Hex.encodeUpper(id));
          if (matchLabel) {
            sb.append(" and ");
          }
        }

        if (matchLabel) {
          sb.append("label=").append(label);
        }

        sb.append(" already exists");
        throw new P11DuplicateEntityException(sb.toString());
      }
    }
  } // method assertNoIdentityAndCert

  public P11ObjectIdentifier getObjectId(byte[] id, String label) {
    if (id == null && label == null) {
      return null;
    }

    for (P11ObjectIdentifier objectId : identities.keySet()) {
      boolean match = true;
      if (id != null) {
        match = objectId.matchesId(id);
      }

      if (label != null) {
        match = label.equals(objectId.getLabel());
      }

      if (match) {
        return objectId;
      }
    }

    for (P11ObjectIdentifier objectId : certificates.keySet()) {
      boolean match = true;
      if (id != null) {
        match = objectId.matchesId(id);
      }

      if (label != null) {
        match = label.equals(objectId.getLabel());
      }

      if (match) {
        return objectId;
      }
    }

    return null;
  } // method getObjectId

  public P11IdentityId getIdentityId(byte[] keyId, String keyLabel) {
    if (keyId == null && keyLabel == null) {
      return null;
    }

    for (P11ObjectIdentifier objectId : identities.keySet()) {
      boolean match = true;
      if (keyId != null) {
        match = objectId.matchesId(keyId);
      }

      if (keyLabel != null) {
        match = keyLabel.equals(objectId.getLabel());
      }

      if (match) {
        return identities.get(objectId).getId();
      }
    }

    return null;
  } // method getIdentityId

  /**
   * Exports the certificate of the given identifier {@code objectId}.
   *
   * @param objectId
   *          Object identifier. Must not be {@code null}.
   * @return the exported certificate
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public X509Cert exportCert(P11ObjectIdentifier objectId)
      throws P11TokenException {
    notNull(objectId, "objectId");
    try {
      return getIdentity(objectId).getCertificate();
    } catch (P11UnknownEntityException ex) {
      // CHECKSTYLE:SKIP
    }

    X509Cert cert = certificates.get(objectId);
    if (cert == null) {
      throw new P11UnknownEntityException(slotId, objectId);
    }
    return cert;
  } // method exportCert

  /**
   * Remove certificates.
   *
   * @param objectId
   *          Object identifier. Must not be {@code null}.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public void removeCerts(P11ObjectIdentifier objectId)
      throws P11TokenException {
    notNull(objectId, "objectId");
    assertWritable("removeCerts");

    P11ObjectIdentifier keyId = null;
    for (P11ObjectIdentifier m : identities.keySet()) {
      P11Identity identity = identities.get(m);
      if (objectId.equals(identity.getId().getCertId())) {
        keyId = m;
        break;
      }
    }

    if (keyId != null) {
      certificates.remove(objectId);
      identities.get(keyId).setCertificates(null);
    } else if (certificates.containsKey(objectId)) {
      certificates.remove(objectId);
    } else {
      throw new P11UnknownEntityException(slotId, objectId);
    }

    updateCaCertsOfIdentities();
    removeCerts0(objectId);
  } // method removeCerts

  /**
   * Removes the key (private key, public key, secret key, and certificates) associated with
   * the given identifier {@code objectId}.
   *
   * @param identityId
   *          Identity identifier. Must not be {@code null}.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public void removeIdentity(P11IdentityId identityId)
      throws P11TokenException {
    notNull(identityId, "identityId");
    assertWritable("removeIdentity");
    P11ObjectIdentifier keyId = identityId.getKeyId();
    if (identities.containsKey(keyId)) {
      if (identityId.getCertId() != null) {
        certificates.remove(identityId.getCertId());
      }
      identities.get(keyId).setCertificates(null);
      identities.remove(keyId);
      updateCaCertsOfIdentities();
    }

    removeIdentity0(identityId);
  } // method removeIdentity

  /**
   * Removes the key (private key, public key, secret key, and certificates) associated with
   * the given identifier {@code objectId}.
   *
   * @param keyId
   *          Key identifier. Must not be {@code null}.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public void removeIdentityByKeyId(P11ObjectIdentifier keyId)
      throws P11TokenException {
    notNull(keyId, "keyId");
    assertWritable("removeIdentityByKeyId");

    P11IdentityId entityId;
    if (identities.containsKey(keyId)) {
      entityId = identities.get(keyId).getId();
      if (entityId.getCertId() != null) {
        certificates.remove(entityId.getCertId());
      }
      identities.get(keyId).setCertificates(null);
      identities.remove(keyId);
      updateCaCertsOfIdentities();

      removeIdentity0(entityId);
    }

  } // method removeIdentityByKeyId

  /**
   * Adds the certificate to the PKCS#11 token under the given identifier {@code objectId}.
   *
   * @param cert
   *          Certificate to be added. Must not be {@code null}.
   * @param control
   *          Control of the object creation process. Must not be {@code null}.
   * @return the identifier of the newly added certificate.
   * @throws CertificateException
   *         if process with certificate fails.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public P11ObjectIdentifier addCert(X509Cert cert, P11NewObjectControl control)
      throws P11TokenException, CertificateException {
    notNull(cert, "cert");
    notNull(control, "control");
    assertWritable("addCert");

    if (control.getLabel() == null) {
      String cn = cert.getCommonName();
      control = new P11NewObjectControl(control.getId(), generateLabel(cn));
    }

    P11ObjectIdentifier objectId = addCert0(cert, control);
    certificates.put(objectId, cert);
    updateCaCertsOfIdentities();
    LOG.info("added certificate {}", objectId);
    return objectId;
  } // method addCert

  protected String generateLabel(String label) {
    String tmpLabel = label;
    int idx = 0;
    while (true) {
      boolean duplicated = false;
      for (P11ObjectIdentifier objectId : identities.keySet()) {
        P11IdentityId identityId = identities.get(objectId).getId();
        P11ObjectIdentifier pubKeyId = identityId.getPublicKeyId();
        P11ObjectIdentifier certId = identityId.getCertId();

        if (label.equals(objectId.getLabel())
            || (pubKeyId != null && label.equals(pubKeyId.getLabel())
            || (certId != null && label.equals(certId.getLabel())))) {
          duplicated = true;
          break;
        }
      }

      if (!duplicated) {
        for (P11ObjectIdentifier objectId : certificates.keySet()) {
          if (objectId.getLabel().equals(label)) {
            duplicated = true;
            break;
          }
        }
      }

      if (!duplicated) {
        return tmpLabel;
      }

      idx++;
      tmpLabel = label + "-" + idx;
    }
  } // method generateLabel

  /**
   * Generates a secret key in the PKCS#11 token.
   *
   * @param keyType
   *          Key type
   * @param keysize
   *          Key size in bit
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public P11IdentityId generateSecretKey(long keyType, int keysize, P11NewKeyControl control)
      throws P11TokenException {
    assertWritable("generateSecretKey");
    notNull(control, "control");
    assertNoIdentityAndCert(control.getId(), control.getLabel());

    P11Identity identity = generateSecretKey0(keyType, keysize, control);
    addIdentity(identity);

    P11IdentityId id = identity.getId();
    LOG.info("generated secret key {}", id);
    return id;
  } // method generateSecretKey

  /**
   * Imports secret key object in the PKCS#11 token. The key itself will not be generated
   * within the PKCS#11 token.
   *
   * @param keyType
   *          Key type
   * @param keyValue
   *          Key value. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the key within the PKCS#11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public P11ObjectIdentifier importSecretKey(long keyType, byte[] keyValue,
      P11NewKeyControl control)
          throws P11TokenException {
    notNull(control, "control");
    assertWritable("createSecretKey");
    assertNoIdentityAndCert(control.getId(), control.getLabel());

    P11Identity identity = importSecretKey0(keyType, keyValue, control);
    addIdentity(identity);

    P11ObjectIdentifier objId = identity.getId().getKeyId();
    LOG.info("created secret key {}", objId);
    return objId;
  } // method importSecretKey

  /**
   * Generates an RSA keypair.
   *
   * @param keysize
   *          key size in bit
   * @param publicExponent
   *          RSA public exponent. Could be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateRSAKeypair(int keysize, BigInteger publicExponent,
      P11NewKeyControl control)
          throws P11TokenException {
    min(keysize, "keysize", 1024);
    if (keysize % 1024 != 0) {
      throw new IllegalArgumentException("key size is not multiple of 1024: " + keysize);
    }
    assertCanGenKeypair("generateRSAKeypair", PKCS11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN, control);

    BigInteger tmpPublicExponent = publicExponent;
    if (tmpPublicExponent == null) {
      tmpPublicExponent = BigInteger.valueOf(65537);
    }

    P11Identity identity = generateRSAKeypair0(keysize, tmpPublicExponent, control);
    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated RSA keypair {}", id);
    return id;
  } // method generateRSAKeypair

  /**
   * Generates a DSA keypair.
   *
   * @param plength
   *          bit length of P
   * @param qlength
   *          bit length of Q
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateDSAKeypair(int plength, int qlength, P11NewKeyControl control)
      throws P11TokenException {
    min(plength, "plength", 1024);
    if (plength % 1024 != 0) {
      throw new IllegalArgumentException("key size is not multiple of 1024: " + plength);
    }
    assertCanGenKeypair("generateDSAKeypair", PKCS11Constants.CKM_DSA_KEY_PAIR_GEN, control);

    DSAParameterSpec dsaParams = DSAParameterCache.getDSAParameterSpec(plength, qlength, random);
    P11Identity identity = generateDSAKeypair0(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG(),
        control);
    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated DSA keypair {}", id);
    return id;
  } // method generateDSAKeypair

  /**
   * Generates a DSA keypair.
   *
   * @param p
   *          p of DSA. Must not be {@code null}.
   * @param q
   *          q of DSA. Must not be {@code null}.
   * @param g
   *          g of DSA. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateDSAKeypair(BigInteger p, BigInteger q, BigInteger g,
      P11NewKeyControl control)
          throws P11TokenException {
    notNull(p, "p");
    notNull(q, "q");
    notNull(g, "g");
    assertCanGenKeypair("generateDSAKeypair", PKCS11Constants.CKM_DSA_KEY_PAIR_GEN, control);

    P11Identity identity = generateDSAKeypair0(p, q, g, control);
    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated DSA keypair {}", id);
    return id;
  } // method generateDSAKeypair

  /**
   * Generates an EC keypair.
   *
   * @param curveOid
   *         Object identifier of the EC curve. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateECKeypair(ASN1ObjectIdentifier curveOid, P11NewKeyControl control)
      throws P11TokenException {
    notNull(curveOid, "curveOid");

    P11Identity identity;
    if (EdECConstants.isEdwardsCurve(curveOid)) {
      assertCanGenKeypair("generateECKeypair", PKCS11Constants.CKM_EC_EDWARDS_KEY_PAIR_GEN,
          control);
      identity = generateECEdwardsKeypair0(curveOid, control);
    } else if (EdECConstants.isMontgomeryCurve(curveOid)) {
      assertCanGenKeypair("generateECKeypair", PKCS11Constants.CKM_EC_MONTGOMERY_KEY_PAIR_GEN,
          control);
      identity = generateECMontgomeryKeypair0(curveOid, control);
    } else {
      assertCanGenKeypair("generateECKeypair", PKCS11Constants.CKM_EC_KEY_PAIR_GEN, control);
      identity = generateECKeypair0(curveOid, control);
    }

    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated EC keypair {}", id);
    return id;
  } // method generateECKeypair

  /**
   * Generates an EC Edwards keypair.
   *
   * @param curveOid
   *         Object Identifier of the EdEC curve as defined in RFC 8410. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateECEdwardsKeypair(ASN1ObjectIdentifier curveOid,
      P11NewKeyControl control)
          throws P11TokenException {
    notNull(curveOid, "curveOid");

    assertCanGenKeypair("generateECEdwardsKeypair0",
        PKCS11Constants.CKM_EC_EDWARDS_KEY_PAIR_GEN, control);
    P11Identity identity = generateECEdwardsKeypair0(curveOid, control);
    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated EC Edwards keypair {}", id);
    return id;
  } // method generateECEdwardsKeypair

  /**
   * Generates an EC Montgomery keypair.
   *
   * @param curveOid
   *         Object Identifier of the EdEC curve as defined in RFC 8410. Must not be {@code null}.
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateECMontgomeryKeypair(ASN1ObjectIdentifier curveOid,
      P11NewKeyControl control)
          throws P11TokenException {
    notNull(curveOid, "curveOid");

    assertCanGenKeypair("generateECMontgomeryKeypair",
        PKCS11Constants.CKM_EC_MONTGOMERY_KEY_PAIR_GEN, control);
    P11Identity identity = generateECMontgomeryKeypair0(curveOid, control);
    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated EC Montgomery keypair {}", id);
    return id;
  } // method generateECMontgomeryKeypair

  /**
   * Generates an SM2 keypair.
   *
   * @param control
   *          Control of the key generation process. Must not be {@code null}.
   * @return the identifier of the identity within the PKCS#P11 token.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  // CHECKSTYLE:SKIP
  public P11IdentityId generateSM2Keypair(P11NewKeyControl control)
      throws P11TokenException {
    assertCanGenKeypair("generateSM2Keypair", PKCS11Constants.CKM_VENDOR_SM2_KEY_PAIR_GEN, control);

    P11Identity identity = generateSM2Keypair0(control);
    addIdentity(identity);
    P11IdentityId id = identity.getId();
    LOG.info("generated SM2 keypair {}", id);
    return id;
  } // method generateSM2Keypair

  private void assertCanGenKeypair(String methodName, long mechanism, P11NewKeyControl control)
      throws P11UnsupportedMechanismException, P11PermissionException, P11DuplicateEntityException {
    notNull(control, "control");
    assertWritable(methodName);
    assertMechanismSupported(mechanism);
    assertNoIdentityAndCert(control.getId(), control.getLabel());
  } // method assertCanGenKeypair

  /**
   * Updates the certificate associated with the given ID {@code keyId} with the given certificate
   * {@code newCert}.
   *
   * @param keyId
   *          Object identifier of the private key. Must not be {@code null}.
   * @param newCert
   *          Certificate to be added. Must not be {@code null}.
   * @throws CertificateException
   *         if process with certificate fails.
   * @throws P11TokenException
   *         if PKCS#11 token exception occurs.
   */
  public void updateCertificate(P11ObjectIdentifier keyId, X509Cert newCert)
      throws P11TokenException, CertificateException {
    notNull(keyId, "keyId");
    notNull(newCert, "newCert");
    assertWritable("updateCertificate");

    P11Identity identity = identities.get(keyId);
    if (identity == null) {
      throw new P11UnknownEntityException("could not find private key " + keyId);
    }

    java.security.PublicKey pk = identity.getPublicKey();
    java.security.PublicKey newPk = newCert.getPublicKey();
    if (!pk.equals(newPk)) {
      throw new P11TokenException("the given certificate is not for key " + keyId);
    }

    updateCertificate0(keyId, newCert);

    certificates.put(keyId, newCert);

    P11IdentityId identityId = identity.getId();
    identityId.setCertLabel(keyId.getLabel());
    identity.setCertificates(new X509Cert[]{newCert});
    updateCaCertsOfIdentities();
    LOG.info("updated certificate for key {}", keyId);
  } // method updateCertificate

  /**
   * Writes the token details to the given {@code stream}.
   * @param stream
   *          Output stream. Must not be {@code null}.
   * @param verbose
   *          Whether to show the details verbosely.
   * @throws IOException
   *         if IO error occurs.
   */
  public void showDetails(OutputStream stream, boolean verbose)
      throws IOException {
    notNull(stream, "stream");

    List sortedKeyIds = getSortedObjectIds(identities.keySet());
    int size = sortedKeyIds.size();

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < size; i++) {
      P11ObjectIdentifier keyId = sortedKeyIds.get(i);
      P11Identity identity = identities.get(keyId);

      String label = keyId.getLabel();
      sb.append("\t").append(i + 1).append(". ").append(label);
      sb.append(" (").append("id: ").append(keyId.getIdHex());
      P11IdentityId identityId = identity.getId();
      P11ObjectIdentifier certId = identityId.getCertId();
      if (certId != null && !certId.equals(keyId)) {
        sb.append(", certificate label: ").append(identityId.getCertId().getLabel());
      }

      P11ObjectIdentifier pubKeyId = identityId.getPublicKeyId();
      if (pubKeyId != null && !pubKeyId.equals(keyId)) {
        sb.append(", publicKey label: ").append(pubKeyId.getLabel());
      }

      sb.append(")\n");

      if (identity.getPublicKey() != null) {
        String algo = getAlgorithmDesc(identity.getPublicKey());
        sb.append("\t\tAlgorithm: ").append(algo).append("\n");
        X509Cert[] certs = identity.certificateChain();
        if (certs == null || certs.length == 0) {
          sb.append("\t\tCertificate: NONE\n");
        } else {
          for (int j = 0; j < certs.length; j++) {
            formatString(j, verbose, sb, certs[j]);
          }
        }
      } else {
        sb.append("\t\tSymmetric key\n");
      }
    }

    sortedKeyIds.clear();
    for (P11ObjectIdentifier objectId : certificates.keySet()) {
      if (!identities.containsKey(objectId)) {
        sortedKeyIds.add(objectId);
      }
    }

    Collections.sort(sortedKeyIds);

    if (!sortedKeyIds.isEmpty()) {
      Collections.sort(sortedKeyIds);
      size = sortedKeyIds.size();
      for (int i = 0; i < size; i++) {
        P11ObjectIdentifier objectId = sortedKeyIds.get(i);
        sb.append("\tCert-").append(i + 1).append(". ").append(objectId.getLabel());
        sb.append(" (").append("id: ").append(objectId.getIdHex())
          .append(", label: ").append(objectId.getLabel()).append(")\n");
        formatString(null, verbose, sb, certificates.get(objectId));
      }
    }

    if (sb.length() > 0) {
      stream.write(toUtf8Bytes(sb.toString()));
    }
  } // method showDetails

  protected void assertWritable(String operationName)
      throws P11PermissionException {
    if (readOnly) {
      throw new P11PermissionException("Writable operation " + operationName + " is not permitted");
    }
  } // method assertWritable

  protected boolean existsIdentityForId(byte[] id) {
    for (P11ObjectIdentifier objectId : identities.keySet()) {
      if (objectId.matchesId(id)) {
        return true;
      }
    }

    return false;
  } // method existsIdentityForId

  protected boolean existsIdentityForLabel(String label) {
    for (P11ObjectIdentifier objectId : identities.keySet()) {
      if (objectId.matchesLabel(label)) {
        return true;
      }
    }

    return false;
  } // method existsIdentityForLabel

  protected boolean existsCertForId(byte[] id) {
    for (P11ObjectIdentifier objectId : certificates.keySet()) {
      if (objectId.matchesId(id)) {
        return true;
      }
    }

    return false;
  } // method existsCertForId

  protected boolean existsCertForLabel(String label) {
    for (P11ObjectIdentifier objectId : certificates.keySet()) {
      if (objectId.matchesLabel(label)) {
        return true;
      }
    }

    return false;
  } // method existsCertForLabel

  private static String getAlgorithmDesc(PublicKey publicKey) {
    String algo = publicKey.getAlgorithm();

    if (publicKey instanceof ECPublicKey) {
      String curveName = "UNKNOWN";
      ECParameterSpec paramSpec = ((ECPublicKey) publicKey).getParams();
      ASN1ObjectIdentifier curveOid = KeyUtil.detectCurveOid(paramSpec);
      if (curveOid != null) {
        String name = AlgorithmUtil.getCurveName(curveOid);
        curveName = name == null ? curveOid.getId() : name;
      }
      algo += "/" + curveName;
    } else if (publicKey instanceof RSAPublicKey) {
      int keylen = ((RSAPublicKey) publicKey).getModulus().bitLength();
      algo += "/" + keylen;
    } else if (publicKey instanceof DSAPublicKey) {
      int keylen = ((DSAPublicKey) publicKey).getParams().getP().bitLength();
      algo += "/" + keylen;
    }

    return algo;
  }

  private static void formatString(Integer index, boolean verbose, StringBuilder sb,
      X509Cert cert) {
    String subject = cert.getSubjectRfc4519Text();
    sb.append("\t\tCertificate");
    if (index != null) {
      sb.append("[").append(index).append("]");
    }
    sb.append(": ");

    if (!verbose) {
      sb.append(subject).append("\n");
      return;
    }

    sb.append("\n\t\t\tSubject: ").append(subject);

    sb.append("\n\t\t\tIssuer: ").append(cert.getIssuerRfc4519Text());
    sb.append("\n\t\t\tSerial: ").append(cert.getSerialNumberHex());
    sb.append("\n\t\t\tStart time: ").append(cert.getNotBefore());
    sb.append("\n\t\t\tEnd time: ").append(cert.getNotAfter());
    sb.append("\n\t\t\tSHA1 Sum: ");
    sb.append(HashAlgo.SHA1.hexHash(cert.getEncoded()));
    sb.append("\n");
  } // method formatString

  private List getSortedObjectIds(Set sets) {
    List ids = new ArrayList<>(sets);
    Collections.sort(ids);
    return ids;
  } // method getSortedObjectIds

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy