21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
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 |
22 * or visit www.oracle.com if you need additional information or have any |
23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 |
|
27 package sun.security.ssl; |
26 package sun.security.ssl; |
28 |
27 |
29 import java.io.IOException; |
28 import java.io.IOException; |
30 import java.io.PrintStream; |
|
31 import java.math.BigInteger; |
29 import java.math.BigInteger; |
|
30 import java.nio.ByteBuffer; |
|
31 import java.security.CryptoPrimitive; |
|
32 import java.security.GeneralSecurityException; |
|
33 import java.security.KeyFactory; |
|
34 import java.text.MessageFormat; |
|
35 import java.util.EnumSet; |
|
36 import java.util.Locale; |
|
37 import javax.crypto.SecretKey; |
|
38 import javax.crypto.interfaces.DHPublicKey; |
|
39 import javax.crypto.spec.DHParameterSpec; |
|
40 import javax.crypto.spec.DHPublicKeySpec; |
32 import javax.net.ssl.SSLHandshakeException; |
41 import javax.net.ssl.SSLHandshakeException; |
33 |
42 import sun.security.ssl.DHKeyExchange.DHECredentials; |
34 /* |
43 import sun.security.ssl.DHKeyExchange.DHEPossession; |
35 * Message used by clients to send their Diffie-Hellman public |
44 import sun.security.ssl.SSLHandshake.HandshakeMessage; |
36 * keys to servers. |
45 import sun.security.ssl.SupportedGroupsExtension.NamedGroup; |
37 * |
46 import sun.security.util.HexDumpEncoder; |
38 * @author David Brownell |
47 |
|
48 /** |
|
49 * Pack of the "ClientKeyExchange" handshake message. |
39 */ |
50 */ |
40 final class DHClientKeyExchange extends HandshakeMessage { |
51 final class DHClientKeyExchange { |
41 |
52 static final DHClientKeyExchangeConsumer dhHandshakeConsumer = |
42 @Override |
53 new DHClientKeyExchangeConsumer(); |
43 int messageType() { |
54 static final DHClientKeyExchangeProducer dhHandshakeProducer = |
44 return ht_client_key_exchange; |
55 new DHClientKeyExchangeProducer(); |
|
56 |
|
57 /** |
|
58 * The DiffieHellman ClientKeyExchange handshake message. |
|
59 * |
|
60 * If the client has sent a certificate which contains a suitable |
|
61 * DiffieHellman key (for fixed_dh client authentication), then the |
|
62 * client public value is implicit and does not need to be sent again. |
|
63 * In this case, the client key exchange message will be sent, but it |
|
64 * MUST be empty. |
|
65 * |
|
66 * Currently, we don't support cipher suite that requires implicit public |
|
67 * key of client. |
|
68 */ |
|
69 private static final |
|
70 class DHClientKeyExchangeMessage extends HandshakeMessage { |
|
71 private byte[] y; // 1 to 2^16 - 1 bytes |
|
72 |
|
73 DHClientKeyExchangeMessage( |
|
74 HandshakeContext handshakeContext) throws IOException { |
|
75 super(handshakeContext); |
|
76 // This happens in client side only. |
|
77 ClientHandshakeContext chc = |
|
78 (ClientHandshakeContext)handshakeContext; |
|
79 |
|
80 DHEPossession dhePossession = null; |
|
81 for (SSLPossession possession : chc.handshakePossessions) { |
|
82 if (possession instanceof DHEPossession) { |
|
83 dhePossession = (DHEPossession)possession; |
|
84 break; |
|
85 } |
|
86 } |
|
87 |
|
88 if (dhePossession == null) { |
|
89 // unlikely |
|
90 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
91 "No DHE credentials negotiated for client key exchange"); |
|
92 } |
|
93 |
|
94 DHPublicKey publicKey = dhePossession.publicKey; |
|
95 DHParameterSpec params = publicKey.getParams(); |
|
96 this.y = Utilities.toByteArray(publicKey.getY()); |
|
97 } |
|
98 |
|
99 DHClientKeyExchangeMessage(HandshakeContext handshakeContext, |
|
100 ByteBuffer m) throws IOException { |
|
101 super(handshakeContext); |
|
102 // This happens in server side only. |
|
103 ServerHandshakeContext shc = |
|
104 (ServerHandshakeContext)handshakeContext; |
|
105 |
|
106 if (m.remaining() < 3) { |
|
107 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
108 "Invalid DH ClientKeyExchange message: insufficient data"); |
|
109 } |
|
110 |
|
111 this.y = Record.getBytes16(m); |
|
112 |
|
113 if (m.hasRemaining()) { |
|
114 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
115 "Invalid DH ClientKeyExchange message: unknown extra data"); |
|
116 } |
|
117 } |
|
118 |
|
119 @Override |
|
120 public SSLHandshake handshakeType() { |
|
121 return SSLHandshake.CLIENT_KEY_EXCHANGE; |
|
122 } |
|
123 |
|
124 @Override |
|
125 public int messageLength() { |
|
126 return y.length + 2; // 2: length filed |
|
127 } |
|
128 |
|
129 @Override |
|
130 public void send(HandshakeOutStream hos) throws IOException { |
|
131 hos.putBytes16(y); |
|
132 } |
|
133 |
|
134 @Override |
|
135 public String toString() { |
|
136 MessageFormat messageFormat = new MessageFormat( |
|
137 "\"DH ClientKeyExchange\": '{'\n" + |
|
138 " \"parameters\": '{'\n" + |
|
139 " \"dh_Yc\": '{'\n" + |
|
140 "{0}\n" + |
|
141 " '}',\n" + |
|
142 " '}'\n" + |
|
143 "'}'", |
|
144 Locale.ENGLISH); |
|
145 |
|
146 HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
147 Object[] messageFields = { |
|
148 Utilities.indent( |
|
149 hexEncoder.encodeBuffer(y), " "), |
|
150 }; |
|
151 return messageFormat.format(messageFields); |
|
152 } |
45 } |
153 } |
46 |
154 |
47 /* |
155 /** |
48 * This value may be empty if it was included in the |
156 * The DiffieHellman "ClientKeyExchange" handshake message producer. |
49 * client's certificate ... |
|
50 */ |
157 */ |
51 private byte[] dh_Yc; // 1 to 2^16 -1 bytes |
158 private static final |
52 |
159 class DHClientKeyExchangeProducer implements HandshakeProducer { |
53 BigInteger getClientPublicKey() { |
160 // Prevent instantiation of this class. |
54 return dh_Yc == null ? null : new BigInteger(1, dh_Yc); |
161 private DHClientKeyExchangeProducer() { |
|
162 // blank |
|
163 } |
|
164 |
|
165 @Override |
|
166 public byte[] produce(ConnectionContext context, |
|
167 HandshakeMessage message) throws IOException { |
|
168 // The producing happens in client side only. |
|
169 ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
170 |
|
171 DHECredentials dheCredentials = null; |
|
172 for (SSLCredentials cd : chc.handshakeCredentials) { |
|
173 if (cd instanceof DHECredentials) { |
|
174 dheCredentials = (DHECredentials)cd; |
|
175 break; |
|
176 } |
|
177 } |
|
178 |
|
179 if (dheCredentials == null) { |
|
180 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
181 "No DHE credentials negotiated for client key exchange"); |
|
182 } |
|
183 |
|
184 |
|
185 DHEPossession dhePossession = new DHEPossession( |
|
186 dheCredentials, chc.sslContext.getSecureRandom()); |
|
187 chc.handshakePossessions.add(dhePossession); |
|
188 DHClientKeyExchangeMessage ckem = |
|
189 new DHClientKeyExchangeMessage(chc); |
|
190 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
191 SSLLogger.fine( |
|
192 "Produced DH ClientKeyExchange handshake message", ckem); |
|
193 } |
|
194 |
|
195 // Output the handshake message. |
|
196 ckem.write(chc.handshakeOutput); |
|
197 chc.handshakeOutput.flush(); |
|
198 |
|
199 // update the states |
|
200 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
201 chc.negotiatedCipherSuite.keyExchange, |
|
202 chc.negotiatedProtocol); |
|
203 if (ke == null) { |
|
204 // unlikely |
|
205 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
206 "Not supported key exchange type"); |
|
207 } else { |
|
208 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); |
|
209 SecretKey masterSecret = |
|
210 masterKD.deriveKey("MasterSecret", null); |
|
211 chc.handshakeSession.setMasterSecret(masterSecret); |
|
212 |
|
213 SSLTrafficKeyDerivation kd = |
|
214 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); |
|
215 if (kd == null) { |
|
216 // unlikely |
|
217 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
218 "Not supported key derivation: " + |
|
219 chc.negotiatedProtocol); |
|
220 } else { |
|
221 chc.handshakeKeyDerivation = |
|
222 kd.createKeyDerivation(chc, masterSecret); |
|
223 } |
|
224 } |
|
225 |
|
226 // The handshake message has been delivered. |
|
227 return null; |
|
228 } |
55 } |
229 } |
56 |
230 |
57 /* |
231 /** |
58 * Either pass the client's public key explicitly (because it's |
232 * The DiffieHellman "ClientKeyExchange" handshake message consumer. |
59 * using DHE or DH_anon), or implicitly (the public key was in the |
|
60 * certificate). |
|
61 */ |
233 */ |
62 DHClientKeyExchange(BigInteger publicKey) { |
234 private static final |
63 dh_Yc = toByteArray(publicKey); |
235 class DHClientKeyExchangeConsumer implements SSLConsumer { |
64 } |
236 // Prevent instantiation of this class. |
65 |
237 private DHClientKeyExchangeConsumer() { |
66 DHClientKeyExchange() { |
238 // blank |
67 dh_Yc = null; |
239 } |
68 } |
240 |
69 |
241 @Override |
70 /* |
242 public void consume(ConnectionContext context, |
71 * Get the client's public key either explicitly or implicitly. |
243 ByteBuffer message) throws IOException { |
72 * (It's ugly to have an empty record be sent in the latter case, |
244 // The consuming happens in server side only. |
73 * but that's what the protocol spec requires.) |
245 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
74 */ |
246 |
75 DHClientKeyExchange(HandshakeInStream input) throws IOException { |
247 DHEPossession dhePossession = null; |
76 if (input.available() >= 2) { |
248 for (SSLPossession possession : shc.handshakePossessions) { |
77 dh_Yc = input.getBytes16(); |
249 if (possession instanceof DHEPossession) { |
78 } else { |
250 dhePossession = (DHEPossession)possession; |
79 // currently, we don't support cipher suites that requires |
251 break; |
80 // implicit public key of client. |
252 } |
81 throw new SSLHandshakeException( |
253 } |
82 "Unsupported implicit client DiffieHellman public key"); |
254 |
83 } |
255 if (dhePossession == null) { |
84 } |
256 // unlikely |
85 |
257 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
86 @Override |
258 "No expected DHE possessions for client key exchange"); |
87 int messageLength() { |
259 } |
88 if (dh_Yc == null) { |
260 |
89 return 0; |
261 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
90 } else { |
262 shc.negotiatedCipherSuite.keyExchange, |
91 return dh_Yc.length + 2; |
263 shc.negotiatedProtocol); |
92 } |
264 if (ke == null) { |
93 } |
265 // unlikely |
94 |
266 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
95 @Override |
267 "Not supported key exchange type"); |
96 void send(HandshakeOutStream s) throws IOException { |
268 } |
97 if (dh_Yc != null && dh_Yc.length != 0) { |
269 |
98 s.putBytes16(dh_Yc); |
270 DHClientKeyExchangeMessage ckem = |
99 } |
271 new DHClientKeyExchangeMessage(shc, message); |
100 } |
272 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
101 |
273 SSLLogger.fine( |
102 @Override |
274 "Consuming DH ClientKeyExchange handshake message", ckem); |
103 void print(PrintStream s) throws IOException { |
275 } |
104 s.println("*** ClientKeyExchange, DH"); |
276 |
105 |
277 // create the credentials |
106 if (debug != null && Debug.isOn("verbose")) { |
278 try { |
107 Debug.println(s, "DH Public key", dh_Yc); |
279 DHParameterSpec params = dhePossession.publicKey.getParams(); |
|
280 DHPublicKeySpec spec = new DHPublicKeySpec( |
|
281 new BigInteger(1, ckem.y), |
|
282 params.getP(), params.getG()); |
|
283 KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman"); |
|
284 DHPublicKey peerPublicKey = |
|
285 (DHPublicKey)kf.generatePublic(spec); |
|
286 |
|
287 // check constraints of peer DHPublicKey |
|
288 if (!shc.algorithmConstraints.permits( |
|
289 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
290 peerPublicKey)) { |
|
291 throw new SSLHandshakeException( |
|
292 "DHPublicKey does not comply to algorithm constraints"); |
|
293 } |
|
294 |
|
295 NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
296 shc.handshakeCredentials.add( |
|
297 new DHECredentials(peerPublicKey, namedGroup)); |
|
298 } catch (GeneralSecurityException | java.io.IOException e) { |
|
299 throw (SSLHandshakeException)(new SSLHandshakeException( |
|
300 "Could not generate DHPublicKey").initCause(e)); |
|
301 } |
|
302 |
|
303 // update the states |
|
304 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); |
|
305 SecretKey masterSecret = |
|
306 masterKD.deriveKey("MasterSecret", null); |
|
307 shc.handshakeSession.setMasterSecret(masterSecret); |
|
308 |
|
309 SSLTrafficKeyDerivation kd = |
|
310 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); |
|
311 if (kd == null) { |
|
312 // unlikely |
|
313 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
314 "Not supported key derivation: " + shc.negotiatedProtocol); |
|
315 } else { |
|
316 shc.handshakeKeyDerivation = |
|
317 kd.createKeyDerivation(shc, masterSecret); |
|
318 } |
108 } |
319 } |
109 } |
320 } |
110 } |
321 } |