Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs11/P11Key.java
38919 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs11;2627import java.io.*;28import java.lang.ref.*;29import java.math.BigInteger;30import java.util.*;31import java.security.*;32import java.security.interfaces.*;33import java.security.spec.*;3435import javax.crypto.*;36import javax.crypto.interfaces.*;37import javax.crypto.spec.*;3839import sun.security.rsa.RSAUtil.KeyType;40import sun.security.rsa.RSAPublicKeyImpl;41import sun.security.rsa.RSAPrivateCrtKeyImpl;4243import sun.security.internal.interfaces.TlsMasterSecret;4445import sun.security.pkcs11.wrapper.*;4647import static sun.security.pkcs11.TemplateManager.O_GENERATE;48import static sun.security.pkcs11.wrapper.PKCS11Constants.*;4950import sun.security.util.DerValue;51import sun.security.util.Length;5253import sun.security.jca.JCAUtil;5455/**56* Key implementation classes.57*58* In PKCS#11, the components of private and secret keys may or may not59* be accessible. If they are, we use the algorithm specific key classes60* (e.g. DSAPrivateKey) for compatibility with existing applications.61* If the components are not accessible, we use a generic class that62* only implements PrivateKey (or SecretKey). Whether the components of a63* key are extractable is automatically determined when the key object is64* created.65*66* @author Andreas Sterbenz67* @since 1.568*/69abstract class P11Key implements Key, Length {7071private static final long serialVersionUID = -2575874101938349339L;7273private final static String PUBLIC = "public";74private final static String PRIVATE = "private";75private final static String SECRET = "secret";7677// type of key, one of (PUBLIC, PRIVATE, SECRET)78final String type;7980// token instance81final Token token;8283// algorithm name, returned by getAlgorithm(), etc.84final String algorithm;8586// effective key length of the key, e.g. 56 for a DES key87final int keyLength;8889// flags indicating whether the key is a token object, sensitive, extractable90final boolean tokenObject, sensitive, extractable;9192private final NativeKeyHolder keyIDHolder;9394private static final boolean DISABLE_NATIVE_KEYS_EXTRACTION;9596/**97* {@systemProperty sun.security.pkcs11.disableKeyExtraction} property98* indicating whether or not cryptographic keys within tokens are99* extracted to a Java byte array for memory management purposes.100*101* Key extraction affects NSS PKCS11 library only.102*103*/104static {105PrivilegedAction<String> getKeyExtractionProp =106() -> System.getProperty(107"sun.security.pkcs11.disableKeyExtraction", "false");108String disableKeyExtraction =109AccessController.doPrivileged(getKeyExtractionProp);110DISABLE_NATIVE_KEYS_EXTRACTION =111"true".equalsIgnoreCase(disableKeyExtraction);112}113114P11Key(String type, Session session, long keyID, String algorithm,115int keyLength, CK_ATTRIBUTE[] attributes) {116this.type = type;117this.token = session.token;118this.algorithm = algorithm;119this.keyLength = keyLength;120boolean tokenObject = false;121boolean sensitive = false;122boolean extractable = true;123int n = (attributes == null) ? 0 : attributes.length;124for (int i = 0; i < n; i++) {125CK_ATTRIBUTE attr = attributes[i];126if (attr.type == CKA_TOKEN) {127tokenObject = attr.getBoolean();128} else if (attr.type == CKA_SENSITIVE) {129sensitive = attr.getBoolean();130} else if (attr.type == CKA_EXTRACTABLE) {131extractable = attr.getBoolean();132}133}134this.tokenObject = tokenObject;135this.sensitive = sensitive;136this.extractable = extractable;137char[] tokenLabel = this.token.tokenInfo.label;138boolean isNSS = (tokenLabel[0] == 'N' && tokenLabel[1] == 'S'139&& tokenLabel[2] == 'S');140boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS &&141extractable && !tokenObject);142this.keyIDHolder = new NativeKeyHolder(this, keyID, session, extractKeyInfo,143tokenObject);144}145146public long getKeyID() {147return keyIDHolder.getKeyID();148}149150public void releaseKeyID() {151keyIDHolder.releaseKeyID();152}153154// see JCA spec155public final String getAlgorithm() {156token.ensureValid();157return algorithm;158}159160// see JCA spec161public final byte[] getEncoded() {162byte[] b = getEncodedInternal();163return (b == null) ? null : b.clone();164}165166abstract byte[] getEncodedInternal();167168public boolean equals(Object obj) {169if (this == obj) {170return true;171}172// equals() should never throw exceptions173if (token.isValid() == false) {174return false;175}176if (obj instanceof Key == false) {177return false;178}179String thisFormat = getFormat();180if (thisFormat == null) {181// no encoding, key only equal to itself182// XXX getEncoded() for unextractable keys will change that183return false;184}185Key other = (Key)obj;186if (thisFormat.equals(other.getFormat()) == false) {187return false;188}189byte[] thisEnc = this.getEncodedInternal();190byte[] otherEnc;191if (obj instanceof P11Key) {192otherEnc = ((P11Key)other).getEncodedInternal();193} else {194otherEnc = other.getEncoded();195}196return MessageDigest.isEqual(thisEnc, otherEnc);197}198199public int hashCode() {200// hashCode() should never throw exceptions201if (token.isValid() == false) {202return 0;203}204byte[] b1 = getEncodedInternal();205if (b1 == null) {206return 0;207}208int r = b1.length;209for (int i = 0; i < b1.length; i++) {210r += (b1[i] & 0xff) * 37;211}212return r;213}214215protected Object writeReplace() throws ObjectStreamException {216KeyRep.Type type;217String format = getFormat();218if (isPrivate() && "PKCS#8".equals(format)) {219type = KeyRep.Type.PRIVATE;220} else if (isPublic() && "X.509".equals(format)) {221type = KeyRep.Type.PUBLIC;222} else if (isSecret() && "RAW".equals(format)) {223type = KeyRep.Type.SECRET;224} else {225// XXX short term serialization for unextractable keys226throw new NotSerializableException227("Cannot serialize sensitive and unextractable keys");228}229return new KeyRep(type, getAlgorithm(), format, getEncoded());230}231232public String toString() {233token.ensureValid();234String s1 = token.provider.getName() + " " + algorithm + " " + type235+ " key, " + keyLength + " bits";236s1 += (tokenObject ? "token" : "session") + " object";237if (isPublic()) {238s1 += ")";239} else {240s1 += ", " + (sensitive ? "" : "not ") + "sensitive";241s1 += ", " + (extractable ? "" : "un") + "extractable)";242}243return s1;244}245246/**247* Return bit length of the key.248*/249@Override250public int length() {251return keyLength;252}253254boolean isPublic() {255return type == PUBLIC;256}257258boolean isPrivate() {259return type == PRIVATE;260}261262boolean isSecret() {263return type == SECRET;264}265266void fetchAttributes(CK_ATTRIBUTE[] attributes) {267Session tempSession = null;268long keyID = this.getKeyID();269try {270tempSession = token.getOpSession();271token.p11.C_GetAttributeValue(tempSession.id(), keyID,272attributes);273} catch (PKCS11Exception e) {274throw new ProviderException(e);275} finally {276this.releaseKeyID();277token.releaseSession(tempSession);278}279}280281private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0];282283private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID,284CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) {285if (knownAttributes == null) {286knownAttributes = A0;287}288for (int i = 0; i < desiredAttributes.length; i++) {289// For each desired attribute, check to see if we have the value290// available already. If everything is here, we save a native call.291CK_ATTRIBUTE attr = desiredAttributes[i];292for (CK_ATTRIBUTE known : knownAttributes) {293if ((attr.type == known.type) && (known.pValue != null)) {294attr.pValue = known.pValue;295break; // break inner for loop296}297}298if (attr.pValue == null) {299// nothing found, need to call C_GetAttributeValue()300for (int j = 0; j < i; j++) {301// clear values copied from knownAttributes302desiredAttributes[j].pValue = null;303}304try {305session.token.p11.C_GetAttributeValue306(session.id(), keyID, desiredAttributes);307} catch (PKCS11Exception e) {308throw new ProviderException(e);309}310break; // break loop, goto return311}312}313return desiredAttributes;314}315316static SecretKey secretKey(Session session, long keyID, String algorithm,317int keyLength, CK_ATTRIBUTE[] attributes) {318attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {319new CK_ATTRIBUTE(CKA_TOKEN),320new CK_ATTRIBUTE(CKA_SENSITIVE),321new CK_ATTRIBUTE(CKA_EXTRACTABLE),322});323return new P11SecretKey(session, keyID, algorithm, keyLength,324attributes);325}326327static SecretKey masterSecretKey(Session session, long keyID, String algorithm,328int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {329attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {330new CK_ATTRIBUTE(CKA_TOKEN),331new CK_ATTRIBUTE(CKA_SENSITIVE),332new CK_ATTRIBUTE(CKA_EXTRACTABLE),333});334return new P11TlsMasterSecretKey(335session, keyID, algorithm, keyLength, attributes, major,336minor);337}338339// we assume that all components of public keys are always accessible340static PublicKey publicKey(Session session, long keyID, String algorithm,341int keyLength, CK_ATTRIBUTE[] attributes) {342switch (algorithm) {343case "RSA":344return new P11RSAPublicKey(session, keyID, algorithm,345keyLength, attributes);346case "DSA":347return new P11DSAPublicKey(session, keyID, algorithm,348keyLength, attributes);349case "DH":350return new P11DHPublicKey(session, keyID, algorithm,351keyLength, attributes);352case "EC":353return new P11ECPublicKey(session, keyID, algorithm,354keyLength, attributes);355default:356throw new ProviderException357("Unknown public key algorithm " + algorithm);358}359}360361static PrivateKey privateKey(Session session, long keyID, String algorithm,362int keyLength, CK_ATTRIBUTE[] attributes) {363attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {364new CK_ATTRIBUTE(CKA_TOKEN),365new CK_ATTRIBUTE(CKA_SENSITIVE),366new CK_ATTRIBUTE(CKA_EXTRACTABLE),367});368if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {369return new P11PrivateKey370(session, keyID, algorithm, keyLength, attributes);371} else {372switch (algorithm) {373case "RSA":374// XXX better test for RSA CRT keys (single getAttributes() call)375// we need to determine whether this is a CRT key376// see if we can obtain the public exponent377// this should also be readable for sensitive/extractable keys378CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] {379new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),380};381boolean crtKey;382try {383session.token.p11.C_GetAttributeValue384(session.id(), keyID, attrs2);385crtKey = (attrs2[0].pValue instanceof byte[]);386} catch (PKCS11Exception e) {387// ignore, assume not available388crtKey = false;389}390if (crtKey) {391return new P11RSAPrivateKey(session, keyID, algorithm,392keyLength, attributes);393} else {394return new P11RSAPrivateNonCRTKey(session, keyID,395algorithm, keyLength, attributes);396}397case "DSA":398return new P11DSAPrivateKey(session, keyID, algorithm,399keyLength, attributes);400case "DH":401return new P11DHPrivateKey(session, keyID, algorithm,402keyLength, attributes);403case "EC":404return new P11ECPrivateKey(session, keyID, algorithm,405keyLength, attributes);406default:407throw new ProviderException408("Unknown private key algorithm " + algorithm);409}410}411}412413// class for sensitive and unextractable private keys414private static final class P11PrivateKey extends P11Key415implements PrivateKey {416private static final long serialVersionUID = -2138581185214187615L;417418P11PrivateKey(Session session, long keyID, String algorithm,419int keyLength, CK_ATTRIBUTE[] attributes) {420super(PRIVATE, session, keyID, algorithm, keyLength, attributes);421}422// XXX temporary encoding for serialization purposes423public String getFormat() {424token.ensureValid();425return null;426}427byte[] getEncodedInternal() {428token.ensureValid();429return null;430}431}432433private static class P11SecretKey extends P11Key implements SecretKey {434private static final long serialVersionUID = -7828241727014329084L;435private volatile byte[] encoded;436P11SecretKey(Session session, long keyID, String algorithm,437int keyLength, CK_ATTRIBUTE[] attributes) {438super(SECRET, session, keyID, algorithm, keyLength, attributes);439}440public String getFormat() {441token.ensureValid();442if (sensitive || (extractable == false)) {443return null;444} else {445return "RAW";446}447}448byte[] getEncodedInternal() {449token.ensureValid();450if (getFormat() == null) {451return null;452}453byte[] b = encoded;454if (b == null) {455synchronized (this) {456b = encoded;457if (b == null) {458Session tempSession = null;459long keyID = this.getKeyID();460try {461tempSession = token.getOpSession();462CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {463new CK_ATTRIBUTE(CKA_VALUE),464};465token.p11.C_GetAttributeValue466(tempSession.id(), keyID, attributes);467b = attributes[0].getByteArray();468} catch (PKCS11Exception e) {469throw new ProviderException(e);470} finally {471this.releaseKeyID();472token.releaseSession(tempSession);473}474encoded = b;475}476}477}478return b;479}480}481482private static class P11TlsMasterSecretKey extends P11SecretKey483implements TlsMasterSecret {484private static final long serialVersionUID = -1318560923770573441L;485486private final int majorVersion, minorVersion;487P11TlsMasterSecretKey(Session session, long keyID, String algorithm,488int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {489super(session, keyID, algorithm, keyLength, attributes);490this.majorVersion = major;491this.minorVersion = minor;492}493public int getMajorVersion() {494return majorVersion;495}496497public int getMinorVersion() {498return minorVersion;499}500}501502// RSA CRT private key503private static final class P11RSAPrivateKey extends P11Key504implements RSAPrivateCrtKey {505private static final long serialVersionUID = 9215872438913515220L;506507private BigInteger n, e, d, p, q, pe, qe, coeff;508private byte[] encoded;509P11RSAPrivateKey(Session session, long keyID, String algorithm,510int keyLength, CK_ATTRIBUTE[] attributes) {511super(PRIVATE, session, keyID, algorithm, keyLength, attributes);512}513private synchronized void fetchValues() {514token.ensureValid();515if (n != null) {516return;517}518CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {519new CK_ATTRIBUTE(CKA_MODULUS),520new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),521new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),522new CK_ATTRIBUTE(CKA_PRIME_1),523new CK_ATTRIBUTE(CKA_PRIME_2),524new CK_ATTRIBUTE(CKA_EXPONENT_1),525new CK_ATTRIBUTE(CKA_EXPONENT_2),526new CK_ATTRIBUTE(CKA_COEFFICIENT),527};528fetchAttributes(attributes);529n = attributes[0].getBigInteger();530e = attributes[1].getBigInteger();531d = attributes[2].getBigInteger();532p = attributes[3].getBigInteger();533q = attributes[4].getBigInteger();534pe = attributes[5].getBigInteger();535qe = attributes[6].getBigInteger();536coeff = attributes[7].getBigInteger();537}538public String getFormat() {539token.ensureValid();540return "PKCS#8";541}542synchronized byte[] getEncodedInternal() {543token.ensureValid();544if (encoded == null) {545fetchValues();546try {547Key newKey = RSAPrivateCrtKeyImpl.newKey548(KeyType.RSA, null, n, e, d, p, q, pe, qe, coeff);549encoded = newKey.getEncoded();550} catch (GeneralSecurityException e) {551throw new ProviderException(e);552}553}554return encoded;555}556public BigInteger getModulus() {557fetchValues();558return n;559}560public BigInteger getPublicExponent() {561fetchValues();562return e;563}564public BigInteger getPrivateExponent() {565fetchValues();566return d;567}568public BigInteger getPrimeP() {569fetchValues();570return p;571}572public BigInteger getPrimeQ() {573fetchValues();574return q;575}576public BigInteger getPrimeExponentP() {577fetchValues();578return pe;579}580public BigInteger getPrimeExponentQ() {581fetchValues();582return qe;583}584public BigInteger getCrtCoefficient() {585fetchValues();586return coeff;587}588}589590// RSA non-CRT private key591private static final class P11RSAPrivateNonCRTKey extends P11Key592implements RSAPrivateKey {593private static final long serialVersionUID = 1137764983777411481L;594595private BigInteger n, d;596private byte[] encoded;597P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,598int keyLength, CK_ATTRIBUTE[] attributes) {599super(PRIVATE, session, keyID, algorithm, keyLength, attributes);600}601private synchronized void fetchValues() {602token.ensureValid();603if (n != null) {604return;605}606CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {607new CK_ATTRIBUTE(CKA_MODULUS),608new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),609};610fetchAttributes(attributes);611n = attributes[0].getBigInteger();612d = attributes[1].getBigInteger();613}614public String getFormat() {615token.ensureValid();616return "PKCS#8";617}618synchronized byte[] getEncodedInternal() {619token.ensureValid();620if (encoded == null) {621fetchValues();622try {623// XXX make constructor in SunRsaSign provider public624// and call it directly625KeyFactory factory = KeyFactory.getInstance626("RSA", P11Util.getSunRsaSignProvider());627Key newKey = factory.translateKey(this);628encoded = newKey.getEncoded();629} catch (GeneralSecurityException e) {630throw new ProviderException(e);631}632}633return encoded;634}635public BigInteger getModulus() {636fetchValues();637return n;638}639public BigInteger getPrivateExponent() {640fetchValues();641return d;642}643}644645private static final class P11RSAPublicKey extends P11Key646implements RSAPublicKey {647private static final long serialVersionUID = -826726289023854455L;648private BigInteger n, e;649private byte[] encoded;650P11RSAPublicKey(Session session, long keyID, String algorithm,651int keyLength, CK_ATTRIBUTE[] attributes) {652super(PUBLIC, session, keyID, algorithm, keyLength, attributes);653}654private synchronized void fetchValues() {655token.ensureValid();656if (n != null) {657return;658}659CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {660new CK_ATTRIBUTE(CKA_MODULUS),661new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),662};663fetchAttributes(attributes);664n = attributes[0].getBigInteger();665e = attributes[1].getBigInteger();666}667public String getFormat() {668token.ensureValid();669return "X.509";670}671synchronized byte[] getEncodedInternal() {672token.ensureValid();673if (encoded == null) {674fetchValues();675try {676encoded = RSAPublicKeyImpl.newKey677(KeyType.RSA, null, n, e).getEncoded();678} catch (InvalidKeyException e) {679throw new ProviderException(e);680}681}682return encoded;683}684public BigInteger getModulus() {685fetchValues();686return n;687}688public BigInteger getPublicExponent() {689fetchValues();690return e;691}692public String toString() {693fetchValues();694return super.toString() + "\n modulus: " + n695+ "\n public exponent: " + e;696}697}698699private static final class P11DSAPublicKey extends P11Key700implements DSAPublicKey {701private static final long serialVersionUID = 5989753793316396637L;702703private BigInteger y;704private DSAParams params;705private byte[] encoded;706P11DSAPublicKey(Session session, long keyID, String algorithm,707int keyLength, CK_ATTRIBUTE[] attributes) {708super(PUBLIC, session, keyID, algorithm, keyLength, attributes);709}710private synchronized void fetchValues() {711token.ensureValid();712if (y != null) {713return;714}715CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {716new CK_ATTRIBUTE(CKA_VALUE),717new CK_ATTRIBUTE(CKA_PRIME),718new CK_ATTRIBUTE(CKA_SUBPRIME),719new CK_ATTRIBUTE(CKA_BASE),720};721fetchAttributes(attributes);722y = attributes[0].getBigInteger();723params = new DSAParameterSpec(724attributes[1].getBigInteger(),725attributes[2].getBigInteger(),726attributes[3].getBigInteger()727);728}729public String getFormat() {730token.ensureValid();731return "X.509";732}733synchronized byte[] getEncodedInternal() {734token.ensureValid();735if (encoded == null) {736fetchValues();737try {738Key key = new sun.security.provider.DSAPublicKey739(y, params.getP(), params.getQ(), params.getG());740encoded = key.getEncoded();741} catch (InvalidKeyException e) {742throw new ProviderException(e);743}744}745return encoded;746}747public BigInteger getY() {748fetchValues();749return y;750}751public DSAParams getParams() {752fetchValues();753return params;754}755public String toString() {756fetchValues();757return super.toString() + "\n y: " + y + "\n p: " + params.getP()758+ "\n q: " + params.getQ() + "\n g: " + params.getG();759}760}761762private static final class P11DSAPrivateKey extends P11Key763implements DSAPrivateKey {764private static final long serialVersionUID = 3119629997181999389L;765766private BigInteger x;767private DSAParams params;768private byte[] encoded;769P11DSAPrivateKey(Session session, long keyID, String algorithm,770int keyLength, CK_ATTRIBUTE[] attributes) {771super(PRIVATE, session, keyID, algorithm, keyLength, attributes);772}773private synchronized void fetchValues() {774token.ensureValid();775if (x != null) {776return;777}778CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {779new CK_ATTRIBUTE(CKA_VALUE),780new CK_ATTRIBUTE(CKA_PRIME),781new CK_ATTRIBUTE(CKA_SUBPRIME),782new CK_ATTRIBUTE(CKA_BASE),783};784fetchAttributes(attributes);785x = attributes[0].getBigInteger();786params = new DSAParameterSpec(787attributes[1].getBigInteger(),788attributes[2].getBigInteger(),789attributes[3].getBigInteger()790);791}792public String getFormat() {793token.ensureValid();794return "PKCS#8";795}796synchronized byte[] getEncodedInternal() {797token.ensureValid();798if (encoded == null) {799fetchValues();800try {801Key key = new sun.security.provider.DSAPrivateKey802(x, params.getP(), params.getQ(), params.getG());803encoded = key.getEncoded();804} catch (InvalidKeyException e) {805throw new ProviderException(e);806}807}808return encoded;809}810public BigInteger getX() {811fetchValues();812return x;813}814public DSAParams getParams() {815fetchValues();816return params;817}818}819820private static final class P11DHPrivateKey extends P11Key821implements DHPrivateKey {822private static final long serialVersionUID = -1698576167364928838L;823824private BigInteger x;825private DHParameterSpec params;826private byte[] encoded;827P11DHPrivateKey(Session session, long keyID, String algorithm,828int keyLength, CK_ATTRIBUTE[] attributes) {829super(PRIVATE, session, keyID, algorithm, keyLength, attributes);830}831private synchronized void fetchValues() {832token.ensureValid();833if (x != null) {834return;835}836CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {837new CK_ATTRIBUTE(CKA_VALUE),838new CK_ATTRIBUTE(CKA_PRIME),839new CK_ATTRIBUTE(CKA_BASE),840};841fetchAttributes(attributes);842x = attributes[0].getBigInteger();843params = new DHParameterSpec(844attributes[1].getBigInteger(),845attributes[2].getBigInteger()846);847}848public String getFormat() {849token.ensureValid();850return "PKCS#8";851}852synchronized byte[] getEncodedInternal() {853token.ensureValid();854if (encoded == null) {855fetchValues();856try {857DHPrivateKeySpec spec = new DHPrivateKeySpec858(x, params.getP(), params.getG());859KeyFactory kf = KeyFactory.getInstance860("DH", P11Util.getSunJceProvider());861Key key = kf.generatePrivate(spec);862encoded = key.getEncoded();863} catch (GeneralSecurityException e) {864throw new ProviderException(e);865}866}867return encoded;868}869public BigInteger getX() {870fetchValues();871return x;872}873public DHParameterSpec getParams() {874fetchValues();875return params;876}877public int hashCode() {878if (token.isValid() == false) {879return 0;880}881fetchValues();882return Objects.hash(x, params.getP(), params.getG());883}884public boolean equals(Object obj) {885if (this == obj) return true;886// equals() should never throw exceptions887if (token.isValid() == false) {888return false;889}890if (!(obj instanceof DHPrivateKey)) {891return false;892}893fetchValues();894DHPrivateKey other = (DHPrivateKey) obj;895DHParameterSpec otherParams = other.getParams();896return ((this.x.compareTo(other.getX()) == 0) &&897(this.params.getP().compareTo(otherParams.getP()) == 0) &&898(this.params.getG().compareTo(otherParams.getG()) == 0));899}900}901902private static final class P11DHPublicKey extends P11Key903implements DHPublicKey {904static final long serialVersionUID = -598383872153843657L;905906private BigInteger y;907private DHParameterSpec params;908private byte[] encoded;909P11DHPublicKey(Session session, long keyID, String algorithm,910int keyLength, CK_ATTRIBUTE[] attributes) {911super(PUBLIC, session, keyID, algorithm, keyLength, attributes);912}913private synchronized void fetchValues() {914token.ensureValid();915if (y != null) {916return;917}918CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {919new CK_ATTRIBUTE(CKA_VALUE),920new CK_ATTRIBUTE(CKA_PRIME),921new CK_ATTRIBUTE(CKA_BASE),922};923fetchAttributes(attributes);924y = attributes[0].getBigInteger();925params = new DHParameterSpec(926attributes[1].getBigInteger(),927attributes[2].getBigInteger()928);929}930public String getFormat() {931token.ensureValid();932return "X.509";933}934synchronized byte[] getEncodedInternal() {935token.ensureValid();936if (encoded == null) {937fetchValues();938try {939DHPublicKeySpec spec = new DHPublicKeySpec940(y, params.getP(), params.getG());941KeyFactory kf = KeyFactory.getInstance942("DH", P11Util.getSunJceProvider());943Key key = kf.generatePublic(spec);944encoded = key.getEncoded();945} catch (GeneralSecurityException e) {946throw new ProviderException(e);947}948}949return encoded;950}951public BigInteger getY() {952fetchValues();953return y;954}955public DHParameterSpec getParams() {956fetchValues();957return params;958}959public String toString() {960fetchValues();961return super.toString() + "\n y: " + y + "\n p: " + params.getP()962+ "\n g: " + params.getG();963}964public int hashCode() {965if (token.isValid() == false) {966return 0;967}968fetchValues();969return Objects.hash(y, params.getP(), params.getG());970}971public boolean equals(Object obj) {972if (this == obj) return true;973// equals() should never throw exceptions974if (token.isValid() == false) {975return false;976}977if (!(obj instanceof DHPublicKey)) {978return false;979}980fetchValues();981DHPublicKey other = (DHPublicKey) obj;982DHParameterSpec otherParams = other.getParams();983return ((this.y.compareTo(other.getY()) == 0) &&984(this.params.getP().compareTo(otherParams.getP()) == 0) &&985(this.params.getG().compareTo(otherParams.getG()) == 0));986}987}988989private static final class P11ECPrivateKey extends P11Key990implements ECPrivateKey {991private static final long serialVersionUID = -7786054399510515515L;992993private BigInteger s;994private ECParameterSpec params;995private byte[] encoded;996P11ECPrivateKey(Session session, long keyID, String algorithm,997int keyLength, CK_ATTRIBUTE[] attributes) {998super(PRIVATE, session, keyID, algorithm, keyLength, attributes);999}1000private synchronized void fetchValues() {1001token.ensureValid();1002if (s != null) {1003return;1004}1005CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {1006new CK_ATTRIBUTE(CKA_VALUE),1007new CK_ATTRIBUTE(CKA_EC_PARAMS, params),1008};1009fetchAttributes(attributes);1010s = attributes[0].getBigInteger();1011try {1012params = P11ECKeyFactory.decodeParameters1013(attributes[1].getByteArray());1014} catch (Exception e) {1015throw new RuntimeException("Could not parse key values", e);1016}1017}1018public String getFormat() {1019token.ensureValid();1020return "PKCS#8";1021}1022synchronized byte[] getEncodedInternal() {1023token.ensureValid();1024if (encoded == null) {1025fetchValues();1026try {1027Key key = P11ECUtil.generateECPrivateKey(s, params);1028encoded = key.getEncoded();1029} catch (InvalidKeySpecException e) {1030throw new ProviderException(e);1031}1032}1033return encoded;1034}1035public BigInteger getS() {1036fetchValues();1037return s;1038}1039public ECParameterSpec getParams() {1040fetchValues();1041return params;1042}1043}10441045private static final class P11ECPublicKey extends P11Key1046implements ECPublicKey {1047private static final long serialVersionUID = -6371481375154806089L;10481049private ECPoint w;1050private ECParameterSpec params;1051private byte[] encoded;1052P11ECPublicKey(Session session, long keyID, String algorithm,1053int keyLength, CK_ATTRIBUTE[] attributes) {1054super(PUBLIC, session, keyID, algorithm, keyLength, attributes);1055}1056private synchronized void fetchValues() {1057token.ensureValid();1058if (w != null) {1059return;1060}1061CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {1062new CK_ATTRIBUTE(CKA_EC_POINT),1063new CK_ATTRIBUTE(CKA_EC_PARAMS),1064};1065fetchAttributes(attributes);10661067try {1068params = P11ECKeyFactory.decodeParameters1069(attributes[1].getByteArray());1070byte[] ecKey = attributes[0].getByteArray();10711072// Check whether the X9.63 encoding of an EC point is wrapped1073// in an ASN.1 OCTET STRING1074if (!token.config.getUseEcX963Encoding()) {1075DerValue wECPoint = new DerValue(ecKey);10761077if (wECPoint.getTag() != DerValue.tag_OctetString) {1078throw new IOException("Could not DER decode EC point." +1079" Unexpected tag: " + wECPoint.getTag());1080}1081w = P11ECKeyFactory.decodePoint1082(wECPoint.getDataBytes(), params.getCurve());10831084} else {1085w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve());1086}10871088} catch (Exception e) {1089throw new RuntimeException("Could not parse key values", e);1090}1091}1092public String getFormat() {1093token.ensureValid();1094return "X.509";1095}1096synchronized byte[] getEncodedInternal() {1097token.ensureValid();1098if (encoded == null) {1099fetchValues();1100try {1101return P11ECUtil.x509EncodeECPublicKey(w, params);1102} catch (InvalidKeySpecException e) {1103throw new ProviderException(e);1104}1105}1106return encoded;1107}1108public ECPoint getW() {1109fetchValues();1110return w;1111}1112public ECParameterSpec getParams() {1113fetchValues();1114return params;1115}1116public String toString() {1117fetchValues();1118return super.toString()1119+ "\n public x coord: " + w.getAffineX()1120+ "\n public y coord: " + w.getAffineY()1121+ "\n parameters: " + params;1122}1123}1124}11251126final class NativeKeyHolder {11271128private static long nativeKeyWrapperKeyID = 0;1129private static CK_MECHANISM nativeKeyWrapperMechanism = null;1130private static long nativeKeyWrapperRefCount = 0;1131private static Session nativeKeyWrapperSession = null;11321133private final P11Key p11Key;1134private final byte[] nativeKeyInfo;1135private boolean wrapperKeyUsed;11361137// destroyed and recreated when refCount toggles to 11138private long keyID;11391140// phantom reference notification clean up for session keys1141private SessionKeyRef ref;11421143private int refCount;11441145private static void createNativeKeyWrapper(Token token)1146throws PKCS11Exception {1147assert(nativeKeyWrapperKeyID == 0);1148assert(nativeKeyWrapperRefCount == 0);1149assert(nativeKeyWrapperSession == null);1150// Create a global wrapping/unwrapping key1151CK_ATTRIBUTE[] wrappingAttributes = token.getAttributes(O_GENERATE,1152CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] {1153new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),1154new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3)});1155Session s = null;1156try {1157s = token.getObjSession();1158nativeKeyWrapperKeyID = token.p11.C_GenerateKey(1159s.id(), new CK_MECHANISM(CKM_AES_KEY_GEN),1160wrappingAttributes);1161nativeKeyWrapperSession = s;1162nativeKeyWrapperSession.addObject();1163byte[] iv = new byte[16];1164JCAUtil.getSecureRandom().nextBytes(iv);1165nativeKeyWrapperMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, iv);1166} catch (PKCS11Exception e) {1167// best effort1168} finally {1169token.releaseSession(s);1170}1171}11721173private static void deleteNativeKeyWrapper() {1174Token token = nativeKeyWrapperSession.token;1175if (token.isValid()) {1176Session s = null;1177try {1178s = token.getOpSession();1179token.p11.C_DestroyObject(s.id(), nativeKeyWrapperKeyID);1180nativeKeyWrapperSession.removeObject();1181} catch (PKCS11Exception e) {1182// best effort1183} finally {1184token.releaseSession(s);1185}1186}1187nativeKeyWrapperKeyID = 0;1188nativeKeyWrapperMechanism = null;1189nativeKeyWrapperSession = null;1190}11911192static void decWrapperKeyRef() {1193synchronized(NativeKeyHolder.class) {1194assert(nativeKeyWrapperKeyID != 0);1195assert(nativeKeyWrapperRefCount > 0);1196nativeKeyWrapperRefCount--;1197if (nativeKeyWrapperRefCount == 0) {1198deleteNativeKeyWrapper();1199}1200}1201}12021203NativeKeyHolder(P11Key p11Key, long keyID, Session keySession,1204boolean extractKeyInfo, boolean isTokenObject) {1205this.p11Key = p11Key;1206this.keyID = keyID;1207this.refCount = -1;1208byte[] ki = null;1209if (isTokenObject) {1210this.ref = null;1211} else {1212// Try extracting key info, if any error, disable it1213Token token = p11Key.token;1214if (extractKeyInfo) {1215try {1216if (p11Key.sensitive) {1217// p11Key native key information has to be wrapped1218synchronized(NativeKeyHolder.class) {1219if (nativeKeyWrapperKeyID == 0) {1220createNativeKeyWrapper(token);1221}1222// If a wrapper-key was successfully created or1223// already exists, increment its reference1224// counter to keep it alive while native key1225// information is being held.1226if (nativeKeyWrapperKeyID != 0) {1227nativeKeyWrapperRefCount++;1228wrapperKeyUsed = true;1229}1230}1231}1232Session opSession = null;1233try {1234opSession = token.getOpSession();1235ki = p11Key.token.p11.getNativeKeyInfo(opSession.id(),1236keyID, nativeKeyWrapperKeyID,1237nativeKeyWrapperMechanism);1238} catch (PKCS11Exception e) {1239// best effort1240} finally {1241token.releaseSession(opSession);1242}1243} catch (PKCS11Exception e) {1244// best effort1245}1246}1247this.ref = new SessionKeyRef(p11Key, keyID, wrapperKeyUsed,1248keySession);1249}1250this.nativeKeyInfo = ((ki == null || ki.length == 0)? null : ki);1251}12521253long getKeyID() throws ProviderException {1254if (this.nativeKeyInfo != null) {1255synchronized(this.nativeKeyInfo) {1256if (this.refCount == -1) {1257this.refCount = 0;1258}1259int cnt = (this.refCount)++;1260if (keyID == 0) {1261if (cnt != 0) {1262throw new RuntimeException(1263"Error: null keyID with non-zero refCount " + cnt);1264}1265Token token = p11Key.token;1266// Create keyID using nativeKeyInfo1267Session session = null;1268try {1269session = token.getObjSession();1270this.keyID = token.p11.createNativeKey(session.id(),1271nativeKeyInfo, nativeKeyWrapperKeyID,1272nativeKeyWrapperMechanism);1273this.ref.registerNativeKey(this.keyID, session);1274} catch (PKCS11Exception e) {1275this.refCount--;1276throw new ProviderException("Error recreating native key", e);1277} finally {1278token.releaseSession(session);1279}1280} else {1281if (cnt < 0) {1282throw new RuntimeException("ERROR: negative refCount");1283}1284}1285}1286}1287return keyID;1288}12891290void releaseKeyID() {1291if (this.nativeKeyInfo != null) {1292synchronized(this.nativeKeyInfo) {1293if (this.refCount == -1) {1294throw new RuntimeException("Error: miss match getKeyID call");1295}1296int cnt = --(this.refCount);1297if (cnt == 0) {1298// destroy1299if (this.keyID == 0) {1300throw new RuntimeException("ERROR: null keyID can't be destroyed");1301}13021303// destroy1304this.keyID = 0;1305this.ref.removeNativeKey();1306} else {1307if (cnt < 0) {1308// should never happen as we start count at 1 and pair get/release calls1309throw new RuntimeException("wrong refCount value: " + cnt);1310}1311}1312}1313}1314}1315}13161317/*1318* NOTE: Must use PhantomReference here and not WeakReference1319* otherwise the key maybe cleared before other objects which1320* still use these keys during finalization such as SSLSocket.1321*/1322final class SessionKeyRef extends PhantomReference<P11Key> {1323private static ReferenceQueue<P11Key> refQueue =1324new ReferenceQueue<P11Key>();1325private static Set<SessionKeyRef> refSet =1326Collections.synchronizedSet(new HashSet<SessionKeyRef>());13271328static ReferenceQueue<P11Key> referenceQueue() {1329return refQueue;1330}13311332private static void drainRefQueueBounded() {1333while (true) {1334SessionKeyRef next = (SessionKeyRef) refQueue.poll();1335if (next == null) break;1336next.dispose();1337}1338}13391340// handle to the native key and the session it is generated under1341private long keyID;1342private Session session;1343private boolean wrapperKeyUsed;13441345SessionKeyRef(P11Key p11Key, long keyID, boolean wrapperKeyUsed,1346Session session) {1347super(p11Key, refQueue);1348if (session == null) {1349throw new ProviderException("key must be associated with a session");1350}1351registerNativeKey(keyID, session);1352this.wrapperKeyUsed = wrapperKeyUsed;13531354refSet.add(this);1355// TBD: run at some interval and not every time?1356drainRefQueueBounded();1357}13581359void registerNativeKey(long newKeyID, Session newSession) {1360assert(newKeyID != 0);1361assert(newSession != null);1362updateNativeKey(newKeyID, newSession);1363}13641365void removeNativeKey() {1366assert(session != null);1367updateNativeKey(0, null);1368}13691370private void updateNativeKey(long newKeyID, Session newSession) {1371if (newKeyID == 0) {1372assert(newSession == null);1373Token token = session.token;1374// If the token is still valid, try to remove the key object1375if (token.isValid()) {1376Session s = null;1377try {1378s = token.getOpSession();1379token.p11.C_DestroyObject(s.id(), this.keyID);1380} catch (PKCS11Exception e) {1381// best effort1382} finally {1383token.releaseSession(s);1384}1385}1386session.removeObject();1387} else {1388newSession.addObject();1389}1390keyID = newKeyID;1391session = newSession;1392}13931394// Called when the GC disposes a p11Key1395void dispose() {1396if (wrapperKeyUsed) {1397// Wrapper-key no longer needed for1398// p11Key native key information1399NativeKeyHolder.decWrapperKeyRef();1400}1401if (keyID != 0) {1402removeNativeKey();1403}1404refSet.remove(this);1405this.clear();1406}1407}140814091410