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