/* * 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); } } }