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