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