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

org.xipki.security.pkcs11.iaik.IaikP11Slot 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.iaik;

import iaik.pkcs.pkcs11.Mechanism;
import iaik.pkcs.pkcs11.*;
import iaik.pkcs.pkcs11.objects.*;
import iaik.pkcs.pkcs11.objects.Certificate.CertificateType;
import iaik.pkcs.pkcs11.objects.Key.KeyType;
import iaik.pkcs.pkcs11.wrapper.Functions;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Exception;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.security.X509Cert;
import org.xipki.security.XiSecurityException;
import org.xipki.security.pkcs11.*;
import org.xipki.security.pkcs11.P11ModuleConf.P11MechanismFilter;
import org.xipki.security.pkcs11.P11ModuleConf.P11NewObjectConf;
import org.xipki.security.util.SignerUtil;
import org.xipki.security.util.X509Util;
import org.xipki.util.LogUtil;
import org.xipki.util.concurrent.ConcurrentBag;
import org.xipki.util.concurrent.ConcurrentBagEntry;

import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static iaik.pkcs.pkcs11.wrapper.PKCS11Constants.*;
import static org.xipki.security.pkcs11.iaik.IaikP11SlotUtil.*;
import static org.xipki.util.Args.notNull;
import static org.xipki.util.Args.positive;
import static org.xipki.util.CollectionUtil.isEmpty;
import static org.xipki.util.CollectionUtil.isNotEmpty;

/**
 * {@link P11Slot} based on the IAIK PKCS#11 wrapper.
 *
 * @author Lijun Liao
 * @since 2.0.0
 */
class IaikP11Slot extends P11Slot {

  public static final AlgorithmIdentifier ALGID_RSA = new AlgorithmIdentifier(
      PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);

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

  private static final long DEFAULT_MAX_COUNT_SESSION = 32;

  private final int maxMessageSize;

  private Slot slot;

  private final long userType;

  private List password;

  private final int maxSessionCount;

  private final long timeOutWaitNewSession = 10000; // maximal wait for 10 second

  private final AtomicLong countSessions = new AtomicLong(0);

  private final SecureRandom random = new SecureRandom();

  private final P11NewObjectConf newObjectConf;

  private final ConcurrentBag> sessions = new ConcurrentBag<>();

  private String libDesc;

  private boolean omitDateAttrsInCertObject;

  IaikP11Slot(String moduleName, P11SlotIdentifier slotId, Slot slot, boolean readOnly, long userType,
              List password, int maxMessageSize, P11MechanismFilter mechanismFilter,
              P11NewObjectConf newObjectConf, Integer numSessions, List secretKeyTypes, List keyPairTypes)
      throws P11TokenException {
    super(moduleName, slotId, readOnly, mechanismFilter, numSessions, secretKeyTypes, keyPairTypes);

    this.newObjectConf = notNull(newObjectConf, "newObjectConf");
    this.slot = notNull(slot, "slot");
    this.maxMessageSize = positive(maxMessageSize, "maxMessageSize");

    this.userType = userType;
    this.password = password;

    boolean successful = false;

    try {
      Info moduleInfo = slot.getModule().getInfo();
      libDesc = moduleInfo.getLibraryDescription();
      if (libDesc == null) {
        libDesc = "";
      }
    } catch (TokenException ex) {
      LogUtil.error(LOG, ex, "Module.getInfo()");
      throw new P11TokenException("could not get Module Info: " + ex.getMessage(), ex);
    }

    try {
      Session session;
      try {
        // SO (Security Officer) cannot login in READ-ONLY session
        session = openSession();
      } catch (P11TokenException ex) {
        LogUtil.error(LOG, ex, "openSession");
        throw ex;
      }

      try {
        firstLogin(session, password);
      } catch (P11TokenException ex) {
        LogUtil.error(LOG, ex, "firstLogin");
        throw ex;
      }

      Token token;
      try {
        token = this.slot.getToken();
      } catch (TokenException ex) {
        throw new P11TokenException("could not getToken: " + ex.getMessage(), ex);
      }

      long maxSessionCount2;
      try {
        maxSessionCount2 = token.getTokenInfo().getMaxSessionCount();
      } catch (TokenException ex) {
        throw new P11TokenException("could not get tokenInfo: " + ex.getMessage(), ex);
      }

      if (maxSessionCount2 <= 0) {
        maxSessionCount2 = DEFAULT_MAX_COUNT_SESSION;
      } else {
        // 2 sessions as buffer, they may be used elsewhere.
        maxSessionCount2 = (maxSessionCount2 < 3) ? 1 : maxSessionCount2 - 2;
      }

      if (numSessions != null) {
        maxSessionCount2 = Math.min(numSessions, maxSessionCount2);
      }

      this.maxSessionCount = (int) maxSessionCount2;
      LOG.info("maxSessionCount: {}", this.maxSessionCount);

      sessions.add(new ConcurrentBagEntry<>(session));
      refresh();
      successful = true;
    } finally {
      if (!successful) {
        close();
      }
    }
  } // constructor

  Slot getSlot() {
    return slot;
  }

