# HG changeset patch # User valeriep # Date 1381255651 25200 # Node ID 0b4f7f1080056268a07b27e4d2964f162f27ba6b # Parent 7b3d199dbe40dd89b64aaafe0e2806fa6ebaaefe 7196382: PKCS11 provider should support 2048-bit DH Summary: Query and enforce range checking using the values from native PKCS11 library. Reviewed-by: xuelei diff -r 7b3d199dbe40 -r 0b4f7f108005 src/share/classes/com/sun/crypto/provider/DHParameterGenerator.java --- a/src/share/classes/com/sun/crypto/provider/DHParameterGenerator.java Tue Sep 25 11:31:17 2012 -0700 +++ b/src/share/classes/com/sun/crypto/provider/DHParameterGenerator.java Tue Oct 08 11:07:31 2013 -0700 @@ -58,6 +58,16 @@ // The source of randomness private SecureRandom random = null; + private static void checkKeySize(int keysize) + throws InvalidAlgorithmParameterException { + if ((keysize != 2048) && + ((keysize < 512) || (keysize > 1024) || (keysize % 64 != 0))) { + throw new InvalidAlgorithmParameterException( + "Keysize must be multiple of 64 ranging from " + + "512 to 1024 (inclusive), or 2048"); + } + } + /** * Initializes this parameter generator for a certain keysize * and source of randomness. @@ -67,11 +77,11 @@ * @param random the source of randomness */ protected void engineInit(int keysize, SecureRandom random) { - if ((keysize < 512) || (keysize > 2048) || (keysize % 64 != 0)) { - throw new InvalidParameterException("Keysize must be multiple " - + "of 64, and can only range " - + "from 512 to 2048 " - + "(inclusive)"); + // Re-uses DSA parameters and thus have the same range + try { + checkKeySize(keysize); + } catch (InvalidAlgorithmParameterException ex) { + throw new InvalidParameterException(ex.getMessage()); } this.primeSize = keysize; this.random = random; @@ -91,31 +101,29 @@ protected void engineInit(AlgorithmParameterSpec genParamSpec, SecureRandom random) throws InvalidAlgorithmParameterException { - if (!(genParamSpec instanceof DHGenParameterSpec)) { - throw new InvalidAlgorithmParameterException - ("Inappropriate parameter type"); - } + if (!(genParamSpec instanceof DHGenParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("Inappropriate parameter type"); + } - DHGenParameterSpec dhParamSpec = (DHGenParameterSpec)genParamSpec; + DHGenParameterSpec dhParamSpec = (DHGenParameterSpec)genParamSpec; + + primeSize = dhParamSpec.getPrimeSize(); + + // Re-uses DSA parameters and thus have the same range + checkKeySize(primeSize); - primeSize = dhParamSpec.getPrimeSize(); - if ((primeSize<512) || (primeSize>2048) || (primeSize%64 != 0)) { - throw new InvalidAlgorithmParameterException - ("Modulus size must be multiple of 64, and can only range " - + "from 512 to 2048 (inclusive)"); - } + exponentSize = dhParamSpec.getExponentSize(); + if (exponentSize <= 0) { + throw new InvalidAlgorithmParameterException + ("Exponent size must be greater than zero"); + } - exponentSize = dhParamSpec.getExponentSize(); - if (exponentSize <= 0) { - throw new InvalidAlgorithmParameterException - ("Exponent size must be greater than zero"); - } - - // Require exponentSize < primeSize - if (exponentSize >= primeSize) { - throw new InvalidAlgorithmParameterException - ("Exponent size must be less than modulus size"); - } + // Require exponentSize < primeSize + if (exponentSize >= primeSize) { + throw new InvalidAlgorithmParameterException + ("Exponent size must be less than modulus size"); + } } /** diff -r 7b3d199dbe40 -r 0b4f7f108005 src/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java --- a/src/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java Tue Sep 25 11:31:17 2012 -0700 +++ b/src/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java Tue Oct 08 11:07:31 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, 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 @@ -70,20 +70,67 @@ // 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 { + // RSA, DH, and DSA + keySize = 1024; + 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; - if (algorithm.equals("EC")) { - initialize(256, null); - } else { - initialize(1024, null); - } + this.minKeySize = minKeyLen; + this.maxKeySize = maxKeyLen; + initialize(keySize, null); } // see JCA spec @@ -94,9 +141,7 @@ } catch (InvalidAlgorithmParameterException e) { throw new InvalidParameterException(e.getMessage()); } - this.keySize = keySize; this.params = null; - this.random = random; if (algorithm.equals("EC")) { params = P11ECKeyFactory.getECParameterSpec(keySize); if (params == null) { @@ -105,33 +150,35 @@ + keySize + " bits"); } } + this.keySize = keySize; + this.random = random; } // see JCA spec 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; - int tmpKeySize = dhParams.getP().bitLength(); - checkKeySize(tmpKeySize, dhParams); - this.keySize = tmpKeySize; - this.params = dhParams; + DHParameterSpec dhParams = (DHParameterSpec) params; + tmpKeySize = dhParams.getP().bitLength(); + checkKeySize(tmpKeySize, null); // 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; - int tmpKeySize = rsaParams.getKeysize(); + RSAKeyGenParameterSpec rsaParams = + (RSAKeyGenParameterSpec) params; + tmpKeySize = rsaParams.getKeysize(); checkKeySize(tmpKeySize, rsaParams); - this.keySize = tmpKeySize; - this.params = null; + // override the supplied params to null + params = null; this.rsaPublicExponent = rsaParams.getPublicExponent(); // XXX sanity check params } else if (algorithm.equals("DSA")) { @@ -139,11 +186,9 @@ throw new InvalidAlgorithmParameterException ("DSAParameterSpec required for DSA"); } - DSAParameterSpec dsaParams = (DSAParameterSpec)params; - int tmpKeySize = dsaParams.getP().bitLength(); - checkKeySize(tmpKeySize, dsaParams); - this.keySize = tmpKeySize; - this.params = dsaParams; + DSAParameterSpec dsaParams = (DSAParameterSpec) params; + tmpKeySize = dsaParams.getP().bitLength(); + checkKeySize(tmpKeySize, null); // XXX sanity check params } else if (algorithm.equals("EC")) { ECParameterSpec ecParams; @@ -155,28 +200,42 @@ ("Unsupported curve: " + params); } } else if (params instanceof ECGenParameterSpec) { - String name = ((ECGenParameterSpec)params).getName(); + 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"); } - int tmpKeySize = ecParams.getCurve().getField().getFieldSize(); - checkKeySize(tmpKeySize, ecParams); - this.keySize = tmpKeySize; - this.params = ecParams; + tmpKeySize = ecParams.getCurve().getField().getFieldSize(); + checkKeySize(tmpKeySize, null); } 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 { + // NOTE: 'params' is only used for checking RSA keys currently. + private void checkKeySize(int keySize, RSAKeyGenParameterSpec params) + throws InvalidAlgorithmParameterException { + // check native range first + if ((minKeySize != -1) && (keySize < minKeySize)) { + throw new InvalidAlgorithmParameterException(algorithm + + " key must be at least " + minKeySize + " bits"); + } + if ((maxKeySize != -1) && (keySize > maxKeySize)) { + throw new InvalidAlgorithmParameterException(algorithm + + " key must be at most " + maxKeySize + " bits"); + } + + // check our own algorithm-specific limits also if (algorithm.equals("EC")) { if (keySize < 112) { throw new InvalidAlgorithmParameterException @@ -187,41 +246,45 @@ throw new InvalidAlgorithmParameterException ("Key size must be at most 2048 bit"); } - return; - } else if (algorithm.equals("RSA")) { - BigInteger tmpExponent = rsaPublicExponent; - if (params != null) { - // Already tested for instanceof RSAKeyGenParameterSpec above - tmpExponent = - ((RSAKeyGenParameterSpec)params).getPublicExponent(); - } - try { - // This provider supports 64K or less. - RSAKeyFactory.checkKeyLengths(keySize, tmpExponent, - 512, 64 * 1024); - } catch (InvalidKeyException e) { - throw new InvalidAlgorithmParameterException(e.getMessage()); + } else { + // RSA, DH, DSA + if (keySize < 512) { + throw new InvalidAlgorithmParameterException + ("Key size must be at least 512 bit"); } - return; - } - - if (keySize < 512) { - throw new InvalidAlgorithmParameterException - ("Key size must be at least 512 bit"); - } - if (algorithm.equals("DH") && (params != null)) { - // sanity check, nobody really wants keys this large - if (keySize > 64 * 1024) { - throw new InvalidAlgorithmParameterException - ("Key size must be at most 65536 bit"); - } - } else { - // this restriction is in the spec for DSA - // since we currently use DSA parameters for DH as well, - // it also applies to DH if no parameters are specified - if ((keySize > 1024) || ((keySize & 0x3f) != 0)) { - throw new InvalidAlgorithmParameterException - ("Key size must be a multiple of 64 and at most 1024 bit"); + if (algorithm.equals("RSA")) { + BigInteger tmpExponent = rsaPublicExponent; + if (params != null) { + tmpExponent = 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.getMessage()); + } + } else { + if (algorithm.equals("DH") && (params != null)) { + // sanity check, nobody really wants keys this large + if (keySize > 64 * 1024) { + throw new InvalidAlgorithmParameterException + ("Key size must be at most 65536 bit"); + } + } else { + // this restriction is in the spec for DSA + // since we currently use DSA parameters for DH as well, + // it also applies to DH if no parameters are specified + if ((keySize != 2048) && + ((keySize > 1024) || ((keySize & 0x3f) != 0))) { + throw new InvalidAlgorithmParameterException(algorithm + + " key must be multiples of 64 if less than 1024 bits" + + ", or 2048 bits"); + } + } } } } @@ -325,5 +388,4 @@ token.releaseSession(session); } } - } diff -r 7b3d199dbe40 -r 0b4f7f108005 test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java Tue Oct 08 11:07:31 2013 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, 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. + * + * 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. + */ + +/** + * @test + * @bug 7196382 + * @summary Ensure that 2048-bit DH key pairs can be generated + * @author Valerie Peng + * @library .. + */ + +import java.io.*; +import java.util.*; + +import java.security.*; + +import javax.crypto.*; + +public class TestDH2048 extends PKCS11Test { + + private static void checkUnsupportedKeySize(KeyPairGenerator kpg, int ks) + throws Exception { + try { + kpg.initialize(ks); + throw new Exception("Expected IPE not thrown for " + ks); + } catch (InvalidParameterException ipe) { + } + } + + public void main(Provider p) throws Exception { + if (p.getService("KeyPairGenerator", "DH") == null) { + System.out.println("KPG for DH not supported, skipping"); + return; + } + KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", p); + kpg.initialize(2048); + KeyPair kp1 = kpg.generateKeyPair(); + checkUnsupportedKeySize(kpg, 1536); + checkUnsupportedKeySize(kpg, 2176); + checkUnsupportedKeySize(kpg, 3072); + } + + public static void main(String[] args) throws Exception { + main(new TestDH2048()); + } +}