diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,1188 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.*; +import java.lang.ref.*; +import java.math.BigInteger; +import java.util.*; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.interfaces.*; +import javax.crypto.spec.*; + +import sun.security.rsa.RSAPublicKeyImpl; + +import sun.security.internal.interfaces.TlsMasterSecret; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +import sun.security.util.Debug; +import sun.security.util.DerValue; +import sun.security.util.Length; +import sun.security.util.ECUtil; + +/** + * Key implementation classes. + * + * In PKCS#11, the components of private and secret keys may or may not + * be accessible. If they are, we use the algorithm specific key classes + * (e.g. DSAPrivateKey) for compatibility with existing applications. + * If the components are not accessible, we use a generic class that + * only implements PrivateKey (or SecretKey). Whether the components of a + * key are extractable is automatically determined when the key object is + * created. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +abstract class P11Key implements Key, Length { + + private static final long serialVersionUID = -2575874101938349339L; + + private final static String PUBLIC = "public"; + private final static String PRIVATE = "private"; + private final static String SECRET = "secret"; + + // type of key, one of (PUBLIC, PRIVATE, SECRET) + final String type; + + // token instance + final Token token; + + // algorithm name, returned by getAlgorithm(), etc. + final String algorithm; + + // key id + final long keyID; + + // effective key length of the key, e.g. 56 for a DES key + final int keyLength; + + // flags indicating whether the key is a token object, sensitive, extractable + final boolean tokenObject, sensitive, extractable; + + // phantom reference notification clean up for session keys + private final SessionKeyRef sessionKeyRef; + + P11Key(String type, Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + this.type = type; + this.token = session.token; + this.keyID = keyID; + this.algorithm = algorithm; + this.keyLength = keyLength; + boolean tokenObject = false; + boolean sensitive = false; + boolean extractable = true; + int n = (attributes == null) ? 0 : attributes.length; + for (int i = 0; i < n; i++) { + CK_ATTRIBUTE attr = attributes[i]; + if (attr.type == CKA_TOKEN) { + tokenObject = attr.getBoolean(); + } else if (attr.type == CKA_SENSITIVE) { + sensitive = attr.getBoolean(); + } else if (attr.type == CKA_EXTRACTABLE) { + extractable = attr.getBoolean(); + } + } + this.tokenObject = tokenObject; + this.sensitive = sensitive; + this.extractable = extractable; + if (tokenObject == false) { + sessionKeyRef = new SessionKeyRef(this, keyID, session); + } else { + sessionKeyRef = null; + } + } + + // see JCA spec + public final String getAlgorithm() { + token.ensureValid(); + return algorithm; + } + + // see JCA spec + public final byte[] getEncoded() { + byte[] b = getEncodedInternal(); + return (b == null) ? null : b.clone(); + } + + abstract byte[] getEncodedInternal(); + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + // equals() should never throw exceptions + if (token.isValid() == false) { + return false; + } + if (obj instanceof Key == false) { + return false; + } + String thisFormat = getFormat(); + if (thisFormat == null) { + // no encoding, key only equal to itself + // XXX getEncoded() for unextractable keys will change that + return false; + } + Key other = (Key)obj; + if (thisFormat.equals(other.getFormat()) == false) { + return false; + } + byte[] thisEnc = this.getEncodedInternal(); + byte[] otherEnc; + if (obj instanceof P11Key) { + otherEnc = ((P11Key)other).getEncodedInternal(); + } else { + otherEnc = other.getEncoded(); + } + return MessageDigest.isEqual(thisEnc, otherEnc); + } + + public int hashCode() { + // hashCode() should never throw exceptions + if (token.isValid() == false) { + return 0; + } + byte[] b1 = getEncodedInternal(); + if (b1 == null) { + return 0; + } + int r = b1.length; + for (int i = 0; i < b1.length; i++) { + r += (b1[i] & 0xff) * 37; + } + return r; + } + + protected Object writeReplace() throws ObjectStreamException { + KeyRep.Type type; + String format = getFormat(); + if (isPrivate() && "PKCS#8".equals(format)) { + type = KeyRep.Type.PRIVATE; + } else if (isPublic() && "X.509".equals(format)) { + type = KeyRep.Type.PUBLIC; + } else if (isSecret() && "RAW".equals(format)) { + type = KeyRep.Type.SECRET; + } else { + // XXX short term serialization for unextractable keys + throw new NotSerializableException + ("Cannot serialize sensitive and unextractable keys"); + } + return new KeyRep(type, getAlgorithm(), format, getEncoded()); + } + + public String toString() { + token.ensureValid(); + String s1 = token.provider.getName() + " " + algorithm + " " + type + + " key, " + keyLength + " bits"; + s1 += " (id " + keyID + ", " + + (tokenObject ? "token" : "session") + " object"; + if (isPublic()) { + s1 += ")"; + } else { + s1 += ", " + (sensitive ? "" : "not ") + "sensitive"; + s1 += ", " + (extractable ? "" : "un") + "extractable)"; + } + return s1; + } + + /** + * Return bit length of the key. + */ + @Override + public int length() { + return keyLength; + } + + boolean isPublic() { + return type == PUBLIC; + } + + boolean isPrivate() { + return type == PRIVATE; + } + + boolean isSecret() { + return type == SECRET; + } + + void fetchAttributes(CK_ATTRIBUTE[] attributes) { + Session tempSession = null; + try { + tempSession = token.getOpSession(); + token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } finally { + token.releaseSession(tempSession); + } + } + + private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0]; + + private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID, + CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) { + if (knownAttributes == null) { + knownAttributes = A0; + } + for (int i = 0; i < desiredAttributes.length; i++) { + // For each desired attribute, check to see if we have the value + // available already. If everything is here, we save a native call. + CK_ATTRIBUTE attr = desiredAttributes[i]; + for (CK_ATTRIBUTE known : knownAttributes) { + if ((attr.type == known.type) && (known.pValue != null)) { + attr.pValue = known.pValue; + break; // break inner for loop + } + } + if (attr.pValue == null) { + // nothing found, need to call C_GetAttributeValue() + for (int j = 0; j < i; j++) { + // clear values copied from knownAttributes + desiredAttributes[j].pValue = null; + } + try { + session.token.p11.C_GetAttributeValue + (session.id(), keyID, desiredAttributes); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } + break; // break loop, goto return + } + } + return desiredAttributes; + } + + static SecretKey secretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + return new P11SecretKey(session, keyID, algorithm, keyLength, attributes); + } + + static SecretKey masterSecretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) { + attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + return new P11TlsMasterSecretKey + (session, keyID, algorithm, keyLength, attributes, major, minor); + } + + // we assume that all components of public keys are always accessible + static PublicKey publicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + switch (algorithm) { + case "RSA": + return new P11RSAPublicKey + (session, keyID, algorithm, keyLength, attributes); + case "DSA": + return new P11DSAPublicKey + (session, keyID, algorithm, keyLength, attributes); + case "DH": + return new P11DHPublicKey + (session, keyID, algorithm, keyLength, attributes); + case "EC": + return new P11ECPublicKey + (session, keyID, algorithm, keyLength, attributes); + default: + throw new ProviderException + ("Unknown public key algorithm " + algorithm); + } + } + + static PrivateKey privateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) { + return new P11PrivateKey + (session, keyID, algorithm, keyLength, attributes); + } else { + switch (algorithm) { + case "RSA": + // In order to decide if this is RSA CRT key, we first query + // and see if all extra CRT attributes are available. + CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + new CK_ATTRIBUTE(CKA_PRIME_1), + new CK_ATTRIBUTE(CKA_PRIME_2), + new CK_ATTRIBUTE(CKA_EXPONENT_1), + new CK_ATTRIBUTE(CKA_EXPONENT_2), + new CK_ATTRIBUTE(CKA_COEFFICIENT), + }; + boolean crtKey; + try { + session.token.p11.C_GetAttributeValue + (session.id(), keyID, attrs2); + crtKey = ((attrs2[0].pValue instanceof byte[]) && + (attrs2[1].pValue instanceof byte[]) && + (attrs2[2].pValue instanceof byte[]) && + (attrs2[3].pValue instanceof byte[]) && + (attrs2[4].pValue instanceof byte[]) && + (attrs2[5].pValue instanceof byte[])) ; + } catch (PKCS11Exception e) { + // ignore, assume not available + crtKey = false; + } + if (crtKey) { + return new P11RSAPrivateKey + (session, keyID, algorithm, keyLength, attributes, attrs2); + } else { + return new P11RSAPrivateNonCRTKey + (session, keyID, algorithm, keyLength, attributes); + } + case "DSA": + return new P11DSAPrivateKey + (session, keyID, algorithm, keyLength, attributes); + case "DH": + return new P11DHPrivateKey + (session, keyID, algorithm, keyLength, attributes); + case "EC": + return new P11ECPrivateKey + (session, keyID, algorithm, keyLength, attributes); + default: + throw new ProviderException + ("Unknown private key algorithm " + algorithm); + } + } + } + + // class for sensitive and unextractable private keys + private static final class P11PrivateKey extends P11Key + implements PrivateKey { + private static final long serialVersionUID = -2138581185214187615L; + + P11PrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + // XXX temporary encoding for serialization purposes + public String getFormat() { + token.ensureValid(); + return null; + } + byte[] getEncodedInternal() { + token.ensureValid(); + return null; + } + } + + private static class P11SecretKey extends P11Key implements SecretKey { + private static final long serialVersionUID = -7828241727014329084L; + private volatile byte[] encoded; + P11SecretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(SECRET, session, keyID, algorithm, keyLength, attributes); + } + public String getFormat() { + token.ensureValid(); + if (sensitive || (extractable == false)) { + return null; + } else { + return "RAW"; + } + } + byte[] getEncodedInternal() { + token.ensureValid(); + if (getFormat() == null) { + return null; + } + byte[] b = encoded; + if (b == null) { + synchronized (this) { + b = encoded; + if (b == null) { + Session tempSession = null; + try { + tempSession = token.getOpSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + }; + token.p11.C_GetAttributeValue + (tempSession.id(), keyID, attributes); + b = attributes[0].getByteArray(); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } finally { + token.releaseSession(tempSession); + } + encoded = b; + } + } + } + return b; + } + } + + @SuppressWarnings("deprecation") + private static class P11TlsMasterSecretKey extends P11SecretKey + implements TlsMasterSecret { + private static final long serialVersionUID = -1318560923770573441L; + + private final int majorVersion, minorVersion; + P11TlsMasterSecretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) { + super(session, keyID, algorithm, keyLength, attributes); + this.majorVersion = major; + this.minorVersion = minor; + } + public int getMajorVersion() { + return majorVersion; + } + + public int getMinorVersion() { + return minorVersion; + } + } + + // RSA CRT private key + private static final class P11RSAPrivateKey extends P11Key + implements RSAPrivateCrtKey { + private static final long serialVersionUID = 9215872438913515220L; + + private BigInteger n, e, d, p, q, pe, qe, coeff; + private byte[] encoded; + P11RSAPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs) { + super(PRIVATE, session, keyID, algorithm, keyLength, attrs); + + for (CK_ATTRIBUTE a : crtAttrs) { + if (a.type == CKA_PUBLIC_EXPONENT) { + e = a.getBigInteger(); + } else if (a.type == CKA_PRIME_1) { + p = a.getBigInteger(); + } else if (a.type == CKA_PRIME_2) { + q = a.getBigInteger(); + } else if (a.type == CKA_EXPONENT_1) { + pe = a.getBigInteger(); + } else if (a.type == CKA_EXPONENT_2) { + qe = a.getBigInteger(); + } else if (a.type == CKA_COEFFICIENT) { + coeff = a.getBigInteger(); + } + } + } + private synchronized void fetchValues() { + token.ensureValid(); + if (n != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), + }; + fetchAttributes(attributes); + n = attributes[0].getBigInteger(); + d = attributes[1].getBigInteger(); + } + + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + // XXX make constructor in SunRsaSign provider public + // and call it directly + KeyFactory factory = KeyFactory.getInstance + ("RSA", P11Util.getSunRsaSignProvider()); + Key newKey = factory.translateKey(this); + encoded = newKey.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getModulus() { + fetchValues(); + return n; + } + public BigInteger getPublicExponent() { + return e; + } + public BigInteger getPrivateExponent() { + fetchValues(); + return d; + } + public BigInteger getPrimeP() { + return p; + } + public BigInteger getPrimeQ() { + return q; + } + public BigInteger getPrimeExponentP() { + return pe; + } + public BigInteger getPrimeExponentQ() { + return qe; + } + public BigInteger getCrtCoefficient() { + return coeff; + } + } + + // RSA non-CRT private key + private static final class P11RSAPrivateNonCRTKey extends P11Key + implements RSAPrivateKey { + private static final long serialVersionUID = 1137764983777411481L; + + private BigInteger n, d; + private byte[] encoded; + P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (n != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), + }; + fetchAttributes(attributes); + n = attributes[0].getBigInteger(); + d = attributes[1].getBigInteger(); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + // XXX make constructor in SunRsaSign provider public + // and call it directly + KeyFactory factory = KeyFactory.getInstance + ("RSA", P11Util.getSunRsaSignProvider()); + Key newKey = factory.translateKey(this); + encoded = newKey.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getModulus() { + fetchValues(); + return n; + } + public BigInteger getPrivateExponent() { + fetchValues(); + return d; + } + } + + private static final class P11RSAPublicKey extends P11Key + implements RSAPublicKey { + private static final long serialVersionUID = -826726289023854455L; + + private BigInteger n, e; + private byte[] encoded; + P11RSAPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (n != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + }; + fetchAttributes(attributes); + n = attributes[0].getBigInteger(); + e = attributes[1].getBigInteger(); + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + encoded = new RSAPublicKeyImpl(n, e).getEncoded(); + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getModulus() { + fetchValues(); + return n; + } + public BigInteger getPublicExponent() { + fetchValues(); + return e; + } + public String toString() { + fetchValues(); + return super.toString() + "\n modulus: " + n + + "\n public exponent: " + e; + } + } + + private static final class P11DSAPublicKey extends P11Key + implements DSAPublicKey { + private static final long serialVersionUID = 5989753793316396637L; + + private BigInteger y; + private DSAParams params; + private byte[] encoded; + P11DSAPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (y != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_SUBPRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + y = attributes[0].getBigInteger(); + params = new DSAParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key key = new sun.security.provider.DSAPublicKey + (y, params.getP(), params.getQ(), params.getG()); + encoded = key.getEncoded(); + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getY() { + fetchValues(); + return y; + } + public DSAParams getParams() { + fetchValues(); + return params; + } + public String toString() { + fetchValues(); + return super.toString() + "\n y: " + y + "\n p: " + params.getP() + + "\n q: " + params.getQ() + "\n g: " + params.getG(); + } + } + + private static final class P11DSAPrivateKey extends P11Key + implements DSAPrivateKey { + private static final long serialVersionUID = 3119629997181999389L; + + private BigInteger x; + private DSAParams params; + private byte[] encoded; + P11DSAPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (x != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_SUBPRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + x = attributes[0].getBigInteger(); + params = new DSAParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key key = new sun.security.provider.DSAPrivateKey + (x, params.getP(), params.getQ(), params.getG()); + encoded = key.getEncoded(); + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getX() { + fetchValues(); + return x; + } + public DSAParams getParams() { + fetchValues(); + return params; + } + } + + private static final class P11DHPrivateKey extends P11Key + implements DHPrivateKey { + private static final long serialVersionUID = -1698576167364928838L; + + private BigInteger x; + private DHParameterSpec params; + private byte[] encoded; + P11DHPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (x != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + x = attributes[0].getBigInteger(); + params = new DHParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + DHPrivateKeySpec spec = new DHPrivateKeySpec + (x, params.getP(), params.getG()); + KeyFactory kf = KeyFactory.getInstance + ("DH", P11Util.getSunJceProvider()); + Key key = kf.generatePrivate(spec); + encoded = key.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getX() { + fetchValues(); + return x; + } + public DHParameterSpec getParams() { + fetchValues(); + return params; + } + public int hashCode() { + if (token.isValid() == false) { + return 0; + } + fetchValues(); + return Objects.hash(x, params.getP(), params.getG()); + } + public boolean equals(Object obj) { + if (this == obj) return true; + // equals() should never throw exceptions + if (token.isValid() == false) { + return false; + } + if (!(obj instanceof DHPrivateKey)) { + return false; + } + fetchValues(); + DHPrivateKey other = (DHPrivateKey) obj; + DHParameterSpec otherParams = other.getParams(); + return ((this.x.compareTo(other.getX()) == 0) && + (this.params.getP().compareTo(otherParams.getP()) == 0) && + (this.params.getG().compareTo(otherParams.getG()) == 0)); + } + } + + private static final class P11DHPublicKey extends P11Key + implements DHPublicKey { + static final long serialVersionUID = -598383872153843657L; + + private BigInteger y; + private DHParameterSpec params; + private byte[] encoded; + P11DHPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (y != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + y = attributes[0].getBigInteger(); + params = new DHParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + DHPublicKeySpec spec = new DHPublicKeySpec + (y, params.getP(), params.getG()); + KeyFactory kf = KeyFactory.getInstance + ("DH", P11Util.getSunJceProvider()); + Key key = kf.generatePublic(spec); + encoded = key.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getY() { + fetchValues(); + return y; + } + public DHParameterSpec getParams() { + fetchValues(); + return params; + } + public String toString() { + fetchValues(); + return super.toString() + "\n y: " + y + "\n p: " + params.getP() + + "\n g: " + params.getG(); + } + public int hashCode() { + if (token.isValid() == false) { + return 0; + } + fetchValues(); + return Objects.hash(y, params.getP(), params.getG()); + } + public boolean equals(Object obj) { + if (this == obj) return true; + // equals() should never throw exceptions + if (token.isValid() == false) { + return false; + } + if (!(obj instanceof DHPublicKey)) { + return false; + } + fetchValues(); + DHPublicKey other = (DHPublicKey) obj; + DHParameterSpec otherParams = other.getParams(); + return ((this.y.compareTo(other.getY()) == 0) && + (this.params.getP().compareTo(otherParams.getP()) == 0) && + (this.params.getG().compareTo(otherParams.getG()) == 0)); + } + } + + private static final class P11ECPrivateKey extends P11Key + implements ECPrivateKey { + private static final long serialVersionUID = -7786054399510515515L; + + private BigInteger s; + private ECParameterSpec params; + private byte[] encoded; + P11ECPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (s != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_EC_PARAMS, params), + }; + fetchAttributes(attributes); + s = attributes[0].getBigInteger(); + try { + params = P11ECKeyFactory.decodeParameters + (attributes[1].getByteArray()); + } catch (Exception e) { + throw new RuntimeException("Could not parse key values", e); + } + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key key = ECUtil.generateECPrivateKey(s, params); + encoded = key.getEncoded(); + } catch (InvalidKeySpecException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getS() { + fetchValues(); + return s; + } + public ECParameterSpec getParams() { + fetchValues(); + return params; + } + } + + private static final class P11ECPublicKey extends P11Key + implements ECPublicKey { + private static final long serialVersionUID = -6371481375154806089L; + + private ECPoint w; + private ECParameterSpec params; + private byte[] encoded; + P11ECPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (w != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_EC_POINT), + new CK_ATTRIBUTE(CKA_EC_PARAMS), + }; + fetchAttributes(attributes); + + try { + params = P11ECKeyFactory.decodeParameters + (attributes[1].getByteArray()); + byte[] ecKey = attributes[0].getByteArray(); + + // Check whether the X9.63 encoding of an EC point is wrapped + // in an ASN.1 OCTET STRING + if (!token.config.getUseEcX963Encoding()) { + DerValue wECPoint = new DerValue(ecKey); + + if (wECPoint.getTag() != DerValue.tag_OctetString) { + throw new IOException("Could not DER decode EC point." + + " Unexpected tag: " + wECPoint.getTag()); + } + w = P11ECKeyFactory.decodePoint + (wECPoint.getDataBytes(), params.getCurve()); + + } else { + w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve()); + } + + } catch (Exception e) { + throw new RuntimeException("Could not parse key values", e); + } + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + return ECUtil.x509EncodeECPublicKey(w, params); + } catch (InvalidKeySpecException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public ECPoint getW() { + fetchValues(); + return w; + } + public ECParameterSpec getParams() { + fetchValues(); + return params; + } + public String toString() { + fetchValues(); + return super.toString() + + "\n public x coord: " + w.getAffineX() + + "\n public y coord: " + w.getAffineY() + + "\n parameters: " + params; + } + } +} + +/* + * NOTE: Must use PhantomReference here and not WeakReference + * otherwise the key maybe cleared before other objects which + * still use these keys during finalization such as SSLSocket. + */ +final class SessionKeyRef extends PhantomReference + implements Comparable { + private static ReferenceQueue refQueue = + new ReferenceQueue(); + private static Set refList = + Collections.synchronizedSortedSet(new TreeSet()); + + static ReferenceQueue referenceQueue() { + return refQueue; + } + + private static void drainRefQueueBounded() { + Session sess = null; + Token tkn = null; + while (true) { + SessionKeyRef next = (SessionKeyRef) refQueue.poll(); + if (next == null) { + break; + } + + // If the token is still valid, try to remove the object + if (next.session.token.isValid()) { + // If this key's token is the same as the previous key, the + // same session can be used for C_DestroyObject. + try { + if (next.session.token != tkn || sess == null) { + // Release session if not using previous token + if (tkn != null && sess != null) { + tkn.releaseSession(sess); + sess = null; + } + + tkn = next.session.token; + sess = tkn.getOpSession(); + } + next.disposeNative(sess); + } catch (PKCS11Exception e) { + // ignore + } + } + // Regardless of native results, dispose of java references + next.dispose(); + } + + if (tkn != null && sess != null) { + tkn.releaseSession(sess); + } + } + + // handle to the native key + private long keyID; + private Session session; + + SessionKeyRef(P11Key key , long keyID, Session session) { + super(key, refQueue); + this.keyID = keyID; + this.session = session; + this.session.addObject(); + refList.add(this); + drainRefQueueBounded(); + } + + private void disposeNative(Session s) throws PKCS11Exception { + session.token.p11.C_DestroyObject(s.id(), keyID); + } + + private void dispose() { + refList.remove(this); + this.clear(); + session.removeObject(); + } + + public int compareTo(SessionKeyRef other) { + if (this.keyID == other.keyID) { + return 0; + } else { + return (this.keyID < other.keyID) ? -1 : 1; + } + } +}