1 /*
   2  * Copyright (c) 2003, 2015, 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                     // XXX better test for RSA CRT keys (single getAttributes() call)
 344                     // we need to determine whether this is a CRT key
 345                     // see if we can obtain the public exponent
 346                     // this should also be readable for sensitive/extractable keys
 347                     CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] {
 348                         new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 349                     };
 350                     boolean crtKey;
 351                     try {
 352                         session.token.p11.C_GetAttributeValue
 353                             (session.id(), keyID, attrs2);
 354                         crtKey = (attrs2[0].pValue instanceof byte[]);
 355                     } catch (PKCS11Exception e) {
 356                         // ignore, assume not available
 357                         crtKey = false;
 358                     }
 359                     if (crtKey) {
 360                         return new P11RSAPrivateKey
 361                                 (session, keyID, algorithm, keyLength, attributes);
 362                     } else {
 363                         return new P11RSAPrivateNonCRTKey
 364                                 (session, keyID, algorithm, keyLength, attributes);
 365                     }
 366                 case "DSA":
 367                     return new P11DSAPrivateKey
 368                             (session, keyID, algorithm, keyLength, attributes);
 369                 case "DH":
 370                     return new P11DHPrivateKey
 371                             (session, keyID, algorithm, keyLength, attributes);
 372                 case "EC":
 373                     return new P11ECPrivateKey
 374                             (session, keyID, algorithm, keyLength, attributes);
 375                 default:
 376                     throw new ProviderException
 377                             ("Unknown private key algorithm " + algorithm);
 378             }
 379         }
 380     }
 381 
 382     // class for sensitive and unextractable private keys
 383     private static final class P11PrivateKey extends P11Key
 384                                                 implements PrivateKey {
 385         private static final long serialVersionUID = -2138581185214187615L;
 386 
 387         P11PrivateKey(Session session, long keyID, String algorithm,
 388                 int keyLength, CK_ATTRIBUTE[] attributes) {
 389             super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
 390         }
 391         // XXX temporary encoding for serialization purposes
 392         public String getFormat() {
 393             token.ensureValid();
 394             return null;
 395         }
 396         byte[] getEncodedInternal() {
 397             token.ensureValid();
 398             return null;
 399         }
 400     }
 401 
 402     private static class P11SecretKey extends P11Key implements SecretKey {
 403         private static final long serialVersionUID = -7828241727014329084L;
 404         private volatile byte[] encoded;
 405         P11SecretKey(Session session, long keyID, String algorithm,
 406                 int keyLength, CK_ATTRIBUTE[] attributes) {
 407             super(SECRET, session, keyID, algorithm, keyLength, attributes);
 408         }
 409         public String getFormat() {
 410             token.ensureValid();
 411             if (sensitive || (extractable == false)) {
 412                 return null;
 413             } else {
 414                 return "RAW";
 415             }
 416         }
 417         byte[] getEncodedInternal() {
 418             token.ensureValid();
 419             if (getFormat() == null) {
 420                 return null;
 421             }
 422             byte[] b = encoded;
 423             if (b == null) {
 424                 synchronized (this) {
 425                     b = encoded;
 426                     if (b == null) {
 427                         Session tempSession = null;
 428                         try {
 429                             tempSession = token.getOpSession();
 430                             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 431                                 new CK_ATTRIBUTE(CKA_VALUE),
 432                             };
 433                             token.p11.C_GetAttributeValue
 434                                 (tempSession.id(), keyID, attributes);
 435                             b = attributes[0].getByteArray();
 436                         } catch (PKCS11Exception e) {
 437                             throw new ProviderException(e);
 438                         } finally {
 439                             token.releaseSession(tempSession);
 440                         }
 441                         encoded = b;
 442                     }
 443                 }
 444             }
 445             return b;
 446         }
 447     }
 448 
 449     @SuppressWarnings("deprecation")
 450     private static class P11TlsMasterSecretKey extends P11SecretKey
 451             implements TlsMasterSecret {
 452         private static final long serialVersionUID = -1318560923770573441L;
 453 
 454         private final int majorVersion, minorVersion;
 455         P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
 456                 int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
 457             super(session, keyID, algorithm, keyLength, attributes);
 458             this.majorVersion = major;
 459             this.minorVersion = minor;
 460         }
 461         public int getMajorVersion() {
 462             return majorVersion;
 463         }
 464 
 465         public int getMinorVersion() {
 466             return minorVersion;
 467         }
 468     }
 469 
 470     // RSA CRT private key
 471     private static final class P11RSAPrivateKey extends P11Key
 472                 implements RSAPrivateCrtKey {
 473         private static final long serialVersionUID = 9215872438913515220L;
 474 
 475         private BigInteger n, e, d, p, q, pe, qe, coeff;
 476         private byte[] encoded;
 477         P11RSAPrivateKey(Session session, long keyID, String algorithm,
 478                 int keyLength, CK_ATTRIBUTE[] attributes) {
 479             super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
 480         }
 481         private synchronized void fetchValues() {
 482             token.ensureValid();
 483             if (n != null) {
 484                 return;
 485             }
 486             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 487                 new CK_ATTRIBUTE(CKA_MODULUS),
 488                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 489                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
 490                 new CK_ATTRIBUTE(CKA_PRIME_1),
 491                 new CK_ATTRIBUTE(CKA_PRIME_2),
 492                 new CK_ATTRIBUTE(CKA_EXPONENT_1),
 493                 new CK_ATTRIBUTE(CKA_EXPONENT_2),
 494                 new CK_ATTRIBUTE(CKA_COEFFICIENT),
 495             };
 496             fetchAttributes(attributes);
 497             n = attributes[0].getBigInteger();
 498             e = attributes[1].getBigInteger();
 499             d = attributes[2].getBigInteger();
 500             p = attributes[3].getBigInteger();
 501             q = attributes[4].getBigInteger();
 502             pe = attributes[5].getBigInteger();
 503             qe = attributes[6].getBigInteger();
 504             coeff = attributes[7].getBigInteger();
 505         }
 506         public String getFormat() {
 507             token.ensureValid();
 508             return "PKCS#8";
 509         }
 510         synchronized byte[] getEncodedInternal() {
 511             token.ensureValid();
 512             if (encoded == null) {
 513                 fetchValues();
 514                 try {
 515                     // XXX make constructor in SunRsaSign provider public
 516                     // and call it directly
 517                     KeyFactory factory = KeyFactory.getInstance
 518                         ("RSA", P11Util.getSunRsaSignProvider());
 519                     Key newKey = factory.translateKey(this);
 520                     encoded = newKey.getEncoded();
 521                 } catch (GeneralSecurityException e) {
 522                     throw new ProviderException(e);
 523                 }
 524             }
 525             return encoded;
 526         }
 527         public BigInteger getModulus() {
 528             fetchValues();
 529             return n;
 530         }
 531         public BigInteger getPublicExponent() {
 532             fetchValues();
 533             return e;
 534         }
 535         public BigInteger getPrivateExponent() {
 536             fetchValues();
 537             return d;
 538         }
 539         public BigInteger getPrimeP() {
 540             fetchValues();
 541             return p;
 542         }
 543         public BigInteger getPrimeQ() {
 544             fetchValues();
 545             return q;
 546         }
 547         public BigInteger getPrimeExponentP() {
 548             fetchValues();
 549             return pe;
 550         }
 551         public BigInteger getPrimeExponentQ() {
 552             fetchValues();
 553             return qe;
 554         }
 555         public BigInteger getCrtCoefficient() {
 556             fetchValues();
 557             return coeff;
 558         }
 559     }
 560 
 561     // RSA non-CRT private key
 562     private static final class P11RSAPrivateNonCRTKey extends P11Key
 563                 implements RSAPrivateKey {
 564         private static final long serialVersionUID = 1137764983777411481L;
 565 
 566         private BigInteger n, d;
 567         private byte[] encoded;
 568         P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
 569                 int keyLength, CK_ATTRIBUTE[] attributes) {
 570             super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
 571         }
 572         private synchronized void fetchValues() {
 573             token.ensureValid();
 574             if (n != null) {
 575                 return;
 576             }
 577             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 578                 new CK_ATTRIBUTE(CKA_MODULUS),
 579                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
 580             };
 581             fetchAttributes(attributes);
 582             n = attributes[0].getBigInteger();
 583             d = attributes[1].getBigInteger();
 584         }
 585         public String getFormat() {
 586             token.ensureValid();
 587             return "PKCS#8";
 588         }
 589         synchronized byte[] getEncodedInternal() {
 590             token.ensureValid();
 591             if (encoded == null) {
 592                 fetchValues();
 593                 try {
 594                     // XXX make constructor in SunRsaSign provider public
 595                     // and call it directly
 596                     KeyFactory factory = KeyFactory.getInstance
 597                         ("RSA", P11Util.getSunRsaSignProvider());
 598                     Key newKey = factory.translateKey(this);
 599                     encoded = newKey.getEncoded();
 600                 } catch (GeneralSecurityException e) {
 601                     throw new ProviderException(e);
 602                 }
 603             }
 604             return encoded;
 605         }
 606         public BigInteger getModulus() {
 607             fetchValues();
 608             return n;
 609         }
 610         public BigInteger getPrivateExponent() {
 611             fetchValues();
 612             return d;
 613         }
 614     }
 615 
 616     private static final class P11RSAPublicKey extends P11Key
 617                                                 implements RSAPublicKey {
 618         private static final long serialVersionUID = -826726289023854455L;
 619 
 620         private BigInteger n, e;
 621         private byte[] encoded;
 622         P11RSAPublicKey(Session session, long keyID, String algorithm,
 623                 int keyLength, CK_ATTRIBUTE[] attributes) {
 624             super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
 625         }
 626         private synchronized void fetchValues() {
 627             token.ensureValid();
 628             if (n != null) {
 629                 return;
 630             }
 631             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 632                 new CK_ATTRIBUTE(CKA_MODULUS),
 633                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
 634             };
 635             fetchAttributes(attributes);
 636             n = attributes[0].getBigInteger();
 637             e = attributes[1].getBigInteger();
 638         }
 639         public String getFormat() {
 640             token.ensureValid();
 641             return "X.509";
 642         }
 643         synchronized byte[] getEncodedInternal() {
 644             token.ensureValid();
 645             if (encoded == null) {
 646                 fetchValues();
 647                 try {
 648                     encoded = new RSAPublicKeyImpl(n, e).getEncoded();
 649                 } catch (InvalidKeyException e) {
 650                     throw new ProviderException(e);
 651                 }
 652             }
 653             return encoded;
 654         }
 655         public BigInteger getModulus() {
 656             fetchValues();
 657             return n;
 658         }
 659         public BigInteger getPublicExponent() {
 660             fetchValues();
 661             return e;
 662         }
 663         public String toString() {
 664             fetchValues();
 665             return super.toString() +  "\n  modulus: " + n
 666                 + "\n  public exponent: " + e;
 667         }
 668     }
 669 
 670     private static final class P11DSAPublicKey extends P11Key
 671                                                 implements DSAPublicKey {
 672         private static final long serialVersionUID = 5989753793316396637L;
 673 
 674         private BigInteger y;
 675         private DSAParams params;
 676         private byte[] encoded;
 677         P11DSAPublicKey(Session session, long keyID, String algorithm,
 678                 int keyLength, CK_ATTRIBUTE[] attributes) {
 679             super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
 680         }
 681         private synchronized void fetchValues() {
 682             token.ensureValid();
 683             if (y != null) {
 684                 return;
 685             }
 686             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 687                 new CK_ATTRIBUTE(CKA_VALUE),
 688                 new CK_ATTRIBUTE(CKA_PRIME),
 689                 new CK_ATTRIBUTE(CKA_SUBPRIME),
 690                 new CK_ATTRIBUTE(CKA_BASE),
 691             };
 692             fetchAttributes(attributes);
 693             y = attributes[0].getBigInteger();
 694             params = new DSAParameterSpec(
 695                 attributes[1].getBigInteger(),
 696                 attributes[2].getBigInteger(),
 697                 attributes[3].getBigInteger()
 698             );
 699         }
 700         public String getFormat() {
 701             token.ensureValid();
 702             return "X.509";
 703         }
 704         synchronized byte[] getEncodedInternal() {
 705             token.ensureValid();
 706             if (encoded == null) {
 707                 fetchValues();
 708                 try {
 709                     Key key = new sun.security.provider.DSAPublicKey
 710                             (y, params.getP(), params.getQ(), params.getG());
 711                     encoded = key.getEncoded();
 712                 } catch (InvalidKeyException e) {
 713                     throw new ProviderException(e);
 714                 }
 715             }
 716             return encoded;
 717         }
 718         public BigInteger getY() {
 719             fetchValues();
 720             return y;
 721         }
 722         public DSAParams getParams() {
 723             fetchValues();
 724             return params;
 725         }
 726         public String toString() {
 727             fetchValues();
 728             return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
 729                 + "\n  q: " + params.getQ() + "\n  g: " + params.getG();
 730         }
 731     }
 732 
 733     private static final class P11DSAPrivateKey extends P11Key
 734                                                 implements DSAPrivateKey {
 735         private static final long serialVersionUID = 3119629997181999389L;
 736 
 737         private BigInteger x;
 738         private DSAParams params;
 739         private byte[] encoded;
 740         P11DSAPrivateKey(Session session, long keyID, String algorithm,
 741                 int keyLength, CK_ATTRIBUTE[] attributes) {
 742             super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
 743         }
 744         private synchronized void fetchValues() {
 745             token.ensureValid();
 746             if (x != null) {
 747                 return;
 748             }
 749             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 750                 new CK_ATTRIBUTE(CKA_VALUE),
 751                 new CK_ATTRIBUTE(CKA_PRIME),
 752                 new CK_ATTRIBUTE(CKA_SUBPRIME),
 753                 new CK_ATTRIBUTE(CKA_BASE),
 754             };
 755             fetchAttributes(attributes);
 756             x = attributes[0].getBigInteger();
 757             params = new DSAParameterSpec(
 758                 attributes[1].getBigInteger(),
 759                 attributes[2].getBigInteger(),
 760                 attributes[3].getBigInteger()
 761             );
 762         }
 763         public String getFormat() {
 764             token.ensureValid();
 765             return "PKCS#8";
 766         }
 767         synchronized byte[] getEncodedInternal() {
 768             token.ensureValid();
 769             if (encoded == null) {
 770                 fetchValues();
 771                 try {
 772                     Key key = new sun.security.provider.DSAPrivateKey
 773                             (x, params.getP(), params.getQ(), params.getG());
 774                     encoded = key.getEncoded();
 775                 } catch (InvalidKeyException e) {
 776                     throw new ProviderException(e);
 777                 }
 778             }
 779             return encoded;
 780         }
 781         public BigInteger getX() {
 782             fetchValues();
 783             return x;
 784         }
 785         public DSAParams getParams() {
 786             fetchValues();
 787             return params;
 788         }
 789     }
 790 
 791     private static final class P11DHPrivateKey extends P11Key
 792                                                 implements DHPrivateKey {
 793         private static final long serialVersionUID = -1698576167364928838L;
 794 
 795         private BigInteger x;
 796         private DHParameterSpec params;
 797         private byte[] encoded;
 798         P11DHPrivateKey(Session session, long keyID, String algorithm,
 799                 int keyLength, CK_ATTRIBUTE[] attributes) {
 800             super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
 801         }
 802         private synchronized void fetchValues() {
 803             token.ensureValid();
 804             if (x != null) {
 805                 return;
 806             }
 807             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 808                 new CK_ATTRIBUTE(CKA_VALUE),
 809                 new CK_ATTRIBUTE(CKA_PRIME),
 810                 new CK_ATTRIBUTE(CKA_BASE),
 811             };
 812             fetchAttributes(attributes);
 813             x = attributes[0].getBigInteger();
 814             params = new DHParameterSpec(
 815                 attributes[1].getBigInteger(),
 816                 attributes[2].getBigInteger()
 817             );
 818         }
 819         public String getFormat() {
 820             token.ensureValid();
 821             return "PKCS#8";
 822         }
 823         synchronized byte[] getEncodedInternal() {
 824             token.ensureValid();
 825             if (encoded == null) {
 826                 fetchValues();
 827                 try {
 828                     DHPrivateKeySpec spec = new DHPrivateKeySpec
 829                         (x, params.getP(), params.getG());
 830                     KeyFactory kf = KeyFactory.getInstance
 831                         ("DH", P11Util.getSunJceProvider());
 832                     Key key = kf.generatePrivate(spec);
 833                     encoded = key.getEncoded();
 834                 } catch (GeneralSecurityException e) {
 835                     throw new ProviderException(e);
 836                 }
 837             }
 838             return encoded;
 839         }
 840         public BigInteger getX() {
 841             fetchValues();
 842             return x;
 843         }
 844         public DHParameterSpec getParams() {
 845             fetchValues();
 846             return params;
 847         }
 848         public int hashCode() {
 849             if (token.isValid() == false) {
 850                 return 0;
 851             }
 852             fetchValues();
 853             return Objects.hash(x, params.getP(), params.getG());
 854         }
 855         public boolean equals(Object obj) {
 856             if (this == obj) return true;
 857             // equals() should never throw exceptions
 858             if (token.isValid() == false) {
 859                 return false;
 860             }
 861             if (!(obj instanceof DHPrivateKey)) {
 862                 return false;
 863             }
 864             fetchValues();
 865             DHPrivateKey other = (DHPrivateKey) obj;
 866             DHParameterSpec otherParams = other.getParams();
 867             return ((this.x.compareTo(other.getX()) == 0) &&
 868                     (this.params.getP().compareTo(otherParams.getP()) == 0) &&
 869                     (this.params.getG().compareTo(otherParams.getG()) == 0));
 870         }
 871     }
 872 
 873     private static final class P11DHPublicKey extends P11Key
 874                                                 implements DHPublicKey {
 875         static final long serialVersionUID = -598383872153843657L;
 876 
 877         private BigInteger y;
 878         private DHParameterSpec params;
 879         private byte[] encoded;
 880         P11DHPublicKey(Session session, long keyID, String algorithm,
 881                 int keyLength, CK_ATTRIBUTE[] attributes) {
 882             super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
 883         }
 884         private synchronized void fetchValues() {
 885             token.ensureValid();
 886             if (y != null) {
 887                 return;
 888             }
 889             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 890                 new CK_ATTRIBUTE(CKA_VALUE),
 891                 new CK_ATTRIBUTE(CKA_PRIME),
 892                 new CK_ATTRIBUTE(CKA_BASE),
 893             };
 894             fetchAttributes(attributes);
 895             y = attributes[0].getBigInteger();
 896             params = new DHParameterSpec(
 897                 attributes[1].getBigInteger(),
 898                 attributes[2].getBigInteger()
 899             );
 900         }
 901         public String getFormat() {
 902             token.ensureValid();
 903             return "X.509";
 904         }
 905         synchronized byte[] getEncodedInternal() {
 906             token.ensureValid();
 907             if (encoded == null) {
 908                 fetchValues();
 909                 try {
 910                     DHPublicKeySpec spec = new DHPublicKeySpec
 911                         (y, params.getP(), params.getG());
 912                     KeyFactory kf = KeyFactory.getInstance
 913                         ("DH", P11Util.getSunJceProvider());
 914                     Key key = kf.generatePublic(spec);
 915                     encoded = key.getEncoded();
 916                 } catch (GeneralSecurityException e) {
 917                     throw new ProviderException(e);
 918                 }
 919             }
 920             return encoded;
 921         }
 922         public BigInteger getY() {
 923             fetchValues();
 924             return y;
 925         }
 926         public DHParameterSpec getParams() {
 927             fetchValues();
 928             return params;
 929         }
 930         public String toString() {
 931             fetchValues();
 932             return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
 933                 + "\n  g: " + params.getG();
 934         }
 935         public int hashCode() {
 936             if (token.isValid() == false) {
 937                 return 0;
 938             }
 939             fetchValues();
 940             return Objects.hash(y, params.getP(), params.getG());
 941         }
 942         public boolean equals(Object obj) {
 943             if (this == obj) return true;
 944             // equals() should never throw exceptions
 945             if (token.isValid() == false) {
 946                 return false;
 947             }
 948             if (!(obj instanceof DHPublicKey)) {
 949                 return false;
 950             }
 951             fetchValues();
 952             DHPublicKey other = (DHPublicKey) obj;
 953             DHParameterSpec otherParams = other.getParams();
 954             return ((this.y.compareTo(other.getY()) == 0) &&
 955                     (this.params.getP().compareTo(otherParams.getP()) == 0) &&
 956                     (this.params.getG().compareTo(otherParams.getG()) == 0));
 957         }
 958     }
 959 
 960     private static final class P11ECPrivateKey extends P11Key
 961                                                 implements ECPrivateKey {
 962         private static final long serialVersionUID = -7786054399510515515L;
 963 
 964         private BigInteger s;
 965         private ECParameterSpec params;
 966         private byte[] encoded;
 967         P11ECPrivateKey(Session session, long keyID, String algorithm,
 968                 int keyLength, CK_ATTRIBUTE[] attributes) {
 969             super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
 970         }
 971         private synchronized void fetchValues() {
 972             token.ensureValid();
 973             if (s != null) {
 974                 return;
 975             }
 976             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 977                 new CK_ATTRIBUTE(CKA_VALUE),
 978                 new CK_ATTRIBUTE(CKA_EC_PARAMS, params),
 979             };
 980             fetchAttributes(attributes);
 981             s = attributes[0].getBigInteger();
 982             try {
 983                 params = P11ECKeyFactory.decodeParameters
 984                             (attributes[1].getByteArray());
 985             } catch (Exception e) {
 986                 throw new RuntimeException("Could not parse key values", e);
 987             }
 988         }
 989         public String getFormat() {
 990             token.ensureValid();
 991             return "PKCS#8";
 992         }
 993         synchronized byte[] getEncodedInternal() {
 994             token.ensureValid();
 995             if (encoded == null) {
 996                 fetchValues();
 997                 try {
 998                     Key key = ECUtil.generateECPrivateKey(s, params);
 999                     encoded = key.getEncoded();
1000                 } catch (InvalidKeySpecException e) {
1001                     throw new ProviderException(e);
1002                 }
1003             }
1004             return encoded;
1005         }
1006         public BigInteger getS() {
1007             fetchValues();
1008             return s;
1009         }
1010         public ECParameterSpec getParams() {
1011             fetchValues();
1012             return params;
1013         }
1014     }
1015 
1016     private static final class P11ECPublicKey extends P11Key
1017                                                 implements ECPublicKey {
1018         private static final long serialVersionUID = -6371481375154806089L;
1019 
1020         private ECPoint w;
1021         private ECParameterSpec params;
1022         private byte[] encoded;
1023         P11ECPublicKey(Session session, long keyID, String algorithm,
1024                 int keyLength, CK_ATTRIBUTE[] attributes) {
1025             super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
1026         }
1027         private synchronized void fetchValues() {
1028             token.ensureValid();
1029             if (w != null) {
1030                 return;
1031             }
1032             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1033                 new CK_ATTRIBUTE(CKA_EC_POINT),
1034                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
1035             };
1036             fetchAttributes(attributes);
1037 
1038             try {
1039                 params = P11ECKeyFactory.decodeParameters
1040                             (attributes[1].getByteArray());
1041                 byte[] ecKey = attributes[0].getByteArray();
1042 
1043                 // Check whether the X9.63 encoding of an EC point is wrapped
1044                 // in an ASN.1 OCTET STRING
1045                 if (!token.config.getUseEcX963Encoding()) {
1046                     DerValue wECPoint = new DerValue(ecKey);
1047 
1048                     if (wECPoint.getTag() != DerValue.tag_OctetString) {
1049                         throw new IOException("Could not DER decode EC point." +
1050                             " Unexpected tag: " + wECPoint.getTag());
1051                     }
1052                     w = P11ECKeyFactory.decodePoint
1053                         (wECPoint.getDataBytes(), params.getCurve());
1054 
1055                 } else {
1056                     w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve());
1057                 }
1058 
1059             } catch (Exception e) {
1060                 throw new RuntimeException("Could not parse key values", e);
1061             }
1062         }
1063         public String getFormat() {
1064             token.ensureValid();
1065             return "X.509";
1066         }
1067         synchronized byte[] getEncodedInternal() {
1068             token.ensureValid();
1069             if (encoded == null) {
1070                 fetchValues();
1071                 try {
1072                     return ECUtil.x509EncodeECPublicKey(w, params);
1073                 } catch (InvalidKeySpecException e) {
1074                     throw new ProviderException(e);
1075                 }
1076             }
1077             return encoded;
1078         }
1079         public ECPoint getW() {
1080             fetchValues();
1081             return w;
1082         }
1083         public ECParameterSpec getParams() {
1084             fetchValues();
1085             return params;
1086         }
1087         public String toString() {
1088             fetchValues();
1089             return super.toString()
1090                 + "\n  public x coord: " + w.getAffineX()
1091                 + "\n  public y coord: " + w.getAffineY()
1092                 + "\n  parameters: " + params;
1093         }
1094     }
1095 }
1096 
1097 /*
1098  * NOTE: Must use PhantomReference here and not WeakReference
1099  * otherwise the key maybe cleared before other objects which
1100  * still use these keys during finalization such as SSLSocket.
1101  */
1102 final class SessionKeyRef extends PhantomReference<P11Key>
1103     implements Comparable<SessionKeyRef> {
1104     private static ReferenceQueue<P11Key> refQueue =
1105         new ReferenceQueue<P11Key>();
1106     private static Set<SessionKeyRef> refList =
1107         Collections.synchronizedSortedSet(new TreeSet<SessionKeyRef>());
1108 
1109     static ReferenceQueue<P11Key> referenceQueue() {
1110         return refQueue;
1111     }
1112 
1113     private static void drainRefQueueBounded() {
1114         Session sess = null;
1115         Token tkn = null;
1116         while (true) {
1117             SessionKeyRef next = (SessionKeyRef) refQueue.poll();
1118             if (next == null) {
1119                 break;
1120             }
1121 
1122             // If the token is still valid, try to remove the object
1123             if (next.session.token.isValid()) {
1124                 // If this key's token is the same as the previous key, the
1125                 // same session can be used for C_DestroyObject.
1126                 try {
1127                     if (next.session.token != tkn || sess == null) {
1128                         // Release session if not using previous token
1129                         if (tkn != null && sess != null) {
1130                             tkn.releaseSession(sess);
1131                             sess = null;
1132                         }
1133 
1134                         tkn = next.session.token;
1135                         sess = tkn.getOpSession();
1136                     }
1137 
1138                     next.disposeNative(sess);
1139                 } catch (PKCS11Exception e) {
1140                     // ignore
1141                 }
1142             }
1143             // Regardless of native results, dispose of java references
1144             next.dispose();
1145         }
1146 
1147         if (tkn != null && sess != null) {
1148             tkn.releaseSession(sess);
1149         }
1150     }
1151 
1152     // handle to the native key
1153     private long keyID;
1154     private Session session;
1155 
1156     SessionKeyRef(P11Key key , long keyID, Session session) {
1157         super(key, refQueue);
1158         this.keyID = keyID;
1159         this.session = session;
1160         this.session.addObject();
1161         refList.add(this);
1162         drainRefQueueBounded();
1163     }
1164 
1165     private void disposeNative(Session s) throws PKCS11Exception {
1166         session.token.p11.C_DestroyObject(s.id(), keyID);
1167     }
1168 
1169     private void dispose() {
1170         refList.remove(this);
1171         this.clear();
1172         session.removeObject();
1173     }
1174 
1175     public int compareTo(SessionKeyRef other) {
1176         if (this.keyID == other.keyID) {
1177             return 0;
1178         } else {
1179             return (this.keyID < other.keyID) ? -1 : 1;
1180         }
1181     }
1182 }