1 /*
2 * Copyright (c) 1996, 2008, 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
26
27 package sun.security.ssl;
28
29 import java.math.BigInteger;
30 import java.security.*;
31
32 import javax.crypto.SecretKey;
33 import javax.crypto.KeyAgreement;
34 import javax.crypto.interfaces.DHPublicKey;
35 import javax.crypto.spec.*;
36
37 /**
38 * This class implements the Diffie-Hellman key exchange algorithm.
39 * D-H means combining your private key with your partners public key to
40 * generate a number. The peer does the same with its private key and our
41 * public key. Through the magic of Diffie-Hellman we both come up with the
42 * same number. This number is secret (discounting MITM attacks) and hence
43 * called the shared secret. It has the same length as the modulus, e.g. 512
44 * or 1024 bit. Man-in-the-middle attacks are typically countered by an
45 * independent authentication step using certificates (RSA, DSA, etc.).
46 *
47 * The thing to note is that the shared secret is constant for two partners
48 * with constant private keys. This is often not what we want, which is why
49 * it is generally a good idea to create a new private key for each session.
50 * Generating a private key involves one modular exponentiation assuming
51 * suitable D-H parameters are available.
52 *
53 * General usage of this class (TLS DHE case):
54 * . if we are server, call DHCrypt(keyLength,random). This generates
55 * an ephemeral keypair of the request length.
56 * . if we are client, call DHCrypt(modulus, base, random). This
57 * generates an ephemeral keypair using the parameters specified by the server.
58 * . send parameters and public value to remote peer
59 * . receive peers ephemeral public key
60 * . call getAgreedSecret() to calculate the shared secret
61 *
62 * In TLS the server chooses the parameter values itself, the client must use
63 * those sent to it by the server.
64 *
65 * The use of ephemeral keys as described above also achieves what is called
66 * "forward secrecy". This means that even if the authentication keys are
67 * broken at a later date, the shared secret remains secure. The session is
68 * compromised only if the authentication keys are already broken at the
69 * time the key exchange takes place and an active MITM attack is used.
70 * This is in contrast to straightforward encrypting RSA key exchanges.
71 *
72 * @author David Brownell
73 */
74 final class DHCrypt {
75
76 // group parameters (prime modulus and generator)
77 private BigInteger modulus; // P (aka N)
78 private BigInteger base; // G (aka alpha)
79
80 // our private key (including private component x)
81 private PrivateKey privateKey;
82
83 // public component of our key, X = (g ^ x) mod p
84 private BigInteger publicValue; // X (aka y)
85
86 /**
87 * Generate a Diffie-Hellman keypair of the specified size.
88 */
89 DHCrypt(int keyLength, SecureRandom random) {
90 try {
91 KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
92 kpg.initialize(keyLength, random);
93 KeyPair kp = kpg.generateKeyPair();
94 privateKey = kp.getPrivate();
95 DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
96 publicValue = spec.getY();
97 modulus = spec.getP();
98 base = spec.getG();
99 } catch (GeneralSecurityException e) {
100 throw new RuntimeException("Could not generate DH keypair", e);
101 }
102 }
103
104
105 /**
106 * Generate a Diffie-Hellman keypair using the specified parameters.
107 *
108 * @param modulus the Diffie-Hellman modulus P
109 * @param base the Diffie-Hellman base G
110 */
111 DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
112 this.modulus = modulus;
113 this.base = base;
114 try {
115 KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
116 DHParameterSpec params = new DHParameterSpec(modulus, base);
117 kpg.initialize(params, random);
118 KeyPair kp = kpg.generateKeyPair();
119 privateKey = kp.getPrivate();
120 DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
121 publicValue = spec.getY();
122 } catch (GeneralSecurityException e) {
123 throw new RuntimeException("Could not generate DH keypair", e);
124 }
125 }
126
127 static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
128 if (key instanceof DHPublicKey) {
129 DHPublicKey dhKey = (DHPublicKey)key;
130 DHParameterSpec params = dhKey.getParams();
131 return new DHPublicKeySpec(dhKey.getY(), params.getP(), params.getG());
132 }
133 try {
134 KeyFactory factory = JsseJce.getKeyFactory("DH");
135 return factory.getKeySpec(key, DHPublicKeySpec.class);
136 } catch (Exception e) {
137 throw new RuntimeException(e);
138 }
139 }
140
141
142 /** Returns the Diffie-Hellman modulus. */
143 BigInteger getModulus() {
144 return modulus;
145 }
146
147 /** Returns the Diffie-Hellman base (generator). */
148 BigInteger getBase() {
149 return base;
150 }
151
152 /**
153 * Gets the public key of this end of the key exchange.
154 */
155 BigInteger getPublicKey() {
156 return publicValue;
157 }
158
159 /**
160 * Get the secret data that has been agreed on through Diffie-Hellman
161 * key agreement protocol. Note that in the two party protocol, if
162 * the peer keys are already known, no other data needs to be sent in
163 * order to agree on a secret. That is, a secured message may be
164 * sent without any mandatory round-trip overheads.
165 *
166 * <P>It is illegal to call this member function if the private key
167 * has not been set (or generated).
168 *
169 * @param peerPublicKey the peer's public key.
170 * @returns the secret, which is an unsigned big-endian integer
171 * the same size as the Diffie-Hellman modulus.
172 */
173 SecretKey getAgreedSecret(BigInteger peerPublicValue) {
174 try {
175 KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
176 DHPublicKeySpec spec =
177 new DHPublicKeySpec(peerPublicValue, modulus, base);
178 PublicKey publicKey = kf.generatePublic(spec);
179 KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
180 ka.init(privateKey);
181 ka.doPhase(publicKey, true);
182 return ka.generateSecret("TlsPremasterSecret");
183 } catch (GeneralSecurityException e) {
184 throw new RuntimeException("Could not generate secret", e);
185 }
186 }
187
188 }
|
1 /*
2 * Copyright (c) 1996, 2012, 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
26
27 package sun.security.ssl;
28
29 import java.math.BigInteger;
30 import java.security.*;
31 import java.io.IOException;
32 import javax.net.ssl.SSLHandshakeException;
33 import javax.crypto.SecretKey;
34 import javax.crypto.KeyAgreement;
35 import javax.crypto.interfaces.DHPublicKey;
36 import javax.crypto.spec.*;
37
38 import sun.security.util.KeyUtil;
39
40 /**
41 * This class implements the Diffie-Hellman key exchange algorithm.
42 * D-H means combining your private key with your partners public key to
43 * generate a number. The peer does the same with its private key and our
44 * public key. Through the magic of Diffie-Hellman we both come up with the
45 * same number. This number is secret (discounting MITM attacks) and hence
46 * called the shared secret. It has the same length as the modulus, e.g. 512
47 * or 1024 bit. Man-in-the-middle attacks are typically countered by an
48 * independent authentication step using certificates (RSA, DSA, etc.).
49 *
50 * The thing to note is that the shared secret is constant for two partners
51 * with constant private keys. This is often not what we want, which is why
52 * it is generally a good idea to create a new private key for each session.
53 * Generating a private key involves one modular exponentiation assuming
54 * suitable D-H parameters are available.
55 *
56 * General usage of this class (TLS DHE case):
57 * . if we are server, call DHCrypt(keyLength,random). This generates
58 * an ephemeral keypair of the request length.
59 * . if we are client, call DHCrypt(modulus, base, random). This
60 * generates an ephemeral keypair using the parameters specified by
61 * the server.
62 * . send parameters and public value to remote peer
63 * . receive peers ephemeral public key
64 * . call getAgreedSecret() to calculate the shared secret
65 *
66 * In TLS the server chooses the parameter values itself, the client must use
67 * those sent to it by the server.
68 *
69 * The use of ephemeral keys as described above also achieves what is called
70 * "forward secrecy". This means that even if the authentication keys are
71 * broken at a later date, the shared secret remains secure. The session is
72 * compromised only if the authentication keys are already broken at the
73 * time the key exchange takes place and an active MITM attack is used.
74 * This is in contrast to straightforward encrypting RSA key exchanges.
75 *
76 * @author David Brownell
77 */
78 final class DHCrypt {
79
80 // group parameters (prime modulus and generator)
81 private BigInteger modulus; // P (aka N)
82 private BigInteger base; // G (aka alpha)
83
84 // our private key (including private component x)
85 private PrivateKey privateKey;
86
87 // public component of our key, X = (g ^ x) mod p
88 private BigInteger publicValue; // X (aka y)
89
90 // the times to recove from failure if public key validation
91 private static int MAX_FAILOVER_TIMES = 2;
92
93 /**
94 * Generate a Diffie-Hellman keypair of the specified size.
95 */
96 DHCrypt(int keyLength, SecureRandom random) {
97 try {
98 KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
99 kpg.initialize(keyLength, random);
100
101 DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
102 if (spec == null) {
103 throw new RuntimeException("Could not generate DH keypair");
104 }
105
106 publicValue = spec.getY();
107 modulus = spec.getP();
108 base = spec.getG();
109 } catch (GeneralSecurityException e) {
110 throw new RuntimeException("Could not generate DH keypair", e);
111 }
112 }
113
114
115 /**
116 * Generate a Diffie-Hellman keypair using the specified parameters.
117 *
118 * @param modulus the Diffie-Hellman modulus P
119 * @param base the Diffie-Hellman base G
120 */
121 DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
122 this.modulus = modulus;
123 this.base = base;
124 try {
125 KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
126 DHParameterSpec params = new DHParameterSpec(modulus, base);
127 kpg.initialize(params, random);
128
129 DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
130 if (spec == null) {
131 throw new RuntimeException("Could not generate DH keypair");
132 }
133
134 publicValue = spec.getY();
135 } catch (GeneralSecurityException e) {
136 throw new RuntimeException("Could not generate DH keypair", e);
137 }
138 }
139
140
141 static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
142 if (key instanceof DHPublicKey) {
143 DHPublicKey dhKey = (DHPublicKey)key;
144 DHParameterSpec params = dhKey.getParams();
145 return new DHPublicKeySpec(dhKey.getY(),
146 params.getP(), params.getG());
147 }
148 try {
149 KeyFactory factory = JsseJce.getKeyFactory("DH");
150 return factory.getKeySpec(key, DHPublicKeySpec.class);
151 } catch (Exception e) {
152 throw new RuntimeException(e);
153 }
154 }
155
156
157 /** Returns the Diffie-Hellman modulus. */
158 BigInteger getModulus() {
159 return modulus;
160 }
161
162 /** Returns the Diffie-Hellman base (generator). */
163 BigInteger getBase() {
164 return base;
165 }
166
167 /**
168 * Gets the public key of this end of the key exchange.
169 */
170 BigInteger getPublicKey() {
171 return publicValue;
172 }
173
174 /**
175 * Get the secret data that has been agreed on through Diffie-Hellman
176 * key agreement protocol. Note that in the two party protocol, if
177 * the peer keys are already known, no other data needs to be sent in
178 * order to agree on a secret. That is, a secured message may be
179 * sent without any mandatory round-trip overheads.
180 *
181 * <P>It is illegal to call this member function if the private key
182 * has not been set (or generated).
183 *
184 * @param peerPublicKey the peer's public key.
185 * @param keyIsValidated whether the {@code peerPublicKey} has beed
186 * validated
187 * @return the secret, which is an unsigned big-endian integer
188 * the same size as the Diffie-Hellman modulus.
189 */
190 SecretKey getAgreedSecret(BigInteger peerPublicValue,
191 boolean keyIsValidated) throws IOException {
192 try {
193 KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
194 DHPublicKeySpec spec =
195 new DHPublicKeySpec(peerPublicValue, modulus, base);
196 PublicKey publicKey = kf.generatePublic(spec);
197 KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
198
199 // validate the Diffie-Hellman public key
200 if (!keyIsValidated &&
201 !KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) {
202 try {
203 KeyUtil.validate(spec);
204 } catch (InvalidKeyException ike) {
205 // prefer handshake_failure alert to internal_error alert
206 throw new SSLHandshakeException(ike.getMessage());
207 }
208 }
209
210 ka.init(privateKey);
211 ka.doPhase(publicKey, true);
212 return ka.generateSecret("TlsPremasterSecret");
213 } catch (GeneralSecurityException e) {
214 throw new RuntimeException("Could not generate secret", e);
215 }
216 }
217
218 // Generate and validate DHPublicKeySpec
219 private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg)
220 throws GeneralSecurityException {
221
222 boolean doExtraValiadtion =
223 (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
224 for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) {
225 KeyPair kp = kpg.generateKeyPair();
226 privateKey = kp.getPrivate();
227 DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
228
229 // validate the Diffie-Hellman public key
230 if (doExtraValiadtion) {
231 try {
232 KeyUtil.validate(spec);
233 } catch (InvalidKeyException ivke) {
234 if (i == MAX_FAILOVER_TIMES) {
235 throw ivke;
236 }
237 // otherwise, ignore the exception and try the next one
238 continue;
239 }
240 }
241
242 return spec;
243 }
244
245 return null;
246 }
247 }
|