  @Override
  protected P11SlotRefreshResult refresh0() throws P11TokenException {
    Mechanism[] mechanisms;
    try {
      mechanisms = slot.getToken().getMechanismList();
    } catch (TokenException ex) {
      throw new P11TokenException("could not getMechanismList: " + ex.getMessage(), ex);
    }

    P11SlotRefreshResult ret = new P11SlotRefreshResult();

    if (mechanisms != null) {
      StringBuilder ignoreMechs = new StringBuilder();
      boolean smartcard = libDesc.toLowerCase().contains("smartcard");
      for (Mechanism mech : mechanisms) {
        long code = mech.getMechanismCode();
        if (smartcard) {
          if (code == CKM_ECDSA_SHA1     || code == CKM_ECDSA_SHA224   || code == CKM_ECDSA_SHA256 ||
              code == CKM_ECDSA_SHA384   || code == CKM_ECDSA_SHA512   || code == CKM_ECDSA_SHA3_224 ||
              code == CKM_ECDSA_SHA3_256 || code == CKM_ECDSA_SHA3_384 || code == CKM_ECDSA_SHA3_512) {
            ignoreMechs.append(Functions.getMechanismDescription(code)).append(", ");
          } else {
            ret.addMechanism(code);
          }
        } else {
          ret.addMechanism(code);
        }
      }

      if (ignoreMechs.length() > 0) {
        LOG.info("Ignore mechanisms in smartcard-based HSM: {}", ignoreMechs.substring(0, ignoreMechs.length() - 2));
      }
    }

    ConcurrentBagEntry bagEntry = borrowSession();

    try {
      Session session = bagEntry.value();
      // secret keys
      List secretKeys;
      if (secretKeyTypes == null) {
        secretKeys = getObjects(session, new SecretKey());
      } else if (secretKeyTypes.isEmpty()) {
        secretKeys = Collections.emptyList();
      } else {
        secretKeys = new LinkedList<>();
        for (Long keyType : secretKeyTypes) {
          secretKeys.addAll(getObjects(session, new ValuedSecretKey(keyType)));
        }
      }

      LOG.info("found {} secret keys", secretKeys.size());
      int count = 0;
      for (Storage secretKey : secretKeys) {
        if (secretKey instanceof SecretKey) {
          if (analyseSingleSecretKey((SecretKey) secretKey, ret)) {
            count++;
          }
        }
      }
      LOG.info("accepted {} secret keys", count);

      // first get the list of all CA certificates
      List p11Certs = getAllCertificateObjects(session);
      LOG.info("found {} X.509 certificates", p11Certs.size());

      count = 0;
      for (X509PublicKeyCertificate p11Cert : p11Certs) {
        byte[] id = value(p11Cert.getId());
        String label = valueStr(p11Cert.getLabel());
        if (id == null || id.length == 0) {
          LOG.warn("ignored X.509 certificate with ID: null and label: " + label);
        } else {
          ret.addCertificate(new P11ObjectIdentifier(id, label), parseCert(p11Cert));
          count++;
        }
      }

      LOG.info("accepted {} X.509 certificates", count);

      List privKeys;
      if (keyPairTypes == null) {
        PrivateKey template = new PrivateKey();
        privKeys = getObjects(session, template);
      } else if (keyPairTypes.isEmpty()) {
        privKeys = Collections.emptyList();
      } else {
        privKeys = new LinkedList<>();
        for (long keyType : keyPairTypes) {
          PrivateKey template;

          if (keyType == KeyType.RSA) {
            template = new RSAPrivateKey();
          } else if (keyType == KeyType.DSA) {
            template = new DSAPrivateKey();
          } else if (keyType == KeyType.EC) {
            template = new ECPrivateKey();
          } else if (keyType == KeyType.VENDOR_SM2) {
            template = ECPrivateKey.newSM2PrivateKey(slot.getModule());
          } else if (keyType == KeyType.EC_EDWARDS) {
            template = new ECPrivateKey(KeyType.EC_EDWARDS);
          } else if (keyType == KeyType.EC_MONTGOMERY) {
            template = new ECPrivateKey(KeyType.EC_MONTGOMERY);
          } else {
            LOG.error("unknown KeyPair keyType " + keyType);
            continue;
          }

          privKeys.addAll(getObjects(session, template));
        }
      }

      LOG.info("found {} private keys", privKeys.size());
      count = 0;
      for (Storage privKey : privKeys) {
        if (privKey instanceof PrivateKey) {
          if (analyseSinglePrivateKey(session, (PrivateKey) privKey, ret)) {
            count++;
          }
        }
      }
      LOG.info("accepted {} private keys", count);

      return ret;
    } finally {
      sessions.requite(bagEntry);
    }
  } // method refresh0

  @Override
  public final void close() {
    if (slot != null) {
      try {
        LOG.info("close all sessions on token: {}", slot.getSlotID());

        for (ConcurrentBagEntry session : sessions.values()) {
          session.value().closeSession();
        }
      } catch (Throwable th) {
        LogUtil.error(LOG, th, "could not slot.getToken().closeAllSessions()");
      }

      slot = null;
    }

    // clear the session pool
    sessions.close();
    countSessions.lazySet(0);
  } // method close

  private boolean analyseSingleSecretKey(SecretKey secretKey, P11SlotRefreshResult refreshResult) {
    byte[] id = value(secretKey.getId());
    String label = valueStr(secretKey.getLabel());

    if (id == null || id.length == 0) {
      LOG.warn("ignored secret key with ID: null and label: " + label);
      return false;
    }

    IaikP11Identity identity = new IaikP11Identity(this,
        new P11IdentityId(slotId, new P11ObjectIdentifier(id, label)), secretKey);
    refreshResult.addIdentity(identity);
    return true;
  } // method analyseSingleKey

  private boolean analyseSinglePrivateKey(Session session, PrivateKey privKey, P11SlotRefreshResult refreshResult) {
    byte[] id = value(privKey.getId());
    String label = valueStr(privKey.getLabel());

    if (id == null || id.length == 0) {
      LOG.warn("ignored private key with ID: null and label: " + label);
      return false;
    }

    try {
      String pubKeyLabel = null;
      PublicKey p11PublicKey = (PublicKey) getKeyObjectForId(session, new PublicKey(), id);
      if (p11PublicKey != null) {
        pubKeyLabel = valueStr(p11PublicKey.getLabel());
      }

      String certLabel = null;
      java.security.PublicKey pubKey = null;
      X509Cert cert = refreshResult.getCertForId(id);

      if (cert != null) {
        certLabel = refreshResult.getCertLabelForId(id);
        pubKey = cert.getPublicKey();
      } else if (p11PublicKey != null) {
        pubKey = generatePublicKey(p11PublicKey);
      } else {
        if (privKey instanceof RSAPrivateKey) {
          RSAPrivateKey p11RsaSk = (RSAPrivateKey) privKey;
          if (p11RsaSk.getPublicExponent() != null && p11RsaSk.getModulus() == null) {
            pubKey = buildRSAKey(new BigInteger(1, value(p11RsaSk.getModulus())),
                new BigInteger(1, value(p11RsaSk.getPublicExponent())));
          }
        }

        if (pubKey == null) {
          LOG.info("neither certificate nor public key for the key (" + hex(id) + " is available");
          return false;
        }
      }

      X509Cert[] certs = (cert == null) ? null : new X509Cert[]{cert};
      P11IdentityId p11Id = new P11IdentityId(slotId, new P11ObjectIdentifier(id, label),
          pubKey != null, pubKeyLabel, cert != null, certLabel);
      refreshResult.addIdentity(new IaikP11Identity(this, p11Id, privKey, pubKey, certs));
      return true;
    } catch (XiSecurityException ex) {
      LogUtil.error(LOG, ex, "XiSecurityException while initializing private key with id " + hex(id));
    } catch (Throwable th) {
      LOG.error("unexpected exception while initializing private key with id "
          + hex(id) + " and label " + label, th);
    }

    return false;
  } // method analyseSingleKey

  byte[] digestSecretKey(long mech, IaikP11Identity identity) throws P11TokenException {
    Key key = notNull(identity, "identity").getSigningKey();
    assertMechanismSupported(mech);
    if (!(key instanceof SecretKey)) {
      throw new P11TokenException("digestSecretKey could not be applied to non-SecretKey");
    }

    if (LOG.isTraceEnabled()) {
      LOG.debug("digest (init, digestKey, then finish)\n{}", key);
    }

    int digestLen;
    if (CKM_SHA_1 == mech) {
      digestLen = 20;
    } else if (CKM_SHA224 == mech || CKM_SHA3_224 == mech) {
      digestLen = 28;
    } else if (CKM_SHA256 == mech || CKM_SHA3_256 == mech) {
      digestLen = 32;
    } else if (CKM_SHA384 == mech || CKM_SHA3_384 == mech) {
      digestLen = 48;
    } else if (CKM_SHA512 == mech || CKM_SHA3_512 == mech) {
      digestLen = 64;
    } else {
      throw new P11TokenException("unsupported mechnism " + mech);
    }

    ConcurrentBagEntry session0 = borrowSession();
    Mechanism mechanismObj = Mechanism.get(mech);

    try {
      Session session = session0.value();
      try {
        return IaikP11SlotUtil.digestKey(session, digestLen, mechanismObj, (SecretKey) key);
      } catch (PKCS11Exception ex) {
        if (ex.getErrorCode() != CKR_USER_NOT_LOGGED_IN) {
          throw new P11TokenException(ex.getMessage(), ex);
        }

        LOG.info("digestKey ended with ERROR CKR_USER_NOT_LOGGED_IN, login and then retry it");
        // force the login
        forceLogin(session);
        try {
          return IaikP11SlotUtil.digestKey(session, digestLen, mechanismObj, (SecretKey) key);
        } catch (TokenException ex2) {
          throw new P11TokenException(ex2.getMessage(), ex2);
        }
      } catch (TokenException ex) {
        throw new P11TokenException(ex.getMessage(), ex);
      }
    } finally {
      sessions.requite(session0);
    }
  } // method digestKey

