Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Token.java
41154 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs11;2627import java.util.*;28import java.util.concurrent.ConcurrentHashMap;29import java.io.*;30import java.lang.ref.*;3132import java.security.*;33import javax.security.auth.login.LoginException;3435import sun.security.jca.JCAUtil;3637import sun.security.pkcs11.wrapper.*;38import static sun.security.pkcs11.TemplateManager.*;39import static sun.security.pkcs11.wrapper.PKCS11Constants.*;40import static sun.security.pkcs11.wrapper.PKCS11Exception.*;4142/**43* PKCS#11 token.44*45* @author Andreas Sterbenz46* @since 1.547*/48class Token implements Serializable {4950// need to be serializable to allow SecureRandom to be serialized51private static final long serialVersionUID = 2541527649100571747L;5253// how often to check if the token is still present (in ms)54// this is different from checking if a token has been inserted,55// that is done in SunPKCS11. Currently 50 ms.56private static final long CHECK_INTERVAL = 50;5758final SunPKCS11 provider;5960final PKCS11 p11;6162final Config config;6364final CK_TOKEN_INFO tokenInfo;6566// session manager to pool sessions67final SessionManager sessionManager;6869// template manager to customize the attributes used when creating objects70private final TemplateManager templateManager;7172// flag indicating whether we need to explicitly cancel operations73// we started on the token. If false, we assume operations are74// automatically cancelled once we start another one75final boolean explicitCancel;7677// translation cache for secret keys78final KeyCache secretCache;7980// translation cache for asymmetric keys (public and private)81final KeyCache privateCache;8283// cached instances of the various key factories, initialized on demand84private volatile P11KeyFactory rsaFactory, dsaFactory, dhFactory, ecFactory;8586// table which maps mechanisms to the corresponding cached87// MechanismInfo objects88private final Map<Long, CK_MECHANISM_INFO> mechInfoMap;8990// single SecureRandomSpi instance we use per token91// initialized on demand (if supported)92private volatile P11SecureRandom secureRandom;9394// single KeyStoreSpi instance we use per provider95// initialized on demand96private volatile P11KeyStore keyStore;9798// whether this token is a removable token99private final boolean removable;100101// for removable tokens: whether this token is valid or has been removed102private volatile boolean valid;103104// for removable tokens: time last checked for token presence105private long lastPresentCheck;106107// unique token id, used for serialization only108private byte[] tokenId;109110// flag indicating whether the token is write protected111private boolean writeProtected;112113// flag indicating whether we are logged in114private volatile boolean loggedIn;115116// time we last checked login status117private long lastLoginCheck;118119// mutex for token-present-check120private static final Object CHECK_LOCK = new Object();121122// object for indicating unsupported mechanism in 'mechInfoMap'123private static final CK_MECHANISM_INFO INVALID_MECH =124new CK_MECHANISM_INFO(0, 0, 0);125126// flag indicating whether the token supports raw secret key material import127private Boolean supportsRawSecretKeyImport;128129Token(SunPKCS11 provider) throws PKCS11Exception {130this.provider = provider;131this.removable = provider.removable;132this.valid = true;133p11 = provider.p11;134config = provider.config;135tokenInfo = p11.C_GetTokenInfo(provider.slotID);136writeProtected = (tokenInfo.flags & CKF_WRITE_PROTECTED) != 0;137// create session manager and open a test session138SessionManager sessionManager;139try {140sessionManager = new SessionManager(this);141Session s = sessionManager.getOpSession();142sessionManager.releaseSession(s);143} catch (PKCS11Exception e) {144if (writeProtected) {145throw e;146}147// token might not permit RW sessions even though148// CKF_WRITE_PROTECTED is not set149writeProtected = true;150sessionManager = new SessionManager(this);151Session s = sessionManager.getOpSession();152sessionManager.releaseSession(s);153}154this.sessionManager = sessionManager;155secretCache = new KeyCache();156privateCache = new KeyCache();157templateManager = config.getTemplateManager();158explicitCancel = config.getExplicitCancel();159mechInfoMap =160new ConcurrentHashMap<Long, CK_MECHANISM_INFO>(10);161}162163boolean isWriteProtected() {164return writeProtected;165}166167// return whether the token supports raw secret key material import168boolean supportsRawSecretKeyImport() {169if (supportsRawSecretKeyImport == null) {170SecureRandom random = JCAUtil.getSecureRandom();171byte[] encoded = new byte[48];172random.nextBytes(encoded);173174CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[3];175attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);176attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET);177attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded);178179Session session = null;180try {181attributes = getAttributes(O_IMPORT,182CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);183session = getObjSession();184long keyID = p11.C_CreateObject(session.id(), attributes);185186supportsRawSecretKeyImport = Boolean.TRUE;187} catch (PKCS11Exception e) {188supportsRawSecretKeyImport = Boolean.FALSE;189} finally {190releaseSession(session);191}192}193194return supportsRawSecretKeyImport;195}196197// return whether we are logged in198// uses cached result if current. session is optional and may be null199boolean isLoggedIn(Session session) throws PKCS11Exception {200// volatile load first201boolean loggedIn = this.loggedIn;202long time = System.currentTimeMillis();203if (time - lastLoginCheck > CHECK_INTERVAL) {204loggedIn = isLoggedInNow(session);205lastLoginCheck = time;206}207return loggedIn;208}209210// return whether we are logged in now211// does not use cache212boolean isLoggedInNow(Session session) throws PKCS11Exception {213boolean allocSession = (session == null);214try {215if (allocSession) {216session = getOpSession();217}218CK_SESSION_INFO info = p11.C_GetSessionInfo(session.id());219boolean loggedIn = (info.state == CKS_RO_USER_FUNCTIONS) ||220(info.state == CKS_RW_USER_FUNCTIONS);221this.loggedIn = loggedIn;222return loggedIn;223} finally {224if (allocSession) {225releaseSession(session);226}227}228}229230// ensure that we are logged in231// call provider.login() if not232void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException {233if (isLoggedIn(session) == false) {234provider.login(null, null);235}236}237238// return whether this token object is valid (i.e. token not removed)239// returns value from last check, does not perform new check240boolean isValid() {241if (removable == false) {242return true;243}244return valid;245}246247void ensureValid() {248if (isValid() == false) {249throw new ProviderException("Token has been removed");250}251}252253// return whether a token is present (i.e. token not removed)254// returns cached value if current, otherwise performs new check255boolean isPresent(long sessionID) {256if (removable == false) {257return true;258}259if (valid == false) {260return false;261}262long time = System.currentTimeMillis();263if ((time - lastPresentCheck) >= CHECK_INTERVAL) {264synchronized (CHECK_LOCK) {265if ((time - lastPresentCheck) >= CHECK_INTERVAL) {266boolean ok = false;267try {268// check if token still present269CK_SLOT_INFO slotInfo =270provider.p11.C_GetSlotInfo(provider.slotID);271if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) {272// if the token has been removed and re-inserted,273// the token should return an error274CK_SESSION_INFO sessInfo =275provider.p11.C_GetSessionInfo276(sessionID);277ok = true;278}279} catch (PKCS11Exception e) {280// empty281}282valid = ok;283lastPresentCheck = System.currentTimeMillis();284if (ok == false) {285destroy();286}287}288}289}290return valid;291}292293void destroy() {294secretCache.clear();295privateCache.clear();296297sessionManager.clearPools();298provider.uninitToken(this);299valid = false;300}301302Session getObjSession() throws PKCS11Exception {303return sessionManager.getObjSession();304}305306Session getOpSession() throws PKCS11Exception {307return sessionManager.getOpSession();308}309310Session releaseSession(Session session) {311return sessionManager.releaseSession(session);312}313314Session killSession(Session session) {315return sessionManager.killSession(session);316}317318CK_ATTRIBUTE[] getAttributes(String op, long type, long alg,319CK_ATTRIBUTE[] attrs) throws PKCS11Exception {320CK_ATTRIBUTE[] newAttrs =321templateManager.getAttributes(op, type, alg, attrs);322for (CK_ATTRIBUTE attr : newAttrs) {323if (attr.type == CKA_TOKEN) {324if (attr.getBoolean()) {325try {326ensureLoggedIn(null);327} catch (LoginException e) {328throw new ProviderException("Login failed", e);329}330}331// break once we have found a CKA_TOKEN attribute332break;333}334}335return newAttrs;336}337338P11KeyFactory getKeyFactory(String algorithm) {339P11KeyFactory f;340if (algorithm.equals("RSA")) {341f = rsaFactory;342if (f == null) {343f = new P11RSAKeyFactory(this, algorithm);344rsaFactory = f;345}346} else if (algorithm.equals("DSA")) {347f = dsaFactory;348if (f == null) {349f = new P11DSAKeyFactory(this, algorithm);350dsaFactory = f;351}352} else if (algorithm.equals("DH")) {353f = dhFactory;354if (f == null) {355f = new P11DHKeyFactory(this, algorithm);356dhFactory = f;357}358} else if (algorithm.equals("EC")) {359f = ecFactory;360if (f == null) {361f = new P11ECKeyFactory(this, algorithm);362ecFactory = f;363}364} else {365throw new ProviderException("Unknown algorithm " + algorithm);366}367return f;368}369370P11SecureRandom getRandom() {371if (secureRandom == null) {372secureRandom = new P11SecureRandom(this);373}374return secureRandom;375}376377P11KeyStore getKeyStore() {378if (keyStore == null) {379keyStore = new P11KeyStore(this);380}381return keyStore;382}383384CK_MECHANISM_INFO getMechanismInfo(long mechanism) throws PKCS11Exception {385CK_MECHANISM_INFO result = mechInfoMap.get(mechanism);386if (result == null) {387try {388result = p11.C_GetMechanismInfo(provider.slotID,389mechanism);390mechInfoMap.put(mechanism, result);391} catch (PKCS11Exception e) {392if (e.getErrorCode() != CKR_MECHANISM_INVALID) {393throw e;394} else {395mechInfoMap.put(mechanism, INVALID_MECH);396}397}398} else if (result == INVALID_MECH) {399result = null;400}401return result;402}403404private synchronized byte[] getTokenId() {405if (tokenId == null) {406SecureRandom random = JCAUtil.getSecureRandom();407tokenId = new byte[20];408random.nextBytes(tokenId);409serializedTokens.add(new WeakReference<Token>(this));410}411return tokenId;412}413414// list of all tokens that have been serialized within this VM415// NOTE that elements are never removed from this list416// the assumption is that the number of tokens that are serialized417// is relatively small418private static final List<Reference<Token>> serializedTokens =419new ArrayList<Reference<Token>>();420421private Object writeReplace() throws ObjectStreamException {422if (isValid() == false) {423throw new NotSerializableException("Token has been removed");424}425return new TokenRep(this);426}427428// serialized representation of a token429// tokens can only be de-serialized within the same VM invocation430// and if the token has not been removed in the meantime431private static class TokenRep implements Serializable {432433private static final long serialVersionUID = 3503721168218219807L;434435private final byte[] tokenId;436437TokenRep(Token token) {438tokenId = token.getTokenId();439}440441private Object readResolve() throws ObjectStreamException {442for (Reference<Token> tokenRef : serializedTokens) {443Token token = tokenRef.get();444if ((token != null) && token.isValid()) {445if (Arrays.equals(token.getTokenId(), tokenId)) {446return token;447}448}449}450throw new NotSerializableException("Could not find token");451}452}453454}455456457