--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java Mon Jan 23 11:49:01 2017 -0800
@@ -0,0 +1,429 @@
+/*
+ * 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.math.BigInteger;
+
+import java.security.*;
+import java.security.spec.*;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import sun.security.provider.ParameterCache;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+import sun.security.rsa.RSAKeyFactory;
+
+/**
+ * KeyPairGenerator implementation class. This class currently supports
+ * RSA, DSA, DH, and EC.
+ *
+ * Note that for DSA and DH we rely on the Sun and SunJCE providers to
+ * obtain the parameters from.
+ *
+ * @author Andreas Sterbenz
+ * @since 1.5
+ */
+final class P11KeyPairGenerator extends KeyPairGeneratorSpi {
+
+ // token instance
+ private final Token token;
+
+ // algorithm name
+ private final String algorithm;
+
+ // mechanism id
+ private final long mechanism;
+
+ // selected or default key size, always valid
+ private int keySize;
+
+ // parameters specified via init, if any
+ private AlgorithmParameterSpec params;
+
+ // for RSA, selected or default value of public exponent, always valid
+ private BigInteger rsaPublicExponent = RSAKeyGenParameterSpec.F4;
+
+ // the supported keysize range of the native PKCS11 library
+ // if the value cannot be retrieved or unspecified, -1 is used.
+ private final int minKeySize;
+ private final int maxKeySize;
+
+ // SecureRandom instance, if specified in init
+ 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) {
+ // Should never happen
+ 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
+ // override upper limit to deter DOS attack
+ if (algorithm.equals("EC")) {
+ keySize = 256;
+ if ((minKeyLen == -1) || (minKeyLen < 112)) {
+ minKeyLen = 112;
+ }
+ if ((maxKeyLen == -1) || (maxKeyLen > 2048)) {
+ maxKeyLen = 2048;
+ }
+ } else {
+ if (algorithm.equals("DSA")) {
+ // keep default keysize at 1024 since larger keysizes may be
+ // incompatible with SHA1withDSA and SHA-2 Signature algs
+ // may not be supported by native pkcs11 implementations
+ keySize = 1024;
+ } else {
+ // RSA and DH
+ keySize = 2048;
+ }
+ if ((minKeyLen == -1) || (minKeyLen < 512)) {
+ minKeyLen = 512;
+ }
+ if (algorithm.equals("RSA")) {
+ if ((maxKeyLen == -1) || (maxKeyLen > 64 * 1024)) {
+ maxKeyLen = 64 * 1024;
+ }
+ }
+ }
+
+ // auto-adjust default keysize in case it's out-of-range
+ 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);
+ }
+
+ // see JCA spec
+ @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;
+ }
+
+ // see JCA spec
+ @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);
+ // override the supplied params to null
+ 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);
+ }
+ // override the supplied params with the derived one
+ 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 {
+ // check native range first
+ 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");
+ }
+
+ // check our own algorithm-specific limits also
+ 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) {
+ // sanity check, nobody really wants keys this large
+ throw new InvalidAlgorithmParameterException(
+ "EC key size must be at most 2048 bit. " +
+ "The specific key size " + keySize + " is not supported");
+ }
+ } else {
+ // RSA, DH, DSA
+ 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
+ // Integer.MAX_VALUE to indicate no limit.
+ 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
+ // sanity check, nobody really wants keys this large
+ 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
+ // sun.security.provider.ParameterCache class.
+ 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
+ // parameters bigger than 1024 bits.
+ if ((cache == null) && (keySize > 1024)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported " + keySize +
+ "-bit DH parameter generation");
+ }
+ }
+ } else {
+ // this restriction is in the spec for DSA
+ 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");
+ }
+ }
+ }
+ }
+
+ // see JCA spec
+ @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) {
+ // XXX find better defaults
+ 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);
+ }
+ }
+}