|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.pkcs11; |
|
|
|
import java.math.BigInteger; |
|
|
|
import java.security.*; |
|
import java.security.spec.*; |
|
|
|
import javax.crypto.spec.DHParameterSpec; |
|
|
|
import sun.security.provider.ParameterCache; |
|
import static sun.security.util.SecurityProviderConstants.*; |
|
|
|
import static sun.security.pkcs11.TemplateManager.*; |
|
import sun.security.pkcs11.wrapper.*; |
|
import static sun.security.pkcs11.wrapper.PKCS11Constants.*; |
|
|
|
|
|
import sun.security.rsa.RSAKeyFactory; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class P11KeyPairGenerator extends KeyPairGeneratorSpi { |
|
|
|
|
|
private final Token token; |
|
|
|
|
|
private final String algorithm; |
|
|
|
|
|
private final long mechanism; |
|
|
|
|
|
private int keySize; |
|
|
|
|
|
private AlgorithmParameterSpec params; |
|
|
|
|
|
private BigInteger rsaPublicExponent = RSAKeyGenParameterSpec.F4; |
|
|
|
// the supported keysize range of the native PKCS11 library |
|
|
|
private final int minKeySize; |
|
private final int maxKeySize; |
|
|
|
|
|
private SecureRandom random; |
|
|
|
P11KeyPairGenerator(Token token, String algorithm, long mechanism) |
|
throws PKCS11Exception { |
|
super(); |
|
int minKeyLen = -1; |
|
int maxKeyLen = -1; |
|
try { |
|
CK_MECHANISM_INFO mechInfo = token.getMechanismInfo(mechanism); |
|
if (mechInfo != null) { |
|
minKeyLen = (int) mechInfo.ulMinKeySize; |
|
maxKeyLen = (int) mechInfo.ulMaxKeySize; |
|
} |
|
} catch (PKCS11Exception p11e) { |
|
|
|
throw new ProviderException |
|
("Unexpected error while getting mechanism info", p11e); |
|
} |
|
// set default key sizes and apply our own algorithm-specific limits |
|
// override lower limit to disallow unsecure keys being generated |
|
|
|
if (algorithm.equals("EC")) { |
|
keySize = DEF_EC_KEY_SIZE; |
|
if ((minKeyLen == -1) || (minKeyLen < 112)) { |
|
minKeyLen = 112; |
|
} |
|
if ((maxKeyLen == -1) || (maxKeyLen > 2048)) { |
|
maxKeyLen = 2048; |
|
} |
|
} else { |
|
if (algorithm.equals("DSA")) { |
|
keySize = DEF_DSA_KEY_SIZE; |
|
} else if (algorithm.equals("RSA")) { |
|
keySize = DEF_RSA_KEY_SIZE; |
|
} else { |
|
keySize = DEF_DH_KEY_SIZE; |
|
} |
|
if ((minKeyLen == -1) || (minKeyLen < 512)) { |
|
minKeyLen = 512; |
|
} |
|
if (algorithm.equals("RSA")) { |
|
if ((maxKeyLen == -1) || (maxKeyLen > 64 * 1024)) { |
|
maxKeyLen = 64 * 1024; |
|
} |
|
} |
|
} |
|
|
|
|
|
if ((minKeyLen != -1) && (keySize < minKeyLen)) { |
|
keySize = minKeyLen; |
|
} |
|
if ((maxKeyLen != -1) && (keySize > maxKeyLen)) { |
|
keySize = maxKeyLen; |
|
} |
|
this.token = token; |
|
this.algorithm = algorithm; |
|
this.mechanism = mechanism; |
|
this.minKeySize = minKeyLen; |
|
this.maxKeySize = maxKeyLen; |
|
initialize(keySize, null); |
|
} |
|
|
|
|
|
@Override |
|
public void initialize(int keySize, SecureRandom random) { |
|
token.ensureValid(); |
|
try { |
|
checkKeySize(keySize, null); |
|
} catch (InvalidAlgorithmParameterException e) { |
|
throw new InvalidParameterException(e.getMessage()); |
|
} |
|
this.params = null; |
|
if (algorithm.equals("EC")) { |
|
params = P11ECKeyFactory.getECParameterSpec(keySize); |
|
if (params == null) { |
|
throw new InvalidParameterException( |
|
"No EC parameters available for key size " |
|
+ keySize + " bits"); |
|
} |
|
} |
|
this.keySize = keySize; |
|
this.random = random; |
|
} |
|
|
|
|
|
@Override |
|
public void initialize(AlgorithmParameterSpec params, SecureRandom random) |
|
throws InvalidAlgorithmParameterException { |
|
token.ensureValid(); |
|
int tmpKeySize; |
|
if (algorithm.equals("DH")) { |
|
if (params instanceof DHParameterSpec == false) { |
|
throw new InvalidAlgorithmParameterException |
|
("DHParameterSpec required for Diffie-Hellman"); |
|
} |
|
DHParameterSpec dhParams = (DHParameterSpec) params; |
|
tmpKeySize = dhParams.getP().bitLength(); |
|
checkKeySize(tmpKeySize, dhParams); |
|
// XXX sanity check params |
|
} else if (algorithm.equals("RSA")) { |
|
if (params instanceof RSAKeyGenParameterSpec == false) { |
|
throw new InvalidAlgorithmParameterException |
|
("RSAKeyGenParameterSpec required for RSA"); |
|
} |
|
RSAKeyGenParameterSpec rsaParams = |
|
(RSAKeyGenParameterSpec) params; |
|
tmpKeySize = rsaParams.getKeysize(); |
|
checkKeySize(tmpKeySize, rsaParams); |
|
|
|
params = null; |
|
this.rsaPublicExponent = rsaParams.getPublicExponent(); |
|
// XXX sanity check params |
|
} else if (algorithm.equals("DSA")) { |
|
if (params instanceof DSAParameterSpec == false) { |
|
throw new InvalidAlgorithmParameterException |
|
("DSAParameterSpec required for DSA"); |
|
} |
|
DSAParameterSpec dsaParams = (DSAParameterSpec) params; |
|
tmpKeySize = dsaParams.getP().bitLength(); |
|
checkKeySize(tmpKeySize, dsaParams); |
|
// XXX sanity check params |
|
} else if (algorithm.equals("EC")) { |
|
ECParameterSpec ecParams; |
|
if (params instanceof ECParameterSpec) { |
|
ecParams = P11ECKeyFactory.getECParameterSpec( |
|
(ECParameterSpec)params); |
|
if (ecParams == null) { |
|
throw new InvalidAlgorithmParameterException |
|
("Unsupported curve: " + params); |
|
} |
|
} else if (params instanceof ECGenParameterSpec) { |
|
String name = ((ECGenParameterSpec) params).getName(); |
|
ecParams = P11ECKeyFactory.getECParameterSpec(name); |
|
if (ecParams == null) { |
|
throw new InvalidAlgorithmParameterException |
|
("Unknown curve name: " + name); |
|
} |
|
|
|
params = ecParams; |
|
} else { |
|
throw new InvalidAlgorithmParameterException |
|
("ECParameterSpec or ECGenParameterSpec required for EC"); |
|
} |
|
tmpKeySize = ecParams.getCurve().getField().getFieldSize(); |
|
checkKeySize(tmpKeySize, ecParams); |
|
} else { |
|
throw new ProviderException("Unknown algorithm: " + algorithm); |
|
} |
|
this.keySize = tmpKeySize; |
|
this.params = params; |
|
this.random = random; |
|
} |
|
|
|
private void checkKeySize(int keySize, AlgorithmParameterSpec params) |
|
throws InvalidAlgorithmParameterException { |
|
|
|
if ((minKeySize != -1) && (keySize < minKeySize)) { |
|
throw new InvalidAlgorithmParameterException(algorithm + |
|
" key must be at least " + minKeySize + " bits. " + |
|
"The specific key size " + keySize + " is not supported"); |
|
} |
|
if ((maxKeySize != -1) && (keySize > maxKeySize)) { |
|
throw new InvalidAlgorithmParameterException(algorithm + |
|
" key must be at most " + maxKeySize + " bits. " + |
|
"The specific key size " + keySize + " is not supported"); |
|
} |
|
|
|
|
|
if (algorithm.equals("EC")) { |
|
if (keySize < 112) { |
|
throw new InvalidAlgorithmParameterException( |
|
"EC key size must be at least 112 bit. " + |
|
"The specific key size " + keySize + " is not supported"); |
|
} |
|
if (keySize > 2048) { |
|
|
|
throw new InvalidAlgorithmParameterException( |
|
"EC key size must be at most 2048 bit. " + |
|
"The specific key size " + keySize + " is not supported"); |
|
} |
|
} else { |
|
|
|
if (keySize < 512) { |
|
throw new InvalidAlgorithmParameterException(algorithm + |
|
" key size must be at least 512 bit. " + |
|
"The specific key size " + keySize + " is not supported"); |
|
} |
|
if (algorithm.equals("RSA")) { |
|
BigInteger tmpExponent = rsaPublicExponent; |
|
if (params != null) { |
|
tmpExponent = |
|
((RSAKeyGenParameterSpec)params).getPublicExponent(); |
|
} |
|
try { |
|
// Reuse the checking in SunRsaSign provider. |
|
// If maxKeySize is -1, then replace it with |
|
|
|
RSAKeyFactory.checkKeyLengths(keySize, tmpExponent, |
|
minKeySize, |
|
(maxKeySize==-1? Integer.MAX_VALUE:maxKeySize)); |
|
} catch (InvalidKeyException e) { |
|
throw new InvalidAlgorithmParameterException(e); |
|
} |
|
} else if (algorithm.equals("DH")) { |
|
if (params != null) { // initialized with specified parameters |
|
|
|
if (keySize > 64 * 1024) { |
|
throw new InvalidAlgorithmParameterException( |
|
"DH key size must be at most 65536 bit. " + |
|
"The specific key size " + |
|
keySize + " is not supported"); |
|
} |
|
} else { // default parameters will be used. |
|
// Range is based on the values in |
|
|
|
if ((keySize > 8192) || (keySize < 512) || |
|
((keySize & 0x3f) != 0)) { |
|
throw new InvalidAlgorithmParameterException( |
|
"DH key size must be multiple of 64, and can " + |
|
"only range from 512 to 8192 (inclusive). " + |
|
"The specific key size " + |
|
keySize + " is not supported"); |
|
} |
|
|
|
DHParameterSpec cache = |
|
ParameterCache.getCachedDHParameterSpec(keySize); |
|
// Except 2048 and 3072, not yet support generation of |
|
|
|
if ((cache == null) && (keySize > 1024)) { |
|
throw new InvalidAlgorithmParameterException( |
|
"Unsupported " + keySize + |
|
"-bit DH parameter generation"); |
|
} |
|
} |
|
} else { |
|
|
|
if ((keySize != 3072) && (keySize != 2048) && |
|
((keySize > 1024) || ((keySize & 0x3f) != 0))) { |
|
throw new InvalidAlgorithmParameterException( |
|
"DSA key must be multiples of 64 if less than " + |
|
"1024 bits, or 2048, 3072 bits. " + |
|
"The specific key size " + |
|
keySize + " is not supported"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
@Override |
|
public KeyPair generateKeyPair() { |
|
token.ensureValid(); |
|
CK_ATTRIBUTE[] publicKeyTemplate; |
|
CK_ATTRIBUTE[] privateKeyTemplate; |
|
long keyType; |
|
if (algorithm.equals("RSA")) { |
|
keyType = CKK_RSA; |
|
publicKeyTemplate = new CK_ATTRIBUTE[] { |
|
new CK_ATTRIBUTE(CKA_MODULUS_BITS, keySize), |
|
new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, rsaPublicExponent), |
|
}; |
|
privateKeyTemplate = new CK_ATTRIBUTE[] { |
|
// empty |
|
}; |
|
} else if (algorithm.equals("DSA")) { |
|
keyType = CKK_DSA; |
|
DSAParameterSpec dsaParams; |
|
if (params == null) { |
|
try { |
|
dsaParams = ParameterCache.getDSAParameterSpec |
|
(keySize, random); |
|
} catch (GeneralSecurityException e) { |
|
throw new ProviderException |
|
("Could not generate DSA parameters", e); |
|
} |
|
} else { |
|
dsaParams = (DSAParameterSpec)params; |
|
} |
|
publicKeyTemplate = new CK_ATTRIBUTE[] { |
|
new CK_ATTRIBUTE(CKA_PRIME, dsaParams.getP()), |
|
new CK_ATTRIBUTE(CKA_SUBPRIME, dsaParams.getQ()), |
|
new CK_ATTRIBUTE(CKA_BASE, dsaParams.getG()), |
|
}; |
|
privateKeyTemplate = new CK_ATTRIBUTE[] { |
|
// empty |
|
}; |
|
} else if (algorithm.equals("DH")) { |
|
keyType = CKK_DH; |
|
DHParameterSpec dhParams; |
|
int privateBits; |
|
if (params == null) { |
|
try { |
|
dhParams = ParameterCache.getDHParameterSpec |
|
(keySize, random); |
|
} catch (GeneralSecurityException e) { |
|
throw new ProviderException |
|
("Could not generate DH parameters", e); |
|
} |
|
privateBits = 0; |
|
} else { |
|
dhParams = (DHParameterSpec)params; |
|
privateBits = dhParams.getL(); |
|
} |
|
if (privateBits <= 0) { |
|
|
|
privateBits = (keySize >= 1024) ? 768 : 512; |
|
} |
|
publicKeyTemplate = new CK_ATTRIBUTE[] { |
|
new CK_ATTRIBUTE(CKA_PRIME, dhParams.getP()), |
|
new CK_ATTRIBUTE(CKA_BASE, dhParams.getG()) |
|
}; |
|
privateKeyTemplate = new CK_ATTRIBUTE[] { |
|
new CK_ATTRIBUTE(CKA_VALUE_BITS, privateBits), |
|
}; |
|
} else if (algorithm.equals("EC")) { |
|
keyType = CKK_EC; |
|
byte[] encodedParams = |
|
P11ECKeyFactory.encodeParameters((ECParameterSpec)params); |
|
publicKeyTemplate = new CK_ATTRIBUTE[] { |
|
new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams), |
|
}; |
|
privateKeyTemplate = new CK_ATTRIBUTE[] { |
|
// empty |
|
}; |
|
} else { |
|
throw new ProviderException("Unknown algorithm: " + algorithm); |
|
} |
|
Session session = null; |
|
try { |
|
session = token.getObjSession(); |
|
publicKeyTemplate = token.getAttributes |
|
(O_GENERATE, CKO_PUBLIC_KEY, keyType, publicKeyTemplate); |
|
privateKeyTemplate = token.getAttributes |
|
(O_GENERATE, CKO_PRIVATE_KEY, keyType, privateKeyTemplate); |
|
long[] keyIDs = token.p11.C_GenerateKeyPair |
|
(session.id(), new CK_MECHANISM(mechanism), |
|
publicKeyTemplate, privateKeyTemplate); |
|
PublicKey publicKey = P11Key.publicKey |
|
(session, keyIDs[0], algorithm, keySize, publicKeyTemplate); |
|
PrivateKey privateKey = P11Key.privateKey |
|
(session, keyIDs[1], algorithm, keySize, privateKeyTemplate); |
|
return new KeyPair(publicKey, privateKey); |
|
} catch (PKCS11Exception e) { |
|
throw new ProviderException(e); |
|
} finally { |
|
token.releaseSession(session); |
|
} |
|
} |
|
} |