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     // Native key objects may be temporal: only exist if in use.
 100     private boolean tmpNativeKey;
 101 
 102     private int nativeKeyRefCounting;
 103 
 104     private static long nativeKeyWrapperKeyID = -1;
 105 
 106     // Buffer to hold native key objects info in Java heap in order to
 107     // re-create them if needed.
 108     private byte[] nativeKeyInfo;
 109 
 110     P11Key(String type, Session session, long keyID, String algorithm,
 111             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 112         this.type = type;
 113         this.token = session.token;
 114         this.keyID = keyID;
 115         this.algorithm = algorithm;
 116         this.keyLength = keyLength;
 117         boolean tokenObject = false;
 118         boolean sensitive = false;
 119         boolean extractable = true;
 120         int n = (attributes == null) ? 0 : attributes.length;
 121         for (int i = 0; i < n; i++) {
 122             CK_ATTRIBUTE attr = attributes[i];
 123             if (attr.type == CKA_TOKEN) {
 124                 tokenObject = attr.getBoolean();
 125             } else if (attr.type == CKA_SENSITIVE) {
 126                 sensitive = attr.getBoolean();
 127             } else if (attr.type == CKA_EXTRACTABLE) {
 128                 extractable = attr.getBoolean();
 129             }
 130         }
 131         this.tokenObject = tokenObject;
 132         this.sensitive = sensitive;
 133         this.extractable = extractable;
 134         if (token.tokenInfo.label[0] == 'N'
 135                 && token.tokenInfo.label[1] == 'S'
 136                 && token.tokenInfo.label[2] == 'S') {
 137             this.tmpNativeKey = tmpNativeKey;
 138         } else {
 139             // Disabled if token is not NSS
 140             this.tmpNativeKey = false;
 141         }
 142         // Disable temporary native keys if the key is not extractable or sensitive
 143         // (when key is a token "secret").
 144         // The foundation for extracting wrapped sensitive keys is in
 145         // place but it's currently disabled for token secrets because:
 146         //      1) there is a bug in NSSDB for persisting CKO_SECRET_KEY
 147         //         in legacy DB (key3.db) which corrupts the key; and,
 148         //      2) sqlite DB (key4.db) is still not supported by SunPKCS11 (see JDK-8165996).
 149         if (!extractable || (sensitive && type.equals(SECRET) && tokenObject)) {
 150             this.tmpNativeKey = false;
 151         }
 152         if (this.tmpNativeKey) {
 153             nativeKeyRefCounting = 0;
 154             try {
 155                 if (sensitive && nativeKeyWrapperKeyID == -1) {
 156                     synchronized(this) {
 157                         if (nativeKeyWrapperKeyID == -1) {
 158                             // Create a global wrapping/unwrapping key
 159                             CK_ATTRIBUTE[] wrappingAttributes = token.getAttributes
 160                             (O_GENERATE, CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] {
 161                                     new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
 162                                     new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3),
 163                                 });
 164                             Session wrappingSession = null;
 165                             try {
 166                                 wrappingSession = token.getObjSession();
 167                                 nativeKeyWrapperKeyID = token.p11.C_GenerateKey
 168                                         (wrappingSession.id(), new CK_MECHANISM(CKM_AES_KEY_GEN), wrappingAttributes);
 169                             } finally {
 170                                 token.releaseSession(wrappingSession);
 171                             }
 172                         }
 173                     }
 174                 }
 175                 nativeKeyInfo = token.p11.getNativeKeyInfo(session.id(), keyID, nativeKeyWrapperKeyID);
 176                 if (nativeKeyInfo != null && nativeKeyInfo.length > 0) {
 177                     destroyNativeKey();
 178                     // If extracted, it's not a token object anymore
 179                     tokenObject = false;
 180                 } else {
 181                     this.tmpNativeKey = false;
 182                 }
 183             } catch (PKCS11Exception e) {
 184                 // Unexpected behaviour when trying to manage the key life-time.
 185                 // Don't manage the native key.
 186                 this.tmpNativeKey = false;
 187             }
 188         }
 189         if (tokenObject == false) {
 190             sessionKeyRef = new SessionKeyRef(this, this.keyID, session);
 191         } else {
 192             sessionKeyRef = null;
 193         }
 194     }
 195 
 196     public void incNativeKeyRef() throws PKCS11Exception {
 197         if (tmpNativeKey) {
 198             synchronized(this) {
 199                 if (++nativeKeyRefCounting == 1 && nativeKeyInfo != null) {
 200                     // Create a Native Key
 201                     Session session = null;
 202                     try {
 203                         session = token.getObjSession();
 204                         keyID = token.p11.createNativeKey(session.id(), nativeKeyInfo, nativeKeyWrapperKeyID);
 205                         if (sessionKeyRef != null) {
 206                             sessionKeyRef.setKeyIDAndSession(keyID, session);
 207                         }
 208                     } catch (PKCS11Exception e) {
 209                         nativeKeyRefCounting--;
 210                         throw e;
 211                     } finally {
 212                         token.releaseSession(session);
 213                     }
 214                 }
 215             }
 216         }
 217     }
 218 
 219     public void makeNativeKeyPersistent() throws PKCS11Exception {
 220         if (tmpNativeKey) {
 221             synchronized(this) {
 222                 if (nativeKeyRefCounting == 0) {
 223                     this.incNativeKeyRef();
 224                 }
 225 
 226                 // This write is not sync protected because reads are done out of the
 227                 // synchronization block. It's just a best-effort.
 228                 tmpNativeKey = false;
 229 
 230                 // This write is sync protected and provides the real guarantee to avoid
 231                 // native key creation or destruction.
 232                 nativeKeyInfo = null;
 233             }
 234         }
 235     }
 236 
 237     public void decNativeKeyRef() {
 238         if (tmpNativeKey) {
 239             synchronized(this) {
 240                 if(--nativeKeyRefCounting == 0  && nativeKeyInfo != null) {
 241                     try {
 242                         destroyNativeKey();
 243                     } catch (PKCS11Exception e) {
 244                         // This is a best-effort.
 245                     }
 246                 }
 247                 if (nativeKeyRefCounting < 0) {
 248                     nativeKeyRefCounting = 0;
 249                 }
 250             }
 251         }
 252     }
 253 
 254     private void destroyNativeKey() throws PKCS11Exception {
 255         // There is no synchronization needed between SessionKeyRef.disposeNative and
 256         // this method. When SessionKeyRef.disposeNative method is executed, no P11Key
 257         // 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, attributes);
 391             } finally {
 392                 this.decNativeKeyRef();
 393             }
 394         } catch (PKCS11Exception e) {
 395             throw new ProviderException(e);
 396         } finally {
 397             token.releaseSession(tempSession);
 398         }
 399     }
 400 
 401     private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0];
 402 
 403     private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID,
 404             CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) {
 405         if (knownAttributes == null) {
 406             knownAttributes = A0;
 407         }
 408         for (int i = 0; i < desiredAttributes.length; i++) {
 409             // For each desired attribute, check to see if we have the value
 410             // available already. If everything is here, we save a native call.
 411             CK_ATTRIBUTE attr = desiredAttributes[i];
 412             for (CK_ATTRIBUTE known : knownAttributes) {
 413                 if ((attr.type == known.type) && (known.pValue != null)) {
 414                     attr.pValue = known.pValue;
 415                     break; // break inner for loop
 416                 }
 417             }
 418             if (attr.pValue == null) {
 419                 // nothing found, need to call C_GetAttributeValue()
 420                 for (int j = 0; j < i; j++) {
 421                     // clear values copied from knownAttributes
 422                     desiredAttributes[j].pValue = null;
 423                 }
 424                 try {
 425                     session.token.p11.C_GetAttributeValue
 426                             (session.id(), keyID, desiredAttributes);
 427                 } catch (PKCS11Exception e) {
 428                     throw new ProviderException(e);
 429                 }
 430                 break; // break loop, goto return
 431             }
 432         }
 433         return desiredAttributes;
 434     }
 435 
 436     static SecretKey secretKey(Session session, long keyID, String algorithm,
 437             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 438         attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
 439             new CK_ATTRIBUTE(CKA_TOKEN),
 440             new CK_ATTRIBUTE(CKA_SENSITIVE),
 441             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
 442         });
 443         return new P11SecretKey(session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 444     }
 445 
 446     static SecretKey masterSecretKey(Session session, long keyID, String algorithm,
 447             int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor,
 448             boolean tmpNativeKey) {
 449         attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
 450             new CK_ATTRIBUTE(CKA_TOKEN),
 451             new CK_ATTRIBUTE(CKA_SENSITIVE),
 452             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
 453         });
 454         return new P11TlsMasterSecretKey
 455                 (session, keyID, algorithm, keyLength, attributes, major, minor,
 456                 tmpNativeKey);
 457     }
 458 
 459     // we assume that all components of public keys are always accessible
 460     static PublicKey publicKey(Session session, long keyID, String algorithm,
 461             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 462         switch (algorithm) {
 463             case "RSA":
 464                 return new P11RSAPublicKey
 465                     (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 466             case "DSA":
 467                 return new P11DSAPublicKey
 468                     (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 469             case "DH":
 470                 return new P11DHPublicKey
 471                     (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 472             case "EC":
 473                 return new P11ECPublicKey
 474                     (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 475             default:
 476                 throw new ProviderException
 477                     ("Unknown public key algorithm " + algorithm);
 478         }
 479     }
 480 
 481     static PrivateKey privateKey(Session session, long keyID, String algorithm,
 482             int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 483         attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
 484             new CK_ATTRIBUTE(CKA_TOKEN),
 485             new CK_ATTRIBUTE(CKA_SENSITIVE),
 486             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
 487         });
 488         if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {
 489             return new P11PrivateKey
 490                 (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 491         } else {
 492             switch (algorithm) {
 493                 case "RSA":
 494                     // In order to decide if this is RSA CRT key, we first query
 495                     // and see if all extra CRT attributes are available.
 496                     CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] {
 497                         new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 498                         new CK_ATTRIBUTE(CKA_PRIME_1),
 499                         new CK_ATTRIBUTE(CKA_PRIME_2),
 500                         new CK_ATTRIBUTE(CKA_EXPONENT_1),
 501                         new CK_ATTRIBUTE(CKA_EXPONENT_2),
 502                         new CK_ATTRIBUTE(CKA_COEFFICIENT),
 503                     };
 504                     boolean crtKey;
 505                     try {
 506                         session.token.p11.C_GetAttributeValue
 507                             (session.id(), keyID, attrs2);
 508                         crtKey = ((attrs2[0].pValue instanceof byte[]) &&
 509                                   (attrs2[1].pValue instanceof byte[]) &&
 510                                   (attrs2[2].pValue instanceof byte[]) &&
 511                                   (attrs2[3].pValue instanceof byte[]) &&
 512                                   (attrs2[4].pValue instanceof byte[]) &&
 513                                   (attrs2[5].pValue instanceof byte[])) ;
 514                     } catch (PKCS11Exception e) {
 515                         // ignore, assume not available
 516                         crtKey = false;
 517                     }
 518                     if (crtKey) {
 519                         return new P11RSAPrivateKey
 520                                 (session, keyID, algorithm, keyLength, attributes, attrs2, tmpNativeKey);
 521                     } else {
 522                         return new P11RSAPrivateNonCRTKey
 523                                 (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 524                     }
 525                 case "DSA":
 526                     return new P11DSAPrivateKey
 527                             (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 528                 case "DH":
 529                     return new P11DHPrivateKey
 530                             (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 531                 case "EC":
 532                     return new P11ECPrivateKey
 533                             (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 534                 default:
 535                     throw new ProviderException
 536                             ("Unknown private key algorithm " + algorithm);
 537             }
 538         }
 539     }
 540 
 541     // class for sensitive and unextractable private keys
 542     private static final class P11PrivateKey extends P11Key
 543                                                 implements PrivateKey {
 544         private static final long serialVersionUID = -2138581185214187615L;
 545 
 546         P11PrivateKey(Session session, long keyID, String algorithm,
 547                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 548             super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 549         }
 550         // XXX temporary encoding for serialization purposes
 551         public String getFormat() {
 552             token.ensureValid();
 553             return null;
 554         }
 555         byte[] getEncodedInternal() {
 556             token.ensureValid();
 557             return null;
 558         }
 559     }
 560 
 561     private static class P11SecretKey extends P11Key implements SecretKey {
 562         private static final long serialVersionUID = -7828241727014329084L;
 563         private volatile byte[] encoded;
 564         P11SecretKey(Session session, long keyID, String algorithm,
 565                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 566             super(SECRET, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 567         }
 568         public String getFormat() {
 569             token.ensureValid();
 570             if (sensitive || (extractable == false)) {
 571                 return null;
 572             } else {
 573                 return "RAW";
 574             }
 575         }
 576         byte[] getEncodedInternal() {
 577             token.ensureValid();
 578             if (getFormat() == null) {
 579                 return null;
 580             }
 581             byte[] b = encoded;
 582             if (b == null) {
 583                 synchronized (this) {
 584                     b = encoded;
 585                     if (b == null) {
 586                         Session tempSession = null;
 587                         try {
 588                             tempSession = token.getOpSession();
 589                             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 590                                 new CK_ATTRIBUTE(CKA_VALUE),
 591                             };
 592                             this.incNativeKeyRef();
 593                             try {
 594                                 token.p11.C_GetAttributeValue
 595                                     (tempSession.id(), keyID, attributes);
 596                             } finally {
 597                                 this.decNativeKeyRef();
 598                             }
 599                             b = attributes[0].getByteArray();
 600                         } catch (PKCS11Exception e) {
 601                             throw new ProviderException(e);
 602                         } finally {
 603                             token.releaseSession(tempSession);
 604                         }
 605                         encoded = b;
 606                     }
 607                 }
 608             }
 609             return b;
 610         }
 611     }
 612 
 613     @SuppressWarnings("deprecation")
 614     private static class P11TlsMasterSecretKey extends P11SecretKey
 615             implements TlsMasterSecret {
 616         private static final long serialVersionUID = -1318560923770573441L;
 617 
 618         private final int majorVersion, minorVersion;
 619         P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
 620                 int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor,
 621                 boolean tmpNativeKey) {
 622             super(session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 623             this.majorVersion = major;
 624             this.minorVersion = minor;
 625         }
 626         public int getMajorVersion() {
 627             return majorVersion;
 628         }
 629 
 630         public int getMinorVersion() {
 631             return minorVersion;
 632         }
 633     }
 634 
 635     // RSA CRT private key
 636     private static final class P11RSAPrivateKey extends P11Key
 637                 implements RSAPrivateCrtKey {
 638         private static final long serialVersionUID = 9215872438913515220L;
 639 
 640         private BigInteger n, e, d, p, q, pe, qe, coeff;
 641         private byte[] encoded;
 642         P11RSAPrivateKey(Session session, long keyID, String algorithm,
 643                 int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs, boolean tmpNativeKey) {
 644             super(PRIVATE, session, keyID, algorithm, keyLength, attrs, tmpNativeKey);
 645 
 646             for (CK_ATTRIBUTE a : crtAttrs) {
 647                 if (a.type == CKA_PUBLIC_EXPONENT) {
 648                     e = a.getBigInteger();
 649                 } else if (a.type == CKA_PRIME_1) {
 650                     p = a.getBigInteger();
 651                 } else if (a.type == CKA_PRIME_2) {
 652                     q = a.getBigInteger();
 653                 } else if (a.type == CKA_EXPONENT_1) {
 654                     pe = a.getBigInteger();
 655                 } else if (a.type == CKA_EXPONENT_2) {
 656                     qe = a.getBigInteger();
 657                 } else if (a.type == CKA_COEFFICIENT) {
 658                     coeff = a.getBigInteger();
 659                 }
 660             }
 661         }
 662         private synchronized void fetchValues() {
 663             token.ensureValid();
 664             if (n != null) {
 665                 return;
 666             }
 667             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 668                 new CK_ATTRIBUTE(CKA_MODULUS),
 669                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
 670             };
 671             fetchAttributes(attributes);
 672             n = attributes[0].getBigInteger();
 673             d = attributes[1].getBigInteger();
 674         }
 675 
 676         public String getFormat() {
 677             token.ensureValid();
 678             return "PKCS#8";
 679         }
 680         synchronized byte[] getEncodedInternal() {
 681             token.ensureValid();
 682             if (encoded == null) {
 683                 fetchValues();
 684                 try {
 685                     Key newKey = RSAPrivateCrtKeyImpl.newKey
 686                         (KeyType.RSA, null, n, e, d, p, q, pe, qe, coeff);
 687                     encoded = newKey.getEncoded();
 688                 } catch (GeneralSecurityException e) {
 689                     throw new ProviderException(e);
 690                 }
 691             }
 692             return encoded;
 693         }
 694         public BigInteger getModulus() {
 695             fetchValues();
 696             return n;
 697         }
 698         public BigInteger getPublicExponent() {
 699             return e;
 700         }
 701         public BigInteger getPrivateExponent() {
 702             fetchValues();
 703             return d;
 704         }
 705         public BigInteger getPrimeP() {
 706             return p;
 707         }
 708         public BigInteger getPrimeQ() {
 709             return q;
 710         }
 711         public BigInteger getPrimeExponentP() {
 712             return pe;
 713         }
 714         public BigInteger getPrimeExponentQ() {
 715             return qe;
 716         }
 717         public BigInteger getCrtCoefficient() {
 718             return coeff;
 719         }
 720     }
 721 
 722     // RSA non-CRT private key
 723     private static final class P11RSAPrivateNonCRTKey extends P11Key
 724                 implements RSAPrivateKey {
 725         private static final long serialVersionUID = 1137764983777411481L;
 726 
 727         private BigInteger n, d;
 728         private byte[] encoded;
 729         P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
 730                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 731             super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 732         }
 733         private synchronized void fetchValues() {
 734             token.ensureValid();
 735             if (n != null) {
 736                 return;
 737             }
 738             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 739                 new CK_ATTRIBUTE(CKA_MODULUS),
 740                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
 741             };
 742             fetchAttributes(attributes);
 743             n = attributes[0].getBigInteger();
 744             d = attributes[1].getBigInteger();
 745         }
 746         public String getFormat() {
 747             token.ensureValid();
 748             return "PKCS#8";
 749         }
 750         synchronized byte[] getEncodedInternal() {
 751             token.ensureValid();
 752             if (encoded == null) {
 753                 fetchValues();
 754                 try {
 755                     // XXX make constructor in SunRsaSign provider public
 756                     // and call it directly
 757                     KeyFactory factory = KeyFactory.getInstance
 758                         ("RSA", P11Util.getSunRsaSignProvider());
 759                     Key newKey = factory.translateKey(this);
 760                     encoded = newKey.getEncoded();
 761                 } catch (GeneralSecurityException e) {
 762                     throw new ProviderException(e);
 763                 }
 764             }
 765             return encoded;
 766         }
 767         public BigInteger getModulus() {
 768             fetchValues();
 769             return n;
 770         }
 771         public BigInteger getPrivateExponent() {
 772             fetchValues();
 773             return d;
 774         }
 775     }
 776 
 777     private static final class P11RSAPublicKey extends P11Key
 778                                                 implements RSAPublicKey {
 779         private static final long serialVersionUID = -826726289023854455L;
 780         private BigInteger n, e;
 781         private byte[] encoded;
 782         P11RSAPublicKey(Session session, long keyID, String algorithm,
 783                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 784             super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 785         }
 786         private synchronized void fetchValues() {
 787             token.ensureValid();
 788             if (n != null) {
 789                 return;
 790             }
 791             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 792                 new CK_ATTRIBUTE(CKA_MODULUS),
 793                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 794             };
 795             fetchAttributes(attributes);
 796             n = attributes[0].getBigInteger();
 797             e = attributes[1].getBigInteger();
 798         }
 799         public String getFormat() {
 800             token.ensureValid();
 801             return "X.509";
 802         }
 803         synchronized byte[] getEncodedInternal() {
 804             token.ensureValid();
 805             if (encoded == null) {
 806                 fetchValues();
 807                 try {
 808                     encoded = RSAPublicKeyImpl.newKey
 809                         (KeyType.RSA, null, n, e).getEncoded();
 810                 } catch (InvalidKeyException e) {
 811                     throw new ProviderException(e);
 812                 }
 813             }
 814             return encoded;
 815         }
 816         public BigInteger getModulus() {
 817             fetchValues();
 818             return n;
 819         }
 820         public BigInteger getPublicExponent() {
 821             fetchValues();
 822             return e;
 823         }
 824         public String toString() {
 825             fetchValues();
 826             return super.toString() +  "\n  modulus: " + n
 827                 + "\n  public exponent: " + e;
 828         }
 829     }
 830 
 831     private static final class P11DSAPublicKey extends P11Key
 832                                                 implements DSAPublicKey {
 833         private static final long serialVersionUID = 5989753793316396637L;
 834 
 835         private BigInteger y;
 836         private DSAParams params;
 837         private byte[] encoded;
 838         P11DSAPublicKey(Session session, long keyID, String algorithm,
 839                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 840             super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 841         }
 842         private synchronized void fetchValues() {
 843             token.ensureValid();
 844             if (y != null) {
 845                 return;
 846             }
 847             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 848                 new CK_ATTRIBUTE(CKA_VALUE),
 849                 new CK_ATTRIBUTE(CKA_PRIME),
 850                 new CK_ATTRIBUTE(CKA_SUBPRIME),
 851                 new CK_ATTRIBUTE(CKA_BASE),
 852             };
 853             fetchAttributes(attributes);
 854             y = attributes[0].getBigInteger();
 855             params = new DSAParameterSpec(
 856                 attributes[1].getBigInteger(),
 857                 attributes[2].getBigInteger(),
 858                 attributes[3].getBigInteger()
 859             );
 860         }
 861         public String getFormat() {
 862             token.ensureValid();
 863             return "X.509";
 864         }
 865         synchronized byte[] getEncodedInternal() {
 866             token.ensureValid();
 867             if (encoded == null) {
 868                 fetchValues();
 869                 try {
 870                     Key key = new sun.security.provider.DSAPublicKey
 871                             (y, params.getP(), params.getQ(), params.getG());
 872                     encoded = key.getEncoded();
 873                 } catch (InvalidKeyException e) {
 874                     throw new ProviderException(e);
 875                 }
 876             }
 877             return encoded;
 878         }
 879         public BigInteger getY() {
 880             fetchValues();
 881             return y;
 882         }
 883         public DSAParams getParams() {
 884             fetchValues();
 885             return params;
 886         }
 887         public String toString() {
 888             fetchValues();
 889             return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
 890                 + "\n  q: " + params.getQ() + "\n  g: " + params.getG();
 891         }
 892     }
 893 
 894     private static final class P11DSAPrivateKey extends P11Key
 895                                                 implements DSAPrivateKey {
 896         private static final long serialVersionUID = 3119629997181999389L;
 897 
 898         private BigInteger x;
 899         private DSAParams params;
 900         private byte[] encoded;
 901         P11DSAPrivateKey(Session session, long keyID, String algorithm,
 902                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 903             super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 904         }
 905         private synchronized void fetchValues() {
 906             token.ensureValid();
 907             if (x != null) {
 908                 return;
 909             }
 910             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 911                 new CK_ATTRIBUTE(CKA_VALUE),
 912                 new CK_ATTRIBUTE(CKA_PRIME),
 913                 new CK_ATTRIBUTE(CKA_SUBPRIME),
 914                 new CK_ATTRIBUTE(CKA_BASE),
 915             };
 916             fetchAttributes(attributes);
 917             x = attributes[0].getBigInteger();
 918             params = new DSAParameterSpec(
 919                 attributes[1].getBigInteger(),
 920                 attributes[2].getBigInteger(),
 921                 attributes[3].getBigInteger()
 922             );
 923         }
 924         public String getFormat() {
 925             token.ensureValid();
 926             return "PKCS#8";
 927         }
 928         synchronized byte[] getEncodedInternal() {
 929             token.ensureValid();
 930             if (encoded == null) {
 931                 fetchValues();
 932                 try {
 933                     Key key = new sun.security.provider.DSAPrivateKey
 934                             (x, params.getP(), params.getQ(), params.getG());
 935                     encoded = key.getEncoded();
 936                 } catch (InvalidKeyException e) {
 937                     throw new ProviderException(e);
 938                 }
 939             }
 940             return encoded;
 941         }
 942         public BigInteger getX() {
 943             fetchValues();
 944             return x;
 945         }
 946         public DSAParams getParams() {
 947             fetchValues();
 948             return params;
 949         }
 950     }
 951 
 952     private static final class P11DHPrivateKey extends P11Key
 953                                                 implements DHPrivateKey {
 954         private static final long serialVersionUID = -1698576167364928838L;
 955 
 956         private BigInteger x;
 957         private DHParameterSpec params;
 958         private byte[] encoded;
 959         P11DHPrivateKey(Session session, long keyID, String algorithm,
 960                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
 961             super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
 962         }
 963         private synchronized void fetchValues() {
 964             token.ensureValid();
 965             if (x != null) {
 966                 return;
 967             }
 968             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 969                 new CK_ATTRIBUTE(CKA_VALUE),
 970                 new CK_ATTRIBUTE(CKA_PRIME),
 971                 new CK_ATTRIBUTE(CKA_BASE),
 972             };
 973             fetchAttributes(attributes);
 974             x = attributes[0].getBigInteger();
 975             params = new DHParameterSpec(
 976                 attributes[1].getBigInteger(),
 977                 attributes[2].getBigInteger()
 978             );
 979         }
 980         public String getFormat() {
 981             token.ensureValid();
 982             return "PKCS#8";
 983         }
 984         synchronized byte[] getEncodedInternal() {
 985             token.ensureValid();
 986             if (encoded == null) {
 987                 fetchValues();
 988                 try {
 989                     DHPrivateKeySpec spec = new DHPrivateKeySpec
 990                         (x, params.getP(), params.getG());
 991                     KeyFactory kf = KeyFactory.getInstance
 992                         ("DH", P11Util.getSunJceProvider());
 993                     Key key = kf.generatePrivate(spec);
 994                     encoded = key.getEncoded();
 995                 } catch (GeneralSecurityException e) {
 996                     throw new ProviderException(e);
 997                 }
 998             }
 999             return encoded;
1000         }
1001         public BigInteger getX() {
1002             fetchValues();
1003             return x;
1004         }
1005         public DHParameterSpec getParams() {
1006             fetchValues();
1007             return params;
1008         }
1009         public int hashCode() {
1010             if (token.isValid() == false) {
1011                 return 0;
1012             }
1013             fetchValues();
1014             return Objects.hash(x, params.getP(), params.getG());
1015         }
1016         public boolean equals(Object obj) {
1017             if (this == obj) return true;
1018             // equals() should never throw exceptions
1019             if (token.isValid() == false) {
1020                 return false;
1021             }
1022             if (!(obj instanceof DHPrivateKey)) {
1023                 return false;
1024             }
1025             fetchValues();
1026             DHPrivateKey other = (DHPrivateKey) obj;
1027             DHParameterSpec otherParams = other.getParams();
1028             return ((this.x.compareTo(other.getX()) == 0) &&
1029                     (this.params.getP().compareTo(otherParams.getP()) == 0) &&
1030                     (this.params.getG().compareTo(otherParams.getG()) == 0));
1031         }
1032     }
1033 
1034     private static final class P11DHPublicKey extends P11Key
1035                                                 implements DHPublicKey {
1036         static final long serialVersionUID = -598383872153843657L;
1037 
1038         private BigInteger y;
1039         private DHParameterSpec params;
1040         private byte[] encoded;
1041         P11DHPublicKey(Session session, long keyID, String algorithm,
1042                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
1043             super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
1044         }
1045         private synchronized void fetchValues() {
1046             token.ensureValid();
1047             if (y != null) {
1048                 return;
1049             }
1050             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1051                 new CK_ATTRIBUTE(CKA_VALUE),
1052                 new CK_ATTRIBUTE(CKA_PRIME),
1053                 new CK_ATTRIBUTE(CKA_BASE),
1054             };
1055             fetchAttributes(attributes);
1056             y = attributes[0].getBigInteger();
1057             params = new DHParameterSpec(
1058                 attributes[1].getBigInteger(),
1059                 attributes[2].getBigInteger()
1060             );
1061         }
1062         public String getFormat() {
1063             token.ensureValid();
1064             return "X.509";
1065         }
1066         synchronized byte[] getEncodedInternal() {
1067             token.ensureValid();
1068             if (encoded == null) {
1069                 fetchValues();
1070                 try {
1071                     DHPublicKeySpec spec = new DHPublicKeySpec
1072                         (y, params.getP(), params.getG());
1073                     KeyFactory kf = KeyFactory.getInstance
1074                         ("DH", P11Util.getSunJceProvider());
1075                     Key key = kf.generatePublic(spec);
1076                     encoded = key.getEncoded();
1077                 } catch (GeneralSecurityException e) {
1078                     throw new ProviderException(e);
1079                 }
1080             }
1081             return encoded;
1082         }
1083         public BigInteger getY() {
1084             fetchValues();
1085             return y;
1086         }
1087         public DHParameterSpec getParams() {
1088             fetchValues();
1089             return params;
1090         }
1091         public String toString() {
1092             fetchValues();
1093             return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
1094                 + "\n  g: " + params.getG();
1095         }
1096         public int hashCode() {
1097             if (token.isValid() == false) {
1098                 return 0;
1099             }
1100             fetchValues();
1101             return Objects.hash(y, params.getP(), params.getG());
1102         }
1103         public boolean equals(Object obj) {
1104             if (this == obj) return true;
1105             // equals() should never throw exceptions
1106             if (token.isValid() == false) {
1107                 return false;
1108             }
1109             if (!(obj instanceof DHPublicKey)) {
1110                 return false;
1111             }
1112             fetchValues();
1113             DHPublicKey other = (DHPublicKey) obj;
1114             DHParameterSpec otherParams = other.getParams();
1115             return ((this.y.compareTo(other.getY()) == 0) &&
1116                     (this.params.getP().compareTo(otherParams.getP()) == 0) &&
1117                     (this.params.getG().compareTo(otherParams.getG()) == 0));
1118         }
1119     }
1120 
1121     private static final class P11ECPrivateKey extends P11Key
1122                                                 implements ECPrivateKey {
1123         private static final long serialVersionUID = -7786054399510515515L;
1124 
1125         private BigInteger s;
1126         private ECParameterSpec params;
1127         private byte[] encoded;
1128         P11ECPrivateKey(Session session, long keyID, String algorithm,
1129                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
1130             super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
1131         }
1132         private synchronized void fetchValues() {
1133             token.ensureValid();
1134             if (s != null) {
1135                 return;
1136             }
1137             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1138                 new CK_ATTRIBUTE(CKA_VALUE),
1139                 new CK_ATTRIBUTE(CKA_EC_PARAMS, params),
1140             };
1141             fetchAttributes(attributes);
1142             s = attributes[0].getBigInteger();
1143             try {
1144                 params = P11ECKeyFactory.decodeParameters
1145                             (attributes[1].getByteArray());
1146             } catch (Exception e) {
1147                 throw new RuntimeException("Could not parse key values", e);
1148             }
1149         }
1150         public String getFormat() {
1151             token.ensureValid();
1152             return "PKCS#8";
1153         }
1154         synchronized byte[] getEncodedInternal() {
1155             token.ensureValid();
1156             if (encoded == null) {
1157                 fetchValues();
1158                 try {
1159                     Key key = ECUtil.generateECPrivateKey(s, params);
1160                     encoded = key.getEncoded();
1161                 } catch (InvalidKeySpecException e) {
1162                     throw new ProviderException(e);
1163                 }
1164             }
1165             return encoded;
1166         }
1167         public BigInteger getS() {
1168             fetchValues();
1169             return s;
1170         }
1171         public ECParameterSpec getParams() {
1172             fetchValues();
1173             return params;
1174         }
1175     }
1176 
1177     private static final class P11ECPublicKey extends P11Key
1178                                                 implements ECPublicKey {
1179         private static final long serialVersionUID = -6371481375154806089L;
1180 
1181         private ECPoint w;
1182         private ECParameterSpec params;
1183         private byte[] encoded;
1184         P11ECPublicKey(Session session, long keyID, String algorithm,
1185                 int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
1186             super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
1187         }
1188         private synchronized void fetchValues() {
1189             token.ensureValid();
1190             if (w != null) {
1191                 return;
1192             }
1193             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1194                 new CK_ATTRIBUTE(CKA_EC_POINT),
1195                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
1196             };
1197             fetchAttributes(attributes);
1198 
1199             try {
1200                 params = P11ECKeyFactory.decodeParameters
1201                             (attributes[1].getByteArray());
1202                 byte[] ecKey = attributes[0].getByteArray();
1203 
1204                 // Check whether the X9.63 encoding of an EC point is wrapped
1205                 // in an ASN.1 OCTET STRING
1206                 if (!token.config.getUseEcX963Encoding()) {
1207                     DerValue wECPoint = new DerValue(ecKey);
1208 
1209                     if (wECPoint.getTag() != DerValue.tag_OctetString) {
1210                         throw new IOException("Could not DER decode EC point." +
1211                             " Unexpected tag: " + wECPoint.getTag());
1212                     }
1213                     w = P11ECKeyFactory.decodePoint
1214                         (wECPoint.getDataBytes(), params.getCurve());
1215 
1216                 } else {
1217                     w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve());
1218                 }
1219 
1220             } catch (Exception e) {
1221                 throw new RuntimeException("Could not parse key values", e);
1222             }
1223         }
1224         public String getFormat() {
1225             token.ensureValid();
1226             return "X.509";
1227         }
1228         synchronized byte[] getEncodedInternal() {
1229             token.ensureValid();
1230             if (encoded == null) {
1231                 fetchValues();
1232                 try {
1233                     return ECUtil.x509EncodeECPublicKey(w, params);
1234                 } catch (InvalidKeySpecException e) {
1235                     throw new ProviderException(e);
1236                 }
1237             }
1238             return encoded;
1239         }
1240         public ECPoint getW() {
1241             fetchValues();
1242             return w;
1243         }
1244         public ECParameterSpec getParams() {
1245             fetchValues();
1246             return params;
1247         }
1248         public String toString() {
1249             fetchValues();
1250             return super.toString()
1251                 + "\n  public x coord: " + w.getAffineX()
1252                 + "\n  public y coord: " + w.getAffineY()
1253                 + "\n  parameters: " + params;
1254         }
1255     }
1256 }
1257 
1258 /*
1259  * NOTE: Must use PhantomReference here and not WeakReference
1260  * otherwise the key maybe cleared before other objects which
1261  * still use these keys during finalization such as SSLSocket.
1262  */
1263 final class SessionKeyRef extends PhantomReference<P11Key>
1264     implements Comparable<SessionKeyRef> {
1265     private static ReferenceQueue<P11Key> refQueue =
1266         new ReferenceQueue<P11Key>();
1267     private static Set<SessionKeyRef> refList =
1268         Collections.synchronizedSortedSet(new TreeSet<SessionKeyRef>());
1269 
1270     static ReferenceQueue<P11Key> referenceQueue() {
1271         return refQueue;
1272     }
1273 
1274     private static void drainRefQueueBounded() {
1275         Session sess = null;
1276         Token tkn = null;
1277         while (true) {
1278             SessionKeyRef next = (SessionKeyRef) refQueue.poll();
1279             if (next == null) {
1280                 break;
1281             }
1282 
1283             // If the token is still valid, try to remove the object
1284             if (next.session != null && next.session.token.isValid()) {
1285                 // If this key's token is the same as the previous key, the
1286                 // same session can be used for C_DestroyObject.
1287                 try {
1288                     if (next.session.token != tkn || sess == null) {
1289                         // Release session if not using previous token
1290                         if (tkn != null && sess != null) {
1291                             tkn.releaseSession(sess);
1292                             sess = null;
1293                         }
1294 
1295                         tkn = next.session.token;
1296                         sess = tkn.getOpSession();
1297                     }
1298                     next.disposeNative(sess);
1299                 } catch (PKCS11Exception e) {
1300                     // ignore
1301                 }
1302             }
1303             // Regardless of native results, dispose of java references
1304             next.dispose();
1305         }
1306 
1307         if (tkn != null && sess != null) {
1308             tkn.releaseSession(sess);
1309         }
1310     }
1311 
1312     // handle to the native key
1313     private long keyID;
1314     private Session session;
1315 
1316     SessionKeyRef(P11Key key , long keyID, Session session) {
1317         super(key, refQueue);
1318         setKeyIDAndSession(keyID, session);
1319         refList.add(this);
1320         drainRefQueueBounded();
1321     }
1322 
1323     public void setKeyIDAndSession(long keyID, Session session) {
1324         if (this.session != null) {
1325             this.session.removeObject();
1326         }
1327         this.keyID = keyID;
1328         this.session = session;
1329         if (this.session != null) {
1330             this.session.addObject();
1331         }
1332     }
1333 
1334     private void disposeNative(Session s) throws PKCS11Exception {
1335         if(this.session != null) {
1336             session.token.p11.C_DestroyObject(s.id(), keyID);
1337         }
1338     }
1339 
1340     private void dispose() {
1341         refList.remove(this);
1342         this.clear();
1343         setKeyIDAndSession(0, null);
1344     }
1345 
1346     public int compareTo(SessionKeyRef other) {
1347         if (this.keyID == other.keyID) {
1348             return 0;
1349         } else {
1350             return (this.keyID < other.keyID) ? -1 : 1;
1351         }
1352     }
1353 }