  byte[] sign(long mech, P11Params parameters, byte[] content, IaikP11Identity identity)
      throws P11TokenException {
    notNull(content, "content");
    assertMechanismSupported(mech);

    int expectedSignatureLen;
    if (mech == CKM_SHA_1_HMAC) {
      expectedSignatureLen = 20;
    } else if (mech == CKM_SHA224_HMAC || mech == CKM_SHA3_224) {
      expectedSignatureLen = 28;
    } else if (mech == CKM_SHA256_HMAC || mech == CKM_SHA3_256) {
      expectedSignatureLen = 32;
    } else if (mech == CKM_SHA384_HMAC || mech == CKM_SHA3_384) {
      expectedSignatureLen = 48;
    } else if (mech == CKM_SHA512_HMAC || mech == CKM_SHA3_512) {
      expectedSignatureLen = 64;
    } else {
      expectedSignatureLen = identity.getExpectedSignatureLen();
    }

    Mechanism mechanismObj = getMechanism(mech, parameters);
    Key signingKey = identity.getSigningKey();

    ConcurrentBagEntry session0 = borrowSession();
    try {
      Session session = session0.value();
      try {
        return sign0(session, expectedSignatureLen, mechanismObj, content, signingKey);
      } catch (PKCS11Exception ex) {
        if (ex.getErrorCode() == CKR_USER_NOT_LOGGED_IN) {
          LOG.info("sign ended with ERROR CKR_USER_NOT_LOGGED_IN, login and then retry it");
          // force the login
          forceLogin(session);
          return sign0(session, expectedSignatureLen, mechanismObj, content, signingKey);
        } else {
          throw ex;
        }
      } finally {
        sessions.requite(session0);
      }
    } catch (TokenException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }
  } // method sign

  private byte[] sign0(Session session, int expectedSignatureLen, Mechanism mechanism, byte[] content, Key signingKey)
      throws TokenException {
    long keytype = signingKey.getKeyType().getLongValue();
    boolean weierstrausKey = KeyType.EC == keytype || KeyType.VENDOR_SM2 == keytype;

    int len = content.length;

    byte[] sigvalue;
    if (len <= maxMessageSize) {
      sigvalue = singleSign(session, mechanism, content, signingKey);
    } else {
      LOG.debug("sign (init, update, then finish)");
      session.signInit(mechanism, signingKey);

      for (int i = 0; i < len; i += maxMessageSize) {
        int blockLen = Math.min(maxMessageSize, len - i);
        session.signUpdate(content, i, blockLen);
      }

      // Some HSM vendor return not the EC plain signature (r || s), but the X.962 encoded one.
      // So we need to increase the expectedSignatureLen
      int maxSignatureLen = weierstrausKey ? expectedSignatureLen + 20 : expectedSignatureLen;
      sigvalue = session.signFinal(maxSignatureLen);
    }

    if (sigvalue.length > expectedSignatureLen) {
      if (sigvalue[0] == 0x30) {
        try {
          sigvalue = SignerUtil.dsaSigX962ToPlain(sigvalue, expectedSignatureLen * 4);
        } catch (XiSecurityException e) {
          LOG.error(String.format("ERROR: sigvalue (%d): %s", sigvalue.length, Hex.toHexString(sigvalue)), e);
          throw new TokenException(e);
        } catch (RuntimeException e) {
          LOG.error(String.format("ERROR: sigvalue (%d): %s", sigvalue.length, Hex.toHexString(sigvalue)), e);
          throw e;
        }
      }
    }

    return sigvalue;
  } // method sign0

  private byte[] singleSign(Session session, Mechanism mechanism, byte[] content, Key signingKey)
      throws TokenException {
    LOG.debug("single sign");
    session.signInit(mechanism, signingKey);
    return session.sign(content);
  } // method singleSign

  private Session openSession() throws P11TokenException {
    Session session;
    try {
      boolean rw = !isReadOnly();
      session = slot.getToken().openSession(Token.SessionType.SERIAL_SESSION, rw, null, null);
    } catch (TokenException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }
    countSessions.incrementAndGet();
    return session;
  } // method openSession

  private ConcurrentBagEntry borrowSession() throws P11TokenException {
    for (int i = 0; i < Math.min(DEFAULT_MAX_COUNT_SESSION, maxSessionCount); i++) {
      try {
        return borrowSession0();
      } catch (P11TokenException ex) {
        Throwable cause = ex.getCause();
        if (cause instanceof PKCS11Exception) {
          long ckr = ((PKCS11Exception) cause).getErrorCode();
          if (ckr == CKR_SESSION_HANDLE_INVALID || ckr == CKR_SESSION_CLOSED) {
            break;
          }
        }
      }
    }
    throw new P11TokenException("could not borrow valid session");
  } // method borrowSession

  private ConcurrentBagEntry borrowSession0() throws P11TokenException {
    ConcurrentBagEntry session = null;
    synchronized (sessions) {
      if (countSessions.get() < maxSessionCount) {
        try {
          session = sessions.borrow(1, TimeUnit.NANOSECONDS);
        } catch (InterruptedException ex) {
        }

        if (session == null) {
          // create new session
          sessions.add(new ConcurrentBagEntry<>(openSession()));
        }
      }
    }

    if (session == null) {
      try {
        session = sessions.borrow(timeOutWaitNewSession, TimeUnit.MILLISECONDS);
      } catch (InterruptedException ex) {
      }
    }

    if (session == null) {
      throw new P11TokenException("no idle session");
    }

    login(session.value());
    return session;
  } // method borrowSession

  private void firstLogin(Session session, List password) throws P11TokenException {
    try {
      boolean isProtectedAuthenticationPath = session.getToken().getTokenInfo().isProtectedAuthenticationPath();

      if (isProtectedAuthenticationPath || isEmpty(password)) {
        LOG.info("verify on PKCS11Module with PROTECTED_AUTHENTICATION_PATH");
        singleLogin(session, userType, null);
      } else {
        LOG.info("verify on PKCS11Module with PIN");
        for (char[] singlePwd : password) {
          singleLogin(session, userType, singlePwd);
        }
        this.password = password;
      }
    } catch (PKCS11Exception ex) {
      // 0x100: user already logged in
      if (ex.getErrorCode() != 0x100) {
        throw new P11TokenException(ex.getMessage(), ex);
      }
    } catch (TokenException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }
  } // method firstLogin

