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