1 /*
   2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.pkcs11;
  27 
  28 import java.io.*;
  29 import java.lang.ref.*;
  30 import java.math.BigInteger;
  31 import java.util.*;
  32 import java.security.*;
  33 import java.security.interfaces.*;
  34 import java.security.spec.*;
  35 
  36 import javax.crypto.*;
  37 import javax.crypto.interfaces.*;
  38 import javax.crypto.spec.*;
  39 
  40 import sun.security.rsa.RSAUtil.KeyType;
  41 import sun.security.rsa.RSAPublicKeyImpl;
  42 import sun.security.rsa.RSAPrivateCrtKeyImpl;
  43 
  44 import sun.security.internal.interfaces.TlsMasterSecret;
  45 
  46 import sun.security.pkcs11.wrapper.*;
  47 
  48 import static sun.security.pkcs11.TemplateManager.O_GENERATE;
  49 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
  50 
  51 import sun.security.util.Debug;
  52 import sun.security.util.DerValue;
  53 import sun.security.util.Length;
  54 import sun.security.util.ECUtil;
  55 
  56 /**
  57  * Key implementation classes.
  58  *
  59  * In PKCS#11, the components of private and secret keys may or may not
  60  * be accessible. If they are, we use the algorithm specific key classes
  61  * (e.g. DSAPrivateKey) for compatibility with existing applications.
  62  * If the components are not accessible, we use a generic class that
  63  * only implements PrivateKey (or SecretKey). Whether the components of a
  64  * key are extractable is automatically determined when the key object is
  65  * created.
  66  *
  67  * @author  Andreas Sterbenz
  68  * @since   1.5
  69  */
  70 abstract class P11Key implements Key, Length {
  71 
  72     private static final long serialVersionUID = -2575874101938349339L;
  73 
  74     private final static String PUBLIC = "public";
  75     private final static String PRIVATE = "private";
  76     private final static String SECRET = "secret";
  77 
  78     // type of key, one of (PUBLIC, PRIVATE, SECRET)
  79     final String type;
  80 
  81     // token instance
  82     final Token token;
  83 
  84     // algorithm name, returned by getAlgorithm(), etc.
  85     final String algorithm;
  86 
  87     // key id
  88     long keyID;
  89 
  90     // effective key length of the key, e.g. 56 for a DES key
  91     final int keyLength;
  92 
  93     // flags indicating whether the key is a token object, sensitive, extractable
  94     final boolean tokenObject, sensitive, extractable;
  95 
  96     // phantom reference notification clean up for session keys
  97     private final SessionKeyRef sessionKeyRef;
  98 
  99     // Temporary native keys feature enabled or not.
 100     // When enabled, keys are packed and stored in the Java heap
 101     // while not in use.
 102     private boolean tmpNativeKey;
 103 
 104     private int nativeKeyRefCounting;
 105 
 106     private static long nativeKeyWrapperKeyID = -1;
 107 
 108     // Buffer to hold native key objects info in Java heap in order to
 109     // re-create them if needed.
 110     private byte[] nativeKeyInfo;
 111 
 112     P11Key(String type, Session session, long keyID, String algorithm,
 113             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 114         this.type = type;
 115         this.token = session.token;
 116         this.keyID = keyID;
 117         this.algorithm = algorithm;
 118         this.keyLength = keyLength;
 119         boolean tokenObject = false;
 120         boolean sensitive = false;
 121         boolean extractable = true;
 122         int n = (attributes == null) ? 0 : attributes.length;
 123         for (int i = 0; i < n; i++) {
 124             CK_ATTRIBUTE attr = attributes[i];
 125             if (attr.type == CKA_TOKEN) {
 126                 tokenObject = attr.getBoolean();
 127             } else if (attr.type == CKA_SENSITIVE) {
 128                 sensitive = attr.getBoolean();
 129             } else if (attr.type == CKA_EXTRACTABLE) {
 130                 extractable = attr.getBoolean();
 131             }
 132         }
 133         this.tokenObject = tokenObject;
 134         this.sensitive = sensitive;
 135         this.extractable = extractable;
 136         if (token.tokenInfo.label[0] == 'N'
 137                 && token.tokenInfo.label[1] == 'S'
 138                 && token.tokenInfo.label[2] == 'S') {
 139             this.tmpNativeKey = tmpNativeKey;
 140         } else {
 141             // Disabled if token is not NSS
 142             this.tmpNativeKey = false;
 143         }
 144         if (!extractable || tokenObject) {
 145             this.tmpNativeKey = false;
 146         }
 147         if (this.tmpNativeKey) {
 148             nativeKeyRefCounting = 0;
 149             try {
 150                 if (sensitive && nativeKeyWrapperKeyID == -1) {
 151                     synchronized(P11Key.class) {
 152                         if (nativeKeyWrapperKeyID == -1) {
 153                             // Create a global wrapping/unwrapping key
 154                             CK_ATTRIBUTE[] wrappingAttributes = token.getAttributes
 155                             (O_GENERATE, CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] {
 156                                     new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
 157                                     new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3),
 158                                 });
 159                             Session wrappingSession = null;
 160                             try {
 161                                 wrappingSession = token.getObjSession();
 162                                 nativeKeyWrapperKeyID = token.p11.C_GenerateKey(
 163                                         wrappingSession.id(),
 164                                         new CK_MECHANISM(CKM_AES_KEY_GEN),
 165                                         wrappingAttributes);
 166                             } finally {
 167                                 token.releaseSession(wrappingSession);
 168                             }
 169                         }
 170                     }
 171                 }
 172                 nativeKeyInfo = token.p11.getNativeKeyInfo(session.id(),
 173                         keyID, nativeKeyWrapperKeyID);
 174                 if (nativeKeyInfo != null && nativeKeyInfo.length > 0) {
 175                     destroyNativeKey();
 176                     // If extracted, it's not a token object anymore
 177                     tokenObject = false;
 178                 } else {
 179                     this.tmpNativeKey = false;
 180                 }
 181             } catch (PKCS11Exception e) {
 182                 // Unexpected behaviour when trying to manage the key life-time.
 183                 // Don't manage the native key.
 184                 this.tmpNativeKey = false;
 185             }
 186         }
 187         if (tokenObject == false) {
 188             sessionKeyRef = new SessionKeyRef(this, this.keyID, session);
 189         } else {
 190             sessionKeyRef = null;
 191         }
 192     }
 193 
 194     public void incNativeKeyRef() throws PKCS11Exception {
 195         if (tmpNativeKey) {
 196             synchronized(this) {
 197                 if (++nativeKeyRefCounting == 1 && nativeKeyInfo != null) {
 198                     // Create a Native Key
 199                     Session session = null;
 200                     try {
 201                         session = token.getObjSession();
 202                         keyID = token.p11.createNativeKey(session.id(),
 203                                 nativeKeyInfo, nativeKeyWrapperKeyID);
 204                         if (sessionKeyRef != null) {
 205                             sessionKeyRef.setKeyIDAndSession(keyID, session);
 206                         }
 207                     } catch (PKCS11Exception e) {
 208                         nativeKeyRefCounting--;
 209                         throw e;
 210                     } finally {
 211                         token.releaseSession(session);
 212                     }
 213                 }
 214             }
 215         }
 216     }
 217 
 218     public void makeNativeKeyPersistent() throws PKCS11Exception {
 219         if (tmpNativeKey) {
 220             synchronized(this) {
 221                 if (nativeKeyRefCounting == 0) {
 222                     this.incNativeKeyRef();
 223                 }
 224 
 225                 // This write is not sync protected because reads are done out
 226                 // of the synchronization block. It's just a best-effort.
 227                 tmpNativeKey = false;
 228 
 229                 // This write is sync protected and provides the real guarantee
 230                 // to avoid native key creation or destruction.
 231                 nativeKeyInfo = null;
 232             }
 233         }
 234     }
 235 
 236     public void decNativeKeyRef() {
 237         if (tmpNativeKey) {
 238             synchronized(this) {
 239                 if(--nativeKeyRefCounting == 0  && nativeKeyInfo != null) {
 240                     try {
 241                         destroyNativeKey();
 242                     } catch (PKCS11Exception e) {
 243                         // This is a best-effort.
 244                     }
 245                 }
 246                 if (nativeKeyRefCounting < 0) {
 247                     nativeKeyRefCounting = 0;
 248                 }
 249             }
 250         }
 251     }
 252 
 253     private void destroyNativeKey() throws PKCS11Exception {
 254         // There is no synchronization needed between
 255         // SessionKeyRef.disposeNative and this method.
 256         // When SessionKeyRef.disposeNative method is
 257         // executed, no P11Key object exists.
 258         Session session = null;
 259         try {
 260             session = token.getObjSession();
 261             token.p11.C_DestroyObject(session.id(), keyID);
 262         } finally {
 263             if (sessionKeyRef != null) {
 264                 sessionKeyRef.setKeyIDAndSession(0, null);
 265             }
 266             keyID = 0;
 267             token.releaseSession(session);
 268         }
 269     }
 270 
 271     // see JCA spec
 272     public final String getAlgorithm() {
 273         token.ensureValid();
 274         return algorithm;
 275     }
 276 
 277     // see JCA spec
 278     public final byte[] getEncoded() {
 279         byte[] b = getEncodedInternal();
 280         return (b == null) ? null : b.clone();
 281     }
 282 
 283     abstract byte[] getEncodedInternal();
 284 
 285     public boolean equals(Object obj) {
 286         if (this == obj) {
 287             return true;
 288         }
 289         // equals() should never throw exceptions
 290         if (token.isValid() == false) {
 291             return false;
 292         }
 293         if (obj instanceof Key == false) {
 294             return false;
 295         }
 296         String thisFormat = getFormat();
 297         if (thisFormat == null) {
 298             // no encoding, key only equal to itself
 299             // XXX getEncoded() for unextractable keys will change that
 300             return false;
 301         }
 302         Key other = (Key)obj;
 303         if (thisFormat.equals(other.getFormat()) == false) {
 304             return false;
 305         }
 306         byte[] thisEnc = this.getEncodedInternal();
 307         byte[] otherEnc;
 308         if (obj instanceof P11Key) {
 309             otherEnc = ((P11Key)other).getEncodedInternal();
 310         } else {
 311             otherEnc = other.getEncoded();
 312         }
 313         return MessageDigest.isEqual(thisEnc, otherEnc);
 314     }
 315 
 316     public int hashCode() {
 317         // hashCode() should never throw exceptions
 318         if (token.isValid() == false) {
 319             return 0;
 320         }
 321         byte[] b1 = getEncodedInternal();
 322         if (b1 == null) {
 323             return 0;
 324         }
 325         int r = b1.length;
 326         for (int i = 0; i < b1.length; i++) {
 327             r += (b1[i] & 0xff) * 37;
 328         }
 329         return r;
 330     }
 331 
 332     protected Object writeReplace() throws ObjectStreamException {
 333         KeyRep.Type type;
 334         String format = getFormat();
 335         if (isPrivate() && "PKCS#8".equals(format)) {
 336             type = KeyRep.Type.PRIVATE;
 337         } else if (isPublic() && "X.509".equals(format)) {
 338             type = KeyRep.Type.PUBLIC;
 339         } else if (isSecret() && "RAW".equals(format)) {
 340             type = KeyRep.Type.SECRET;
 341         } else {
 342             // XXX short term serialization for unextractable keys
 343             throw new NotSerializableException
 344                 ("Cannot serialize sensitive and unextractable keys");
 345         }
 346         return new KeyRep(type, getAlgorithm(), format, getEncoded());
 347     }
 348 
 349     public String toString() {
 350         token.ensureValid();
 351         String s1 = token.provider.getName() + " " + algorithm + " " + type
 352                 + " key, " + keyLength + " bits";
 353         s1 += " (id " + keyID + ", "
 354                 + (tokenObject ? "token" : "session") + " object";
 355         if (isPublic()) {
 356             s1 += ")";
 357         } else {
 358             s1 += ", " + (sensitive ? "" : "not ") + "sensitive";
 359             s1 += ", " + (extractable ? "" : "un") + "extractable)";
 360         }
 361         return s1;
 362     }
 363 
 364     /**
 365      * Return bit length of the key.
 366      */
 367     @Override
 368     public int length() {
 369         return keyLength;
 370     }
 371 
 372     boolean isPublic() {
 373         return type == PUBLIC;
 374     }
 375 
 376     boolean isPrivate() {
 377         return type == PRIVATE;
 378     }
 379 
 380     boolean isSecret() {
 381         return type == SECRET;
 382     }
 383 
 384     void fetchAttributes(CK_ATTRIBUTE[] attributes) {
 385         Session tempSession = null;
 386         try {
 387             tempSession = token.getOpSession();
 388             this.incNativeKeyRef();
 389             try {
 390                 token.p11.C_GetAttributeValue(tempSession.id(), keyID,
 391                         attributes);
 392             } finally {
 393                 this.decNativeKeyRef();
 394             }
 395         } catch (PKCS11Exception e) {
 396             throw new ProviderException(e);
 397         } finally {
 398             token.releaseSession(tempSession);
 399         }
 400     }
 401 
 402     private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0];
 403 
 404     private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID,
 405             CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) {
 406         if (knownAttributes == null) {
 407             knownAttributes = A0;
 408         }
 409         for (int i = 0; i < desiredAttributes.length; i++) {
 410             // For each desired attribute, check to see if we have the value
 411             // available already. If everything is here, we save a native call.
 412             CK_ATTRIBUTE attr = desiredAttributes[i];
 413             for (CK_ATTRIBUTE known : knownAttributes) {
 414                 if ((attr.type == known.type) && (known.pValue != null)) {
 415                     attr.pValue = known.pValue;
 416                     break; // break inner for loop
 417                 }
 418             }
 419             if (attr.pValue == null) {
 420                 // nothing found, need to call C_GetAttributeValue()
 421                 for (int j = 0; j < i; j++) {
 422                     // clear values copied from knownAttributes
 423                     desiredAttributes[j].pValue = null;
 424                 }
 425                 try {
 426                     session.token.p11.C_GetAttributeValue
 427                             (session.id(), keyID, desiredAttributes);
 428                 } catch (PKCS11Exception e) {
 429                     throw new ProviderException(e);
 430                 }
 431                 break; // break loop, goto return
 432             }
 433         }
 434         return desiredAttributes;
 435     }
 436 
 437     static SecretKey secretKey(Session session, long keyID, String algorithm,
 438             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 439         attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
 440             new CK_ATTRIBUTE(CKA_TOKEN),
 441             new CK_ATTRIBUTE(CKA_SENSITIVE),
 442             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
 443         });
 444         return new P11SecretKey(session, keyID, algorithm, keyLength,
 445                 attributes, tmpNativeKey);
 446     }
 447 
 448     static SecretKey masterSecretKey(Session session, long keyID, String algorithm,
 449             int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor,
 450             boolean tmpNativeKey) {
 451         attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
 452             new CK_ATTRIBUTE(CKA_TOKEN),
 453             new CK_ATTRIBUTE(CKA_SENSITIVE),
 454             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
 455         });
 456         return new P11TlsMasterSecretKey(
 457                 session, keyID, algorithm, keyLength, attributes, major,
 458                 minor, tmpNativeKey);
 459     }
 460 
 461     // we assume that all components of public keys are always accessible
 462     static PublicKey publicKey(Session session, long keyID, String algorithm,
 463             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 464         switch (algorithm) {
 465             case "RSA":
 466                 return new P11RSAPublicKey(session, keyID, algorithm,
 467                         keyLength, attributes, tmpNativeKey);
 468             case "DSA":
 469                 return new P11DSAPublicKey(session, keyID, algorithm,
 470                         keyLength, attributes, tmpNativeKey);
 471             case "DH":
 472                 return new P11DHPublicKey(session, keyID, algorithm,
 473                         keyLength, attributes, tmpNativeKey);
 474             case "EC":
 475                 return new P11ECPublicKey(session, keyID, algorithm,
 476                         keyLength, attributes, tmpNativeKey);
 477             default:
 478                 throw new ProviderException
 479                     ("Unknown public key algorithm " + algorithm);
 480         }
 481     }
 482 
 483     static PrivateKey privateKey(Session session, long keyID, String algorithm,
 484             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 485         attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
 486             new CK_ATTRIBUTE(CKA_TOKEN),
 487             new CK_ATTRIBUTE(CKA_SENSITIVE),
 488             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
 489         });
 490         if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {
 491             return new P11PrivateKey
 492                 (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 493         } else {
 494             switch (algorithm) {
 495                 case "RSA":
 496                     // In order to decide if this is RSA CRT key, we first query
 497                     // and see if all extra CRT attributes are available.
 498                     CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] {
 499                         new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 500                         new CK_ATTRIBUTE(CKA_PRIME_1),
 501                         new CK_ATTRIBUTE(CKA_PRIME_2),
 502                         new CK_ATTRIBUTE(CKA_EXPONENT_1),
 503                         new CK_ATTRIBUTE(CKA_EXPONENT_2),
 504                         new CK_ATTRIBUTE(CKA_COEFFICIENT),
 505                     };
 506                     boolean crtKey;
 507                     try {
 508                         session.token.p11.C_GetAttributeValue
 509                             (session.id(), keyID, attrs2);
 510                         crtKey = ((attrs2[0].pValue instanceof byte[]) &&
 511                                   (attrs2[1].pValue instanceof byte[]) &&
 512                                   (attrs2[2].pValue instanceof byte[]) &&
 513                                   (attrs2[3].pValue instanceof byte[]) &&
 514                                   (attrs2[4].pValue instanceof byte[]) &&
 515                                   (attrs2[5].pValue instanceof byte[])) ;
 516                     } catch (PKCS11Exception e) {
 517                         // ignore, assume not available
 518                         crtKey = false;
 519                     }
 520                     if (crtKey) {
 521                         return new P11RSAPrivateKey(session, keyID, algorithm,
 522                                 keyLength, attributes, attrs2, tmpNativeKey);
 523                     } else {
 524                         return new P11RSAPrivateNonCRTKey(session, keyID,
 525                                 algorithm, keyLength, attributes, tmpNativeKey);
 526                     }
 527                 case "DSA":
 528                     return new P11DSAPrivateKey(session, keyID, algorithm,
 529                             keyLength, attributes, tmpNativeKey);
 530                 case "DH":
 531                     return new P11DHPrivateKey(session, keyID, algorithm,
 532                             keyLength, attributes, tmpNativeKey);
 533                 case "EC":
 534                     return new P11ECPrivateKey(session, keyID, algorithm,
 535                             keyLength, attributes, tmpNativeKey);
 536                 default:
 537                     throw new ProviderException
 538                             ("Unknown private key algorithm " + algorithm);
 539             }
 540         }
 541     }
 542 
 543     // class for sensitive and unextractable private keys
 544     private static final class P11PrivateKey extends P11Key
 545                                                 implements PrivateKey {
 546         private static final long serialVersionUID = -2138581185214187615L;
 547 
 548         P11PrivateKey(Session session, long keyID, String algorithm,
 549                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 550             super(PRIVATE, session, keyID, algorithm, keyLength, attributes,
 551                     tmpNativeKey);
 552         }
 553         // XXX temporary encoding for serialization purposes
 554         public String getFormat() {
 555             token.ensureValid();
 556             return null;
 557         }
 558         byte[] getEncodedInternal() {
 559             token.ensureValid();
 560             return null;
 561         }
 562     }
 563 
 564     private static class P11SecretKey extends P11Key implements SecretKey {
 565         private static final long serialVersionUID = -7828241727014329084L;
 566         private volatile byte[] encoded;
 567         P11SecretKey(Session session, long keyID, String algorithm,
 568                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 569             super(SECRET, session, keyID, algorithm, keyLength, attributes,
 570                     tmpNativeKey);
 571         }
 572         public String getFormat() {
 573             token.ensureValid();
 574             if (sensitive || (extractable == false)) {
 575                 return null;
 576             } else {
 577                 return "RAW";
 578             }
 579         }
 580         byte[] getEncodedInternal() {
 581             token.ensureValid();
 582             if (getFormat() == null) {
 583                 return null;
 584             }
 585             byte[] b = encoded;
 586             if (b == null) {
 587                 synchronized (this) {
 588                     b = encoded;
 589                     if (b == null) {
 590                         Session tempSession = null;
 591                         try {
 592                             tempSession = token.getOpSession();
 593                             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 594                                 new CK_ATTRIBUTE(CKA_VALUE),
 595                             };
 596                             this.incNativeKeyRef();
 597                             try {
 598                                 token.p11.C_GetAttributeValue
 599                                     (tempSession.id(), keyID, attributes);
 600                             } finally {
 601                                 this.decNativeKeyRef();
 602                             }
 603                             b = attributes[0].getByteArray();
 604                         } catch (PKCS11Exception e) {
 605                             throw new ProviderException(e);
 606                         } finally {
 607                             token.releaseSession(tempSession);
 608                         }
 609                         encoded = b;
 610                     }
 611                 }
 612             }
 613             return b;
 614         }
 615     }
 616 
 617     @SuppressWarnings("deprecation")
 618     private static class P11TlsMasterSecretKey extends P11SecretKey
 619             implements TlsMasterSecret {
 620         private static final long serialVersionUID = -1318560923770573441L;
 621 
 622         private final int majorVersion, minorVersion;
 623         P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
 624                 int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor,
 625                 boolean tmpNativeKey) {
 626             super(session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 627             this.majorVersion = major;
 628             this.minorVersion = minor;
 629         }
 630         public int getMajorVersion() {
 631             return majorVersion;
 632         }
 633 
 634         public int getMinorVersion() {
 635             return minorVersion;
 636         }
 637     }
 638 
 639     // RSA CRT private key
 640     private static final class P11RSAPrivateKey extends P11Key
 641                 implements RSAPrivateCrtKey {
 642         private static final long serialVersionUID = 9215872438913515220L;
 643 
 644         private BigInteger n, e, d, p, q, pe, qe, coeff;
 645         private byte[] encoded;
 646         P11RSAPrivateKey(Session session, long keyID, String algorithm,
 647                 int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs,
 648                 boolean tmpNativeKey) {
 649             super(PRIVATE, session, keyID, algorithm, keyLength, attrs,
 650                     tmpNativeKey);
 651 
 652             for (CK_ATTRIBUTE a : crtAttrs) {
 653                 if (a.type == CKA_PUBLIC_EXPONENT) {
 654                     e = a.getBigInteger();
 655                 } else if (a.type == CKA_PRIME_1) {
 656                     p = a.getBigInteger();
 657                 } else if (a.type == CKA_PRIME_2) {
 658                     q = a.getBigInteger();
 659                 } else if (a.type == CKA_EXPONENT_1) {
 660                     pe = a.getBigInteger();
 661                 } else if (a.type == CKA_EXPONENT_2) {
 662                     qe = a.getBigInteger();
 663                 } else if (a.type == CKA_COEFFICIENT) {
 664                     coeff = a.getBigInteger();
 665                 }
 666             }
 667         }
 668         private synchronized void fetchValues() {
 669             token.ensureValid();
 670             if (n != null) {
 671                 return;
 672             }
 673             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 674                 new CK_ATTRIBUTE(CKA_MODULUS),
 675                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
 676             };
 677             fetchAttributes(attributes);
 678             n = attributes[0].getBigInteger();
 679             d = attributes[1].getBigInteger();
 680         }
 681 
 682         public String getFormat() {
 683             token.ensureValid();
 684             return "PKCS#8";
 685         }
 686         synchronized byte[] getEncodedInternal() {
 687             token.ensureValid();
 688             if (encoded == null) {
 689                 fetchValues();
 690                 try {
 691                     Key newKey = RSAPrivateCrtKeyImpl.newKey
 692                         (KeyType.RSA, null, n, e, d, p, q, pe, qe, coeff);
 693                     encoded = newKey.getEncoded();
 694                 } catch (GeneralSecurityException e) {
 695                     throw new ProviderException(e);
 696                 }
 697             }
 698             return encoded;
 699         }
 700         public BigInteger getModulus() {
 701             fetchValues();
 702             return n;
 703         }
 704         public BigInteger getPublicExponent() {
 705             return e;
 706         }
 707         public BigInteger getPrivateExponent() {
 708             fetchValues();
 709             return d;
 710         }
 711         public BigInteger getPrimeP() {
 712             return p;
 713         }
 714         public BigInteger getPrimeQ() {
 715             return q;
 716         }
 717         public BigInteger getPrimeExponentP() {
 718             return pe;
 719         }
 720         public BigInteger getPrimeExponentQ() {
 721             return qe;
 722         }
 723         public BigInteger getCrtCoefficient() {
 724             return coeff;
 725         }
 726     }
 727 
 728     // RSA non-CRT private key
 729     private static final class P11RSAPrivateNonCRTKey extends P11Key
 730                 implements RSAPrivateKey {
 731         private static final long serialVersionUID = 1137764983777411481L;
 732 
 733         private BigInteger n, d;
 734         private byte[] encoded;
 735         P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
 736                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 737             super(PRIVATE, session, keyID, algorithm, keyLength, attributes,
 738                     tmpNativeKey);
 739         }
 740         private synchronized void fetchValues() {
 741             token.ensureValid();
 742             if (n != null) {
 743                 return;
 744             }
 745             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 746                 new CK_ATTRIBUTE(CKA_MODULUS),
 747                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
 748             };
 749             fetchAttributes(attributes);
 750             n = attributes[0].getBigInteger();
 751             d = attributes[1].getBigInteger();
 752         }
 753         public String getFormat() {
 754             token.ensureValid();
 755             return "PKCS#8";
 756         }
 757         synchronized byte[] getEncodedInternal() {
 758             token.ensureValid();
 759             if (encoded == null) {
 760                 fetchValues();
 761                 try {
 762                     // XXX make constructor in SunRsaSign provider public
 763                     // and call it directly
 764                     KeyFactory factory = KeyFactory.getInstance
 765                         ("RSA", P11Util.getSunRsaSignProvider());
 766                     Key newKey = factory.translateKey(this);
 767                     encoded = newKey.getEncoded();
 768                 } catch (GeneralSecurityException e) {
 769                     throw new ProviderException(e);
 770                 }
 771             }
 772             return encoded;
 773         }
 774         public BigInteger getModulus() {
 775             fetchValues();
 776             return n;
 777         }
 778         public BigInteger getPrivateExponent() {
 779             fetchValues();
 780             return d;
 781         }
 782     }
 783 
 784     private static final class P11RSAPublicKey extends P11Key
 785                                                 implements RSAPublicKey {
 786         private static final long serialVersionUID = -826726289023854455L;
 787         private BigInteger n, e;
 788         private byte[] encoded;
 789         P11RSAPublicKey(Session session, long keyID, String algorithm,
 790                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 791             super(PUBLIC, session, keyID, algorithm, keyLength, attributes,
 792                     tmpNativeKey);
 793         }
 794         private synchronized void fetchValues() {
 795             token.ensureValid();
 796             if (n != null) {
 797                 return;
 798             }
 799             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 800                 new CK_ATTRIBUTE(CKA_MODULUS),
 801                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 802             };
 803             fetchAttributes(attributes);
 804             n = attributes[0].getBigInteger();
 805             e = attributes[1].getBigInteger();
 806         }
 807         public String getFormat() {
 808             token.ensureValid();
 809             return "X.509";
 810         }
 811         synchronized byte[] getEncodedInternal() {
 812             token.ensureValid();
 813             if (encoded == null) {
 814                 fetchValues();
 815                 try {
 816                     encoded = RSAPublicKeyImpl.newKey
 817                         (KeyType.RSA, null, n, e).getEncoded();
 818                 } catch (InvalidKeyException e) {
 819                     throw new ProviderException(e);
 820                 }
 821             }
 822             return encoded;
 823         }
 824         public BigInteger getModulus() {
 825             fetchValues();
 826             return n;
 827         }
 828         public BigInteger getPublicExponent() {
 829             fetchValues();
 830             return e;
 831         }
 832         public String toString() {
 833             fetchValues();
 834             return super.toString() +  "\n  modulus: " + n
 835                 + "\n  public exponent: " + e;
 836         }
 837     }
 838 
 839     private static final class P11DSAPublicKey extends P11Key
 840                                                 implements DSAPublicKey {
 841         private static final long serialVersionUID = 5989753793316396637L;
 842 
 843         private BigInteger y;
 844         private DSAParams params;
 845         private byte[] encoded;
 846         P11DSAPublicKey(Session session, long keyID, String algorithm,
 847                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 848             super(PUBLIC, session, keyID, algorithm, keyLength, attributes,
 849                     tmpNativeKey);
 850         }
 851         private synchronized void fetchValues() {
 852             token.ensureValid();
 853             if (y != null) {
 854                 return;
 855             }
 856             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 857                 new CK_ATTRIBUTE(CKA_VALUE),
 858                 new CK_ATTRIBUTE(CKA_PRIME),
 859                 new CK_ATTRIBUTE(CKA_SUBPRIME),
 860                 new CK_ATTRIBUTE(CKA_BASE),
 861             };
 862             fetchAttributes(attributes);
 863             y = attributes[0].getBigInteger();
 864             params = new DSAParameterSpec(
 865                 attributes[1].getBigInteger(),
 866                 attributes[2].getBigInteger(),
 867                 attributes[3].getBigInteger()
 868             );
 869         }
 870         public String getFormat() {
 871             token.ensureValid();
 872             return "X.509";
 873         }
 874         synchronized byte[] getEncodedInternal() {
 875             token.ensureValid();
 876             if (encoded == null) {
 877                 fetchValues();
 878                 try {
 879                     Key key = new sun.security.provider.DSAPublicKey
 880                             (y, params.getP(), params.getQ(), params.getG());
 881                     encoded = key.getEncoded();
 882                 } catch (InvalidKeyException e) {
 883                     throw new ProviderException(e);
 884                 }
 885             }
 886             return encoded;
 887         }
 888         public BigInteger getY() {
 889             fetchValues();
 890             return y;
 891         }
 892         public DSAParams getParams() {
 893             fetchValues();
 894             return params;
 895         }
 896         public String toString() {
 897             fetchValues();
 898             return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
 899                 + "\n  q: " + params.getQ() + "\n  g: " + params.getG();
 900         }
 901     }
 902 
 903     private static final class P11DSAPrivateKey extends P11Key
 904                                                 implements DSAPrivateKey {
 905         private static final long serialVersionUID = 3119629997181999389L;
 906 
 907         private BigInteger x;
 908         private DSAParams params;
 909         private byte[] encoded;
 910         P11DSAPrivateKey(Session session, long keyID, String algorithm,
 911                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 912             super(PRIVATE, session, keyID, algorithm, keyLength, attributes,
 913                     tmpNativeKey);
 914         }
 915         private synchronized void fetchValues() {
 916             token.ensureValid();
 917             if (x != null) {
 918                 return;
 919             }
 920             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 921                 new CK_ATTRIBUTE(CKA_VALUE),
 922                 new CK_ATTRIBUTE(CKA_PRIME),
 923                 new CK_ATTRIBUTE(CKA_SUBPRIME),
 924                 new CK_ATTRIBUTE(CKA_BASE),
 925             };
 926             fetchAttributes(attributes);
 927             x = attributes[0].getBigInteger();
 928             params = new DSAParameterSpec(
 929                 attributes[1].getBigInteger(),
 930                 attributes[2].getBigInteger(),
 931                 attributes[3].getBigInteger()
 932             );
 933         }
 934         public String getFormat() {
 935             token.ensureValid();
 936             return "PKCS#8";
 937         }
 938         synchronized byte[] getEncodedInternal() {
 939             token.ensureValid();
 940             if (encoded == null) {
 941                 fetchValues();
 942                 try {
 943                     Key key = new sun.security.provider.DSAPrivateKey
 944                             (x, params.getP(), params.getQ(), params.getG());
 945                     encoded = key.getEncoded();
 946                 } catch (InvalidKeyException e) {
 947                     throw new ProviderException(e);
 948                 }
 949             }
 950             return encoded;
 951         }
 952         public BigInteger getX() {
 953             fetchValues();
 954             return x;
 955         }
 956         public DSAParams getParams() {
 957             fetchValues();
 958             return params;
 959         }
 960     }
 961 
 962     private static final class P11DHPrivateKey extends P11Key
 963                                                 implements DHPrivateKey {
 964         private static final long serialVersionUID = -1698576167364928838L;
 965 
 966         private BigInteger x;
 967         private DHParameterSpec params;
 968         private byte[] encoded;
 969         P11DHPrivateKey(Session session, long keyID, String algorithm,
 970                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 971             super(PRIVATE, session, keyID, algorithm, keyLength, attributes,
 972                     tmpNativeKey);
 973         }
 974         private synchronized void fetchValues() {
 975             token.ensureValid();
 976             if (x != null) {
 977                 return;
 978             }
 979             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 980                 new CK_ATTRIBUTE(CKA_VALUE),
 981                 new CK_ATTRIBUTE(CKA_PRIME),
 982                 new CK_ATTRIBUTE(CKA_BASE),
 983             };
 984             fetchAttributes(attributes);
 985             x = attributes[0].getBigInteger();
 986             params = new DHParameterSpec(
 987                 attributes[1].getBigInteger(),
 988                 attributes[2].getBigInteger()
 989             );
 990         }
 991         public String getFormat() {
 992             token.ensureValid();
 993             return "PKCS#8";
 994         }
 995         synchronized byte[] getEncodedInternal() {
 996             token.ensureValid();
 997             if (encoded == null) {
 998                 fetchValues();
 999                 try {
1000                     DHPrivateKeySpec spec = new DHPrivateKeySpec
1001                         (x, params.getP(), params.getG());
1002                     KeyFactory kf = KeyFactory.getInstance
1003                         ("DH", P11Util.getSunJceProvider());
1004                     Key key = kf.generatePrivate(spec);
1005                     encoded = key.getEncoded();
1006                 } catch (GeneralSecurityException e) {
1007                     throw new ProviderException(e);
1008                 }
1009             }
1010             return encoded;
1011         }
1012         public BigInteger getX() {
1013             fetchValues();
1014             return x;
1015         }
1016         public DHParameterSpec getParams() {
1017             fetchValues();
1018             return params;
1019         }
1020         public int hashCode() {
1021             if (token.isValid() == false) {
1022                 return 0;
1023             }
1024             fetchValues();
1025             return Objects.hash(x, params.getP(), params.getG());
1026         }
1027         public boolean equals(Object obj) {
1028             if (this == obj) return true;
1029             // equals() should never throw exceptions
1030             if (token.isValid() == false) {
1031                 return false;
1032             }
1033             if (!(obj instanceof DHPrivateKey)) {
1034                 return false;
1035             }
1036             fetchValues();
1037             DHPrivateKey other = (DHPrivateKey) obj;
1038             DHParameterSpec otherParams = other.getParams();
1039             return ((this.x.compareTo(other.getX()) == 0) &&
1040                     (this.params.getP().compareTo(otherParams.getP()) == 0) &&
1041                     (this.params.getG().compareTo(otherParams.getG()) == 0));
1042         }
1043     }
1044 
1045     private static final class P11DHPublicKey extends P11Key
1046                                                 implements DHPublicKey {
1047         static final long serialVersionUID = -598383872153843657L;
1048 
1049         private BigInteger y;
1050         private DHParameterSpec params;
1051         private byte[] encoded;
1052         P11DHPublicKey(Session session, long keyID, String algorithm,
1053                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
1054             super(PUBLIC, session, keyID, algorithm, keyLength, attributes,
1055                     tmpNativeKey);
1056         }
1057         private synchronized void fetchValues() {
1058             token.ensureValid();
1059             if (y != null) {
1060                 return;
1061             }
1062             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1063                 new CK_ATTRIBUTE(CKA_VALUE),
1064                 new CK_ATTRIBUTE(CKA_PRIME),
1065                 new CK_ATTRIBUTE(CKA_BASE),
1066             };
1067             fetchAttributes(attributes);
1068             y = attributes[0].getBigInteger();
1069             params = new DHParameterSpec(
1070                 attributes[1].getBigInteger(),
1071                 attributes[2].getBigInteger()
1072             );
1073         }
1074         public String getFormat() {
1075             token.ensureValid();
1076             return "X.509";
1077         }
1078         synchronized byte[] getEncodedInternal() {
1079             token.ensureValid();
1080             if (encoded == null) {
1081                 fetchValues();
1082                 try {
1083                     DHPublicKeySpec spec = new DHPublicKeySpec
1084                         (y, params.getP(), params.getG());
1085                     KeyFactory kf = KeyFactory.getInstance
1086                         ("DH", P11Util.getSunJceProvider());
1087                     Key key = kf.generatePublic(spec);
1088                     encoded = key.getEncoded();
1089                 } catch (GeneralSecurityException e) {
1090                     throw new ProviderException(e);
1091                 }
1092             }
1093             return encoded;
1094         }
1095         public BigInteger getY() {
1096             fetchValues();
1097             return y;
1098         }
1099         public DHParameterSpec getParams() {
1100             fetchValues();
1101             return params;
1102         }
1103         public String toString() {
1104             fetchValues();
1105             return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
1106                 + "\n  g: " + params.getG();
1107         }
1108         public int hashCode() {
1109             if (token.isValid() == false) {
1110                 return 0;
1111             }
1112             fetchValues();
1113             return Objects.hash(y, params.getP(), params.getG());
1114         }
1115         public boolean equals(Object obj) {
1116             if (this == obj) return true;
1117             // equals() should never throw exceptions
1118             if (token.isValid() == false) {
1119                 return false;
1120             }
1121             if (!(obj instanceof DHPublicKey)) {
1122                 return false;
1123             }
1124             fetchValues();
1125             DHPublicKey other = (DHPublicKey) obj;
1126             DHParameterSpec otherParams = other.getParams();
1127             return ((this.y.compareTo(other.getY()) == 0) &&
1128                     (this.params.getP().compareTo(otherParams.getP()) == 0) &&
1129                     (this.params.getG().compareTo(otherParams.getG()) == 0));
1130         }
1131     }
1132 
1133     private static final class P11ECPrivateKey extends P11Key
1134                                                 implements ECPrivateKey {
1135         private static final long serialVersionUID = -7786054399510515515L;
1136 
1137         private BigInteger s;
1138         private ECParameterSpec params;
1139         private byte[] encoded;
1140         P11ECPrivateKey(Session session, long keyID, String algorithm,
1141                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
1142             super(PRIVATE, session, keyID, algorithm, keyLength, attributes,
1143                     tmpNativeKey);
1144         }
1145         private synchronized void fetchValues() {
1146             token.ensureValid();
1147             if (s != null) {
1148                 return;
1149             }
1150             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1151                 new CK_ATTRIBUTE(CKA_VALUE),
1152                 new CK_ATTRIBUTE(CKA_EC_PARAMS, params),
1153             };
1154             fetchAttributes(attributes);
1155             s = attributes[0].getBigInteger();
1156             try {
1157                 params = P11ECKeyFactory.decodeParameters
1158                             (attributes[1].getByteArray());
1159             } catch (Exception e) {
1160                 throw new RuntimeException("Could not parse key values", e);
1161             }
1162         }
1163         public String getFormat() {
1164             token.ensureValid();
1165             return "PKCS#8";
1166         }
1167         synchronized byte[] getEncodedInternal() {
1168             token.ensureValid();
1169             if (encoded == null) {
1170                 fetchValues();
1171                 try {
1172                     Key key = ECUtil.generateECPrivateKey(s, params);
1173                     encoded = key.getEncoded();
1174                 } catch (InvalidKeySpecException e) {
1175                     throw new ProviderException(e);
1176                 }
1177             }
1178             return encoded;
1179         }
1180         public BigInteger getS() {
1181             fetchValues();
1182             return s;
1183         }
1184         public ECParameterSpec getParams() {
1185             fetchValues();
1186             return params;
1187         }
1188     }
1189 
1190     private static final class P11ECPublicKey extends P11Key
1191                                                 implements ECPublicKey {
1192         private static final long serialVersionUID = -6371481375154806089L;
1193 
1194         private ECPoint w;
1195         private ECParameterSpec params;
1196         private byte[] encoded;
1197         P11ECPublicKey(Session session, long keyID, String algorithm,
1198                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
1199             super(PUBLIC, session, keyID, algorithm, keyLength, attributes,
1200                     tmpNativeKey);
1201         }
1202         private synchronized void fetchValues() {
1203             token.ensureValid();
1204             if (w != null) {
1205                 return;
1206             }
1207             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1208                 new CK_ATTRIBUTE(CKA_EC_POINT),
1209                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
1210             };
1211             fetchAttributes(attributes);
1212 
1213             try {
1214                 params = P11ECKeyFactory.decodeParameters
1215                             (attributes[1].getByteArray());
1216                 byte[] ecKey = attributes[0].getByteArray();
1217 
1218                 // Check whether the X9.63 encoding of an EC point is wrapped
1219                 // in an ASN.1 OCTET STRING
1220                 if (!token.config.getUseEcX963Encoding()) {
1221                     DerValue wECPoint = new DerValue(ecKey);
1222 
1223                     if (wECPoint.getTag() != DerValue.tag_OctetString) {
1224                         throw new IOException("Could not DER decode EC point." +
1225                             " Unexpected tag: " + wECPoint.getTag());
1226                     }
1227                     w = P11ECKeyFactory.decodePoint
1228                         (wECPoint.getDataBytes(), params.getCurve());
1229 
1230                 } else {
1231                     w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve());
1232                 }
1233 
1234             } catch (Exception e) {
1235                 throw new RuntimeException("Could not parse key values", e);
1236             }
1237         }
1238         public String getFormat() {
1239             token.ensureValid();
1240             return "X.509";
1241         }
1242         synchronized byte[] getEncodedInternal() {
1243             token.ensureValid();
1244             if (encoded == null) {
1245                 fetchValues();
1246                 try {
1247                     return ECUtil.x509EncodeECPublicKey(w, params);
1248                 } catch (InvalidKeySpecException e) {
1249                     throw new ProviderException(e);
1250                 }
1251             }
1252             return encoded;
1253         }
1254         public ECPoint getW() {
1255             fetchValues();
1256             return w;
1257         }
1258         public ECParameterSpec getParams() {
1259             fetchValues();
1260             return params;
1261         }
1262         public String toString() {
1263             fetchValues();
1264             return super.toString()
1265                 + "\n  public x coord: " + w.getAffineX()
1266                 + "\n  public y coord: " + w.getAffineY()
1267                 + "\n  parameters: " + params;
1268         }
1269     }
1270 }
1271 
1272 /*
1273  * NOTE: Must use PhantomReference here and not WeakReference
1274  * otherwise the key maybe cleared before other objects which
1275  * still use these keys during finalization such as SSLSocket.
1276  */
1277 final class SessionKeyRef extends PhantomReference<P11Key>
1278     implements Comparable<SessionKeyRef> {
1279     private static ReferenceQueue<P11Key> refQueue =
1280         new ReferenceQueue<P11Key>();
1281     private static Set<SessionKeyRef> refList =
1282         Collections.synchronizedSortedSet(new TreeSet<SessionKeyRef>());
1283 
1284     static ReferenceQueue<P11Key> referenceQueue() {
1285         return refQueue;
1286     }
1287 
1288     private static void drainRefQueueBounded() {
1289         Session sess = null;
1290         Token tkn = null;
1291         while (true) {
1292             SessionKeyRef next = (SessionKeyRef) refQueue.poll();
1293             if (next == null) {
1294                 break;
1295             }
1296 
1297             // If the token is still valid, try to remove the object
1298             if (next.session != null && next.session.token.isValid()) {
1299                 // If this key's token is the same as the previous key, the
1300                 // same session can be used for C_DestroyObject.
1301                 try {
1302                     if (next.session.token != tkn || sess == null) {
1303                         // Release session if not using previous token
1304                         if (tkn != null && sess != null) {
1305                             tkn.releaseSession(sess);
1306                             sess = null;
1307                         }
1308 
1309                         tkn = next.session.token;
1310                         sess = tkn.getOpSession();
1311                     }
1312                     next.disposeNative(sess);
1313                 } catch (PKCS11Exception e) {
1314                     // ignore
1315                 }
1316             }
1317             // Regardless of native results, dispose of java references
1318             next.dispose();
1319         }
1320 
1321         if (tkn != null && sess != null) {
1322             tkn.releaseSession(sess);
1323         }
1324     }
1325 
1326     // handle to the native key
1327     private long keyID;
1328     private Session session;
1329 
1330     SessionKeyRef(P11Key key , long keyID, Session session) {
1331         super(key, refQueue);
1332         setKeyIDAndSession(keyID, session);
1333         refList.add(this);
1334         drainRefQueueBounded();
1335     }
1336 
1337     public void setKeyIDAndSession(long keyID, Session session) {
1338         if (this.session != null) {
1339             this.session.removeObject();
1340         }
1341         this.keyID = keyID;
1342         this.session = session;
1343         if (this.session != null) {
1344             this.session.addObject();
1345         }
1346     }
1347 
1348     private void disposeNative(Session s) throws PKCS11Exception {
1349         if(this.session != null) {
1350             session.token.p11.C_DestroyObject(s.id(), keyID);
1351         }
1352     }
1353 
1354     private void dispose() {
1355         refList.remove(this);
1356         this.clear();
1357         setKeyIDAndSession(0, null);
1358     }
1359 
1360     public int compareTo(SessionKeyRef other) {
1361         if (this.keyID == other.keyID) {
1362             return 0;
1363         } else {
1364             return (this.keyID < other.keyID) ? -1 : 1;
1365         }
1366     }
1367 }