  private void login(Session session) throws P11TokenException {
    boolean isSessionLoggedIn = checkSessionLoggedIn(session, userType);
    if (isSessionLoggedIn) {
      return;
    }

    boolean loginRequired;
    try {
      loginRequired = session.getToken().getTokenInfo().isLoginRequired();
    } catch (TokenException ex) {
      LogUtil.error(LOG, ex, "could not check isLoginRequired of token");
      loginRequired = true;
    }

    LOG.debug("loginRequired: {}", loginRequired);
    if (!loginRequired) {
      return;
    }

    if (isEmpty(password)) {
      singleLogin(session, userType, null);
    } else {
      for (char[] singlePwd : password) {
        singleLogin(session, userType, singlePwd);
      }
    }
  } // method login

  private void forceLogin(Session session) throws P11TokenException {
    if (isEmpty(password)) {
      LOG.info("verify on PKCS11Module with NULL PIN");
      singleLogin(session, userType, null);
    } else {
      LOG.info("verify on PKCS11Module with PIN");
      for (char[] singlePwd : password) {
        singleLogin(session, userType, singlePwd);
      }
    }
  } // method forceLogin

  private Key getKeyObjectForId(Session session, Key template, byte[] keyId)
      throws P11TokenException {
    return getKeyObject(session, template, keyId, true, null);
  }

  private Key getKeyObject(Session session, Key template, byte[] keyId, char[] keyLabel)
      throws P11TokenException {
    return getKeyObject(session, template, keyId, false, keyLabel);
  }

  private Key getKeyObject(Session session, Key template, byte[] keyId, boolean ignoreLabel, char[] keyLabel)
      throws P11TokenException {
    template.getId().setByteArrayValue(notNull(keyId, "keyId"));
    if (!ignoreLabel) {
      template.getLabel().setCharArrayValue(keyLabel);
    }

    List tmpObjects = getObjects(session, template, 2);
    if (isEmpty(tmpObjects)) {
      return null;
    }

    int size = tmpObjects.size();
    if (size > 1) {
      LOG.warn("found {} public key identified by {}, use the first one", size, getDescription(keyId, keyLabel));
    }

    return (Key) tmpObjects.get(0);
  } // method getKeyObject

  @Override
  public int removeObjectsForId(byte[] id) throws P11TokenException {
    return removeObjects(id, true, null);
  }

  @Override
  public int removeObjectsForLabel(String label) throws P11TokenException {
    return removeObjects(null, false, notNull(label, "label").toCharArray());
  }

  @Override
  public int removeObjects(byte[] id, String label) throws P11TokenException {
    return removeObjects(id, false, (label == null) ? null : label.toCharArray());
  }

