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