--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java Mon Jan 23 11:49:01 2017 -0800
@@ -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<P11Key>
+ implements Comparable<SessionKeyRef> {
+ private static ReferenceQueue<P11Key> refQueue =
+ new ReferenceQueue<P11Key>();
+ private static Set<SessionKeyRef> refList =
+ Collections.synchronizedSortedSet(new TreeSet<SessionKeyRef>());
+
+ static ReferenceQueue<P11Key> 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;
+ }
+ }
+}