Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs11/Token.java
38919 views
/*1* Copyright (c) 2003, 2013, 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.*;4041/**42* PKCS#11 token.43*44* @author Andreas Sterbenz45* @since 1.546*/47class Token implements Serializable {4849// need to be serializable to allow SecureRandom to be serialized50private static final long serialVersionUID = 2541527649100571747L;5152// how often to check if the token is still present (in ms)53// this is different from checking if a token has been inserted,54// that is done in SunPKCS11. Currently 50 ms.55private final static long CHECK_INTERVAL = 50;5657final SunPKCS11 provider;5859final PKCS11 p11;6061final Config config;6263final CK_TOKEN_INFO tokenInfo;6465// session manager to pool sessions66final SessionManager sessionManager;6768// template manager to customize the attributes used when creating objects69private final TemplateManager templateManager;7071// flag indicating whether we need to explicitly cancel operations72// we started on the token. If false, we assume operations are73// automatically cancelled once we start another one74final boolean explicitCancel;7576// translation cache for secret keys77final KeyCache secretCache;7879// translation cache for asymmetric keys (public and private)80final KeyCache privateCache;8182// cached instances of the various key factories, initialized on demand83private volatile P11KeyFactory rsaFactory, dsaFactory, dhFactory, ecFactory;8485// table which maps mechanisms to the corresponding cached86// MechanismInfo objects87private final Map<Long, CK_MECHANISM_INFO> mechInfoMap;8889// single SecureRandomSpi instance we use per token90// initialized on demand (if supported)91private volatile P11SecureRandom secureRandom;9293// single KeyStoreSpi instance we use per provider94// initialized on demand95private volatile P11KeyStore keyStore;9697// whether this token is a removable token98private final boolean removable;99100// for removable tokens: whether this token is valid or has been removed101private volatile boolean valid;102103// for removable tokens: time last checked for token presence104private long lastPresentCheck;105106// unique token id, used for serialization only107private byte[] tokenId;108109// flag indicating whether the token is write protected110private boolean writeProtected;111112// flag indicating whether we are logged in113private volatile boolean loggedIn;114115// time we last checked login status116private long lastLoginCheck;117118// mutex for token-present-check119private final static Object CHECK_LOCK = new Object();120121// object for indicating unsupported mechanism in 'mechInfoMap'122private final static CK_MECHANISM_INFO INVALID_MECH =123new CK_MECHANISM_INFO(0, 0, 0);124125// flag indicating whether the token supports raw secret key material import126private Boolean supportsRawSecretKeyImport;127128Token(SunPKCS11 provider) throws PKCS11Exception {129this.provider = provider;130this.removable = provider.removable;131this.valid = true;132p11 = provider.p11;133config = provider.config;134tokenInfo = p11.C_GetTokenInfo(provider.slotID);135writeProtected = (tokenInfo.flags & CKF_WRITE_PROTECTED) != 0;136// create session manager and open a test session137SessionManager sessionManager;138try {139sessionManager = new SessionManager(this);140Session s = sessionManager.getOpSession();141sessionManager.releaseSession(s);142} catch (PKCS11Exception e) {143if (writeProtected) {144throw e;145}146// token might not permit RW sessions even though147// CKF_WRITE_PROTECTED is not set148writeProtected = true;149sessionManager = new SessionManager(this);150Session s = sessionManager.getOpSession();151sessionManager.releaseSession(s);152}153this.sessionManager = sessionManager;154secretCache = new KeyCache();155privateCache = new KeyCache();156templateManager = config.getTemplateManager();157explicitCancel = config.getExplicitCancel();158mechInfoMap =159new ConcurrentHashMap<Long, CK_MECHANISM_INFO>(10);160}161162boolean isWriteProtected() {163return writeProtected;164}165166// return whether the token supports raw secret key material import167boolean supportsRawSecretKeyImport() {168if (supportsRawSecretKeyImport == null) {169SecureRandom random = JCAUtil.getSecureRandom();170byte[] encoded = new byte[48];171random.nextBytes(encoded);172173CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[3];174attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);175attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET);176attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded);177178Session session = null;179try {180attributes = getAttributes(O_IMPORT,181CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);182session = getObjSession();183long keyID = p11.C_CreateObject(session.id(), attributes);184185supportsRawSecretKeyImport = Boolean.TRUE;186} catch (PKCS11Exception e) {187supportsRawSecretKeyImport = Boolean.FALSE;188} finally {189releaseSession(session);190}191}192193return supportsRawSecretKeyImport;194}195196// return whether we are logged in197// uses cached result if current. session is optional and may be null198boolean isLoggedIn(Session session) throws PKCS11Exception {199// volatile load first200boolean loggedIn = this.loggedIn;201long time = System.currentTimeMillis();202if (time - lastLoginCheck > CHECK_INTERVAL) {203loggedIn = isLoggedInNow(session);204lastLoginCheck = time;205}206return loggedIn;207}208209// return whether we are logged in now210// does not use cache211boolean isLoggedInNow(Session session) throws PKCS11Exception {212boolean allocSession = (session == null);213try {214if (allocSession) {215session = getOpSession();216}217CK_SESSION_INFO info = p11.C_GetSessionInfo(session.id());218boolean loggedIn = (info.state == CKS_RO_USER_FUNCTIONS) ||219(info.state == CKS_RW_USER_FUNCTIONS);220this.loggedIn = loggedIn;221return loggedIn;222} finally {223if (allocSession) {224releaseSession(session);225}226}227}228229// ensure that we are logged in230// call provider.login() if not231void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException {232if (isLoggedIn(session) == false) {233provider.login(null, null);234}235}236237// return whether this token object is valid (i.e. token not removed)238// returns value from last check, does not perform new check239boolean isValid() {240if (removable == false) {241return true;242}243return valid;244}245246void ensureValid() {247if (isValid() == false) {248throw new ProviderException("Token has been removed");249}250}251252// return whether a token is present (i.e. token not removed)253// returns cached value if current, otherwise performs new check254boolean isPresent(long sessionID) {255if (removable == false) {256return true;257}258if (valid == false) {259return false;260}261long time = System.currentTimeMillis();262if ((time - lastPresentCheck) >= CHECK_INTERVAL) {263synchronized (CHECK_LOCK) {264if ((time - lastPresentCheck) >= CHECK_INTERVAL) {265boolean ok = false;266try {267// check if token still present268CK_SLOT_INFO slotInfo =269provider.p11.C_GetSlotInfo(provider.slotID);270if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) {271// if the token has been removed and re-inserted,272// the token should return an error273CK_SESSION_INFO sessInfo =274provider.p11.C_GetSessionInfo275(sessionID);276ok = true;277}278} catch (PKCS11Exception e) {279// empty280}281valid = ok;282lastPresentCheck = System.currentTimeMillis();283if (ok == false) {284destroy();285}286}287}288}289return valid;290}291292void destroy() {293valid = false;294provider.uninitToken(this);295}296297Session getObjSession() throws PKCS11Exception {298return sessionManager.getObjSession();299}300301Session getOpSession() throws PKCS11Exception {302return sessionManager.getOpSession();303}304305Session releaseSession(Session session) {306return sessionManager.releaseSession(session);307}308309Session killSession(Session session) {310return sessionManager.killSession(session);311}312313CK_ATTRIBUTE[] getAttributes(String op, long type, long alg,314CK_ATTRIBUTE[] attrs) throws PKCS11Exception {315CK_ATTRIBUTE[] newAttrs =316templateManager.getAttributes(op, type, alg, attrs);317for (CK_ATTRIBUTE attr : newAttrs) {318if (attr.type == CKA_TOKEN) {319if (attr.getBoolean()) {320try {321ensureLoggedIn(null);322} catch (LoginException e) {323throw new ProviderException("Login failed", e);324}325}326// break once we have found a CKA_TOKEN attribute327break;328}329}330return newAttrs;331}332333P11KeyFactory getKeyFactory(String algorithm) {334P11KeyFactory f;335if (algorithm.equals("RSA")) {336f = rsaFactory;337if (f == null) {338f = new P11RSAKeyFactory(this, algorithm);339rsaFactory = f;340}341} else if (algorithm.equals("DSA")) {342f = dsaFactory;343if (f == null) {344f = new P11DSAKeyFactory(this, algorithm);345dsaFactory = f;346}347} else if (algorithm.equals("DH")) {348f = dhFactory;349if (f == null) {350f = new P11DHKeyFactory(this, algorithm);351dhFactory = f;352}353} else if (algorithm.equals("EC")) {354f = ecFactory;355if (f == null) {356f = new P11ECKeyFactory(this, algorithm);357ecFactory = f;358}359} else {360throw new ProviderException("Unknown algorithm " + algorithm);361}362return f;363}364365P11SecureRandom getRandom() {366if (secureRandom == null) {367secureRandom = new P11SecureRandom(this);368}369return secureRandom;370}371372P11KeyStore getKeyStore() {373if (keyStore == null) {374keyStore = new P11KeyStore(this);375}376return keyStore;377}378379CK_MECHANISM_INFO getMechanismInfo(long mechanism) throws PKCS11Exception {380CK_MECHANISM_INFO result = mechInfoMap.get(mechanism);381if (result == null) {382try {383result = p11.C_GetMechanismInfo(provider.slotID,384mechanism);385mechInfoMap.put(mechanism, result);386} catch (PKCS11Exception e) {387if (e.getErrorCode() != PKCS11Constants.CKR_MECHANISM_INVALID) {388throw e;389} else {390mechInfoMap.put(mechanism, INVALID_MECH);391}392}393} else if (result == INVALID_MECH) {394result = null;395}396return result;397}398399private synchronized byte[] getTokenId() {400if (tokenId == null) {401SecureRandom random = JCAUtil.getSecureRandom();402tokenId = new byte[20];403random.nextBytes(tokenId);404serializedTokens.add(new WeakReference<Token>(this));405}406return tokenId;407}408409// list of all tokens that have been serialized within this VM410// NOTE that elements are never removed from this list411// the assumption is that the number of tokens that are serialized412// is relatively small413private static final List<Reference<Token>> serializedTokens =414new ArrayList<Reference<Token>>();415416private Object writeReplace() throws ObjectStreamException {417if (isValid() == false) {418throw new NotSerializableException("Token has been removed");419}420return new TokenRep(this);421}422423// serialized representation of a token424// tokens can only be de-serialized within the same VM invocation425// and if the token has not been removed in the meantime426private static class TokenRep implements Serializable {427428private static final long serialVersionUID = 3503721168218219807L;429430private final byte[] tokenId;431432TokenRep(Token token) {433tokenId = token.getTokenId();434}435436private Object readResolve() throws ObjectStreamException {437for (Reference<Token> tokenRef : serializedTokens) {438Token token = tokenRef.get();439if ((token != null) && token.isValid()) {440if (Arrays.equals(token.getTokenId(), tokenId)) {441return token;442}443}444}445throw new NotSerializableException("Could not find token");446}447}448449}450451452