mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
1625 lines
56 KiB
Java
1625 lines
56 KiB
Java
/*
|
|
* Copyright (c) 2003, 2023, 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.RSAUtil.KeyType;
|
|
import sun.security.rsa.RSAPublicKeyImpl;
|
|
import sun.security.rsa.RSAPrivateCrtKeyImpl;
|
|
|
|
import sun.security.internal.interfaces.TlsMasterSecret;
|
|
|
|
import sun.security.pkcs11.wrapper.*;
|
|
|
|
import static sun.security.pkcs11.TemplateManager.O_GENERATE;
|
|
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
|
|
|
|
import sun.security.util.DerValue;
|
|
import sun.security.util.Length;
|
|
import sun.security.util.ECUtil;
|
|
import sun.security.jca.JCAUtil;
|
|
|
|
/**
|
|
* 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 {
|
|
|
|
@Serial
|
|
private static final long serialVersionUID = -2575874101938349339L;
|
|
|
|
private static final String PUBLIC = "public";
|
|
private static final String PRIVATE = "private";
|
|
private static final 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;
|
|
|
|
// 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;
|
|
|
|
// flag indicating whether the current token is NSS
|
|
final transient boolean isNSS;
|
|
|
|
@SuppressWarnings("serial") // Type of field is not Serializable
|
|
private final NativeKeyHolder keyIDHolder;
|
|
|
|
private static final boolean DISABLE_NATIVE_KEYS_EXTRACTION;
|
|
|
|
/**
|
|
* {@systemProperty sun.security.pkcs11.disableKeyExtraction} property
|
|
* indicating whether or not cryptographic keys within tokens are
|
|
* extracted to a Java byte array for memory management purposes.
|
|
*
|
|
* Key extraction affects NSS PKCS11 library only.
|
|
*
|
|
*/
|
|
static {
|
|
PrivilegedAction<String> getKeyExtractionProp =
|
|
() -> System.getProperty(
|
|
"sun.security.pkcs11.disableKeyExtraction", "false");
|
|
@SuppressWarnings("removal")
|
|
String disableKeyExtraction =
|
|
AccessController.doPrivileged(getKeyExtractionProp);
|
|
DISABLE_NATIVE_KEYS_EXTRACTION =
|
|
"true".equalsIgnoreCase(disableKeyExtraction);
|
|
}
|
|
|
|
P11Key(String type, Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
this.type = type;
|
|
this.token = session.token;
|
|
this.algorithm = algorithm;
|
|
this.keyLength = keyLength;
|
|
boolean tokenObject = false;
|
|
boolean sensitive = false;
|
|
boolean extractable = true;
|
|
if (attrs != null) {
|
|
for (CK_ATTRIBUTE attr : attrs) {
|
|
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;
|
|
isNSS = P11Util.isNSS(this.token);
|
|
boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS &&
|
|
extractable && !tokenObject);
|
|
this.keyIDHolder = new NativeKeyHolder(this, keyID, session,
|
|
extractKeyInfo, tokenObject);
|
|
}
|
|
|
|
public long getKeyID() {
|
|
return keyIDHolder.getKeyID();
|
|
}
|
|
|
|
public void releaseKeyID() {
|
|
keyIDHolder.releaseKeyID();
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
// Called by the NativeResourceCleaner at specified intervals
|
|
// See NativeResourceCleaner for more information
|
|
static boolean drainRefQueue() {
|
|
boolean found = false;
|
|
SessionKeyRef next;
|
|
while ((next = (SessionKeyRef) SessionKeyRef.refQueue.poll()) != null) {
|
|
found = true;
|
|
next.dispose();
|
|
}
|
|
return found;
|
|
}
|
|
|
|
abstract byte[] getEncodedInternal();
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
// equals() should never throw exceptions
|
|
if (!token.isValid()) {
|
|
return false;
|
|
}
|
|
if (!(obj instanceof Key other)) {
|
|
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;
|
|
}
|
|
if (!thisFormat.equals(other.getFormat())) {
|
|
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);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
// hashCode() should never throw exceptions
|
|
if (!token.isValid()) {
|
|
return 0;
|
|
}
|
|
return Arrays.hashCode(getEncodedInternal());
|
|
}
|
|
|
|
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, unextractable " + (isNSS ?
|
|
", and NSS token keys" : "keys"));
|
|
}
|
|
return new KeyRep(type, getAlgorithm(), format, getEncodedInternal());
|
|
}
|
|
|
|
public String toString() {
|
|
token.ensureValid();
|
|
String s1 = token.provider.getName() + " " + algorithm + " " + type
|
|
+ " key, " + keyLength + " bits ";
|
|
s1 += (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;
|
|
}
|
|
|
|
CK_ATTRIBUTE[] fetchAttributes(CK_ATTRIBUTE[] attrs) {
|
|
Objects.requireNonNull(attrs, "attrs must be non-null");
|
|
Session tempSession = null;
|
|
long keyID = this.getKeyID();
|
|
try {
|
|
tempSession = token.getOpSession();
|
|
token.p11.C_GetAttributeValue(tempSession.id(), keyID,
|
|
attrs);
|
|
} catch (PKCS11Exception e) {
|
|
throw new ProviderException(e);
|
|
} finally {
|
|
this.releaseKeyID();
|
|
token.releaseSession(tempSession);
|
|
}
|
|
return attrs;
|
|
}
|
|
|
|
// convenience method which returns the attribute values as BigInteger[]
|
|
BigInteger[] fetchAttributesAsInts(CK_ATTRIBUTE[] attrs) {
|
|
attrs = fetchAttributes(attrs);
|
|
BigInteger[] res = new BigInteger[attrs.length];
|
|
for (int i = 0; i < attrs.length; i++) {
|
|
res[i] = attrs[i].getBigInteger();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
private static final 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[] attrs) {
|
|
attrs = getAttributes(session, keyID, attrs, 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, attrs);
|
|
}
|
|
|
|
static SecretKey pbeKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs, char[] password, byte[] salt,
|
|
int iterationCount) {
|
|
attrs = getAttributes(session, keyID, attrs, new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_TOKEN),
|
|
new CK_ATTRIBUTE(CKA_SENSITIVE),
|
|
new CK_ATTRIBUTE(CKA_EXTRACTABLE),
|
|
});
|
|
return new P11PBEKey(session, keyID, algorithm, keyLength,
|
|
attrs, password, salt, iterationCount);
|
|
}
|
|
|
|
static SecretKey masterSecretKey(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs,
|
|
int major, int minor) {
|
|
attrs = getAttributes(session, keyID, attrs, 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,
|
|
attrs, 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[] attrs) {
|
|
return switch (algorithm) {
|
|
case "RSA" -> new P11RSAPublicKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
case "DSA" -> new P11DSAPublicKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
case "DH" -> new P11DHPublicKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
case "EC" -> new P11ECPublicKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
default -> throw new ProviderException
|
|
("Unknown public key algorithm " + algorithm);
|
|
};
|
|
}
|
|
|
|
static PrivateKey privateKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
attrs = getAttributes(session, keyID, attrs, new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_TOKEN),
|
|
new CK_ATTRIBUTE(CKA_SENSITIVE),
|
|
new CK_ATTRIBUTE(CKA_EXTRACTABLE),
|
|
});
|
|
|
|
boolean keySensitive =
|
|
(attrs[0].getBoolean() && P11Util.isNSS(session.token)) ||
|
|
attrs[1].getBoolean() || !attrs[2].getBoolean();
|
|
|
|
return switch (algorithm) {
|
|
case "RSA" -> P11RSAPrivateKeyInternal.of(session, keyID, algorithm,
|
|
keyLength, attrs, keySensitive);
|
|
case "DSA" -> P11DSAPrivateKeyInternal.of(session, keyID, algorithm,
|
|
keyLength, attrs, keySensitive);
|
|
case "DH" -> P11DHPrivateKeyInternal.of(session, keyID, algorithm,
|
|
keyLength, attrs, keySensitive);
|
|
case "EC" -> P11ECPrivateKeyInternal.of(session, keyID, algorithm,
|
|
keyLength, attrs, keySensitive);
|
|
default -> throw new ProviderException
|
|
("Unknown private key algorithm " + algorithm);
|
|
};
|
|
}
|
|
|
|
// base class for all PKCS11 private keys
|
|
private abstract static class P11PrivateKey extends P11Key implements
|
|
PrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = -2138581185214187615L;
|
|
|
|
protected byte[] encoded; // guard by synchronized
|
|
|
|
P11PrivateKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(PRIVATE, session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
// 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 {
|
|
@Serial
|
|
private static final long serialVersionUID = -7828241727014329084L;
|
|
|
|
private volatile byte[] encoded; // guard by double-checked locking
|
|
|
|
P11SecretKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(SECRET, session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
public String getFormat() {
|
|
token.ensureValid();
|
|
if (sensitive || !extractable || (isNSS && tokenObject)) {
|
|
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) {
|
|
b = fetchAttributes(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_VALUE),
|
|
})[0].getByteArray();
|
|
encoded = b;
|
|
}
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
}
|
|
|
|
// base class for all PKCS11 public keys
|
|
private abstract static class P11PublicKey extends P11Key implements
|
|
PublicKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
protected byte[] encoded; // guard by synchronized
|
|
|
|
P11PublicKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(PUBLIC, session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
}
|
|
|
|
static final class P11PBEKey extends P11SecretKey
|
|
implements PBEKey {
|
|
private static final long serialVersionUID = 6847576994253634876L;
|
|
private char[] password;
|
|
private final byte[] salt;
|
|
private final int iterationCount;
|
|
P11PBEKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attributes,
|
|
char[] password, byte[] salt, int iterationCount) {
|
|
super(session, keyID, algorithm, keyLength, attributes);
|
|
this.password = password.clone();
|
|
this.salt = salt.clone();
|
|
this.iterationCount = iterationCount;
|
|
}
|
|
|
|
@Override
|
|
public char[] getPassword() {
|
|
if (password == null) {
|
|
throw new IllegalStateException("password has been cleared");
|
|
}
|
|
return password.clone();
|
|
}
|
|
|
|
@Override
|
|
public byte[] getSalt() {
|
|
return salt.clone();
|
|
}
|
|
|
|
@Override
|
|
public int getIterationCount() {
|
|
return iterationCount;
|
|
}
|
|
|
|
void clearPassword() {
|
|
Arrays.fill(password, '\0');
|
|
password = null;
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
private static class P11TlsMasterSecretKey extends P11SecretKey
|
|
implements TlsMasterSecret {
|
|
@Serial
|
|
private static final long serialVersionUID = -1318560923770573441L;
|
|
|
|
private final int majorVersion, minorVersion;
|
|
P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs, int major, int minor) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
this.majorVersion = major;
|
|
this.minorVersion = minor;
|
|
}
|
|
public int getMajorVersion() {
|
|
return majorVersion;
|
|
}
|
|
|
|
public int getMinorVersion() {
|
|
return minorVersion;
|
|
}
|
|
}
|
|
|
|
// impl class for sensitive/unextractable RSA private keys
|
|
static class P11RSAPrivateKeyInternal extends P11PrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = -2138581185214187615L;
|
|
|
|
static P11RSAPrivateKeyInternal of(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs,
|
|
boolean keySensitive) {
|
|
if (keySensitive) {
|
|
return new P11RSAPrivateKeyInternal(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
} else {
|
|
CK_ATTRIBUTE[] rsaAttrs = new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_MODULUS),
|
|
new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
|
|
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 isCRT = true;
|
|
Session tempSession = null;
|
|
try {
|
|
tempSession = session.token.getOpSession();
|
|
session.token.p11.C_GetAttributeValue(tempSession.id(),
|
|
keyID, rsaAttrs);
|
|
for (CK_ATTRIBUTE attr : rsaAttrs) {
|
|
isCRT &= (attr.pValue instanceof byte[]);
|
|
if (!isCRT) break;
|
|
}
|
|
} catch (PKCS11Exception e) {
|
|
// ignore, assume not available
|
|
isCRT = false;
|
|
} finally {
|
|
session.token.releaseSession(tempSession);
|
|
}
|
|
BigInteger n = rsaAttrs[0].getBigInteger();
|
|
BigInteger d = rsaAttrs[1].getBigInteger();
|
|
if (isCRT) {
|
|
return new P11RSAPrivateKey(session, keyID, algorithm,
|
|
keyLength, attrs, n, d,
|
|
Arrays.copyOfRange(rsaAttrs, 2, rsaAttrs.length));
|
|
} else {
|
|
return new P11RSAPrivateNonCRTKey(session, keyID,
|
|
algorithm, keyLength, attrs, n, d);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected transient BigInteger n;
|
|
|
|
private P11RSAPrivateKeyInternal(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (n != null) return;
|
|
|
|
n = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_MODULUS)
|
|
})[0];
|
|
}
|
|
|
|
public BigInteger getModulus() {
|
|
fetchValues();
|
|
return n;
|
|
}
|
|
}
|
|
|
|
// RSA CRT private key
|
|
private static final class P11RSAPrivateKey extends P11RSAPrivateKeyInternal
|
|
implements RSAPrivateCrtKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 9215872438913515220L;
|
|
|
|
private transient BigInteger e, d, p, q, pe, qe, coeff;
|
|
|
|
private P11RSAPrivateKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs, BigInteger n, BigInteger d,
|
|
CK_ATTRIBUTE[] crtAttrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
|
|
this.n = n;
|
|
this.d = d;
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
public String getFormat() {
|
|
token.ensureValid();
|
|
return "PKCS#8";
|
|
}
|
|
|
|
synchronized byte[] getEncodedInternal() {
|
|
token.ensureValid();
|
|
if (encoded == null) {
|
|
try {
|
|
Key newKey = RSAPrivateCrtKeyImpl.newKey
|
|
(KeyType.RSA, null, n, e, d, p, q, pe, qe, coeff);
|
|
encoded = newKey.getEncoded();
|
|
} catch (GeneralSecurityException e) {
|
|
throw new ProviderException(e);
|
|
}
|
|
}
|
|
return encoded;
|
|
}
|
|
|
|
@Override
|
|
public BigInteger getModulus() {
|
|
return n;
|
|
}
|
|
public BigInteger getPublicExponent() {
|
|
return e;
|
|
}
|
|
public BigInteger getPrivateExponent() {
|
|
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
|
|
P11RSAPrivateKeyInternal implements RSAPrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 1137764983777411481L;
|
|
|
|
private transient BigInteger d;
|
|
|
|
P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs, BigInteger n,
|
|
BigInteger d) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
this.n = n;
|
|
this.d = d;
|
|
}
|
|
|
|
public String getFormat() {
|
|
token.ensureValid();
|
|
return "PKCS#8";
|
|
}
|
|
|
|
synchronized byte[] getEncodedInternal() {
|
|
token.ensureValid();
|
|
if (encoded == null) {
|
|
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;
|
|
}
|
|
|
|
@Override
|
|
public BigInteger getModulus() {
|
|
return n;
|
|
}
|
|
public BigInteger getPrivateExponent() {
|
|
return d;
|
|
}
|
|
}
|
|
|
|
private static final class P11RSAPublicKey extends P11PublicKey
|
|
implements RSAPublicKey {
|
|
@Serial
|
|
private static final long serialVersionUID = -826726289023854455L;
|
|
private transient BigInteger n, e;
|
|
|
|
P11RSAPublicKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (n != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_MODULUS),
|
|
new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT)
|
|
});
|
|
n = res[0];
|
|
e = res[1];
|
|
}
|
|
|
|
public String getFormat() {
|
|
token.ensureValid();
|
|
return "X.509";
|
|
}
|
|
|
|
synchronized byte[] getEncodedInternal() {
|
|
token.ensureValid();
|
|
if (encoded == null) {
|
|
fetchValues();
|
|
try {
|
|
encoded = RSAPublicKeyImpl.newKey
|
|
(KeyType.RSA, null, 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 P11PublicKey
|
|
implements DSAPublicKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 5989753793316396637L;
|
|
|
|
private transient BigInteger y;
|
|
private transient DSAParams params;
|
|
|
|
P11DSAPublicKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (y != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_VALUE),
|
|
new CK_ATTRIBUTE(CKA_PRIME),
|
|
new CK_ATTRIBUTE(CKA_SUBPRIME),
|
|
new CK_ATTRIBUTE(CKA_BASE)
|
|
});
|
|
y = res[0];
|
|
params = new DSAParameterSpec(res[1], res[2], res[3]);
|
|
}
|
|
|
|
public String getFormat() {
|
|
token.ensureValid();
|
|
return "X.509";
|
|
}
|
|
|
|
synchronized byte[] getEncodedInternal() {
|
|
token.ensureValid();
|
|
if (encoded == null) {
|
|
fetchValues();
|
|
Key key = new sun.security.provider.DSAPublicKey
|
|
(y, params.getP(), params.getQ(), params.getG());
|
|
encoded = key.getEncoded();
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
|
|
static class P11DSAPrivateKeyInternal extends P11PrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 3119629997181999389L;
|
|
|
|
protected transient DSAParams params;
|
|
|
|
static P11DSAPrivateKeyInternal of(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs,
|
|
boolean keySensitive) {
|
|
if (keySensitive) {
|
|
return new P11DSAPrivateKeyInternal(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
} else {
|
|
return new P11DSAPrivateKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
}
|
|
}
|
|
|
|
private P11DSAPrivateKeyInternal(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (params != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_PRIME),
|
|
new CK_ATTRIBUTE(CKA_SUBPRIME),
|
|
new CK_ATTRIBUTE(CKA_BASE),
|
|
});
|
|
params = new DSAParameterSpec(res[0], res[1], res[2]);
|
|
}
|
|
|
|
@Override
|
|
public DSAParams getParams() {
|
|
fetchValues();
|
|
return params;
|
|
}
|
|
}
|
|
|
|
private static final class P11DSAPrivateKey extends P11DSAPrivateKeyInternal
|
|
implements DSAPrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 3119629997181999389L;
|
|
|
|
private transient BigInteger x; // params inside P11DSAPrivateKeyInternal
|
|
|
|
P11DSAPrivateKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (x != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_VALUE),
|
|
new CK_ATTRIBUTE(CKA_PRIME),
|
|
new CK_ATTRIBUTE(CKA_SUBPRIME),
|
|
new CK_ATTRIBUTE(CKA_BASE),
|
|
});
|
|
x = res[0];
|
|
params = new DSAParameterSpec(res[1], res[2], res[3]);
|
|
}
|
|
|
|
public String getFormat() {
|
|
token.ensureValid();
|
|
return "PKCS#8";
|
|
}
|
|
|
|
synchronized byte[] getEncodedInternal() {
|
|
token.ensureValid();
|
|
if (encoded == null) {
|
|
fetchValues();
|
|
Key key = new sun.security.provider.DSAPrivateKey
|
|
(x, params.getP(), params.getQ(), params.getG());
|
|
encoded = key.getEncoded();
|
|
}
|
|
return encoded;
|
|
}
|
|
|
|
public BigInteger getX() {
|
|
fetchValues();
|
|
return x;
|
|
}
|
|
|
|
@Override
|
|
public DSAParams getParams() {
|
|
fetchValues();
|
|
return params;
|
|
}
|
|
}
|
|
|
|
static class P11DHPrivateKeyInternal extends P11PrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
protected transient DHParameterSpec params;
|
|
|
|
static P11DHPrivateKeyInternal of(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs,
|
|
boolean keySensitive) {
|
|
if (keySensitive) {
|
|
return new P11DHPrivateKeyInternal(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
} else {
|
|
return new P11DHPrivateKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
}
|
|
}
|
|
|
|
private P11DHPrivateKeyInternal(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (params != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_PRIME),
|
|
new CK_ATTRIBUTE(CKA_BASE),
|
|
});
|
|
params = new DHParameterSpec(res[0], res[1]);
|
|
}
|
|
|
|
public DHParameterSpec getParams() {
|
|
fetchValues();
|
|
return params;
|
|
}
|
|
}
|
|
|
|
private static final class P11DHPrivateKey extends P11DHPrivateKeyInternal
|
|
implements DHPrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = -1698576167364928838L;
|
|
|
|
private transient BigInteger x; // params in P11DHPrivateKeyInternal
|
|
|
|
P11DHPrivateKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (x != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_VALUE),
|
|
new CK_ATTRIBUTE(CKA_PRIME),
|
|
new CK_ATTRIBUTE(CKA_BASE),
|
|
});
|
|
x = res[0];
|
|
params = new DHParameterSpec(res[1], res[2]);
|
|
}
|
|
|
|
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() {
|
|
fetchValues();
|
|
if (!token.isValid()) {
|
|
return 0;
|
|
}
|
|
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()) {
|
|
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 P11PublicKey
|
|
implements DHPublicKey {
|
|
static final long serialVersionUID = -598383872153843657L;
|
|
|
|
private transient BigInteger y;
|
|
private transient DHParameterSpec params;
|
|
|
|
P11DHPublicKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (y != null) return;
|
|
|
|
BigInteger[] res = fetchAttributesAsInts(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_VALUE),
|
|
new CK_ATTRIBUTE(CKA_PRIME),
|
|
new CK_ATTRIBUTE(CKA_BASE),
|
|
});
|
|
y = res[0];
|
|
params = new DHParameterSpec(res[1], res[2]);
|
|
}
|
|
|
|
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()) {
|
|
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()) {
|
|
return false;
|
|
}
|
|
if (!(obj instanceof DHPublicKey other)) {
|
|
return false;
|
|
}
|
|
fetchValues();
|
|
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));
|
|
}
|
|
}
|
|
|
|
static class P11ECPrivateKeyInternal extends P11PrivateKey {
|
|
|
|
@Serial
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
protected transient ECParameterSpec params;
|
|
|
|
static P11ECPrivateKeyInternal of(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs,
|
|
boolean keySensitive) {
|
|
if (keySensitive) {
|
|
return new P11ECPrivateKeyInternal(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
} else {
|
|
return new P11ECPrivateKey(session, keyID, algorithm,
|
|
keyLength, attrs);
|
|
}
|
|
}
|
|
|
|
private P11ECPrivateKeyInternal(Session session, long keyID,
|
|
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (params != null) return;
|
|
|
|
try {
|
|
byte[] paramBytes = fetchAttributes(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_EC_PARAMS)
|
|
})[0].getByteArray();
|
|
|
|
params = P11ECKeyFactory.decodeParameters(paramBytes);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Could not parse key values", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ECParameterSpec getParams() {
|
|
fetchValues();
|
|
return params;
|
|
}
|
|
}
|
|
|
|
private static final class P11ECPrivateKey extends P11ECPrivateKeyInternal
|
|
implements ECPrivateKey {
|
|
@Serial
|
|
private static final long serialVersionUID = -7786054399510515515L;
|
|
|
|
private transient BigInteger s; // params in P11ECPrivateKeyInternal
|
|
|
|
P11ECPrivateKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (s != null) return;
|
|
|
|
CK_ATTRIBUTE[] attrs = fetchAttributes(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_VALUE),
|
|
new CK_ATTRIBUTE(CKA_EC_PARAMS),
|
|
});
|
|
|
|
s = attrs[0].getBigInteger();
|
|
try {
|
|
params = P11ECKeyFactory.decodeParameters
|
|
(attrs[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() {
|
|
if (encoded == null) {
|
|
try {
|
|
fetchValues();
|
|
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 P11PublicKey
|
|
implements ECPublicKey {
|
|
@Serial
|
|
private static final long serialVersionUID = -6371481375154806089L;
|
|
|
|
private transient ECPoint w;
|
|
private transient ECParameterSpec params;
|
|
|
|
P11ECPublicKey(Session session, long keyID, String algorithm,
|
|
int keyLength, CK_ATTRIBUTE[] attrs) {
|
|
super(session, keyID, algorithm, keyLength, attrs);
|
|
}
|
|
|
|
private synchronized void fetchValues() {
|
|
token.ensureValid();
|
|
if (w != null) return;
|
|
|
|
CK_ATTRIBUTE[] attrs = fetchAttributes(new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_EC_POINT),
|
|
new CK_ATTRIBUTE(CKA_EC_PARAMS),
|
|
});
|
|
|
|
try {
|
|
params = P11ECKeyFactory.decodeParameters
|
|
(attrs[1].getByteArray());
|
|
byte[] ecKey = attrs[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;
|
|
}
|
|
}
|
|
}
|
|
final class NativeKeyHolder {
|
|
|
|
private static long nativeKeyWrapperKeyID = 0;
|
|
private static CK_MECHANISM nativeKeyWrapperMechanism = null;
|
|
private static long nativeKeyWrapperRefCount = 0;
|
|
private static Session nativeKeyWrapperSession = null;
|
|
|
|
private final P11Key p11Key;
|
|
private final byte[] nativeKeyInfo;
|
|
private boolean wrapperKeyUsed;
|
|
|
|
// destroyed and recreated when refCount toggles to 1
|
|
private long keyID;
|
|
|
|
// phantom reference notification clean up for session keys
|
|
private final SessionKeyRef ref;
|
|
|
|
private int refCount;
|
|
|
|
private static void createNativeKeyWrapper(Token token)
|
|
throws PKCS11Exception {
|
|
assert(nativeKeyWrapperKeyID == 0);
|
|
assert(nativeKeyWrapperRefCount == 0);
|
|
assert(nativeKeyWrapperSession == null);
|
|
// Create a global wrapping/unwrapping key
|
|
CK_ATTRIBUTE[] wrappingAttributes = token.getAttributes(O_GENERATE,
|
|
CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] {
|
|
new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
|
|
new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3)});
|
|
Session s = null;
|
|
try {
|
|
s = token.getObjSession();
|
|
nativeKeyWrapperKeyID = token.p11.C_GenerateKey(
|
|
s.id(), new CK_MECHANISM(CKM_AES_KEY_GEN),
|
|
wrappingAttributes);
|
|
nativeKeyWrapperSession = s;
|
|
nativeKeyWrapperSession.addObject();
|
|
byte[] iv = new byte[16];
|
|
JCAUtil.getSecureRandom().nextBytes(iv);
|
|
nativeKeyWrapperMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, iv);
|
|
} catch (PKCS11Exception e) {
|
|
// best effort
|
|
} finally {
|
|
token.releaseSession(s);
|
|
}
|
|
}
|
|
|
|
private static void deleteNativeKeyWrapper() {
|
|
Token token = nativeKeyWrapperSession.token;
|
|
if (token.isValid()) {
|
|
Session s = null;
|
|
try {
|
|
s = token.getOpSession();
|
|
token.p11.C_DestroyObject(s.id(), nativeKeyWrapperKeyID);
|
|
nativeKeyWrapperSession.removeObject();
|
|
} catch (PKCS11Exception e) {
|
|
// best effort
|
|
} finally {
|
|
token.releaseSession(s);
|
|
}
|
|
}
|
|
nativeKeyWrapperKeyID = 0;
|
|
nativeKeyWrapperMechanism = null;
|
|
nativeKeyWrapperSession = null;
|
|
}
|
|
|
|
static void decWrapperKeyRef() {
|
|
synchronized(NativeKeyHolder.class) {
|
|
assert(nativeKeyWrapperKeyID != 0);
|
|
assert(nativeKeyWrapperRefCount > 0);
|
|
nativeKeyWrapperRefCount--;
|
|
if (nativeKeyWrapperRefCount == 0) {
|
|
deleteNativeKeyWrapper();
|
|
}
|
|
}
|
|
}
|
|
|
|
NativeKeyHolder(P11Key p11Key, long keyID, Session keySession,
|
|
boolean extractKeyInfo, boolean isTokenObject) {
|
|
this.p11Key = p11Key;
|
|
this.keyID = keyID;
|
|
this.refCount = -1;
|
|
byte[] ki = null;
|
|
if (isTokenObject) {
|
|
this.ref = null;
|
|
} else {
|
|
// Try extracting key info, if any error, disable it
|
|
Token token = p11Key.token;
|
|
if (extractKeyInfo) {
|
|
try {
|
|
if (p11Key.sensitive) {
|
|
// p11Key native key information has to be wrapped
|
|
synchronized(NativeKeyHolder.class) {
|
|
if (nativeKeyWrapperKeyID == 0) {
|
|
createNativeKeyWrapper(token);
|
|
}
|
|
// If a wrapper-key was successfully created or
|
|
// already exists, increment its reference
|
|
// counter to keep it alive while native key
|
|
// information is being held.
|
|
if (nativeKeyWrapperKeyID != 0) {
|
|
nativeKeyWrapperRefCount++;
|
|
wrapperKeyUsed = true;
|
|
}
|
|
}
|
|
}
|
|
Session opSession = null;
|
|
try {
|
|
opSession = token.getOpSession();
|
|
ki = p11Key.token.p11.getNativeKeyInfo(opSession.id(),
|
|
keyID, nativeKeyWrapperKeyID,
|
|
nativeKeyWrapperMechanism);
|
|
} catch (PKCS11Exception e) {
|
|
// best effort
|
|
} finally {
|
|
token.releaseSession(opSession);
|
|
}
|
|
} catch (PKCS11Exception e) {
|
|
// best effort
|
|
}
|
|
}
|
|
this.ref = new SessionKeyRef(p11Key, keyID, wrapperKeyUsed,
|
|
keySession);
|
|
}
|
|
|
|
this.nativeKeyInfo = ((ki == null || ki.length == 0)? null : ki);
|
|
}
|
|
|
|
long getKeyID() throws ProviderException {
|
|
if (this.nativeKeyInfo != null) {
|
|
synchronized(this.nativeKeyInfo) {
|
|
if (this.refCount == -1) {
|
|
this.refCount = 0;
|
|
}
|
|
int cnt = (this.refCount)++;
|
|
if (keyID == 0) {
|
|
if (cnt != 0) {
|
|
throw new RuntimeException(
|
|
"Error: null keyID with non-zero refCount " + cnt);
|
|
}
|
|
Token token = p11Key.token;
|
|
// Create keyID using nativeKeyInfo
|
|
Session session = null;
|
|
try {
|
|
session = token.getObjSession();
|
|
this.keyID = token.p11.createNativeKey(session.id(),
|
|
nativeKeyInfo, nativeKeyWrapperKeyID,
|
|
nativeKeyWrapperMechanism);
|
|
this.ref.registerNativeKey(this.keyID, session);
|
|
} catch (PKCS11Exception e) {
|
|
this.refCount--;
|
|
throw new ProviderException("Error recreating native key", e);
|
|
} finally {
|
|
token.releaseSession(session);
|
|
}
|
|
} else {
|
|
if (cnt < 0) {
|
|
throw new RuntimeException("ERROR: negative refCount");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return keyID;
|
|
}
|
|
|
|
void releaseKeyID() {
|
|
if (this.nativeKeyInfo != null) {
|
|
synchronized(this.nativeKeyInfo) {
|
|
if (this.refCount == -1) {
|
|
throw new RuntimeException("Error: miss match getKeyID call");
|
|
}
|
|
int cnt = --(this.refCount);
|
|
if (cnt == 0) {
|
|
// destroy
|
|
if (this.keyID == 0) {
|
|
throw new RuntimeException("ERROR: null keyID can't be destroyed");
|
|
}
|
|
|
|
// destroy
|
|
this.keyID = 0;
|
|
this.ref.removeNativeKey();
|
|
} else {
|
|
if (cnt < 0) {
|
|
// should never happen as we start count at 1 and pair get/release calls
|
|
throw new RuntimeException("wrong refCount value: " + cnt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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> {
|
|
static ReferenceQueue<P11Key> refQueue = new ReferenceQueue<>();
|
|
private static Set<SessionKeyRef> refSet =
|
|
Collections.synchronizedSet(new HashSet<>());
|
|
|
|
// handle to the native key and the session it is generated under
|
|
private long keyID;
|
|
private Session session;
|
|
private boolean wrapperKeyUsed;
|
|
|
|
SessionKeyRef(P11Key p11Key, long keyID, boolean wrapperKeyUsed,
|
|
Session session) {
|
|
super(p11Key, refQueue);
|
|
if (session == null) {
|
|
throw new ProviderException
|
|
("key must be associated with a session");
|
|
}
|
|
registerNativeKey(keyID, session);
|
|
this.wrapperKeyUsed = wrapperKeyUsed;
|
|
|
|
refSet.add(this);
|
|
}
|
|
|
|
void registerNativeKey(long newKeyID, Session newSession) {
|
|
assert(newKeyID != 0);
|
|
assert(newSession != null);
|
|
updateNativeKey(newKeyID, newSession);
|
|
}
|
|
|
|
void removeNativeKey() {
|
|
assert(session != null);
|
|
updateNativeKey(0, null);
|
|
}
|
|
|
|
private void updateNativeKey(long newKeyID, Session newSession) {
|
|
if (newKeyID == 0) {
|
|
assert(newSession == null);
|
|
Token token = session.token;
|
|
// If the token is still valid, try to remove the key object
|
|
if (token.isValid()) {
|
|
Session s = null;
|
|
try {
|
|
s = token.getOpSession();
|
|
token.p11.C_DestroyObject(s.id(), this.keyID);
|
|
} catch (PKCS11Exception e) {
|
|
// best effort
|
|
} finally {
|
|
token.releaseSession(s);
|
|
}
|
|
}
|
|
session.removeObject();
|
|
} else {
|
|
newSession.addObject();
|
|
}
|
|
keyID = newKeyID;
|
|
session = newSession;
|
|
}
|
|
|
|
// Called when the GC disposes a p11Key
|
|
void dispose() {
|
|
if (wrapperKeyUsed) {
|
|
// Wrapper-key no longer needed for
|
|
// p11Key native key information
|
|
NativeKeyHolder.decWrapperKeyRef();
|
|
}
|
|
if (keyID != 0) {
|
|
removeNativeKey();
|
|
}
|
|
refSet.remove(this);
|
|
this.clear();
|
|
}
|
|
}
|
|
|