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