  private int removeObjects(byte[] id, boolean ignoreLabel, char[] label) throws P11TokenException {
    if (ignoreLabel) {
      label = null;
    }
    boolean labelBlank = label == null || label.length == 0;
    if ((id == null || id.length == 0) && labelBlank) {
      throw new IllegalArgumentException("at least one of id and label may not be null");
    }

    Key keyTemplate = new Key();
    if (id != null && id.length > 0) {
      keyTemplate.getId().setByteArrayValue(id);
    }

    if (!ignoreLabel) {
      keyTemplate.getLabel().setCharArrayValue(label);
    }

    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();
      String objIdDesc = getDescription(id, label);
      int num = removeObjects0(session, keyTemplate, "keys " + objIdDesc);

      X509PublicKeyCertificate certTemplate = new X509PublicKeyCertificate();
      if (id != null && id.length > 0) {
        certTemplate.getId().setByteArrayValue(id);
      }
      if (!ignoreLabel) {
        certTemplate.getLabel().setCharArrayValue(label);
      }

      num += removeObjects0(session, certTemplate, "certificates " + objIdDesc);
      return num;
    } finally {
      sessions.requite(bagEntry);
    }
  } // method removeObjects

  @Override
  protected void removeCerts0(P11ObjectIdentifier objectId) throws P11TokenException {
    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();
      X509PublicKeyCertificate[] existingCerts = getCertificateObjects(session, objectId.getId(),
          objectId.getLabelChars());
      if (existingCerts == null || existingCerts.length == 0) {
        LOG.warn("could not find certificates " + objectId);
        return;
      }

      for (X509PublicKeyCertificate cert : existingCerts) {
        session.destroyObject(cert);
      }
    } catch (TokenException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    } finally {
      sessions.requite(bagEntry);
    }
  } // method removeCerts0

  @Override
  protected P11ObjectIdentifier addCert0(X509Cert cert, P11NewObjectControl control)
      throws P11TokenException {
    ConcurrentBagEntry bagEntry = borrowSession();

    try {
      Session session = bagEntry.value();
      // get a local copy
      boolean omit = omitDateAttrsInCertObject;
      X509PublicKeyCertificate newCertTemp = createPkcs11Template(session, cert, control, omit);
      X509PublicKeyCertificate newCert;
      try {
        newCert = (X509PublicKeyCertificate) session.createObject(newCertTemp);
      } catch (PKCS11Exception ex) {
         long errCode = ex.getErrorCode();
         if (!omit && CKR_TEMPLATE_INCONSISTENT == errCode) {
           // some HSMs like NFAST does not like the attributes CKA_START_DATE and CKA_END_DATE, try without them.
           newCertTemp = createPkcs11Template(session, cert, control, true);
           newCert = (X509PublicKeyCertificate) session.createObject(newCertTemp);
           omitDateAttrsInCertObject = true;
           LOG.warn("The HSM does not accept certificate object with attributes "
               + "CKA_START_DATE and CKA_END_DATE, ignore them");
         } else {
           throw ex;
         }
      }

      return new P11ObjectIdentifier(value(newCert.getId()), valueStr(newCert.getLabel()));
    } catch (TokenException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    } finally {
      sessions.requite(bagEntry);
    }
  } // method addCert0

  @Override
  protected P11Identity generateSecretKey0(long keyType, int keysize, P11NewKeyControl control)
      throws P11TokenException {
    if (keysize % 8 != 0) {
      throw new IllegalArgumentException("keysize is not multiple of 8: " + keysize);
    }

    long mech;
    if (CKK_AES == keyType) {
      mech = CKM_AES_KEY_GEN;
    } else if (CKK_DES3 == keyType) {
      mech = CKM_DES3_KEY_GEN;
    } else if (CKK_GENERIC_SECRET == keyType) {
      mech = CKM_GENERIC_SECRET_KEY_GEN;
    } else if (CKK_SHA_1_HMAC == keyType || CKK_SHA224_HMAC == keyType   || CKK_SHA256_HMAC == keyType
        || CKK_SHA384_HMAC == keyType    || CKK_SHA512_HMAC == keyType   || CKK_SHA3_224_HMAC == keyType
        || CKK_SHA3_256_HMAC == keyType  || CKK_SHA3_384_HMAC == keyType || CKK_SHA3_512_HMAC == keyType) {
      mech = CKM_GENERIC_SECRET_KEY_GEN;
    } else {
      throw new IllegalArgumentException("unsupported key type 0x" + Functions.toFullHex((int)keyType));
    }

    assertMechanismSupported(mech);

    char[] labelChars;
    if (newObjectConf.isIgnoreLabel()) {
      if (control.getLabel() != null) {
        LOG.warn("label is set, but ignored: '{}'", control.getLabel());
      }
      labelChars = null;
    } else {
      labelChars = control.getLabel().toCharArray();
    }

    byte[] id = control.getId();

    ValuedSecretKey template = new ValuedSecretKey(keyType);
    IaikP11SlotUtil.setKeyAttributes(control, template, labelChars);
    template.getValueLen().setLongValue((long) (keysize / 8));

    Mechanism mechanism = Mechanism.get(mech);
    SecretKey key;
    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();
      if (labelChars != null && labelExists(session, labelChars)) {
        throw new IllegalArgumentException("label " + control.getLabel() + " exists, please specify another one");
      }

      if (id == null) {
        id = generateId(session);
      }

      template.getId().setByteArrayValue(id);

      try {
        key = (SecretKey) session.generateKey(mechanism, template);
      } catch (TokenException ex) {
        throw new P11TokenException("could not generate generic secret key using " + mechanism.getName(), ex);
      }

      P11ObjectIdentifier objId = new P11ObjectIdentifier(id, valueStr(key.getLabel()));
      P11IdentityId entityId = new P11IdentityId(slotId, objId);

      return new IaikP11Identity(this, entityId, key);
    } finally {
      sessions.requite(bagEntry);
    }
  } // method generateSecretKey0

  @Override
  protected P11Identity importSecretKey0(long keyType, byte[] keyValue, P11NewKeyControl control)
      throws P11TokenException {
    ValuedSecretKey template = new ValuedSecretKey(keyType);
    char[] labelChars;
    if (newObjectConf.isIgnoreLabel()) {
      if (control.getLabel() != null) {
        LOG.warn("label is set, but ignored: '{}'", control.getLabel());
      }
      labelChars = null;
    } else {
      labelChars = control.getLabel().toCharArray();
    }

    IaikP11SlotUtil.setKeyAttributes(control, template, labelChars);
    template.getValue().setByteArrayValue(keyValue);

    SecretKey key;
    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();
      if (labelChars != null && labelExists(session, labelChars)) {
        throw new IllegalArgumentException("label " + control.getLabel() + " exists, please specify another one");
      }

      byte[] id = control.getId();
      if (id == null) {
        id = generateId(session);
      }

      template.getId().setByteArrayValue(id);

      try {
        key = (SecretKey) session.createObject(template);
      } catch (TokenException ex) {
        throw new P11TokenException("could not create secret key", ex);
      }

      P11ObjectIdentifier objId = new P11ObjectIdentifier(id, valueStr(key.getLabel()));
      P11IdentityId entityId = new P11IdentityId(slotId, objId);

      return new IaikP11Identity(this, entityId, key);
    } finally {
      sessions.requite(bagEntry);
    }
  } // method importSecretKey0

  @Override
  protected P11Identity generateRSAKeypair0(int keysize, BigInteger publicExponent, P11NewKeyControl control)
      throws P11TokenException {
    RSAPrivateKey privateKey = new RSAPrivateKey();
    RSAPublicKey publicKey = new RSAPublicKey();
    setKeyAttributes(control, publicKey, privateKey, newObjectConf);

    publicKey.getModulusBits().setLongValue((long) keysize);
    publicKey.getPublicExponent().setByteArrayValue(publicExponent.toByteArray());

    return generateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, control.getId(), privateKey, publicKey);
  } // method generateRSAKeypair0

  @Override
  protected PrivateKeyInfo generateRSAKeypairOtf0(int keysize, BigInteger publicExponent)
      throws P11TokenException {
    RSAPublicKey publicKeyTemplate = new RSAPublicKey();
    publicKeyTemplate.getModulusBits().setLongValue((long) keysize);
    publicKeyTemplate.getPublicExponent().setByteArrayValue(publicExponent.toByteArray());

    RSAPrivateKey privateKeyTemplate = new RSAPrivateKey();
    setPrivateKeyAttrsOtf(privateKeyTemplate);

    long mech = CKM_RSA_PKCS_KEY_PAIR_GEN;
    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();

      KeyPair keypair = null;
      try {
        keypair = session.generateKeyPair(Mechanism.get(mech), publicKeyTemplate, privateKeyTemplate);
        RSAPrivateKey sk = (RSAPrivateKey) keypair.getPrivateKey();

        return new PrivateKeyInfo(ALGID_RSA,
            new org.bouncycastle.asn1.pkcs.RSAPrivateKey(
                toBigInt(sk.getModulus()),   toBigInt(sk.getPublicExponent()), toBigInt(sk.getPrivateExponent()),
                toBigInt(sk.getPrime1()),    toBigInt(sk.getPrime2()),         toBigInt(sk.getExponent1()),
                toBigInt(sk.getExponent2()), toBigInt(sk.getCoefficient())));

      } catch (TokenException | IOException ex) {
        throw new P11TokenException("could not generate keypair " + Functions.mechanismCodeToString(mech), ex);
      } finally {
        if (keypair != null) {
          destroyObject(session, keypair.getPrivateKey());
          destroyObject(session, keypair.getPublicKey());
        }
      }
    } finally {
      sessions.requite(bagEntry);
    }
  } // method generateRSAKeypairOtf0

  @Override
  protected P11Identity generateDSAKeypair0(BigInteger p, BigInteger q, BigInteger g, P11NewKeyControl control)
      throws P11TokenException {
    DSAPrivateKey privateKey = new DSAPrivateKey();
    DSAPublicKey publicKey = new DSAPublicKey();
    setKeyAttributes(control, publicKey, privateKey, newObjectConf);

    publicKey.getPrime().setByteArrayValue(Util.unsignedBigIntergerToByteArray(p));
    publicKey.getSubprime().setByteArrayValue(Util.unsignedBigIntergerToByteArray(q));
    publicKey.getBase().setByteArrayValue(Util.unsignedBigIntergerToByteArray(g));

    return generateKeyPair(CKM_DSA_KEY_PAIR_GEN, control.getId(), privateKey, publicKey);
  } // method generateDSAKeypair0

  @Override
  protected PrivateKeyInfo generateDSAKeypairOtf0(BigInteger p, BigInteger q, BigInteger g)
      throws P11TokenException {
    DSAPublicKey publicKeyTemplate = new DSAPublicKey();
    publicKeyTemplate.getPrime().setByteArrayValue(Util.unsignedBigIntergerToByteArray(p));
    publicKeyTemplate.getSubprime().setByteArrayValue(Util.unsignedBigIntergerToByteArray(q));
    publicKeyTemplate.getBase().setByteArrayValue(Util.unsignedBigIntergerToByteArray(g));

    DSAPrivateKey privateKeyTemplate = new DSAPrivateKey();
    setPrivateKeyAttrsOtf(privateKeyTemplate);

    long mech = CKM_DSA_KEY_PAIR_GEN;
    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();

      KeyPair keypair = null;
      try {
        DSAParameter parameter = new DSAParameter(p, q, g);
        AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, parameter);

        keypair = session.generateKeyPair(Mechanism.get(mech),
            publicKeyTemplate, privateKeyTemplate);
        DSAPrivateKey sk = (DSAPrivateKey) keypair.getPrivateKey();
        DSAPublicKey pk = (DSAPublicKey) keypair.getPublicKey();

        BigInteger value = toBigInt(pk.getValue()); // y
        byte[] publicKey = new ASN1Integer(value).getEncoded();

        return new PrivateKeyInfo(algId, new ASN1Integer(toBigInt(sk.getValue())), null, publicKey);
      } catch (TokenException | IOException ex) {
        throw new P11TokenException("could not generate keypair " + Functions.mechanismCodeToString(mech), ex);
      } finally {
        if (keypair != null) {
          destroyObject(session, keypair.getPrivateKey());
          destroyObject(session, keypair.getPublicKey());
        }
      }
    } finally {
      sessions.requite(bagEntry);
    }
  } // method generateDSAKeypairOtf0

  @Override
  protected P11Identity generateECEdwardsKeypair0(ASN1ObjectIdentifier curveId, P11NewKeyControl control)
      throws P11TokenException {
    ECPrivateKey privateKey = new ECPrivateKey(KeyType.EC_EDWARDS);
    ECPublicKey publicKey = new ECPublicKey(KeyType.EC_EDWARDS);
    setKeyAttributes(control, publicKey, privateKey, newObjectConf);
    byte[] encodedCurveId;
    try {
      encodedCurveId = curveId.getEncoded();
    } catch (IOException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }
    publicKey.getEcdsaParams().setByteArrayValue(encodedCurveId);
    return generateKeyPair(CKM_EC_EDWARDS_KEY_PAIR_GEN, control.getId(), privateKey, publicKey);
  } // method generateECEdwardsKeypair0

  @Override
  protected PrivateKeyInfo generateECEdwardsKeypairOtf0(ASN1ObjectIdentifier curveId)
      throws P11TokenException {
    return generateECKeypairOtf0(KeyType.EC_EDWARDS, CKM_EC_EDWARDS_KEY_PAIR_GEN, curveId);
  }

  @Override
  protected P11Identity generateECMontgomeryKeypair0(ASN1ObjectIdentifier curveId, P11NewKeyControl control)
      throws P11TokenException {
    ECPrivateKey privateKey = new ECPrivateKey(KeyType.EC_MONTGOMERY);
    ECPublicKey publicKey = new ECPublicKey(KeyType.EC_MONTGOMERY);
    setKeyAttributes(control, publicKey, privateKey, newObjectConf);
    try {
      publicKey.getEcdsaParams().setByteArrayValue(curveId.getEncoded());
    } catch (IOException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }

    return generateKeyPair(CKM_EC_MONTGOMERY_KEY_PAIR_GEN, control.getId(), privateKey, publicKey);
  } // method generateECMontgomeryKeypair0

  @Override
  protected PrivateKeyInfo generateECMontgomeryKeypairOtf0(ASN1ObjectIdentifier curveId)
      throws P11TokenException {
    return generateECKeypairOtf0(KeyType.EC_MONTGOMERY, CKM_EC_MONTGOMERY_KEY_PAIR_GEN, curveId);
  }

  @Override
  protected P11Identity generateECKeypair0(ASN1ObjectIdentifier curveId, P11NewKeyControl control)
      throws P11TokenException {
    ECPrivateKey privateKey = new ECPrivateKey();
    ECPublicKey publicKey = new ECPublicKey();
    setKeyAttributes(control, publicKey, privateKey, newObjectConf);
    byte[] encodedCurveId;
    try {
      encodedCurveId = curveId.getEncoded();
    } catch (IOException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }

    long mech = CKM_EC_KEY_PAIR_GEN;
    try {
      publicKey.getEcdsaParams().setByteArrayValue(encodedCurveId);
      return generateKeyPair(mech, control.getId(), privateKey, publicKey);
    } catch (P11TokenException ex) {
      X9ECParameters ecParams = ECNamedCurveTable.getByOID(curveId);
      if (ecParams == null) {
        throw new IllegalArgumentException("got no X9ECParameters for curve " + curveId.getId());
      }

      try {
        publicKey.getEcdsaParams().setByteArrayValue(ecParams.getEncoded());
      } catch (IOException ex2) {
        throw new P11TokenException(ex2.getMessage(), ex2);
      }
      return generateKeyPair(mech, control.getId(), privateKey, publicKey);
    }
  } // method generateECKeypair0

  @Override
  protected PrivateKeyInfo generateECKeypairOtf0(ASN1ObjectIdentifier curveId) throws P11TokenException {
    return generateECKeypairOtf0(KeyType.EC, CKM_EC_KEY_PAIR_GEN, curveId);
  }

  private PrivateKeyInfo generateECKeypairOtf0(long keyType, long mech, ASN1ObjectIdentifier curveId)
      throws P11TokenException {
    ECPrivateKey privateKeyTemplate;
    ECPublicKey publicKeyTemplate;

    if (keyType == KeyType.VENDOR_SM2) {
      if (!GMObjectIdentifiers.sm2p256v1.equals(curveId)) {
        throw new P11TokenException("keyType and curveId do not match.");
      }

      privateKeyTemplate = ECPrivateKey.newSM2PrivateKey(slot.getModule());
      publicKeyTemplate  = ECPublicKey.newSM2PublicKey(slot.getModule());
    } else {
      privateKeyTemplate = new ECPrivateKey(keyType);
      publicKeyTemplate  = new ECPublicKey(keyType);
    }

    try {
      publicKeyTemplate.getEcdsaParams().setByteArrayValue(curveId.getEncoded());
    } catch (IOException ex) {
      throw new P11TokenException(ex.getMessage(), ex);
    }

    setPrivateKeyAttrsOtf(privateKeyTemplate);

    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();

      KeyPair keypair = null;
      try {
        keypair = session.generateKeyPair(Mechanism.get(mech), publicKeyTemplate, privateKeyTemplate);
        ECPrivateKey sk = (ECPrivateKey) keypair.getPrivateKey();
        ECPublicKey pk = (ECPublicKey) keypair.getPublicKey();

        if (KeyType.EC_EDWARDS == keyType || KeyType.EC_MONTGOMERY == keyType) {
          AlgorithmIdentifier algId = new AlgorithmIdentifier(curveId);
          byte[] encodedPublicPoint = ASN1OctetString.getInstance(pk.getEcPoint().getByteArrayValue()).getOctets();
          byte[] privValue = sk.getValue().getByteArrayValue();
          return new PrivateKeyInfo(algId, new DEROctetString(privValue), null, encodedPublicPoint);
        } else {
          AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, curveId);

          byte[] encodedPublicPoint =
              ASN1OctetString.getInstance(pk.getEcPoint().getByteArrayValue()).getOctets();
          if (encodedPublicPoint[0] != 4) {
            throw new P11TokenException("EcPoint does not start with 0x04");
          }

          int orderBigLen = (encodedPublicPoint.length - 1) / 2 * 8;
          return new PrivateKeyInfo(algId,
              new org.bouncycastle.asn1.sec.ECPrivateKey(orderBigLen,
                  new BigInteger(1, sk.getValue().getByteArrayValue()),
                  new DERBitString(encodedPublicPoint), null));
        }
      } catch (TokenException | IOException ex) {
        throw new P11TokenException("could not generate keypair " + Functions.mechanismCodeToString(mech), ex);
      } finally {
        if (keypair != null) {
          destroyObject(session, keypair.getPrivateKey());
          destroyObject(session, keypair.getPublicKey());
        }
      }
    } finally {
      sessions.requite(bagEntry);
    }

  }

  @Override
  protected P11Identity generateSM2Keypair0(P11NewKeyControl control) throws P11TokenException {
    long ckm = CKM_VENDOR_SM2_KEY_PAIR_GEN;
    if (supportsMechanism(ckm)) {
      ECPrivateKey privateKey = ECPrivateKey.newSM2PrivateKey(slot.getModule());
      ECPublicKey publicKey = ECPublicKey.newSM2PublicKey(slot.getModule());
      publicKey.getEcdsaParams().setByteArrayValue(Hex.decode("06082A811CCF5501822D"));
      setKeyAttributes(control, publicKey, privateKey, newObjectConf);

      return generateKeyPair(ckm, control.getId(), privateKey, publicKey);
    } else {
      return generateECKeypair0(GMObjectIdentifiers.sm2p256v1, control);
    }
  } // method generateSM2Keypair0

  @Override
  protected PrivateKeyInfo generateSM2KeypairOtf0() throws P11TokenException {
    long ckm = CKM_VENDOR_SM2_KEY_PAIR_GEN;
    if (supportsMechanism(ckm)) {
      return generateECKeypairOtf0(KeyType.VENDOR_SM2, ckm, GMObjectIdentifiers.sm2p256v1);
    } else {
      return generateECKeypairOtf0(GMObjectIdentifiers.sm2p256v1);
    }
  }

  private P11Identity generateKeyPair(long mech, byte[] id, PrivateKey privateKeyTemplate, PublicKey publicKeyTemplate)
      throws P11TokenException {
    char[] labelChars = value(privateKeyTemplate.getLabel());

    boolean succ = false;

    try {
      KeyPair keypair;
      ConcurrentBagEntry bagEntry = borrowSession();
      try {
        Session session = bagEntry.value();
        if (labelChars != null && labelExists(session, labelChars)) {
          throw new IllegalArgumentException("label " + new String(labelChars) + " exists, please specify another one");
        }

        if (id == null) {
          id = generateId(session);
        }

        privateKeyTemplate.getId().setByteArrayValue(id);
        publicKeyTemplate.getId().setByteArrayValue(id);

        try {
          keypair = session.generateKeyPair(Mechanism.get(mech), publicKeyTemplate, privateKeyTemplate);
          labelChars = value(keypair.getPrivateKey().getLabel());
        } catch (TokenException ex) {
          throw new P11TokenException("could not generate keypair " + Functions.mechanismCodeToString(mech), ex);
        }

        if (labelChars == null) {
          throw new P11TokenException("Label of the generated PrivateKey is not set");
        }

        String pubKeyLabel = valueStr(keypair.getPublicKey().getLabel());

        P11ObjectIdentifier objId = new P11ObjectIdentifier(id, new String(labelChars));
        java.security.PublicKey jcePublicKey;
        try {
          jcePublicKey = generatePublicKey(keypair.getPublicKey());
        } catch (XiSecurityException ex) {
          throw new P11TokenException("could not generate public key " + objId, ex);
        }

        PrivateKey privKey2 = (PrivateKey) getKeyObject(session, new PrivateKey(), id, labelChars);
        if (privKey2 == null) {
          throw new P11TokenException("could not read the generated private key");
        }

        // certificate: some vendors generate also certificate
        X509PublicKeyCertificate cert2 = getCertificateObject(session, id, null);
        String certLabel = null;
        X509Cert[] certs = null;
        if (cert2 != null) {
          certLabel = valueStr(cert2.getLabel());
          certs = new X509Cert[1];
          try {
            certs[0] = X509Util.parseCert(value(cert2.getValue()));
          } catch (CertificateException ex) {
            throw new P11TokenException("could not parse certifcate", ex);
          }
        }

        P11IdentityId entityId = new P11IdentityId(slotId, objId, true, pubKeyLabel, certs != null, certLabel);
        IaikP11Identity ret = new IaikP11Identity(this, entityId, privKey2, jcePublicKey, certs);
        succ = true;
        return ret;
      } finally {
        sessions.requite(bagEntry);
      }
    } finally {
      if (!succ && (id != null)) {
        try {
          removeObjects(id, false, labelChars);
        } catch (Throwable th) {
          LogUtil.error(LOG, th, "could not remove objects");
        }
      }
    }
  } // method generateKeyPair

  private X509PublicKeyCertificate createPkcs11Template(
      Session session, X509Cert cert, P11NewObjectControl control, boolean omitDateAttrs)
      throws P11TokenException {
    X509PublicKeyCertificate newCertTemp = new X509PublicKeyCertificate();
    byte[] id = control.getId();
    if (id == null) {
      id = generateId(session);
    }

    newCertTemp.getId().setByteArrayValue(id);

    if (newObjectConf.isIgnoreLabel()) {
      if (control.getLabel() != null) {
        LOG.warn("label is set, but ignored: '{}'", control.getLabel());
      }
    } else {
      newCertTemp.getLabel().setCharArrayValue(control.getLabel().toCharArray());
    }

    newCertTemp.getToken().setBooleanValue(true);
    newCertTemp.getCertificateType().setLongValue(CertificateType.X_509_PUBLIC_KEY);

    Set setCertAttributes = newObjectConf.getSetCertObjectAttributes();

    try {
      if (setCertAttributes.contains(CKA_SUBJECT)) {
        newCertTemp.getSubject().setByteArrayValue(cert.getSubject().getEncoded());
      }

      if (setCertAttributes.contains(CKA_ISSUER)) {
        newCertTemp.getIssuer().setByteArrayValue(cert.getIssuer().getEncoded());
      }
    } catch (IOException ex) {
      throw new P11TokenException("error encoding certificate: " + ex.getMessage(), ex);
    }

    if (setCertAttributes.contains(CKA_SERIAL_NUMBER)) {
      newCertTemp.getSerialNumber().setByteArrayValue(cert.getSerialNumber().toByteArray());
    }

    if (!omitDateAttrs) {
      if (setCertAttributes.contains(CKA_START_DATE)) {
        newCertTemp.getStartDate().setDateValue(cert.getNotBefore());
      }

      if (setCertAttributes.contains(CKA_END_DATE)) {
        newCertTemp.getStartDate().setDateValue(cert.getNotAfter());
      }
    }

    newCertTemp.getValue().setByteArrayValue(cert.getEncoded());
    return newCertTemp;
  } // method createPkcs11Template

  @Override
  protected void updateCertificate0(P11ObjectIdentifier keyId, X509Cert newCert)
      throws P11TokenException {
    try {
      removeCerts(keyId);
    } catch (P11UnknownEntityException ex) {
      // certificates do not exist, do nothing
    }

    try {
      Thread.sleep(1000);
    } catch (InterruptedException ex) {
    }

    addCert0(newCert, new P11NewObjectControl(keyId.getId(), keyId.getLabel()));
  } // method updateCertificate0

  @Override
  protected void removeIdentity0(P11IdentityId identityId) throws P11TokenException {
    ConcurrentBagEntry bagEntry = borrowSession();
    try {
      Session session = bagEntry.value();
      P11ObjectIdentifier keyId = identityId.getKeyId();
      byte[] id = keyId.getId();
      char[] label = keyId.getLabelChars();
      SecretKey secretKey = (SecretKey) getKeyObject(session, new SecretKey(), id, label);
      if (secretKey != null) {
        try {
          session.destroyObject(secretKey);
        } catch (TokenException ex) {
          String msg = "could not delete secret key " + keyId;
          LogUtil.error(LOG, ex, msg);
          throw new P11TokenException(msg);
        }
      }

      PrivateKey privKey = (PrivateKey) getKeyObject(session, new PrivateKey(), id, label);

      if (privKey != null) {
        try {
          session.destroyObject(privKey);
        } catch (TokenException ex) {
          String msg = "could not delete private key " + keyId;
          LogUtil.error(LOG, ex, msg);
          throw new P11TokenException(msg);
        }
      }

      P11ObjectIdentifier pubKeyId = identityId.getPublicKeyId();
      if (pubKeyId != null) {
        PublicKey pubKey =
            (PublicKey) getKeyObject(session, new PublicKey(), pubKeyId.getId(), pubKeyId.getLabelChars());
        if (pubKey != null) {
          try {
            session.destroyObject(pubKey);
          } catch (TokenException ex) {
            String msg = "could not delete public key " + pubKeyId;
            LogUtil.error(LOG, ex, msg);
            throw new P11TokenException(msg);
          }
        }
      }

      P11ObjectIdentifier certId = identityId.getCertId();
      if (certId != null) {
        X509PublicKeyCertificate[] certs = getCertificateObjects(session, certId.getId(), certId.getLabelChars());
        if (certs != null && certs.length > 0) {
          for (X509PublicKeyCertificate cert : certs) {
            try {
              session.destroyObject(cert);
            } catch (TokenException ex) {
              String msg = "could not delete certificate " + certId;
              LogUtil.error(LOG, ex, msg);
              throw new P11TokenException(msg);
            }
          }
        }
      }
    } finally {
      sessions.requite(bagEntry);
    }
  } // method removeIdentity0

  private byte[] generateId(Session session) throws P11TokenException {
    while (true) {
      byte[] keyId = new byte[newObjectConf.getIdLength()];
      random.nextBytes(keyId);
      // clear the first bit
      keyId[0] = (byte) (0x7F & keyId[0]);

      if (existsIdentityForId(keyId) || existsCertForId(keyId)) {
        continue;
      }

      Key key = new Key();
      key.getId().setByteArrayValue(keyId);
      if (!isEmpty(getObjects(session, key, 1))) {
        continue;
      }

      X509PublicKeyCertificate cert = new X509PublicKeyCertificate();
      cert.getId().setByteArrayValue(keyId);
      if (!isEmpty(getObjects(session, cert, 1))) {
        continue;
      }

      return keyId;
    }
  }

  private boolean labelExists(Session session, char[] keyLabel) throws P11TokenException {
    notNull(keyLabel, "keyLabel");

    String strLabel = new String(keyLabel);
    if (existsIdentityForLabel(strLabel) || existsCertForLabel(strLabel)) {
      return true;
    }

    Key key = new Key();
    key.getLabel().setCharArrayValue(keyLabel);
    if (!isEmpty(getObjects(session, key, 1))) {
      return true;
    }

    X509PublicKeyCertificate cert = new X509PublicKeyCertificate();
    cert.getLabel().setCharArrayValue(keyLabel);
    return !isEmpty(getObjects(session, cert, 1));
  } // method labelExists

  void setKeyAttributes(
      P11NewKeyControl control, PublicKey publicKey, PrivateKey privateKey, P11NewObjectConf newObjectConf) {
    if (privateKey != null) {
      privateKey.getToken().setBooleanValue(true);
      if (newObjectConf.isIgnoreLabel()) {
        if (control.getLabel() != null) {
          LOG.warn("label is set, but ignored: '{}'", control.getLabel());
        }
      } else {
        privateKey.getLabel().setCharArrayValue(control.getLabel().toCharArray());
      }
      privateKey.getPrivate().setBooleanValue(true);

      if (control.getExtractable() != null) {
        privateKey.getExtractable().setBooleanValue(control.getExtractable());
      }

      if (control.getSensitive() != null) {
        privateKey.getSensitive().setBooleanValue(control.getSensitive());
      }

      Set usages = control.getUsages();
      final Boolean TRUE = Boolean.TRUE;
      if (isNotEmpty(usages)) {
        for (P11KeyUsage usage : usages) {
          if (usage == P11KeyUsage.DECRYPT) {
            privateKey.getDecrypt().setBooleanValue(TRUE);
          } else if (usage == P11KeyUsage.DERIVE) {
            privateKey.getDerive().setBooleanValue(TRUE);
          } else if (usage == P11KeyUsage.SIGN) {
            privateKey.getSign().setBooleanValue(TRUE);
          } else if (usage == P11KeyUsage.SIGN_RECOVER) {
            privateKey.getSignRecover().setBooleanValue(TRUE);
          } else if (usage == P11KeyUsage.UNWRAP) {
            privateKey.getUnwrap().setBooleanValue(TRUE);
          }
        }
      } else {
        long keyType = privateKey.getKeyType().getLongValue();
        // if not set
        if (keyType == PKCS11Constants.CKK_EC || keyType == PKCS11Constants.CKK_RSA
            || keyType == PKCS11Constants.CKK_DSA) {
          privateKey.getSign().setBooleanValue(TRUE);
        }

        if (slot.getModule().getVendorCodeConverter() != null) {
          if (keyType == slot.getModule().getVendorCodeConverter().genericToVendorCKK(CKK_VENDOR_SM2)) {
            privateKey.getSign().setBooleanValue(TRUE);
          }
        }

        if (keyType == PKCS11Constants.CKK_RSA) {
          privateKey.getUnwrap().setBooleanValue(TRUE);
          privateKey.getDecrypt().setBooleanValue(TRUE);
        }
      }
    }

    if (publicKey != null) {
      publicKey.getToken().setBooleanValue(true);
      if (!newObjectConf.isIgnoreLabel()) {
        publicKey.getLabel().setCharArrayValue(control.getLabel().toCharArray());
      }
      publicKey.getVerify().setBooleanValue(true);
    }
  } // method setKeyAttributes

  private static void setPrivateKeyAttrsOtf(PrivateKey privateKeyTemplate) {
    privateKeyTemplate.getToken().setBooleanValue(false);
    privateKeyTemplate.getSensitive().setBooleanValue(false);
    privateKeyTemplate.getExtractable().setBooleanValue(true);
  }

  private static void destroyObject(Session session, PKCS11Object object) {
    try {
      session.destroyObject(object);
    } catch (TokenException ex) {
      LogUtil.warn(LOG, ex, "error destroying object");
    }
  }

  private static BigInteger toBigInt(ByteArrayAttribute attr) {
    return new BigInteger(1, attr.getByteArrayValue());
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy