org.xipki.pkcs11.wrapper.PKCS11Token Maven / Gradle / Ivy
The newest version!
// Copyright (c) 2022 xipki. All rights reserved.
// License Apache License 2.0
package org.xipki.pkcs11.wrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.PublicKey;
import java.time.Clock;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import static org.xipki.pkcs11.wrapper.PKCS11Constants.*;
/**
* This is a PKCS#11 token with session management.
*
* @author xipki
*/
public class PKCS11Token {
private enum OP {
DIGEST,
SIGN,
VERIFY,
ENCRYPT,
DECRYPT,
SIGN_RECOVER,
VERIFY_RECOVER
}
private static final Clock clock = Clock.systemUTC();
private int maxMessageSize = 2048;
private final Token token;
private final Map mechanisms = new HashMap<>();
private final long userType;
private final List pins;
private final int maxSessionCount;
private final boolean readOnly;
private long timeOutWaitNewSessionMs = 10000; // maximal wait for 10 second
private final AtomicLong countSessions = new AtomicLong(0);
private final LinkedBlockingQueue sessions = new LinkedBlockingQueue<>();
private final Object loginSync = new Object();
/**
* The simple constructor.
*
* @param token The token
* @param readOnly True if this token is read only, false if read-write.
* @param pin The PIN of user type CKU_USER. May be null.
* @throws TokenException If accessing the PKCS#11 device failed.
*/
public PKCS11Token(Token token, boolean readOnly, char[] pin) throws TokenException {
this(token, readOnly, CKU_USER, null, (pin == null ? null : Collections.singletonList(pin)), null);
}
/**
* The simple constructor.
*
* @param token The token
* @param readOnly True if this token is read only, false if read-write.
* @param pin The PIN of user type CKU_USER. May be null.
* @param numSessions Number of sessions. May be null.
* @throws TokenException If accessing the PKCS#11 device failed.
*/
public PKCS11Token(Token token, boolean readOnly, char[] pin, Integer numSessions) throws TokenException {
this(token, readOnly, CKU_USER, null, (pin == null ? null : Collections.singletonList(pin)), numSessions);
}
/**
* The advanced constructor.
*
* @param token The token
* @param readOnly True if this token is read only, false if read-write.
* @param userType The user type. In general, it is CKU_USER.
* @param userName The user name. In this version, it must be null or empty.
* @param pins The PINs. May be null and empty list.
* @param numSessions Number of sessions. May be null.
* @throws TokenException If accessing the PKCS#11 device failed.
*/
public PKCS11Token(Token token, boolean readOnly, long userType, char[] userName, List pins,
Integer numSessions) throws TokenException {
if (userName != null && userName.length != 0) {
throw new IllegalArgumentException("userName is not null or empty");
}
if (numSessions != null && numSessions < 1) {
throw new IllegalArgumentException("numSession is not valid: " + numSessions);
}
this.token = Objects.requireNonNull(token, "token shall not be null");
this.readOnly = readOnly;
this.userType = userType;
this.pins = pins;
TokenInfo tokenInfo = token.getTokenInfo();
long lc = tokenInfo.getMaxSessionCount();
int tokenMaxSessionCount = lc > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) lc;
if (numSessions == null) {
this.maxSessionCount = (tokenMaxSessionCount < 1) ? 32 : Math.min(32, tokenMaxSessionCount);
} else {
if (tokenMaxSessionCount < 1) {
this.maxSessionCount = numSessions;
} else {
this.maxSessionCount = Math.min(numSessions, tokenMaxSessionCount);
}
}
StaticLogger.info("tokenMaxSessionCount={}, maxSessionCount={}", tokenMaxSessionCount, this.maxSessionCount);
for (long mech : token.getMechanismList()) {
try {
MechanismInfo mechInfo = token.getMechanismInfo(mech);
mechanisms.put(mech, mechInfo);
} catch (Exception e) {
StaticLogger.warn("error getMechanism for {} (0x{}): {}",
token.getSlot().getModule().codeToName(Category.CKM, mech), Functions.toFullHex(mech), e.getMessage());
}
}
// login
Session session = openSession();
login(session);
sessions.add(session);
}
public PKCS11Module getModule() {
return token.getSlot().getModule();
}
public void setTimeOutWaitNewSession(int timeOutWaitNewSessionMs) {
if (timeOutWaitNewSessionMs < 1000) {
throw new IllegalArgumentException("timeOutWaitNewSessionMs is not greater than 999");
}
this.timeOutWaitNewSessionMs = timeOutWaitNewSessionMs;
StaticLogger.info("timeOutWaitNewSession = {} milli-seconds", timeOutWaitNewSessionMs);
}
/**
* Sets the maximal message size sent to the PKCS#11 device in one command.
*
* @param maxMessageSize the maximal message size in bytes.
*/
public void setMaxMessageSize(int maxMessageSize) {
if (maxMessageSize < 256) {
throw new IllegalArgumentException("maxMessageSize too small, at least 256 is required: " + maxMessageSize);
}
this.maxMessageSize = maxMessageSize % 16 * 16; // multiple of 16.
}
public Set getMechanisms() {
return Collections.unmodifiableSet(mechanisms.keySet());
}
/**
* Gets the {@link MechanismInfo} for given mechanism code.
*
* @param mechanism The mechanism code.
* @return the {@link MechanismInfo}.
*/
public MechanismInfo getMechanismInfo(long mechanism) {
return mechanisms.get(mechanism);
}
/**
* Returns whether the mechanism for given purpose is supported.
*
* @param mechanism The mechanism.
* @param flagBit The purpose. Valid values are (could be extended in the future PKCS#11 version):
* {@link PKCS11Constants#CKF_SIGN}, {@link PKCS11Constants#CKF_VERIFY},
* {@link PKCS11Constants#CKF_SIGN_RECOVER}, {@link PKCS11Constants#CKF_VERIFY_RECOVER},
* {@link PKCS11Constants#CKF_ENCRYPT}, {@link PKCS11Constants#CKF_DECRYPT},
* {@link PKCS11Constants#CKF_DERIVE}, {@link PKCS11Constants#CKF_DIGEST},
* {@link PKCS11Constants#CKF_UNWRAP}, {@link PKCS11Constants#CKF_WRAP}.
* @return whether mechanism with given flag bit is supported.
*/
public boolean supportsMechanism(long mechanism, long flagBit) {
MechanismInfo info = mechanisms.get(mechanism);
return info != null && info.hasFlagBit(flagBit);
}
/**
* Closes all sessions.
*/
public void closeAllSessions() {
if (token != null) {
try {
StaticLogger.info("close all sessions on token: {}", token.getTokenInfo());
for (Session session : sessions) {
session.closeSession();
}
} catch (Throwable th) {
StaticLogger.error("error closing sessions, {}", th.getMessage());
}
}
// clear the session pool
sessions.clear();
countSessions.lazySet(0);
}
/**
* Get the token (slot) identifier of this token.
*
* @return the slot identifier of this token.
*/
public long getTokenId() {
return token.getTokenID();
}
/**
* Get the token that created this Session object.
*
* @return The token.
*/
public Token getToken() {
return token;
}
public String getModuleInfo() throws TokenException {
return token.getSlot().getModule().getInfo().toString();
}
/**
* Returns whether this token is read-only.
*
* @return true if read-only, false if read-write.
*/
public boolean isReadOnly() {
return readOnly;
}
/**
* Login this session as CKU_SO (Security Officer).
*
* @param userName Username of user type CKU_SO. In this version, it must be null or empty.
* @param pin PIN.
* @throws TokenException If logging in the session fails.
*/
public void logInSecurityOfficer(char[] userName, char[] pin) throws TokenException {
if (userName != null && userName.length != 0) {
throw new IllegalArgumentException("userName is not null or empty");
}
Session session = borrowNoLoginSession();
try {
login(session, CKU_SO, (pin == null) ? null : Collections.singletonList(pin));
StaticLogger.info("logIn CKU_SO");
} finally {
sessions.add(session);
}
}
/**
* Logs out this session.
*
* @throws TokenException If logging out the session fails.
*/
public void logout() throws TokenException {
Session session = borrowSession();
try {
session.logout();
StaticLogger.info("logout");
} finally {
sessions.add(session);
}
}
/**
* Create a new object on the token (or in the session). The application must provide a template
* that holds enough information to create a certain object. For instance, if the application
* wants to create a new DES key object it creates a new instance of the AttributesTemplate class to
* serve as a template. The application must set all attributes of this new object which are
* required for the creation of such an object on the token. Then it passes this DESSecretKey
* object to this method to create the object on the token. Example:
* AttributesTemplate desKeyTemplate = AttributesTemplate.newSecretKey(CKK_DES3);
* // the key type is set by the DESSecretKey's constructor, so you need not do it
* desKeyTemplate.value(myDesKeyValueAs8BytesLongByteArray)
* .token(true)
* .private(true);
* .encrypt(true);
* .decrypt(true);
* ...
* long theCreatedDESKeyObjectHandle = userSession.createObject(desKeyTemplate);
*
Refer to the PKCS#11 standard to find out what attributes must be set for certain types
* of objects to create them on the token.
*
* @param template The template object that holds all values that the new object on the token should
* contain.
* @return A new PKCS#11 Object that serves holds all the
* (readable) attributes of the object on the token. In contrast to the templateObject,
* this object might have certain attributes set to token-dependent default-values.
* @throws TokenException If the creation of the new object fails. If it fails, the no new object was
* created on the token.
*/
public long createObject(AttributeVector template) throws TokenException {
Session session = borrowSession();
try {
return session.createObject(template);
} finally {
sessions.add(session);
}
}
public long createPrivateKeyObject(AttributeVector template, PublicKey publicKey) throws TokenException {
Session session = borrowSession();
try {
return session.createPrivateKeyObject(template, publicKey);
} finally {
sessions.add(session);
}
}
/**
* Create EC private key object in the PKCS#11 device.
*
* @param template Template of the EC private key.
* @param ecPoint The encoded EC-Point. May be null.
* @return object handle of the new EC private key.
* @throws TokenException if creating new object failed.
*/
public long createECPrivateKeyObject(AttributeVector template, byte[] ecPoint) throws TokenException {
Session session = borrowSession();
try {
return session.createECPrivateKeyObject(template, ecPoint);
} finally {
sessions.add(session);
}
}
/**
* Copy an existing object. The source object and a template object are given. Any value set in
* the template object will override the corresponding value from the source object, when the new
* object is created. See the PKCS#11 standard for details.
*
* @param sourceObjectHandle The source object of the copy operation.
* @param template A template object whose attribute values are used for the new object; i.e. they have
* higher priority than the attribute values from the source object. May be null; in that
* case the new object is just a one-to-one copy of the sourceObject.
* @return The new object that is created by copying the source object and setting attributes to
* the values given by the template.
* @throws TokenException If copying the object fails for some reason.
*/
public long copyObject(long sourceObjectHandle, AttributeVector template) throws TokenException {
Session session = borrowSession();
try {
return session.copyObject(sourceObjectHandle, template);
} finally {
sessions.add(session);
}
}
/**
* Gets all present attributes of the given template object and writes them to the object to update
* on the token (or in the session). Both parameters may refer to the same Java object. This is
* possible, because this method only needs the object handle of the objectToUpdate, and gets the
* attributes to set from the template. This means, an application can get the object using
* createObject of findObject, then modify attributes of this Java object and then call this
* method passing this object as both parameters. This will update the object on the token to the
* values as modified in the Java object.
*
* @param objectToUpdateHandle The attributes of this object get updated.
* @param template This method gets all present attributes of this template object and set this
* attributes at the objectToUpdate.
* @throws TokenException If updating the attributes fails. All or no attributes are updated.
*/
public void setAttributeValues(long objectToUpdateHandle, AttributeVector template) throws TokenException {
Session session = borrowSession();
try {
session.setAttributeValues(objectToUpdateHandle, template);
} finally {
sessions.add(session);
}
}
/**
* Destroy a certain object on the token (or in the session). Give the object that you want to
* destroy. This method uses only the internal object handle of the given object to identify the
* object.
*
* @param objectHandle The object handle that should be destroyed.
* @throws TokenException If the object could not be destroyed.
*/
public void destroyObject(long objectHandle) throws TokenException {
Session session = borrowSession();
try {
session.destroyObject(objectHandle);
} finally {
sessions.add(session);
}
}
/**
* Destroy a certain object on the token (or in the session). Give the object that you want to
* destroy. This method uses only the internal object handle of the given object to identify the
* object.
*
* @param objectHandles The object handles that should be destroyed.
* @return objects that have been destroyed.
* @throws TokenException If could not get a valid session.
*/
public long[] destroyObjects(long... objectHandles) throws TokenException {
List list = new ArrayList<>(objectHandles.length);
for (long handle : objectHandles) {
list.add(handle);
}
List destroyedHandles = destroyObjects(list);
long[] ret = new long[destroyedHandles.size()];
for (int i = 0; i < ret.length; i++) {
ret[i] = destroyedHandles.get(i);
}
return ret;
}
public List destroyObjects(List objectHandles) throws TokenException {
Session session = borrowSession();
try {
List destroyedHandles = new ArrayList<>(objectHandles.size());
for (long objectHandle : objectHandles) {
try {
session.destroyObject(objectHandle);
destroyedHandles.add(objectHandle);
} catch (PKCS11Exception e) {
StaticLogger.warn("error destroying object {}: {}", objectHandle, e.getMessage());
}
}
return destroyedHandles;
} finally {
sessions.add(session);
}
}
/**
* Generate a unique CKA_ID.
* @param template The search criteria for the uniqueness.
* @param idLength Length of the CKA_ID.
* @param random random to generate the random CKA_ID.
* @return the unique CKA_ID.
* @throws TokenException If executing operation fails.
*/
public byte[] generateUniqueId(AttributeVector template, int idLength, Random random) throws TokenException {
if (template != null && template.id() != null) {
throw new IllegalArgumentException("template shall not have CKA_ID");
}
if (template == null) {
template = new AttributeVector();
}
byte[] keyId = new byte[idLength];
template.id(keyId);
Session session = borrowSession();
try {
while (true) {
random.nextBytes(keyId);
if (session.findObjectsSingle(template, 1).length == 0) {
return keyId;
}
}
} finally {
sessions.add(session);
}
}
/**
* Gets the {@link PKCS11Key} identified by the given {@link PKCS11KeyId}.
* @param keyId The key identifier.
* @return {@link PKCS11Key} identified by the given {@link PKCS11KeyId}.
* @throws TokenException If executing operation fails.
*/
public PKCS11Key getKey(PKCS11KeyId keyId) throws TokenException {
if (keyId == null) {
return null;
}
Session session = borrowSession();
try {
return getKey(session, keyId);
} finally {
sessions.add(session);
}
}
/**
* Gets the {@link PKCS11Key} of a key satisfying the given criteria.
* @param criteria The criteria. At one of the CKA_ID and CKA_LABEL must be set.
* @return {@link PKCS11Key} of a key satisfying the given criteria
* @throws TokenException If executing operation fails.
*/
public PKCS11Key getKey(AttributeVector criteria) throws TokenException {
Session session = borrowSession();
try {
PKCS11KeyId keyId = getKeyId(session, criteria);
return (keyId == null) ? null : getKey(session, keyId);
} finally {
sessions.add(session);
}
}
private PKCS11Key getKey(Session session, PKCS11KeyId keyId) throws TokenException {
long objClass = keyId.getObjectCLass();
long keyType = keyId.getKeyType();
List ckaTypes = new LinkedList<>();
if (objClass == CKO_SECRET_KEY || objClass == CKO_PRIVATE_KEY) {
addCkaTypes(ckaTypes, CKA_EXTRACTABLE, CKA_NEVER_EXTRACTABLE, CKA_PRIVATE,
CKA_DECRYPT, CKA_SIGN, CKA_UNWRAP, CKA_WRAP_WITH_TRUSTED, CKA_SENSITIVE, CKA_ALWAYS_SENSITIVE);
if (objClass == CKO_SECRET_KEY) {
addCkaTypes(ckaTypes, CKA_ENCRYPT, CKA_TRUSTED, CKA_VERIFY, CKA_WRAP);
if (!(keyType == CKK_DES || keyType == CKK_DES2 || keyType == CKK_DES3)) {
ckaTypes.add(CKA_VALUE_LEN);
}
} else {
addCkaTypes(ckaTypes, CKA_ALWAYS_AUTHENTICATE, CKA_SIGN_RECOVER);
if (keyType == CKK_RSA) {
addCkaTypes(ckaTypes, CKA_MODULUS, CKA_PUBLIC_EXPONENT);
} else if (keyType == CKK_EC || keyType == CKK_EC_EDWARDS || keyType == CKK_EC_MONTGOMERY
|| keyType == CKK_VENDOR_SM2) {
ckaTypes.add(CKA_EC_PARAMS);
} else if (keyType == CKK_DSA) {
addCkaTypes(ckaTypes, CKA_PRIME, CKA_SUBPRIME, CKA_BASE);
}
}
} else { // if (objClass == CKO_PUBLIC_KEY) {
addCkaTypes(ckaTypes, CKA_ENCRYPT, CKA_TRUSTED, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP);
if (keyType == CKK_RSA) {
addCkaTypes(ckaTypes, CKA_MODULUS, CKA_PUBLIC_EXPONENT);
} else if (keyType == CKK_EC || keyType == CKK_EC_EDWARDS || keyType == CKK_EC_MONTGOMERY
|| keyType == CKK_VENDOR_SM2) {
addCkaTypes(ckaTypes, CKA_EC_PARAMS, CKA_EC_POINT);
} else if (keyType == CKK_DSA) {
addCkaTypes(ckaTypes, CKA_PRIME, CKA_SUBPRIME, CKA_BASE);
}
}
AttributeVector attrs = session.getAttrValues(keyId.getHandle(), ckaTypes);
return new PKCS11Key(keyId, attrs);
}
/**
* Gets the {@link PKCS11KeyId} of a key satisfying the given criteria.
* @param criteria The criteria. At one of the CKA_ID and CKA_LABEL must be set.
* @return {@link PKCS11KeyId} of a key satisfying the given criteria
* @throws TokenException If executing operation fails.
*/
public PKCS11KeyId getKeyId(AttributeVector criteria) throws TokenException {
Session session = borrowSession();
try {
return getKeyId(session, criteria);
} finally {
sessions.add(session);
}
}
private PKCS11KeyId getKeyId(Session session, AttributeVector criteria) throws TokenException {
byte[] id = criteria.id();
String label = criteria.label();
if ((id == null || id.length == 0) && (label == null || label.isEmpty())) {
return null;
}
Long oClass = criteria.class_();
if (oClass != null) {
// CKA_CLASS is set in criteria
if (!(CKO_PRIVATE_KEY == oClass || CKO_PUBLIC_KEY == oClass || CKO_SECRET_KEY == oClass)) {
return null;
}
long[] handles = session.findObjectsSingle(criteria, 2);
if (handles.length == 0) {
return null;
} else if (handles.length > 1) {
throw new TokenException("found more than 1 key for the criteria " + criteria);
} else {
return getKeyIdByHandle(session, handles[0]);
}
}
// CKA_CLASS is not set in criteria
oClass = CKO_PRIVATE_KEY;
long[] handles = session.findObjectsSingle(criteria.class_(oClass), 2);
if (handles.length == 0) {
oClass = CKO_SECRET_KEY;
handles = session.findObjectsSingle(criteria.class_(oClass), 2);
if (handles.length == 0) {
oClass = CKO_PUBLIC_KEY;
handles = session.findObjectsSingle(criteria.class_(oClass), 2);
}
}
if (handles.length == 0) {
return null;
} else if (handles.length > 1) {
throw new TokenException(("found more than 1 key of " + ckoCodeToName(oClass)
+ " for the criteria " + criteria.class_(null)));
} else {
return getKeyIdByHandle(session, handles[0]);
}
}
private PKCS11KeyId getKeyIdByHandle(Session session, long hKey) throws TokenException {
AttributeVector attrs = session.getAttrValues(hKey, CKA_CLASS, CKA_KEY_TYPE, CKA_ID, CKA_LABEL);
Long oClass = attrs.class_();
Long keyType = attrs.keyType();
if (oClass == null || keyType == null) {
return null;
}
byte[] id = attrs.id();
PKCS11KeyId ret = new PKCS11KeyId(hKey, oClass, keyType, id, attrs.label());
if (oClass == CKO_PRIVATE_KEY) {
// find the public key
long[] pubKeyHandles = session.findObjectsSingle(AttributeVector.newPublicKey(keyType).id(id), 2);
if (pubKeyHandles.length == 1) {
ret.setPublicKeyHandle(pubKeyHandles[0]);
} else if (pubKeyHandles.length > 1) {
StaticLogger.warn("found more than 1 public key for the private key {}, ignore them.", hKey);
}
}
return ret;
}
/**
* Finds all objects that match the template.
*
* @param template The object that serves as a template for searching. If this object is null, the find
* operation will find all objects that this session can see. Notice, that only a user
* session will see private objects.
* @return An array of found objects. The maximum size of this array is maxObjectCount, the
* minimum length is 0. Never returns null.
* @throws TokenException if finding objects failed.
*/
public long[] findAllObjects(AttributeVector template) throws TokenException {
Session session = borrowSession();
try {
return session.findAllObjectsSingle(template);
} finally {
sessions.add(session);
}
}
/**
* Finds objects that match the template.
*
* @param template The object that serves as a template for searching. If this object is null, the find
* operation will find all objects that this session can see. Notice, that only a user
* session will see private objects.
* @param maxObjectCount Specifies how many objects to return with this call.
* @return An array of found objects. The maximum size of this array is maxObjectCount, the
* minimum length is 0. Never returns null.
* @throws TokenException if finding objects failed.
*/
public long[] findObjects(AttributeVector template, int maxObjectCount) throws TokenException {
Session session = borrowSession();
try {
return session.findObjectsSingle(template, maxObjectCount);
} finally {
sessions.add(session);
}
}
/**
* Encrypts the given data with the key and mechanism.
*
* @param mechanism The mechanism to use; e.g. Mechanism.DES_CBC.
* @param keyHandle The decryption key to use.
* @param in buffer containing the to-be-encrypted data
* @param out buffer for the encrypted data
* @return the length of encrypted data
* @throws PKCS11Exception If encrypting failed.
*/
public int encrypt(Mechanism mechanism, long keyHandle, byte[] in, byte[] out) throws TokenException {
return encrypt(mechanism, keyHandle, in, 0, in.length, out, 0, out.length);
}
/**
* Encrypts the given data with the key and mechanism.
*
* @param mechanism The mechanism to use; e.g. Mechanism.DES_CBC.
* @param keyHandle The decryption key to use.
* @param in buffer containing the to-be-encrypted data
* @param inOfs buffer offset of the to-be-encrypted data
* @param inLen length of the to-be-encrypted data
* @param out buffer for the encrypted data
* @param outOfs buffer offset for the encrypted data
* @param outLen buffer size for the encrypted data
* @return the length of encrypted data
* @throws PKCS11Exception If encrypting failed.
*/
public int encrypt(Mechanism mechanism, long keyHandle, byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen) throws TokenException {
Session session = borrowSession();
try {
opInit(OP.ENCRYPT, session, mechanism, keyHandle);
if (inLen <= maxMessageSize) {
return session.encrypt(in, inOfs, inLen, out, outOfs, outLen);
} else {
int origOutOfs = outOfs;
int endInOfs = inOfs + inLen;
int endOutOfs = outOfs + outLen;
try {
for (int ofs = inOfs; ofs < endInOfs; ofs += maxMessageSize) {
int ciphertextPartLen = session.encryptUpdate(in, ofs, Math.min(maxMessageSize, endInOfs - ofs),
out, outOfs, endOutOfs - outOfs);
outOfs += ciphertextPartLen;
}
} finally {
int ciphertextPartLen = session.encryptFinal(out, outOfs, endOutOfs - outOfs);
outOfs += ciphertextPartLen;
}
return outOfs - origOutOfs;
}
} finally {
sessions.add(session);
}
}
/**
* This method can be used to encrypt large data.
*
* @param out Stream to which the cipher text is written.
* @param mechanism The mechanism to use.
* @param keyHandle The decryption key to use.
* @param plaintext Input-stream of the to-be-encrypted data
* @return length of the encrypted data.
* @throws TokenException If encrypting the data failed.
* @throws IOException if reading data from the plaintext stream failed or writing to the ciphertext stream failed.
*/
public int encrypt(OutputStream out, Mechanism mechanism, long keyHandle, InputStream plaintext)
throws TokenException, IOException {
Session session = borrowSession();
try {
byte[] outBuffer = new byte[maxMessageSize + 256];
byte[] buffer = new byte[maxMessageSize];
int read;
int inSum = 0;
int outSum = 0;
// encryptInit
opInit(OP.ENCRYPT, session, mechanism, keyHandle);
try {
while ((read = plaintext.read(buffer)) != -1) {
int estimatedResLen = maxMessageSize + 32 + (inSum - outSum);
if (outBuffer.length < estimatedResLen) {
outBuffer = new byte[estimatedResLen];
}
if (read > 0) {
inSum += read;
int resLen = session.encryptUpdate(buffer, 0, read, outBuffer, 0, outBuffer.length);
outSum += resLen;
if (resLen > 0) {
out.write(outBuffer, 0, resLen);
}
}
}
} finally {
int estimatedResLen = maxMessageSize + 32 + (inSum - outSum);
if (outBuffer.length < estimatedResLen) {
outBuffer = new byte[estimatedResLen];
}
int resLen = session.encryptFinal(outBuffer, 0, outBuffer.length);
outSum += resLen;
if (resLen > 0) {
out.write(outBuffer, 0, resLen);
}
}
return outSum;
} finally {
sessions.add(session);
}
}
/**
* Decrypts the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The decryption key to use.
* @param in buffer containing the to-be-decrypted data
* @param out buffer for the decrypted data
* @return the length of decrypted data
* @throws PKCS11Exception If decrypting failed.
*/
public int decrypt(Mechanism mechanism, long keyHandle, byte[] in, byte[] out) throws TokenException {
return decrypt(mechanism, keyHandle, in, 0, in.length, out, 0, out.length);
}
/**
* Decrypts the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The decryption key to use.
* @param in buffer containing the to-be-decrypted data
* @param inOfs buffer offset of the to-be-decrypted data
* @param inLen length of the to-be-decrypted data
* @param out buffer for the decrypted data
* @param outOfs buffer offset for the decrypted data
* @param outLen buffer size for the decrypted data
* @return the length of decrypted data
* @throws PKCS11Exception If decrypting failed.
*/
public int decrypt(Mechanism mechanism, long keyHandle, byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen) throws TokenException {
Session session = borrowSession();
try {
opInit(OP.DECRYPT, session, mechanism, keyHandle);
return session.decrypt(in, inOfs, inLen, out, outOfs, outLen);
/* BUGs in the underlying PKCS11 of JDK.
if (inLen <= maxMessageSize) {
return session.decryptSingle(mechanism, keyHandle, in, inOfs, inLen, out, outOfs, outLen);
} else {
int origOutOfs = outOfs;
int endInOfs = inOfs + inLen;
int endOutOfs = outOfs + outLen;
session.decryptInit(mechanism, keyHandle);
try {
for (int ofs = inOfs; ofs < endInOfs; ofs += maxMessageSize) {
int plaintextPartLen = session.decryptUpdate(in, inOfs, Math.min(maxMessageSize, endInOfs- ofs),
out, outOfs, endOutOfs - outOfs);
outOfs += plaintextPartLen;
}
} finally {
int plaintextPartLen = session.decryptFinal(out, outOfs, endOutOfs - outOfs);
outOfs += plaintextPartLen;
}
return outOfs - origOutOfs;
}
*/
} finally {
sessions.add(session);
}
}
/**
* This method can be used to decrypt large data.
*
* @param out Stream to which the plain text is written.
* @param mechanism The mechanism to use.
* @param keyHandle The decryption key to use.
* @param ciphertext Input-stream of the to-be-encrypted data
* @return length of the decrypted data.
* @throws TokenException If decrypting the data failed.
* @throws IOException if reading data from the ciphertext stream failed or writing to the plaintext stream failed.
*/
public int decrypt(OutputStream out, Mechanism mechanism, long keyHandle, InputStream ciphertext)
throws TokenException, IOException {
Session session = borrowSession();
try {
byte[] ciphertextBytes = readAllBytes(ciphertext);
byte[] plaintextBytes = new byte[ciphertextBytes.length];
opInit(OP.DECRYPT, session, mechanism, keyHandle);
int plaintextLen = session.decrypt(ciphertextBytes, plaintextBytes);
out.write(plaintextBytes, 0, plaintextLen);
return plaintextLen;
/* BUGs in the PKCS11 of JDK
byte[] outBuffer = new byte[maxMessageSize + 256];
byte[] buffer = new byte[maxMessageSize];
int read;
int inSum = 0;
int outSum = 0;
// decryptInit
session.decryptInit(mechanism, keyHandle);
try {
while ((read = ciphertext.read(buffer)) != -1) {
if (read > 0) {
inSum += read;
int resLen = session.decryptUpdate(buffer, 0, read, outBuffer, 0, outBuffer.length);
outSum += resLen;
if (resLen > 0) {
out.write(outBuffer, 0, resLen);
}
}
}
} finally {
int estimatedResLen = maxMessageSize + 32 + (inSum - outSum);
if (outBuffer.length < estimatedResLen) {
outBuffer = new byte[estimatedResLen];
}
int resLen = session.decryptFinal(outBuffer, 0, outBuffer.length);
outSum += resLen;
if (resLen > 0) {
out.write(outBuffer, 0, resLen);
}
}
return outSum;
*/
} finally {
sessions.add(session);
}
}
/**
* Digests the given data with the mechanism.
*
* @param mechanism The mechanism to use.
* @param in the to-be-digested data
* @return the length of digested data for this update
* @throws PKCS11Exception If digesting the data failed.
*/
public byte[] digest(Mechanism mechanism, byte[] in) throws TokenException {
return digest(mechanism, in, 0, in.length);
}
/**
* Digests the given data with the mechanism.
*
* @param mechanism The mechanism to use.
* @param in buffer containing the to-be-digested data
* @param inOfs buffer offset of the to-be-digested data
* @param inLen length of the to-be-digested data
* @return the length of digested data for this update
* @throws PKCS11Exception If digesting the data failed.
*/
public byte[] digest(Mechanism mechanism, byte[] in, int inOfs, int inLen) throws TokenException {
Session session = borrowSession();
try {
int digestLen = estimateDigestLen(mechanism);
byte[] digest = new byte[digestLen];
int outLen;
opInit(OP.DIGEST, session, mechanism, 0);
if (inLen < maxMessageSize) {
outLen = session.digest(in, inOfs, inLen, digest, 0, digestLen);
} else {
try {
int endInOfs = inOfs + inLen;
for (int ofs = inOfs; ofs < endInOfs; ofs += maxMessageSize) {
session.signUpdate(in, ofs, Math.min(maxMessageSize, endInOfs - ofs));
}
} finally {
outLen = session.digestFinal(digest, 0, digestLen);
}
}
return (outLen == digestLen) ? digest : Arrays.copyOf(digest, outLen);
} finally {
sessions.add(session);
}
}
/**
* Digests the given key with the mechanism.
*
* @param mechanism The mechanism to use; e.g. Mechanism.SHA_1.
* @param keyHandle handle of the to-be-digested key.
* @return the message digest. Never returns {@code null}.
* @throws TokenException If digesting the data failed.
*/
public byte[] digestKey(Mechanism mechanism, long keyHandle) throws TokenException {
Session session = borrowSession();
try {
opInit(OP.DIGEST, session, mechanism, 0);
byte[] digest;
try {
session.digestKey(keyHandle);
} finally {
int digestLen = estimateDigestLen(mechanism);
digest = new byte[digestLen];
int len = session.digestFinal(digest, 0, digest.length);
if (len != digestLen) {
digest = Arrays.copyOf(digest, len);
}
}
return digest;
} finally {
sessions.add(session);
}
}
/**
* Digests the large data with the mechanism.
*
* @param mechanism The mechanism to use; e.g. Mechanism.SHA_1.
* @param data the to-be-digested data
* @return the message digest. Never returns {@code null}.
* @throws TokenException If digesting the data failed.
* @throws IOException if reading data from stream failed.
*/
public byte[] digest(Mechanism mechanism, InputStream data) throws TokenException, IOException {
Session session = borrowSession();
try {
byte[] buffer = new byte[maxMessageSize];
int read;
int digestLen = estimateDigestLen(mechanism);
byte[] digest = new byte[digestLen];
opInit(OP.DIGEST, session, mechanism, 0);
int outLen;
try {
while ((read = data.read(buffer)) != -1) {
if (read > 0) {
session.digestUpdate(copyOfLen(buffer, read));
}
}
} finally {
outLen = session.digestFinal(digest, 0, digestLen);
}
return (outLen == digestLen) ? digest : Arrays.copyOf(digest, outLen);
} finally {
sessions.add(session);
}
}
/**
* Signs the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The signing key to use.
* @param in The data to sign.
* @return The signed data. Never returns {@code null}.
* @throws TokenException If signing the data failed.
*/
public byte[] sign(Mechanism mechanism, long keyHandle, byte[] in) throws TokenException {
return sign(mechanism, keyHandle, in, 0, in.length);
}
/**
* Signs the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The signing key to use.
* @param in The data to sign.
* @param inOfs buffer offset of the to-be-signed data
* @param inLen length of the to-be-signed data
* @return The signed data. Never returns {@code null}.
* @throws TokenException If signing the data failed.
*/
public byte[] sign(Mechanism mechanism, long keyHandle, byte[] in, int inOfs, int inLen) throws TokenException {
Session session = borrowSession();
try {
opInit(OP.SIGN, session, mechanism, keyHandle);
if (inLen < maxMessageSize) {
return session.sign(copyOfLen(in, inOfs, inLen));
} else {
try {
byte[] signature;
int endInOfs = inOfs + inLen;
try {
for (int ofs = inOfs; ofs < endInOfs; ofs += maxMessageSize) {
session.signUpdate(in, ofs, Math.min(maxMessageSize, endInOfs - ofs));
}
} finally {
signature = session.signFinal();
}
return signature;
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
opInit(OP.SIGN, session, mechanism, keyHandle);
return session.sign(copyOfLen(in, inOfs, inLen));
} else {
throw e;
}
}
}
} finally {
sessions.add(session);
}
}
/**
* This method can be used to sign large data.
*
* @param mechanism The mechanism to use.
* @param keyHandle The signing key to use.
* @param data Input-stream of the to-be-signed data
* @return length of the signature.
* @throws TokenException If signing the data failed.
* @throws IOException If reading data stream failed.
*/
public byte[] sign(Mechanism mechanism, long keyHandle, InputStream data)
throws TokenException, IOException {
Session session = borrowSession();
try {
byte[] buffer = new byte[maxMessageSize];
int firstBlockLen = readBytes(data, buffer, maxMessageSize);
byte[] firstBlock = copyOfLen(buffer, firstBlockLen);
opInit(OP.SIGN, session, mechanism, keyHandle);
if (firstBlockLen < maxMessageSize) {
return session.sign(firstBlock);
} else {
int read;
try {
session.signUpdate(firstBlock);
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(maxMessageSize + data.available());
bout.write(firstBlock);
while ((read = data.read(buffer)) != -1) {
bout.write(buffer, 0, read);
}
opInit(OP.SIGN, session, mechanism, keyHandle);
return session.sign(bout.toByteArray());
}
}
byte[] signature;
try {
while ((read = data.read(buffer)) != -1) {
if (read > 0) {
session.signUpdate(copyOfLen(buffer, read));
}
}
} finally {
signature = session.signFinal();
}
return signature;
}
} finally {
sessions.add(session);
}
}
/**
* Sign-recovers the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The signing key to use.
* @param in buffer containing the to-be-signed data
* @param out buffer for the signed data
* @return the length of signed data
* @throws PKCS11Exception If signing the data failed.
*/
public int signRecover(Mechanism mechanism, long keyHandle, byte[] in, byte[] out) throws TokenException {
return signRecover(mechanism, keyHandle, in, 0, in.length, out, 0, out.length);
}
/**
* Sign-recovers the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The signing key to use.
* @param in buffer containing the to-be-signed data
* @param inOfs buffer offset of the to-be-signed data
* @param inLen length of the to-be-signed data
* @param out buffer for the signed data
* @param outOfs buffer offset for the signed data
* @param outLen buffer size for the signed data
* @return the length of signed data
* @throws PKCS11Exception If signing the data failed.
*/
public int signRecover(Mechanism mechanism, long keyHandle, byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen) throws TokenException {
Session session = borrowSession();
try {
opInit(OP.SIGN_RECOVER, session, mechanism, keyHandle);
return session.signRecover(in, inOfs, inLen, out, outOfs, outLen);
} finally {
sessions.add(session);
}
}
/**
* Verifies the given signature against the given data with the key and mechanism.
* This method throws an exception, if the verification of the signature fails.
*
* @param mechanism The mechanism to use; e.g. Mechanism.RSA_PKCS.
* @param keyHandle The verification key to use.
* @param data The data that was signed.
* @param signature The signature or MAC to verify.
* @return true if signature is invalid, false otherwise.
* @throws TokenException If verifying the signature fails.
*/
public boolean verify(Mechanism mechanism, long keyHandle, byte[] data, byte[] signature) throws TokenException {
Session session = borrowSession();
int len = data.length;
long code = mechanism.getMechanismCode();
try {
if (supportsMechanism(code, CKF_VERIFY)) {
try {
opInit(OP.VERIFY, session, mechanism, keyHandle);
if (len <= maxMessageSize) {
session.verify(data, signature);
} else {
try {
try {
for (int ofs = 0; ofs < len; ofs += maxMessageSize) {
session.verifyUpdate(copyOfLen(data, ofs, Math.min(maxMessageSize, len - ofs)));
}
} finally {
session.verifyFinal(signature);
}
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
opInit(OP.VERIFY, session, mechanism, keyHandle);
session.verify(data, signature);
} else {
throw e;
}
}
}
return true;
} catch (PKCS11Exception e) {
long ckr = e.getErrorCode();
if (ckr == CKR_SIGNATURE_INVALID || ckr == CKR_SIGNATURE_LEN_RANGE) {
return false;
} else {
throw e;
}
}
} else if (supportsMechanism(code, CKF_SIGN) && isMacMechanism(code)) {
// CKF_VERIFY is not supported, use CKF_SIGN to verify the MAC tags.
opInit(OP.SIGN, session, mechanism, keyHandle);
byte[] sig2;
if (len <= maxMessageSize) {
sig2 = session.sign(data);
} else {
try {
for (int ofs = 0; ofs < len; ofs += maxMessageSize) {
session.signUpdate(copyOfLen(data, ofs, Math.min(maxMessageSize, len - ofs)));
}
} finally {
sig2 = session.signFinal();
}
}
return Arrays.equals(signature, sig2);
} else {
throw new PKCS11Exception(CKR_MECHANISM_INVALID);
}
} finally {
sessions.add(session);
}
}
/**
* This method can be used to verify large data.
*
* @param mechanism The mechanism to use.
* @param keyHandle The signing key to use.
* @param data Input-stream of the to-be-verified data
* @param signature the signature.
* @return true if signature is invalid, false otherwise.
* @throws TokenException If signing the data failed.
* @throws IOException If reading data stream failed.
*/
public boolean verify(Mechanism mechanism, long keyHandle, InputStream data, byte[] signature)
throws TokenException, IOException {
Session session = borrowSession();
try {
byte[] buffer = new byte[maxMessageSize];
int firstBlockLen = readBytes(data, buffer, maxMessageSize);
byte[] firstBlock = copyOfLen(buffer, firstBlockLen);
long code = mechanism.getMechanismCode();
if (supportsMechanism(code, CKF_VERIFY)) {
opInit(OP.VERIFY, session, mechanism, keyHandle);
if (firstBlockLen < maxMessageSize) {
session.verify(firstBlock, signature);
return true;
} else {
try {
session.verifyUpdate(firstBlock);
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(maxMessageSize + data.available());
bout.write(firstBlock);
int read;
while ((read = data.read(buffer)) != -1) {
bout.write(buffer, 0, read);
}
opInit(OP.VERIFY, session, mechanism, keyHandle);
session.verify(bout.toByteArray(), signature);
return true;
}
}
try {
int read;
while ((read = data.read(buffer)) != -1) {
if (read > 0) {
session.verifyUpdate(copyOfLen(buffer, read));
}
}
} finally {
session.verifyFinal(signature);
}
return true;
}
} else if (supportsMechanism(code, CKF_SIGN) && isMacMechanism(code)) {
byte[] sig2;
opInit(OP.SIGN, session, mechanism, keyHandle);
if (firstBlockLen < maxMessageSize) {
sig2 = session.sign(firstBlock);
} else {
try {
session.signUpdate(firstBlock);
int read;
while ((read = data.read(buffer)) != -1) {
if (read > 0) {
session.signUpdate(buffer, 0, read);
}
}
} finally {
sig2 = session.signFinal();
}
}
return Arrays.equals(signature, sig2);
} else {
throw new PKCS11Exception(CKR_MECHANISM_INVALID);
}
} catch (PKCS11Exception e) {
long ckr = e.getErrorCode();
if (ckr == CKR_SIGNATURE_INVALID || ckr == CKR_SIGNATURE_LEN_RANGE) {
return false;
} else {
throw e;
}
} finally {
sessions.add(session);
}
}
/**
* Verifies the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The verification key to use.
* @param in buffer containing the to-be-verified data
* @param out buffer for the verified data
* @return the length of verified data
* @exception PKCS11Exception
* If signing the data failed.
*/
public int verifyRecover(Mechanism mechanism, long keyHandle, byte[] in, byte[] out) throws TokenException {
return verifyRecover(mechanism, keyHandle, in, 0, in.length, out, 0, out.length);
}
/**
* Verifies the given data with the key and mechanism.
*
* @param mechanism The mechanism to use.
* @param keyHandle The verification key to use.
* @param in buffer containing the to-be-verified data
* @param inOfs buffer offset of the to-be-verified data
* @param inLen length of the to-be-verified data
* @param out buffer for the verified data
* @param outOfs buffer offset for the verified data
* @param outLen buffer size for the verified data
* @return the length of verified data
* @exception PKCS11Exception
* If signing the data failed.
*/
public int verifyRecover(Mechanism mechanism, long keyHandle, byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen) throws TokenException {
Session session = borrowSession();
try {
opInit(OP.VERIFY_RECOVER, session, mechanism, keyHandle);
return session.verifyRecover(in, inOfs, inLen, out, outOfs, outLen);
} finally {
sessions.add(session);
}
}
/**
* Generate a new secret key or a set of domain parameters. It uses the set attributes of the
* template for setting the attributes of the new key object. As mechanism the application can use
* a constant of the Mechanism class.
*
* @param mechanism The mechanism to generate a key for; e.g. Mechanism.DES to generate a DES key.
* @param template The template for the new key or domain parameters; e.g. a DESSecretKey object which
* has set certain attributes.
* @return The newly generated secret key or domain parameters.
* @throws TokenException If generating a new secret key or domain parameters failed.
*/
public long generateKey(Mechanism mechanism, AttributeVector template) throws TokenException {
Session session = borrowSession();
try {
return session.generateKey(mechanism, template);
} finally {
sessions.add(session);
}
}
/**
* Generate a new public key - private key key-pair and use the set attributes of the template
* objects for setting the attributes of the new public key and private key objects. As mechanism
* the application can use a constant of the Mechanism class.
*
* @param mechanism The mechanism to generate a key for; e.g. Mechanism.RSA to generate a new RSA
* key-pair.
* @param template The template for the new keypair.
* @return The newly generated key-pair.
* @throws TokenException If generating a new key-pair failed.
*/
public PKCS11KeyPair generateKeyPair(Mechanism mechanism, KeyPairTemplate template) throws TokenException {
Session session = borrowSession();
try {
return session.generateKeyPair(mechanism, template);
} finally {
sessions.add(session);
}
}
/**
* Wraps (encrypts) the given key with the wrapping key using the given mechanism.
*
* @param mechanism The mechanism to use for wrapping the key.
* @param wrappingKeyHandle The key to use for wrapping (encrypting).
* @param keyHandle The key to wrap (encrypt).
* @return The wrapped key as byte array. Never returns {@code null}.
* @throws TokenException If wrapping the key failed.
*/
public byte[] wrapKey(Mechanism mechanism, long wrappingKeyHandle, long keyHandle) throws TokenException {
Session session = borrowSession();
try {
return session.wrapKey(mechanism, wrappingKeyHandle, keyHandle);
} finally {
sessions.add(session);
}
}
/**
* Unwraps (decrypts) the given encrypted key with the unwrapping key using the given mechanism.
* The application can also pass a template key to set certain attributes of the unwrapped key.
* This creates a key object after unwrapping the key and returns an object representing this key.
*
* @param mechanism The mechanism to use for unwrapping the key.
* @param unwrappingKeyHandle The key to use for unwrapping (decrypting).
* @param wrappedKey The encrypted key to unwrap (decrypt).
* @param keyTemplate The template for creating the new key object.
* @return A key object representing the newly created key object.
* @throws TokenException If unwrapping the key or creating a new key object failed.
*/
public long unwrapKey(Mechanism mechanism, long unwrappingKeyHandle, byte[] wrappedKey,
AttributeVector keyTemplate) throws TokenException {
Session session = borrowSession();
try {
return session.unwrapKey(mechanism, unwrappingKeyHandle, wrappedKey, keyTemplate);
} finally {
sessions.add(session);
}
}
/**
* Derives a new key from a specified base key using the given mechanism. After deriving a new
* key from the base key, a new key object is created and a representation of it is returned. The
* application can provide a template key to set certain attributes of the new key object.
*
* @param mechanism The mechanism to use for deriving the new key from the base key.
* @param baseKeyHandle The key to use as base for derivation.
* @param template The template for creating the new key object.
* @return A key object representing the newly derived (created) key object or null, if the used
* mechanism uses other means to return its values; e.g. the CKM_SSL3_KEY_AND_MAC_DERIVE
* mechanism.
* @throws TokenException If deriving the key or creating a new key object failed.
*/
public long deriveKey(Mechanism mechanism, long baseKeyHandle, AttributeVector template) throws TokenException {
Session session = borrowSession();
try {
return session.deriveKey(mechanism, baseKeyHandle, template);
} finally {
sessions.add(session);
}
}
/**
* Generates a certain number of random bytes.
*
* @param numberOfBytesToGenerate The number of random bytes to generate.
* @return An array of random bytes with length numberOfBytesToGenerate.
* @throws TokenException If generating random bytes failed.
*/
public byte[] generateRandom(int numberOfBytesToGenerate) throws TokenException {
return generateRandom(numberOfBytesToGenerate, null);
}
/**
* Generates a certain number of random bytes.
*
* @param numberOfBytesToGenerate The number of random bytes to generate.
* @param extraSeed The seed bytes to mix in.
* @return An array of random bytes with length numberOfBytesToGenerate.
* @throws TokenException If generating random bytes failed.
*/
public byte[] generateRandom(int numberOfBytesToGenerate, byte[] extraSeed) throws TokenException {
Session session = borrowSession();
try {
if (extraSeed != null && extraSeed.length > 0) {
session.seedRandom(extraSeed);
}
return session.generateRandom(numberOfBytesToGenerate);
} finally {
sessions.add(session);
}
}
/**
* Returns the string representation of this object.
*
* @return the string representation of this object
*/
@Override
public String toString() {
return "User type: " + codeToName(Category.CKU, userType) +
"\nMaximal session count: " + maxSessionCount +
"\nNew session timeout: " + timeOutWaitNewSessionMs + " ms" +
"\nRead only: " + readOnly +
"\nToken: " + token;
}
/**
* Gets give attributes for the given object handle.
* @param objectHandle the object handle.
* @param attributeTypes types of attributes to be read.
* @return attributes for the given object handle.
* @throws TokenException if getting attributes failed.
*/
public AttributeVector getAttrValues(long objectHandle, long... attributeTypes) throws TokenException {
List typeList = new ArrayList<>(attributeTypes.length);
for (long attrType : attributeTypes) {
typeList.add(attrType);
}
return getAttrValues(objectHandle, typeList);
}
/**
* Gets give attributes for the given object handle.
* @param objectHandle the object handle.
* @param attributeTypes types of attributes to be read.
* @return attributes for the given object handle.
* @throws TokenException if getting attributes failed.
*/
public AttributeVector getAttrValues(long objectHandle, List attributeTypes) throws TokenException {
Session session = borrowSession();
try {
return session.getAttrValues(objectHandle, attributeTypes);
} finally {
sessions.add(session);
}
}
/**
* Gets all attributes for the given object handle.
* @param objectHandle the object handle.
* @return all attributes for the given object handle.
* @throws TokenException if getting attributes failed.
*/
public AttributeVector getDefaultAttrValues(long objectHandle) throws TokenException {
Session session = borrowSession();
try {
return session.getDefaultAttrValues(objectHandle);
} finally {
sessions.add(session);
}
}
private Session openSession() throws PKCS11Exception {
Session session = token.openSession(!readOnly);
countSessions.incrementAndGet();
return session;
}
private Session borrowSession() throws TokenException {
return borrowSession(true);
}
private Session borrowNoLoginSession() throws TokenException {
return borrowSession(false);
}
private Session borrowSession(boolean login) throws TokenException {
long maxTimeMs = clock.millis() + timeOutWaitNewSessionMs;
int maxTries = maxSessionCount + 1;
for (int retries = 0; retries < maxTries; retries++) {
Session session = borrowSession(login, maxTimeMs);
if (session != null) {
if (retries != 0) {
StaticLogger.info("Borrowed session after " + (retries + 1) + " tries.");
}
return session;
}
}
throw new TokenException("could not borrow session after " + maxTries + " tries.");
}
private Session borrowSession(boolean login, long maxTimeMs) throws TokenException {
if (maxTimeMs == 0) {
maxTimeMs = clock.millis() + timeOutWaitNewSessionMs;
}
Session session = null;
synchronized (sessions) {
if (countSessions.get() < maxSessionCount) {
session = sessions.poll();
if (session == null) {
// create new session
sessions.add(openSession());
}
}
}
if (session == null) {
long timeOutMs = maxTimeMs - clock.millis();
try {
session = sessions.poll(Math.max(1, timeOutMs), TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
}
}
if (session == null) {
throw new TokenException("no idle session");
}
boolean requiteSession = true;
try {
boolean sessionActive = true;
SessionInfo sessionInfo = null;
try {
sessionInfo = session.getSessionInfo();
} catch (PKCS11Exception ex) {
long ckr = ex.getErrorCode();
if (ckr == CKR_SESSION_CLOSED || ckr == CKR_SESSION_HANDLE_INVALID) {
sessionActive = false;
}
StaticLogger.warn("error getSessionInfo: {}", ckrCodeToName(ckr));
}
if (sessionActive && sessionInfo != null) {
long deviceError = sessionInfo.getDeviceError();
if (deviceError != 0) {
if (getModule().hasVendorBehaviour(PKCS11Module.BEHAVIOUR_IGNORE_DEVICE_ERROR)) {
StaticLogger.warn("ignore device error {}", deviceError);
} else {
sessionActive = false;
StaticLogger.error("device has error {}", deviceError);
}
}
}
if (!sessionActive) {
requiteSession = false;
sessions.remove(session);
countSessions.decrementAndGet();
return null;
}
if (login) {
boolean loggedIn = false;
if (sessionInfo != null) {
long state = sessionInfo.getState();
loggedIn = (state == CKS_RW_SO_FUNCTIONS)
|| (state == CKS_RW_USER_FUNCTIONS) || (state == CKS_RO_USER_FUNCTIONS);
}
if (!loggedIn) {
synchronized (loginSync) {
try {
sessionInfo = session.getSessionInfo();
loggedIn = isSessionLoggedIn(sessionInfo);
} catch (Exception e) {
StaticLogger.debug("Error while getSessionInfo()", e);
}
if (!loggedIn) {
login(session);
}
}
}
}
requiteSession = false;
return session;
} finally {
if (requiteSession) {
sessions.add(session);
}
}
} // method borrowSession
private static boolean isSessionLoggedIn(SessionInfo sessionInfo) {
long state = sessionInfo.getState();
return (state == CKS_RW_SO_FUNCTIONS)
|| (state == CKS_RW_USER_FUNCTIONS) || (state == CKS_RO_USER_FUNCTIONS);
}
private void login(Session session) throws TokenException {
login(session, userType, pins);
}
private void login(Session session, long userType, List pins) throws TokenException {
synchronized (loginSync) {
StaticLogger.info("verify on PKCS11Module with " + (pins == null || pins.isEmpty() ? "NULL pin" : "pin"));
String userText = "user of type " + codeToName(Category.CKU, userType);
boolean nullPins;
if (pins == null || pins.isEmpty()) {
nullPins = true;
} else if (pins.size() == 1) {
char[] pin = pins.get(0);
nullPins = pin == null || pin.length == 0;
} else {
nullPins = false;
}
try {
if (nullPins) {
session.login(userType, new char[0]);
StaticLogger.info("login successful as " + userText + " with NULL PIN");
} else {
for (char[] pin : pins) {
session.login(userType, pin == null ? new char[0] : pin);
}
StaticLogger.info("login successful as " + userText + " with PIN");
}
} catch (PKCS11Exception ex) {
long ckr = ex.getErrorCode();
if (ckr == CKR_USER_ALREADY_LOGGED_IN) {
StaticLogger.info("user already logged in");
} else {
StaticLogger.warn("login failed as {}: {}", userText, ckrCodeToName(ckr));
throw ex;
}
}
}
}
private void opInit(OP op, Session session, Mechanism mechanism, long keyHandle) throws TokenException {
try {
opInit0(op, session, mechanism, keyHandle);
} catch (PKCS11Exception ex) {
if (ex.getErrorCode() == CKR_USER_NOT_LOGGED_IN) {
login(session);
opInit0(op, session, mechanism, keyHandle);
}
}
}
private void opInit0(OP op, Session session, Mechanism mechanism, long keyHandle) throws TokenException {
switch (op) {
case SIGN:
session.signInit(mechanism, keyHandle);
break;
case VERIFY:
session.verifyInit(mechanism, keyHandle);
break;
case DECRYPT:
session.decryptInit(mechanism, keyHandle);
break;
case ENCRYPT:
session.encryptInit(mechanism, keyHandle);
break;
case DIGEST:
session.digestInit(mechanism);
break;
case SIGN_RECOVER:
session.signRecoverInit(mechanism, keyHandle);
break;
case VERIFY_RECOVER:
session.verifyRecoverInit(mechanism, keyHandle);
break;
default:
throw new IllegalStateException("unknown OP " + op);
}
}
private static byte[] copyOfLen(byte[] bytes, int len) {
return bytes.length == len ? bytes : Arrays.copyOf(bytes, len);
}
private static byte[] copyOfLen(byte[] bytes, int offset, int len) {
return (offset == 0 && bytes.length == len) ? bytes : Arrays.copyOfRange(bytes, offset, offset + len);
}
private static int estimateDigestLen(Mechanism mechanism) {
long mechCode = mechanism.getMechanismCode();
int digestLen;
if (mechCode == CKM_SHA_1) {
digestLen = 20;
} else if (mechCode == CKM_SHA224 || mechCode == CKM_SHA3_224) {
digestLen = 28;
} else if (mechCode == CKM_SHA256 || mechCode == CKM_SHA3_256 || mechCode == CKM_VENDOR_SM3) {
digestLen = 32;
} else if (mechCode == CKM_SHA384 || mechCode == CKM_SHA3_384) {
digestLen = 48;
} else if (mechCode == CKM_SHA512 || mechCode == CKM_SHA3_512) {
digestLen = 64;
} else {
digestLen = 64;
}
return digestLen;
}
private static byte[] readAllBytes(InputStream stream) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream(Math.min(32, stream.available()));
int read;
byte[] buffer = new byte[4096];
while ((read = stream.read(buffer)) != -1) {
bout.write(buffer, 0, read);
}
return bout.toByteArray();
}
private static int readBytes(InputStream stream, byte[] buffer, int numBytes) throws IOException {
int ofs = 0;
int read;
while ((read = stream.read(buffer, ofs, numBytes - ofs)) != -1) {
ofs += read;
if (ofs >= numBytes) {
break;
}
}
return ofs;
}
private static boolean isMacMechanism(long mechanism) {
return mechanism == CKM_AES_CMAC || mechanism == CKM_AES_GMAC || mechanism == CKM_SHA_1_HMAC ||
mechanism == CKM_SHA224_HMAC || mechanism == CKM_SHA256_HMAC ||
mechanism == CKM_SHA384_HMAC || mechanism == CKM_SHA512_HMAC ||
mechanism == CKM_SHA3_224_HMAC || mechanism == CKM_SHA3_256_HMAC ||
mechanism == CKM_SHA3_384_HMAC || mechanism == CKM_SHA3_512_HMAC ||
mechanism == CKM_SHA224_HMAC_GENERAL || mechanism == CKM_SHA256_HMAC_GENERAL ||
mechanism == CKM_SHA384_HMAC_GENERAL || mechanism == CKM_SHA512_HMAC_GENERAL ||
mechanism == CKM_SHA3_224_HMAC_GENERAL || mechanism == CKM_SHA3_256_HMAC_GENERAL ||
mechanism == CKM_SHA3_384_HMAC_GENERAL || mechanism == CKM_SHA3_512_HMAC_GENERAL;
}
private static void addCkaTypes(List list, long... types) {
for (long type : types) {
list.add(type);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy