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