24 */ |
24 */ |
25 |
25 |
26 package sun.security.ssl; |
26 package sun.security.ssl; |
27 |
27 |
28 import java.io.IOException; |
28 import java.io.IOException; |
29 import java.io.PrintStream; |
29 import java.nio.ByteBuffer; |
30 |
30 import java.security.AlgorithmConstraints; |
|
31 import java.security.CryptoPrimitive; |
|
32 import java.security.GeneralSecurityException; |
|
33 import java.security.KeyFactory; |
|
34 import java.security.PrivateKey; |
31 import java.security.PublicKey; |
35 import java.security.PublicKey; |
|
36 import java.security.interfaces.ECPrivateKey; |
32 import java.security.interfaces.ECPublicKey; |
37 import java.security.interfaces.ECPublicKey; |
33 import java.security.spec.*; |
38 import java.security.spec.ECParameterSpec; |
|
39 import java.security.spec.ECPoint; |
|
40 import java.security.spec.ECPublicKeySpec; |
|
41 import java.text.MessageFormat; |
|
42 import java.util.EnumSet; |
|
43 import java.util.Locale; |
|
44 import javax.crypto.SecretKey; |
|
45 import javax.net.ssl.SSLHandshakeException; |
|
46 import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; |
|
47 import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; |
|
48 import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
49 import sun.security.ssl.SupportedGroupsExtension.NamedGroup; |
|
50 import sun.security.ssl.X509Authentication.X509Credentials; |
|
51 import sun.security.ssl.X509Authentication.X509Possession; |
|
52 import sun.security.util.HexDumpEncoder; |
34 |
53 |
35 /** |
54 /** |
36 * ClientKeyExchange message for all ECDH based key exchange methods. It |
55 * Pack of the "ClientKeyExchange" handshake message. |
37 * contains the client's ephemeral public value. |
|
38 * |
|
39 * @since 1.6 |
|
40 * @author Andreas Sterbenz |
|
41 */ |
56 */ |
42 final class ECDHClientKeyExchange extends HandshakeMessage { |
57 final class ECDHClientKeyExchange { |
43 |
58 static final SSLConsumer ecdhHandshakeConsumer = |
44 @Override |
59 new ECDHClientKeyExchangeConsumer(); |
45 int messageType() { |
60 static final HandshakeProducer ecdhHandshakeProducer = |
46 return ht_client_key_exchange; |
61 new ECDHClientKeyExchangeProducer(); |
|
62 |
|
63 static final SSLConsumer ecdheHandshakeConsumer = |
|
64 new ECDHEClientKeyExchangeConsumer(); |
|
65 static final HandshakeProducer ecdheHandshakeProducer = |
|
66 new ECDHEClientKeyExchangeProducer(); |
|
67 |
|
68 /** |
|
69 * The ECDH/ECDHE ClientKeyExchange handshake message. |
|
70 */ |
|
71 private static final |
|
72 class ECDHClientKeyExchangeMessage extends HandshakeMessage { |
|
73 private final byte[] encodedPoint; |
|
74 |
|
75 ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, |
|
76 ECPublicKey publicKey) { |
|
77 super(handshakeContext); |
|
78 |
|
79 ECPoint point = publicKey.getW(); |
|
80 ECParameterSpec params = publicKey.getParams(); |
|
81 encodedPoint = JsseJce.encodePoint(point, params.getCurve()); |
|
82 } |
|
83 |
|
84 ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, |
|
85 ByteBuffer m) throws IOException { |
|
86 super(handshakeContext); |
|
87 if (m.remaining() != 0) { // explicit PublicValueEncoding |
|
88 this.encodedPoint = Record.getBytes8(m); |
|
89 } else { |
|
90 this.encodedPoint = new byte[0]; |
|
91 } |
|
92 } |
|
93 |
|
94 // Check constraints of the specified EC public key. |
|
95 static void checkConstraints(AlgorithmConstraints constraints, |
|
96 ECPublicKey publicKey, |
|
97 byte[] encodedPoint) throws SSLHandshakeException { |
|
98 |
|
99 try { |
|
100 ECParameterSpec params = publicKey.getParams(); |
|
101 ECPoint point = |
|
102 JsseJce.decodePoint(encodedPoint, params.getCurve()); |
|
103 ECPublicKeySpec spec = new ECPublicKeySpec(point, params); |
|
104 |
|
105 KeyFactory kf = JsseJce.getKeyFactory("EC"); |
|
106 ECPublicKey peerPublicKey = |
|
107 (ECPublicKey)kf.generatePublic(spec); |
|
108 |
|
109 // check constraints of ECPublicKey |
|
110 if (!constraints.permits( |
|
111 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
112 peerPublicKey)) { |
|
113 throw new SSLHandshakeException( |
|
114 "ECPublicKey does not comply to algorithm constraints"); |
|
115 } |
|
116 } catch (GeneralSecurityException | java.io.IOException e) { |
|
117 throw (SSLHandshakeException) new SSLHandshakeException( |
|
118 "Could not generate ECPublicKey").initCause(e); |
|
119 } |
|
120 } |
|
121 |
|
122 @Override |
|
123 public SSLHandshake handshakeType() { |
|
124 return SSLHandshake.CLIENT_KEY_EXCHANGE; |
|
125 } |
|
126 |
|
127 @Override |
|
128 public int messageLength() { |
|
129 if (encodedPoint == null || encodedPoint.length == 0) { |
|
130 return 0; |
|
131 } else { |
|
132 return 1 + encodedPoint.length; |
|
133 } |
|
134 } |
|
135 |
|
136 @Override |
|
137 public void send(HandshakeOutStream hos) throws IOException { |
|
138 if (encodedPoint != null && encodedPoint.length != 0) { |
|
139 hos.putBytes8(encodedPoint); |
|
140 } |
|
141 } |
|
142 |
|
143 @Override |
|
144 public String toString() { |
|
145 MessageFormat messageFormat = new MessageFormat( |
|
146 "\"ECDH ClientKeyExchange\": '{'\n" + |
|
147 " \"ecdh public\": '{'\n" + |
|
148 "{0}\n" + |
|
149 " '}',\n" + |
|
150 "'}'", |
|
151 Locale.ENGLISH); |
|
152 if (encodedPoint == null || encodedPoint.length == 0) { |
|
153 Object[] messageFields = { |
|
154 " <implicit>" |
|
155 }; |
|
156 return messageFormat.format(messageFields); |
|
157 } else { |
|
158 HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
159 Object[] messageFields = { |
|
160 Utilities.indent( |
|
161 hexEncoder.encodeBuffer(encodedPoint), " "), |
|
162 }; |
|
163 return messageFormat.format(messageFields); |
|
164 } |
|
165 } |
47 } |
166 } |
48 |
167 |
49 private byte[] encodedPoint; |
168 /** |
50 |
169 * The ECDH "ClientKeyExchange" handshake message producer. |
51 byte[] getEncodedPoint() { |
170 */ |
52 return encodedPoint; |
171 private static final |
|
172 class ECDHClientKeyExchangeProducer implements HandshakeProducer { |
|
173 // Prevent instantiation of this class. |
|
174 private ECDHClientKeyExchangeProducer() { |
|
175 // blank |
|
176 } |
|
177 |
|
178 @Override |
|
179 public byte[] produce(ConnectionContext context, |
|
180 HandshakeMessage message) throws IOException { |
|
181 // The producing happens in client side only. |
|
182 ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
183 |
|
184 X509Credentials x509Credentials = null; |
|
185 for (SSLCredentials credential : chc.handshakeCredentials) { |
|
186 if (credential instanceof X509Credentials) { |
|
187 x509Credentials = (X509Credentials)credential; |
|
188 break; |
|
189 } |
|
190 } |
|
191 |
|
192 if (x509Credentials == null) { |
|
193 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
194 "No server certificate for ECDH client key exchange"); |
|
195 } |
|
196 |
|
197 PublicKey publicKey = x509Credentials.popPublicKey; |
|
198 if (!publicKey.getAlgorithm().equals("EC")) { |
|
199 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
200 "Not EC server certificate for ECDH client key exchange"); |
|
201 } |
|
202 |
|
203 ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); |
|
204 NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
205 if (namedGroup == null) { |
|
206 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
207 "Unsupported EC server cert for ECDH client key exchange"); |
|
208 } |
|
209 |
|
210 ECDHEPossession ecdhePossession = new ECDHEPossession( |
|
211 namedGroup, chc.sslContext.getSecureRandom()); |
|
212 chc.handshakePossessions.add(ecdhePossession); |
|
213 ECDHClientKeyExchangeMessage cke = |
|
214 new ECDHClientKeyExchangeMessage( |
|
215 chc, ecdhePossession.publicKey); |
|
216 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
217 SSLLogger.fine( |
|
218 "Produced ECDH ClientKeyExchange handshake message", cke); |
|
219 } |
|
220 |
|
221 // Output the handshake message. |
|
222 cke.write(chc.handshakeOutput); |
|
223 chc.handshakeOutput.flush(); |
|
224 |
|
225 // update the states |
|
226 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
227 chc.negotiatedCipherSuite.keyExchange, |
|
228 chc.negotiatedProtocol); |
|
229 if (ke == null) { |
|
230 // unlikely |
|
231 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
232 "Not supported key exchange type"); |
|
233 } else { |
|
234 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); |
|
235 SecretKey masterSecret = |
|
236 masterKD.deriveKey("MasterSecret", null); |
|
237 chc.handshakeSession.setMasterSecret(masterSecret); |
|
238 |
|
239 SSLTrafficKeyDerivation kd = |
|
240 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); |
|
241 if (kd == null) { |
|
242 // unlikely |
|
243 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
244 "Not supported key derivation: " + |
|
245 chc.negotiatedProtocol); |
|
246 } else { |
|
247 chc.handshakeKeyDerivation = |
|
248 kd.createKeyDerivation(chc, masterSecret); |
|
249 } |
|
250 } |
|
251 |
|
252 // The handshake message has been delivered. |
|
253 return null; |
|
254 } |
53 } |
255 } |
54 |
256 |
55 // Called by the client with its ephemeral public key. |
257 /** |
56 ECDHClientKeyExchange(PublicKey publicKey) { |
258 * The ECDH "ClientKeyExchange" handshake message consumer. |
57 ECPublicKey ecKey = (ECPublicKey)publicKey; |
259 */ |
58 ECPoint point = ecKey.getW(); |
260 private static final |
59 ECParameterSpec params = ecKey.getParams(); |
261 class ECDHClientKeyExchangeConsumer implements SSLConsumer { |
60 encodedPoint = JsseJce.encodePoint(point, params.getCurve()); |
262 // Prevent instantiation of this class. |
|
263 private ECDHClientKeyExchangeConsumer() { |
|
264 // blank |
|
265 } |
|
266 |
|
267 @Override |
|
268 public void consume(ConnectionContext context, |
|
269 ByteBuffer message) throws IOException { |
|
270 // The consuming happens in server side only. |
|
271 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
272 |
|
273 X509Possession x509Possession = null; |
|
274 for (SSLPossession possession : shc.handshakePossessions) { |
|
275 if (possession instanceof X509Possession) { |
|
276 x509Possession = (X509Possession)possession; |
|
277 break; |
|
278 } |
|
279 } |
|
280 |
|
281 if (x509Possession == null) { |
|
282 // unlikely, have been checked during cipher suite negotiation. |
|
283 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
284 "No expected EC server cert for ECDH client key exchange"); |
|
285 return; // make the compiler happy |
|
286 } |
|
287 |
|
288 PrivateKey privateKey = x509Possession.popPrivateKey; |
|
289 if (!privateKey.getAlgorithm().equals("EC")) { |
|
290 // unlikely, have been checked during cipher suite negotiation. |
|
291 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
292 "Not EC server cert for ECDH client key exchange"); |
|
293 } |
|
294 |
|
295 ECParameterSpec params = ((ECPrivateKey)privateKey).getParams(); |
|
296 NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
297 if (namedGroup == null) { |
|
298 // unlikely, have been checked during cipher suite negotiation. |
|
299 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
300 "Unsupported EC server cert for ECDH client key exchange"); |
|
301 } |
|
302 |
|
303 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
304 shc.negotiatedCipherSuite.keyExchange, |
|
305 shc.negotiatedProtocol); |
|
306 if (ke == null) { |
|
307 // unlikely |
|
308 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
309 "Not supported key exchange type"); |
|
310 return; // make the compiler happy |
|
311 } |
|
312 |
|
313 // parse the handshake message |
|
314 ECDHClientKeyExchangeMessage cke = |
|
315 new ECDHClientKeyExchangeMessage(shc, message); |
|
316 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
317 SSLLogger.fine( |
|
318 "Consuming ECDH ClientKeyExchange handshake message", cke); |
|
319 } |
|
320 |
|
321 // create the credentials |
|
322 try { |
|
323 ECPoint point = |
|
324 JsseJce.decodePoint(cke.encodedPoint, params.getCurve()); |
|
325 ECPublicKeySpec spec = new ECPublicKeySpec(point, params); |
|
326 |
|
327 KeyFactory kf = JsseJce.getKeyFactory("EC"); |
|
328 ECPublicKey peerPublicKey = |
|
329 (ECPublicKey)kf.generatePublic(spec); |
|
330 |
|
331 // check constraints of peer ECPublicKey |
|
332 if (!shc.algorithmConstraints.permits( |
|
333 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
334 peerPublicKey)) { |
|
335 throw new SSLHandshakeException( |
|
336 "ECPublicKey does not comply to algorithm constraints"); |
|
337 } |
|
338 |
|
339 shc.handshakeCredentials.add(new ECDHECredentials( |
|
340 peerPublicKey, namedGroup)); |
|
341 } catch (GeneralSecurityException | java.io.IOException e) { |
|
342 throw (SSLHandshakeException)(new SSLHandshakeException( |
|
343 "Could not generate ECPublicKey").initCause(e)); |
|
344 } |
|
345 |
|
346 // update the states |
|
347 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); |
|
348 SecretKey masterSecret = |
|
349 masterKD.deriveKey("MasterSecret", null); |
|
350 shc.handshakeSession.setMasterSecret(masterSecret); |
|
351 |
|
352 SSLTrafficKeyDerivation kd = |
|
353 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); |
|
354 if (kd == null) { |
|
355 // unlikely |
|
356 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
357 "Not supported key derivation: " + shc.negotiatedProtocol); |
|
358 } else { |
|
359 shc.handshakeKeyDerivation = |
|
360 kd.createKeyDerivation(shc, masterSecret); |
|
361 } |
|
362 } |
61 } |
363 } |
62 |
364 |
63 ECDHClientKeyExchange(HandshakeInStream input) throws IOException { |
365 /** |
64 encodedPoint = input.getBytes8(); |
366 * The ECDHE "ClientKeyExchange" handshake message producer. |
|
367 */ |
|
368 private static final |
|
369 class ECDHEClientKeyExchangeProducer implements HandshakeProducer { |
|
370 // Prevent instantiation of this class. |
|
371 private ECDHEClientKeyExchangeProducer() { |
|
372 // blank |
|
373 } |
|
374 |
|
375 @Override |
|
376 public byte[] produce(ConnectionContext context, |
|
377 HandshakeMessage message) throws IOException { |
|
378 // The producing happens in client side only. |
|
379 ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
380 |
|
381 ECDHECredentials ecdheCredentials = null; |
|
382 for (SSLCredentials cd : chc.handshakeCredentials) { |
|
383 if (cd instanceof ECDHECredentials) { |
|
384 ecdheCredentials = (ECDHECredentials)cd; |
|
385 break; |
|
386 } |
|
387 } |
|
388 |
|
389 if (ecdheCredentials == null) { |
|
390 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
391 "No ECDHE credentials negotiated for client key exchange"); |
|
392 } |
|
393 |
|
394 ECDHEPossession ecdhePossession = new ECDHEPossession( |
|
395 ecdheCredentials, chc.sslContext.getSecureRandom()); |
|
396 chc.handshakePossessions.add(ecdhePossession); |
|
397 ECDHClientKeyExchangeMessage cke = |
|
398 new ECDHClientKeyExchangeMessage( |
|
399 chc, ecdhePossession.publicKey); |
|
400 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
401 SSLLogger.fine( |
|
402 "Produced ECDHE ClientKeyExchange handshake message", cke); |
|
403 } |
|
404 |
|
405 // Output the handshake message. |
|
406 cke.write(chc.handshakeOutput); |
|
407 chc.handshakeOutput.flush(); |
|
408 |
|
409 // update the states |
|
410 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
411 chc.negotiatedCipherSuite.keyExchange, |
|
412 chc.negotiatedProtocol); |
|
413 if (ke == null) { |
|
414 // unlikely |
|
415 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
416 "Not supported key exchange type"); |
|
417 } else { |
|
418 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); |
|
419 SecretKey masterSecret = |
|
420 masterKD.deriveKey("MasterSecret", null); |
|
421 chc.handshakeSession.setMasterSecret(masterSecret); |
|
422 |
|
423 SSLTrafficKeyDerivation kd = |
|
424 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); |
|
425 if (kd == null) { |
|
426 // unlikely |
|
427 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
428 "Not supported key derivation: " + |
|
429 chc.negotiatedProtocol); |
|
430 } else { |
|
431 chc.handshakeKeyDerivation = |
|
432 kd.createKeyDerivation(chc, masterSecret); |
|
433 } |
|
434 } |
|
435 |
|
436 // The handshake message has been delivered. |
|
437 return null; |
|
438 } |
65 } |
439 } |
66 |
440 |
67 @Override |
441 /** |
68 int messageLength() { |
442 * The ECDHE "ClientKeyExchange" handshake message consumer. |
69 return encodedPoint.length + 1; |
443 */ |
70 } |
444 private static final |
71 |
445 class ECDHEClientKeyExchangeConsumer implements SSLConsumer { |
72 @Override |
446 // Prevent instantiation of this class. |
73 void send(HandshakeOutStream s) throws IOException { |
447 private ECDHEClientKeyExchangeConsumer() { |
74 s.putBytes8(encodedPoint); |
448 // blank |
75 } |
449 } |
76 |
450 |
77 @Override |
451 @Override |
78 void print(PrintStream s) throws IOException { |
452 public void consume(ConnectionContext context, |
79 s.println("*** ECDHClientKeyExchange"); |
453 ByteBuffer message) throws IOException { |
80 |
454 // The consuming happens in server side only. |
81 if (debug != null && Debug.isOn("verbose")) { |
455 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
82 Debug.println(s, "ECDH Public value", encodedPoint); |
456 |
|
457 ECDHEPossession ecdhePossession = null; |
|
458 for (SSLPossession possession : shc.handshakePossessions) { |
|
459 if (possession instanceof ECDHEPossession) { |
|
460 ecdhePossession = (ECDHEPossession)possession; |
|
461 break; |
|
462 } |
|
463 } |
|
464 if (ecdhePossession == null) { |
|
465 // unlikely |
|
466 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
467 "No expected ECDHE possessions for client key exchange"); |
|
468 return; // make the compiler happy |
|
469 } |
|
470 |
|
471 ECParameterSpec params = ecdhePossession.publicKey.getParams(); |
|
472 NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
473 if (namedGroup == null) { |
|
474 // unlikely, have been checked during cipher suite negotiation. |
|
475 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
476 "Unsupported EC server cert for ECDHE client key exchange"); |
|
477 } |
|
478 |
|
479 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
480 shc.negotiatedCipherSuite.keyExchange, |
|
481 shc.negotiatedProtocol); |
|
482 if (ke == null) { |
|
483 // unlikely |
|
484 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
485 "Not supported key exchange type"); |
|
486 return; // make the compiler happy |
|
487 } |
|
488 |
|
489 // parse the handshake message |
|
490 ECDHClientKeyExchangeMessage cke = |
|
491 new ECDHClientKeyExchangeMessage(shc, message); |
|
492 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
493 SSLLogger.fine( |
|
494 "Consuming ECDHE ClientKeyExchange handshake message", cke); |
|
495 } |
|
496 |
|
497 // create the credentials |
|
498 try { |
|
499 ECPoint point = |
|
500 JsseJce.decodePoint(cke.encodedPoint, params.getCurve()); |
|
501 ECPublicKeySpec spec = new ECPublicKeySpec(point, params); |
|
502 |
|
503 KeyFactory kf = JsseJce.getKeyFactory("EC"); |
|
504 ECPublicKey peerPublicKey = |
|
505 (ECPublicKey)kf.generatePublic(spec); |
|
506 |
|
507 // check constraints of peer ECPublicKey |
|
508 if (!shc.algorithmConstraints.permits( |
|
509 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
510 peerPublicKey)) { |
|
511 throw new SSLHandshakeException( |
|
512 "ECPublicKey does not comply to algorithm constraints"); |
|
513 } |
|
514 |
|
515 shc.handshakeCredentials.add(new ECDHECredentials( |
|
516 peerPublicKey, namedGroup)); |
|
517 } catch (GeneralSecurityException | java.io.IOException e) { |
|
518 throw (SSLHandshakeException)(new SSLHandshakeException( |
|
519 "Could not generate ECPublicKey").initCause(e)); |
|
520 } |
|
521 |
|
522 // update the states |
|
523 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); |
|
524 SecretKey masterSecret = |
|
525 masterKD.deriveKey("MasterSecret", null); |
|
526 shc.handshakeSession.setMasterSecret(masterSecret); |
|
527 |
|
528 SSLTrafficKeyDerivation kd = |
|
529 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); |
|
530 if (kd == null) { |
|
531 // unlikely |
|
532 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
533 "Not supported key derivation: " + shc.negotiatedProtocol); |
|
534 } else { |
|
535 shc.handshakeKeyDerivation = |
|
536 kd.createKeyDerivation(shc, masterSecret); |
|
537 } |
83 } |
538 } |
84 } |
539 } |
85 } |
540 } |