1/*
2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.crypto.provider;
27
28import java.math.BigInteger;
29import java.security.*;
30import java.security.spec.AlgorithmParameterSpec;
31import java.security.spec.InvalidParameterSpecException;
32import javax.crypto.spec.DHParameterSpec;
33import javax.crypto.spec.DHGenParameterSpec;
34
35import sun.security.provider.ParameterCache;
36
37/**
38 * This class represents the key pair generator for Diffie-Hellman key pairs.
39 *
40 * <p>This key pair generator may be initialized in two different ways:
41 *
42 * <ul>
43 * <li>By providing the size in bits of the prime modulus -
44 * This will be used to create a prime modulus and base generator, which will
45 * then be used to create the Diffie-Hellman key pair. The default size of the
46 * prime modulus is 2048 bits.
47 * <li>By providing a prime modulus and base generator
48 * </ul>
49 *
50 * @author Jan Luehe
51 *
52 *
53 * @see java.security.KeyPairGenerator
54 */
55public final class DHKeyPairGenerator extends KeyPairGeneratorSpi {
56
57    // parameters to use or null if not specified
58    private DHParameterSpec params;
59
60    // The size in bits of the prime modulus
61    private int pSize;
62
63    // The size in bits of the random exponent (private value)
64    private int lSize;
65
66    // The source of randomness
67    private SecureRandom random;
68
69    public DHKeyPairGenerator() {
70        super();
71        initialize(2048, null);
72    }
73
74    private static void checkKeySize(int keysize)
75            throws InvalidParameterException {
76
77        if ((keysize < 512) || (keysize > 8192) || ((keysize & 0x3F) != 0)) {
78            throw new InvalidParameterException(
79                    "DH key size must be multiple of 64, and can only range " +
80                    "from 512 to 8192 (inclusive). " +
81                    "The specific key size " + keysize + " is not supported");
82        }
83    }
84
85    /**
86     * Initializes this key pair generator for a certain keysize and source of
87     * randomness.
88     * The keysize is specified as the size in bits of the prime modulus.
89     *
90     * @param keysize the keysize (size of prime modulus) in bits
91     * @param random the source of randomness
92     */
93    public void initialize(int keysize, SecureRandom random) {
94        checkKeySize(keysize);
95
96        // Use the built-in parameters (ranging from 512 to 8192)
97        // when available.
98        this.params = ParameterCache.getCachedDHParameterSpec(keysize);
99
100        // Due to performance issue, only support DH parameters generation
101        // up to 1024 bits.
102        if ((this.params == null) && (keysize > 1024)) {
103            throw new InvalidParameterException(
104                "Unsupported " + keysize + "-bit DH parameter generation");
105        }
106
107        this.pSize = keysize;
108        this.lSize = 0;
109        this.random = random;
110    }
111
112    /**
113     * Initializes this key pair generator for the specified parameter
114     * set and source of randomness.
115     *
116     * <p>The given parameter set contains the prime modulus, the base
117     * generator, and optionally the requested size in bits of the random
118     * exponent (private value).
119     *
120     * @param algParams the parameter set used to generate the key pair
121     * @param random the source of randomness
122     *
123     * @exception InvalidAlgorithmParameterException if the given parameters
124     * are inappropriate for this key pair generator
125     */
126    public void initialize(AlgorithmParameterSpec algParams,
127            SecureRandom random) throws InvalidAlgorithmParameterException {
128        if (!(algParams instanceof DHParameterSpec)){
129            throw new InvalidAlgorithmParameterException
130                ("Inappropriate parameter type");
131        }
132
133        params = (DHParameterSpec)algParams;
134        pSize = params.getP().bitLength();
135        try {
136            checkKeySize(pSize);
137        } catch (InvalidParameterException ipe) {
138            throw new InvalidAlgorithmParameterException(ipe.getMessage());
139        }
140
141        // exponent size is optional, could be 0
142        lSize = params.getL();
143
144        // Require exponentSize < primeSize
145        if ((lSize != 0) && (lSize > pSize)) {
146            throw new InvalidAlgorithmParameterException
147                ("Exponent size must not be larger than modulus size");
148        }
149        this.random = random;
150    }
151
152    /**
153     * Generates a key pair.
154     *
155     * @return the new key pair
156     */
157    public KeyPair generateKeyPair() {
158        if (random == null) {
159            random = SunJCE.getRandom();
160        }
161
162        if (params == null) {
163            try {
164                params = ParameterCache.getDHParameterSpec(pSize, random);
165            } catch (GeneralSecurityException e) {
166                // should never happen
167                throw new ProviderException(e);
168            }
169        }
170
171        BigInteger p = params.getP();
172        BigInteger g = params.getG();
173
174        if (lSize <= 0) {
175            lSize = pSize >> 1;
176            // use an exponent size of (pSize / 2) but at least 384 bits
177            if (lSize < 384) {
178                lSize = 384;
179            }
180        }
181
182        BigInteger x;
183        BigInteger pMinus2 = p.subtract(BigInteger.TWO);
184
185        //
186        // PKCS#3 section 7.1 "Private-value generation"
187        // Repeat if either of the followings does not hold:
188        //     0 < x < p-1
189        //     2^(lSize-1) <= x < 2^(lSize)
190        //
191        do {
192            // generate random x up to 2^lSize bits long
193            x = new BigInteger(lSize, random);
194        } while ((x.compareTo(BigInteger.ONE) < 0) ||
195            ((x.compareTo(pMinus2) > 0)) || (x.bitLength() != lSize));
196
197        // calculate public value y
198        BigInteger y = g.modPow(x, p);
199
200        DHPublicKey pubKey = new DHPublicKey(y, p, g, lSize);
201        DHPrivateKey privKey = new DHPrivateKey(x, p, g, lSize);
202        return new KeyPair(pubKey, privKey);
203    }
204}
205