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 = |
|
201 SSLKeyExchange.valueOf(chc.negotiatedCipherSuite.keyExchange); |
|
202 if (ke == null) { |
|
203 // unlikely |
|
204 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
205 "Not supported key exchange type"); |
|
206 } else { |
|
207 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); |
|
208 SecretKey masterSecret = masterKD.deriveKey("TODO", null); |
|
209 chc.handshakeSession.setMasterSecret(masterSecret); |
|
210 |
|
211 SSLTrafficKeyDerivation kd = |
|
212 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); |
|
213 if (kd == null) { |
|
214 // unlikely |
|
215 chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
216 "Not supported key derivation: " + |
|
217 chc.negotiatedProtocol); |
|
218 } else { |
|
219 chc.handshakeKeyDerivation = |
|
220 kd.createKeyDerivation(chc, masterSecret); |
|
221 } |
|
222 } |
|
223 |
|
224 // The handshake message has been delivered. |
|
225 return null; |
|
226 } |
55 } |
227 } |
56 |
228 |
57 /* |
229 /** |
58 * Either pass the client's public key explicitly (because it's |
230 * The DiffieHellman "ClientKeyExchange" handshake message consumer. |
59 * using DHE or DH_anon), or implicitly (the public key was in the |
|
60 * certificate). |
|
61 */ |
231 */ |
62 DHClientKeyExchange(BigInteger publicKey) { |
232 private static final |
63 dh_Yc = toByteArray(publicKey); |
233 class DHClientKeyExchangeConsumer implements SSLConsumer { |
64 } |
234 // Prevent instantiation of this class. |
65 |
235 private DHClientKeyExchangeConsumer() { |
66 DHClientKeyExchange() { |
236 // blank |
67 dh_Yc = null; |
237 } |
68 } |
238 |
69 |
239 @Override |
70 /* |
240 public void consume(ConnectionContext context, |
71 * Get the client's public key either explicitly or implicitly. |
241 ByteBuffer message) throws IOException { |
72 * (It's ugly to have an empty record be sent in the latter case, |
242 // The consuming happens in server side only. |
73 * but that's what the protocol spec requires.) |
243 ServerHandshakeContext shc = (ServerHandshakeContext)context; |
74 */ |
244 |
75 DHClientKeyExchange(HandshakeInStream input) throws IOException { |
245 DHEPossession dhePossession = null; |
76 if (input.available() >= 2) { |
246 for (SSLPossession possession : shc.handshakePossessions) { |
77 dh_Yc = input.getBytes16(); |
247 if (possession instanceof DHEPossession) { |
78 } else { |
248 dhePossession = (DHEPossession)possession; |
79 // currently, we don't support cipher suites that requires |
249 break; |
80 // implicit public key of client. |
250 } |
81 throw new SSLHandshakeException( |
251 } |
82 "Unsupported implicit client DiffieHellman public key"); |
252 |
83 } |
253 if (dhePossession == null) { |
84 } |
254 // unlikely |
85 |
255 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
86 @Override |
256 "No expected DHE possessions for client key exchange"); |
87 int messageLength() { |
257 } |
88 if (dh_Yc == null) { |
258 |
89 return 0; |
259 SSLKeyExchange ke = SSLKeyExchange.valueOf( |
90 } else { |
260 shc.negotiatedCipherSuite.keyExchange); |
91 return dh_Yc.length + 2; |
261 if (ke == null) { |
92 } |
262 // unlikely |
93 } |
263 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
94 |
264 "Not supported key exchange type"); |
95 @Override |
265 } |
96 void send(HandshakeOutStream s) throws IOException { |
266 |
97 if (dh_Yc != null && dh_Yc.length != 0) { |
267 DHClientKeyExchangeMessage ckem = |
98 s.putBytes16(dh_Yc); |
268 new DHClientKeyExchangeMessage(shc, message); |
99 } |
269 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
100 } |
270 SSLLogger.fine( |
101 |
271 "Consuming DH ClientKeyExchange handshake message", ckem); |
102 @Override |
272 } |
103 void print(PrintStream s) throws IOException { |
273 |
104 s.println("*** ClientKeyExchange, DH"); |
274 // create the credentials |
105 |
275 try { |
106 if (debug != null && Debug.isOn("verbose")) { |
276 DHParameterSpec params = dhePossession.publicKey.getParams(); |
107 Debug.println(s, "DH Public key", dh_Yc); |
277 DHPublicKeySpec spec = new DHPublicKeySpec( |
|
278 new BigInteger(1, ckem.y), |
|
279 params.getP(), params.getG()); |
|
280 KeyFactory kf = JsseJce.getKeyFactory("DH"); |
|
281 DHPublicKey peerPublicKey = |
|
282 (DHPublicKey)kf.generatePublic(spec); |
|
283 |
|
284 // check constraints of peer DHPublicKey |
|
285 if (!shc.algorithmConstraints.permits( |
|
286 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
287 peerPublicKey)) { |
|
288 throw new SSLHandshakeException( |
|
289 "DHPublicKey does not comply to algorithm constraints"); |
|
290 } |
|
291 |
|
292 NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
293 shc.handshakeCredentials.add( |
|
294 new DHECredentials(peerPublicKey, namedGroup)); |
|
295 } catch (GeneralSecurityException | java.io.IOException e) { |
|
296 throw (SSLHandshakeException)(new SSLHandshakeException( |
|
297 "Could not generate DHPublicKey").initCause(e)); |
|
298 } |
|
299 |
|
300 // update the states |
|
301 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); |
|
302 SecretKey masterSecret = masterKD.deriveKey("TODO", null); |
|
303 shc.handshakeSession.setMasterSecret(masterSecret); |
|
304 |
|
305 SSLTrafficKeyDerivation kd = |
|
306 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); |
|
307 if (kd == null) { |
|
308 // unlikely |
|
309 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
310 "Not supported key derivation: " + shc.negotiatedProtocol); |
|
311 } else { |
|
312 shc.handshakeKeyDerivation = |
|
313 kd.createKeyDerivation(shc, masterSecret); |
|
314 } |
108 } |
315 } |
109 } |
316 } |
110 } |
317 } |