|
1 /* |
|
2 * Copyright (c) 2015, 2018, 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 package sun.security.ssl; |
|
27 |
|
28 import java.io.ByteArrayInputStream; |
|
29 import java.io.IOException; |
|
30 import java.nio.ByteBuffer; |
|
31 import java.security.PublicKey; |
|
32 import java.security.cert.CertPathValidatorException; |
|
33 import java.security.cert.CertPathValidatorException.BasicReason; |
|
34 import java.security.cert.CertPathValidatorException.Reason; |
|
35 import java.security.cert.CertificateEncodingException; |
|
36 import java.security.cert.CertificateException; |
|
37 import java.security.cert.CertificateFactory; |
|
38 import java.security.cert.CertificateParsingException; |
|
39 import java.security.cert.X509Certificate; |
|
40 import java.text.MessageFormat; |
|
41 import java.util.ArrayList; |
|
42 import java.util.Arrays; |
|
43 import java.util.Collection; |
|
44 import java.util.Collections; |
|
45 import java.util.HashSet; |
|
46 import java.util.LinkedList; |
|
47 import java.util.List; |
|
48 import java.util.Locale; |
|
49 import javax.net.ssl.SSLEngine; |
|
50 import javax.net.ssl.SSLException; |
|
51 import javax.net.ssl.SSLProtocolException; |
|
52 import javax.net.ssl.SSLSocket; |
|
53 import javax.net.ssl.X509ExtendedTrustManager; |
|
54 import javax.net.ssl.X509TrustManager; |
|
55 import javax.security.auth.x500.X500Principal; |
|
56 import sun.security.ssl.CertificateMessage.T12CertificateMessage; |
|
57 import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED; |
|
58 import sun.security.ssl.ClientHello.ClientHelloMessage; |
|
59 import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
60 import sun.security.ssl.X509Authentication.X509Credentials; |
|
61 import sun.security.ssl.X509Authentication.X509Possession; |
|
62 |
|
63 /** |
|
64 * Pack of the CertificateMessage handshake message. |
|
65 */ |
|
66 final class CertificateMessage { |
|
67 static final SSLConsumer t12HandshakeConsumer = |
|
68 new T12CertificateConsumer(); |
|
69 static final HandshakeProducer t12HandshakeProducer = |
|
70 new T12CertificateProducer(); |
|
71 |
|
72 static final SSLConsumer t13HandshakeConsumer = |
|
73 new T13CertificateConsumer(); |
|
74 static final HandshakeProducer t13HandshakeProducer = |
|
75 new T13CertificateProducer(); |
|
76 |
|
77 /** |
|
78 * The Certificate handshake message for TLS 1.2 and previous |
|
79 * SSL/TLS protocol versions. |
|
80 * |
|
81 * In server mode, the certificate handshake message is sent whenever the |
|
82 * agreed-upon key exchange method uses certificates for authentication. |
|
83 * In client mode, this message is only sent if the server requests a |
|
84 * certificate for client authentication. |
|
85 * |
|
86 * opaque ASN.1Cert<1..2^24-1>; |
|
87 * |
|
88 * SSL 3.0: |
|
89 * struct { |
|
90 * ASN.1Cert certificate_list<1..2^24-1>; |
|
91 * } Certificate; |
|
92 * Note: For SSL 3.0 client authentication, if no suitable certificate |
|
93 * is available, the client should send a no_certificate alert instead. |
|
94 * This alert is only a warning; however, the server may respond with |
|
95 * a fatal handshake failure alert if client authentication is required. |
|
96 * |
|
97 * TLS 1.0/1.1/1.2: |
|
98 * struct { |
|
99 * ASN.1Cert certificate_list<0..2^24-1>; |
|
100 * } Certificate; |
|
101 */ |
|
102 static final class T12CertificateMessage extends HandshakeMessage { |
|
103 final List<byte[]> encodedCertChain; |
|
104 |
|
105 T12CertificateMessage(HandshakeContext handshakeContext, |
|
106 X509Certificate[] certChain) throws SSLException { |
|
107 super(handshakeContext); |
|
108 |
|
109 List<byte[]> encodedCerts = new ArrayList<>(certChain.length); |
|
110 for (X509Certificate cert : certChain) { |
|
111 try { |
|
112 encodedCerts.add(cert.getEncoded()); |
|
113 } catch (CertificateEncodingException cee) { |
|
114 // unlikely |
|
115 handshakeContext.conContext.fatal(Alert.INTERNAL_ERROR, |
|
116 "Could not encode certificate (" + |
|
117 cert.getSubjectX500Principal() + ")", cee); |
|
118 break; // make the complier happy |
|
119 } |
|
120 } |
|
121 |
|
122 this.encodedCertChain = encodedCerts; |
|
123 } |
|
124 |
|
125 T12CertificateMessage(HandshakeContext handshakeContext, |
|
126 ByteBuffer m) throws IOException { |
|
127 super(handshakeContext); |
|
128 |
|
129 int listLen = Record.getInt24(m); |
|
130 if (listLen > m.remaining()) { |
|
131 handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
132 "Error parsing certificate message:no sufficient data"); |
|
133 } |
|
134 if (listLen > 0) { |
|
135 List<byte[]> encodedCerts = new LinkedList<>(); |
|
136 while (listLen > 0) { |
|
137 byte[] encodedCert = Record.getBytes24(m); |
|
138 listLen -= (3 + encodedCert.length); |
|
139 encodedCerts.add(encodedCert); |
|
140 } |
|
141 this.encodedCertChain = encodedCerts; |
|
142 } else { |
|
143 this.encodedCertChain = Collections.emptyList(); |
|
144 } |
|
145 } |
|
146 |
|
147 @Override |
|
148 public SSLHandshake handshakeType() { |
|
149 return SSLHandshake.CERTIFICATE; |
|
150 } |
|
151 |
|
152 @Override |
|
153 public int messageLength() { |
|
154 int msgLen = 3; |
|
155 for (byte[] encodedCert : encodedCertChain) { |
|
156 msgLen += (encodedCert.length + 3); |
|
157 } |
|
158 |
|
159 return msgLen; |
|
160 } |
|
161 |
|
162 @Override |
|
163 public void send(HandshakeOutStream hos) throws IOException { |
|
164 int listLen = 0; |
|
165 for (byte[] encodedCert : encodedCertChain) { |
|
166 listLen += (encodedCert.length + 3); |
|
167 } |
|
168 |
|
169 hos.putInt24(listLen); |
|
170 for (byte[] encodedCert : encodedCertChain) { |
|
171 hos.putBytes24(encodedCert); |
|
172 } |
|
173 } |
|
174 |
|
175 @Override |
|
176 public String toString() { |
|
177 if (encodedCertChain.isEmpty()) { |
|
178 return "\"Certificates\": <empty list>"; |
|
179 } |
|
180 |
|
181 Object[] x509Certs = new Object[encodedCertChain.size()]; |
|
182 try { |
|
183 CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
|
184 int i = 0; |
|
185 for (byte[] encodedCert : encodedCertChain) { |
|
186 Object obj; |
|
187 try { |
|
188 obj = (X509Certificate)cf.generateCertificate( |
|
189 new ByteArrayInputStream(encodedCert)); |
|
190 } catch (CertificateException ce) { |
|
191 obj = encodedCert; |
|
192 } |
|
193 x509Certs[i++] = obj; |
|
194 } |
|
195 } catch (CertificateException ce) { |
|
196 // no X.509 certificate factory service |
|
197 int i = 0; |
|
198 for (byte[] encodedCert : encodedCertChain) { |
|
199 x509Certs[i++] = encodedCert; |
|
200 } |
|
201 } |
|
202 |
|
203 MessageFormat messageFormat = new MessageFormat( |
|
204 "\"Certificates\": [\n" + |
|
205 "{0}\n" + |
|
206 "]", |
|
207 Locale.ENGLISH); |
|
208 Object[] messageFields = { |
|
209 SSLLogger.toString(x509Certs) |
|
210 }; |
|
211 |
|
212 return messageFormat.format(messageFields); |
|
213 } |
|
214 } |
|
215 |
|
216 /** |
|
217 * The "Certificate" handshake message producer for TLS 1.2 and |
|
218 * previous SSL/TLS protocol versions. |
|
219 */ |
|
220 private static final |
|
221 class T12CertificateProducer implements HandshakeProducer { |
|
222 // Prevent instantiation of this class. |
|
223 private T12CertificateProducer() { |
|
224 // blank |
|
225 } |
|
226 |
|
227 @Override |
|
228 public byte[] produce(ConnectionContext context, |
|
229 HandshakeMessage message) throws IOException { |
|
230 // The producing happens in handshake context only. |
|
231 HandshakeContext hc = (HandshakeContext)context; |
|
232 if (hc.sslConfig.isClientMode) { |
|
233 return onProduceCertificate( |
|
234 (ClientHandshakeContext)context, message); |
|
235 } else { |
|
236 return onProduceCertificate( |
|
237 (ServerHandshakeContext)context, message); |
|
238 } |
|
239 } |
|
240 |
|
241 private byte[] onProduceCertificate(ServerHandshakeContext shc, |
|
242 SSLHandshake.HandshakeMessage message) throws IOException { |
|
243 X509Possession x509Possession = null; |
|
244 for (SSLPossession possession : shc.handshakePossessions) { |
|
245 if (possession instanceof X509Possession) { |
|
246 x509Possession = (X509Possession)possession; |
|
247 break; |
|
248 } |
|
249 } |
|
250 |
|
251 if (x509Possession == null) { // unlikely |
|
252 shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
253 "No expected X.509 certificate for server authentication"); |
|
254 |
|
255 return null; // make the compiler happy |
|
256 } |
|
257 |
|
258 shc.handshakeSession.setLocalPrivateKey( |
|
259 x509Possession.popPrivateKey); |
|
260 shc.handshakeSession.setLocalCertificates(x509Possession.popCerts); |
|
261 T12CertificateMessage cm = |
|
262 new T12CertificateMessage(shc, x509Possession.popCerts); |
|
263 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
264 SSLLogger.fine( |
|
265 "Produced server Certificate handshake message", cm); |
|
266 } |
|
267 |
|
268 // Output the handshake message. |
|
269 cm.write(shc.handshakeOutput); |
|
270 shc.handshakeOutput.flush(); |
|
271 |
|
272 // The handshake message has been delivered. |
|
273 return null; |
|
274 } |
|
275 |
|
276 private byte[] onProduceCertificate(ClientHandshakeContext chc, |
|
277 SSLHandshake.HandshakeMessage message) throws IOException { |
|
278 X509Possession x509Possession = null; |
|
279 for (SSLPossession possession : chc.handshakePossessions) { |
|
280 if (possession instanceof X509Possession) { |
|
281 x509Possession = (X509Possession)possession; |
|
282 break; |
|
283 } |
|
284 } |
|
285 |
|
286 // Report to the server if no appropriate cert was found. For |
|
287 // SSL 3.0, send a no_certificate alert; TLS 1.0/1.1/1.2 uses |
|
288 // an empty cert chain instead. |
|
289 if (x509Possession == null) { |
|
290 if (chc.negotiatedProtocol.useTLS10PlusSpec()) { |
|
291 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
292 SSLLogger.fine( |
|
293 "No X.509 certificate for client authentication, " + |
|
294 "use empty Certificate message instead"); |
|
295 } |
|
296 |
|
297 x509Possession = |
|
298 new X509Possession(null, new X509Certificate[0]); |
|
299 } else { |
|
300 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
301 SSLLogger.fine( |
|
302 "No X.509 certificate for client authentication, " + |
|
303 "send a no_certificate alert"); |
|
304 } |
|
305 |
|
306 chc.conContext.warning(Alert.NO_CERTIFICATE); |
|
307 return null; |
|
308 } |
|
309 } |
|
310 |
|
311 chc.handshakeSession.setLocalPrivateKey( |
|
312 x509Possession.popPrivateKey); |
|
313 if (x509Possession.popCerts != null && |
|
314 x509Possession.popCerts.length != 0) { |
|
315 chc.handshakeSession.setLocalCertificates( |
|
316 x509Possession.popCerts); |
|
317 } else { |
|
318 chc.handshakeSession.setLocalCertificates(null); |
|
319 } |
|
320 T12CertificateMessage cm = |
|
321 new T12CertificateMessage(chc, x509Possession.popCerts); |
|
322 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
323 SSLLogger.fine( |
|
324 "Produced client Certificate handshake message", cm); |
|
325 } |
|
326 |
|
327 // Output the handshake message. |
|
328 cm.write(chc.handshakeOutput); |
|
329 chc.handshakeOutput.flush(); |
|
330 |
|
331 // The handshake message has been delivered. |
|
332 return null; |
|
333 } |
|
334 } |
|
335 |
|
336 /** |
|
337 * The "Certificate" handshake message consumer for TLS 1.2 and |
|
338 * previous SSL/TLS protocol versions. |
|
339 */ |
|
340 static final |
|
341 class T12CertificateConsumer implements SSLConsumer { |
|
342 // Prevent instantiation of this class. |
|
343 private T12CertificateConsumer() { |
|
344 // blank |
|
345 } |
|
346 |
|
347 @Override |
|
348 public void consume(ConnectionContext context, |
|
349 ByteBuffer message) throws IOException { |
|
350 // The consuming happens in handshake context only. |
|
351 HandshakeContext hc = (HandshakeContext)context; |
|
352 |
|
353 // clean up this consumer |
|
354 hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); |
|
355 |
|
356 T12CertificateMessage cm = new T12CertificateMessage(hc, message); |
|
357 if (hc.sslConfig.isClientMode) { |
|
358 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
359 SSLLogger.fine( |
|
360 "Consuming server Certificate handshake message", cm); |
|
361 } |
|
362 onCertificate((ClientHandshakeContext)context, cm); |
|
363 } else { |
|
364 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
365 SSLLogger.fine( |
|
366 "Consuming client Certificate handshake message", cm); |
|
367 } |
|
368 onCertificate((ServerHandshakeContext)context, cm); |
|
369 } |
|
370 } |
|
371 |
|
372 private void onCertificate(ServerHandshakeContext shc, |
|
373 T12CertificateMessage certificateMessage )throws IOException { |
|
374 List<byte[]> encodedCerts = certificateMessage.encodedCertChain; |
|
375 if (encodedCerts == null || encodedCerts.isEmpty()) { |
|
376 if (shc.sslConfig.clientAuthType != |
|
377 ClientAuthType.CLIENT_AUTH_REQUESTED) { |
|
378 // unexpected or require client authentication |
|
379 shc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
380 "Empty server certificate chain"); |
|
381 } else { |
|
382 return; |
|
383 } |
|
384 } |
|
385 |
|
386 X509Certificate[] x509Certs = |
|
387 new X509Certificate[encodedCerts.size()]; |
|
388 try { |
|
389 CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
|
390 int i = 0; |
|
391 for (byte[] encodedCert : encodedCerts) { |
|
392 x509Certs[i++] = (X509Certificate)cf.generateCertificate( |
|
393 new ByteArrayInputStream(encodedCert)); |
|
394 } |
|
395 } catch (CertificateException ce) { |
|
396 shc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
397 "Failed to parse server certificates", ce); |
|
398 } |
|
399 |
|
400 checkClientCerts(shc, x509Certs); |
|
401 |
|
402 // |
|
403 // update |
|
404 // |
|
405 shc.handshakeCredentials.add( |
|
406 new X509Credentials(x509Certs[0].getPublicKey(), x509Certs)); |
|
407 shc.handshakeSession.setPeerCertificates(x509Certs); |
|
408 } |
|
409 |
|
410 private void onCertificate(ClientHandshakeContext chc, |
|
411 T12CertificateMessage certificateMessage) throws IOException { |
|
412 List<byte[]> encodedCerts = certificateMessage.encodedCertChain; |
|
413 if (encodedCerts == null || encodedCerts.isEmpty()) { |
|
414 chc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
415 "Empty server certificate chain"); |
|
416 } |
|
417 |
|
418 X509Certificate[] x509Certs = |
|
419 new X509Certificate[encodedCerts.size()]; |
|
420 try { |
|
421 CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
|
422 int i = 0; |
|
423 for (byte[] encodedCert : encodedCerts) { |
|
424 x509Certs[i++] = (X509Certificate)cf.generateCertificate( |
|
425 new ByteArrayInputStream(encodedCert)); |
|
426 } |
|
427 } catch (CertificateException ce) { |
|
428 chc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
429 "Failed to parse server certificates", ce); |
|
430 } |
|
431 |
|
432 // Allow server certificate change in client side during |
|
433 // renegotiation after a session-resumption abbreviated |
|
434 // initial handshake? |
|
435 // |
|
436 // DO NOT need to check allowUnsafeServerCertChange here. We only |
|
437 // reserve server certificates when allowUnsafeServerCertChange is |
|
438 // flase. |
|
439 if (chc.reservedServerCerts != null) { |
|
440 // It is not necessary to check the certificate update if |
|
441 // endpoint identification is enabled. |
|
442 String identityAlg = chc.sslConfig.identificationProtocol; |
|
443 if ((identityAlg == null || identityAlg.length() == 0) && |
|
444 !isIdentityEquivalent(x509Certs[0], |
|
445 chc.reservedServerCerts[0])) { |
|
446 chc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
447 "server certificate change is restricted " + |
|
448 "during renegotiation"); |
|
449 } |
|
450 } |
|
451 |
|
452 // ask the trust manager to verify the chain |
|
453 if (chc.staplingActive) { |
|
454 // Defer the certificate check until after we've received the |
|
455 // CertificateStatus message. If that message doesn't come in |
|
456 // immediately following this message we will execute the |
|
457 // check from CertificateStatus' absent handler. |
|
458 chc.deferredCerts = x509Certs; |
|
459 } else { |
|
460 // We're not doing stapling, so perform the check right now |
|
461 checkServerCerts(chc, x509Certs); |
|
462 } |
|
463 |
|
464 // |
|
465 // update |
|
466 // |
|
467 chc.handshakeCredentials.add( |
|
468 new X509Credentials(x509Certs[0].getPublicKey(), x509Certs)); |
|
469 chc.handshakeSession.setPeerCertificates(x509Certs); |
|
470 } |
|
471 |
|
472 /* |
|
473 * Whether the certificates can represent the same identity? |
|
474 * |
|
475 * The certificates can be used to represent the same identity: |
|
476 * 1. If the subject alternative names of IP address are present |
|
477 * in both certificates, they should be identical; otherwise, |
|
478 * 2. if the subject alternative names of DNS name are present in |
|
479 * both certificates, they should be identical; otherwise, |
|
480 * 3. if the subject fields are present in both certificates, the |
|
481 * certificate subjects and issuers should be identical. |
|
482 */ |
|
483 private static boolean isIdentityEquivalent(X509Certificate thisCert, |
|
484 X509Certificate prevCert) { |
|
485 if (thisCert.equals(prevCert)) { |
|
486 return true; |
|
487 } |
|
488 |
|
489 // check subject alternative names |
|
490 Collection<List<?>> thisSubjectAltNames = null; |
|
491 try { |
|
492 thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); |
|
493 } catch (CertificateParsingException cpe) { |
|
494 if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { |
|
495 SSLLogger.fine( |
|
496 "Attempt to obtain subjectAltNames extension failed!"); |
|
497 } |
|
498 } |
|
499 |
|
500 Collection<List<?>> prevSubjectAltNames = null; |
|
501 try { |
|
502 prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); |
|
503 } catch (CertificateParsingException cpe) { |
|
504 if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { |
|
505 SSLLogger.fine( |
|
506 "Attempt to obtain subjectAltNames extension failed!"); |
|
507 } |
|
508 } |
|
509 |
|
510 if (thisSubjectAltNames != null && prevSubjectAltNames != null) { |
|
511 // check the iPAddress field in subjectAltName extension |
|
512 // |
|
513 // 7: subject alternative name of type IP. |
|
514 Collection<String> thisSubAltIPAddrs = |
|
515 getSubjectAltNames(thisSubjectAltNames, 7); |
|
516 Collection<String> prevSubAltIPAddrs = |
|
517 getSubjectAltNames(prevSubjectAltNames, 7); |
|
518 if (thisSubAltIPAddrs != null && prevSubAltIPAddrs != null && |
|
519 isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs)) { |
|
520 return true; |
|
521 } |
|
522 |
|
523 // check the dNSName field in subjectAltName extension |
|
524 // 2: subject alternative name of type IP. |
|
525 Collection<String> thisSubAltDnsNames = |
|
526 getSubjectAltNames(thisSubjectAltNames, 2); |
|
527 Collection<String> prevSubAltDnsNames = |
|
528 getSubjectAltNames(prevSubjectAltNames, 2); |
|
529 if (thisSubAltDnsNames != null && prevSubAltDnsNames != null && |
|
530 isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames)) { |
|
531 return true; |
|
532 } |
|
533 } |
|
534 |
|
535 // check the certificate subject and issuer |
|
536 X500Principal thisSubject = thisCert.getSubjectX500Principal(); |
|
537 X500Principal prevSubject = prevCert.getSubjectX500Principal(); |
|
538 X500Principal thisIssuer = thisCert.getIssuerX500Principal(); |
|
539 X500Principal prevIssuer = prevCert.getIssuerX500Principal(); |
|
540 |
|
541 return (!thisSubject.getName().isEmpty() && |
|
542 !prevSubject.getName().isEmpty() && |
|
543 thisSubject.equals(prevSubject) && |
|
544 thisIssuer.equals(prevIssuer)); |
|
545 } |
|
546 |
|
547 /* |
|
548 * Returns the subject alternative name of the specified type in the |
|
549 * subjectAltNames extension of a certificate. |
|
550 * |
|
551 * Note that only those subjectAltName types that use String data |
|
552 * should be passed into this function. |
|
553 */ |
|
554 private static Collection<String> getSubjectAltNames( |
|
555 Collection<List<?>> subjectAltNames, int type) { |
|
556 HashSet<String> subAltDnsNames = null; |
|
557 for (List<?> subjectAltName : subjectAltNames) { |
|
558 int subjectAltNameType = (Integer)subjectAltName.get(0); |
|
559 if (subjectAltNameType == type) { |
|
560 String subAltDnsName = (String)subjectAltName.get(1); |
|
561 if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) { |
|
562 if (subAltDnsNames == null) { |
|
563 subAltDnsNames = |
|
564 new HashSet<>(subjectAltNames.size()); |
|
565 } |
|
566 subAltDnsNames.add(subAltDnsName); |
|
567 } |
|
568 } |
|
569 } |
|
570 |
|
571 return subAltDnsNames; |
|
572 } |
|
573 |
|
574 private static boolean isEquivalent(Collection<String> thisSubAltNames, |
|
575 Collection<String> prevSubAltNames) { |
|
576 for (String thisSubAltName : thisSubAltNames) { |
|
577 for (String prevSubAltName : prevSubAltNames) { |
|
578 // Only allow the exactly match. No wildcard character |
|
579 // checking. |
|
580 if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) { |
|
581 return true; |
|
582 } |
|
583 } |
|
584 } |
|
585 |
|
586 return false; |
|
587 } |
|
588 |
|
589 /** |
|
590 * Perform client-side checking of server certificates. |
|
591 * |
|
592 * @param certs an array of {@code X509Certificate} objects presented |
|
593 * by the server in the ServerCertificate message. |
|
594 * |
|
595 * @throws IOException if a failure occurs during validation or |
|
596 * the trust manager associated with the {@code SSLContext} is not |
|
597 * an {@code X509ExtendedTrustManager}. |
|
598 */ |
|
599 static void checkServerCerts(ClientHandshakeContext chc, |
|
600 X509Certificate[] certs) throws IOException { |
|
601 |
|
602 X509TrustManager tm = chc.sslContext.getX509TrustManager(); |
|
603 |
|
604 // find out the key exchange algorithm used |
|
605 // use "RSA" for non-ephemeral "RSA_EXPORT" |
|
606 String keyExchangeString; |
|
607 if (chc.negotiatedCipherSuite.keyExchange == |
|
608 CipherSuite.KeyExchange.K_RSA_EXPORT || |
|
609 chc.negotiatedCipherSuite.keyExchange == |
|
610 CipherSuite.KeyExchange.K_DHE_RSA_EXPORT) { |
|
611 keyExchangeString = CipherSuite.KeyExchange.K_RSA.name; |
|
612 } else { |
|
613 keyExchangeString = chc.negotiatedCipherSuite.keyExchange.name; |
|
614 } |
|
615 |
|
616 try { |
|
617 if (tm instanceof X509ExtendedTrustManager) { |
|
618 if (chc.conContext.transport instanceof SSLEngine) { |
|
619 SSLEngine engine = (SSLEngine)chc.conContext.transport; |
|
620 ((X509ExtendedTrustManager)tm).checkServerTrusted( |
|
621 certs.clone(), |
|
622 keyExchangeString, |
|
623 engine); |
|
624 } else { |
|
625 SSLSocket socket = (SSLSocket)chc.conContext.transport; |
|
626 ((X509ExtendedTrustManager)tm).checkServerTrusted( |
|
627 certs.clone(), |
|
628 keyExchangeString, |
|
629 socket); |
|
630 } |
|
631 } else { |
|
632 // Unlikely to happen, because we have wrapped the old |
|
633 // X509TrustManager with the new X509ExtendedTrustManager. |
|
634 throw new CertificateException( |
|
635 "Improper X509TrustManager implementation"); |
|
636 } |
|
637 |
|
638 // Once the server certificate chain has been validated, set |
|
639 // the certificate chain in the TLS session. |
|
640 chc.handshakeSession.setPeerCertificates(certs); |
|
641 } catch (CertificateException ce) { |
|
642 chc.conContext.fatal(getCertificateAlert(chc, ce), ce); |
|
643 } |
|
644 } |
|
645 |
|
646 private static void checkClientCerts(ServerHandshakeContext shc, |
|
647 X509Certificate[] certs) throws IOException { |
|
648 X509TrustManager tm = shc.sslContext.getX509TrustManager(); |
|
649 |
|
650 // find out the types of client authentication used |
|
651 PublicKey key = certs[0].getPublicKey(); |
|
652 String keyAlgorithm = key.getAlgorithm(); |
|
653 String authType; |
|
654 if (keyAlgorithm.equals("RSA")) { |
|
655 authType = "RSA"; |
|
656 } else if (keyAlgorithm.equals("DSA")) { |
|
657 authType = "DSA"; |
|
658 } else if (keyAlgorithm.equals("EC")) { |
|
659 authType = "EC"; |
|
660 } else { |
|
661 // unknown public key type |
|
662 authType = "UNKNOWN"; |
|
663 } |
|
664 |
|
665 try { |
|
666 if (tm instanceof X509ExtendedTrustManager) { |
|
667 if (shc.conContext.transport instanceof SSLEngine) { |
|
668 SSLEngine engine = (SSLEngine)shc.conContext.transport; |
|
669 ((X509ExtendedTrustManager)tm).checkClientTrusted( |
|
670 certs.clone(), |
|
671 authType, |
|
672 engine); |
|
673 } else { |
|
674 SSLSocket socket = (SSLSocket)shc.conContext.transport; |
|
675 ((X509ExtendedTrustManager)tm).checkClientTrusted( |
|
676 certs.clone(), |
|
677 authType, |
|
678 socket); |
|
679 } |
|
680 } else { |
|
681 // Unlikely to happen, because we have wrapped the old |
|
682 // X509TrustManager with the new X509ExtendedTrustManager. |
|
683 throw new CertificateException( |
|
684 "Improper X509TrustManager implementation"); |
|
685 } |
|
686 } catch (CertificateException ce) { |
|
687 shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce); |
|
688 } |
|
689 } |
|
690 |
|
691 /** |
|
692 * When a failure happens during certificate checking from an |
|
693 * {@link X509TrustManager}, determine what TLS alert description |
|
694 * to use. |
|
695 * |
|
696 * @param cexc The exception thrown by the {@link X509TrustManager} |
|
697 * |
|
698 * @return A byte value corresponding to a TLS alert description number. |
|
699 */ |
|
700 private static Alert getCertificateAlert( |
|
701 ClientHandshakeContext chc, CertificateException cexc) { |
|
702 // The specific reason for the failure will determine how to |
|
703 // set the alert description value |
|
704 Alert alert = Alert.CERTIFICATE_UNKNOWN; |
|
705 |
|
706 Throwable baseCause = cexc.getCause(); |
|
707 if (baseCause instanceof CertPathValidatorException) { |
|
708 CertPathValidatorException cpve = |
|
709 (CertPathValidatorException)baseCause; |
|
710 Reason reason = cpve.getReason(); |
|
711 if (reason == BasicReason.REVOKED) { |
|
712 alert = chc.staplingActive ? |
|
713 Alert.BAD_CERT_STATUS_RESPONSE : |
|
714 Alert.CERTIFICATE_REVOKED; |
|
715 } else if ( |
|
716 reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { |
|
717 alert = chc.staplingActive ? |
|
718 Alert.BAD_CERT_STATUS_RESPONSE : |
|
719 Alert.CERTIFICATE_UNKNOWN; |
|
720 } |
|
721 } |
|
722 |
|
723 return alert; |
|
724 } |
|
725 |
|
726 } |
|
727 |
|
728 /** |
|
729 * The certificate entry used in Certificate handshake message for TLS 1.3. |
|
730 */ |
|
731 static final class CertificateEntry { |
|
732 final byte[] encoded; // encoded cert or public key |
|
733 private final SSLExtensions extensions; |
|
734 |
|
735 CertificateEntry(byte[] encoded, SSLExtensions extensions) { |
|
736 this.encoded = encoded; |
|
737 this.extensions = extensions; |
|
738 } |
|
739 |
|
740 private int getEncodedSize() { |
|
741 int extLen = extensions.length(); |
|
742 if (extLen == 0) { |
|
743 extLen = 2; // empty extensions |
|
744 } |
|
745 return 3 + encoded.length + extLen; |
|
746 } |
|
747 |
|
748 @Override |
|
749 public String toString() { |
|
750 MessageFormat messageFormat = new MessageFormat( |
|
751 "\n'{'\n" + |
|
752 "{0}\n" + // X.509 certificate |
|
753 " \"extensions\": '{'\n" + |
|
754 "{1}\n" + |
|
755 " '}'\n" + |
|
756 "'}',", Locale.ENGLISH); |
|
757 |
|
758 Object x509Certs; |
|
759 try { |
|
760 // Don't support certificate type extension (RawPublicKey) yet. |
|
761 CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
|
762 x509Certs = |
|
763 cf.generateCertificate(new ByteArrayInputStream(encoded)); |
|
764 } catch (CertificateException ce) { |
|
765 // no X.509 certificate factory service |
|
766 x509Certs = encoded; |
|
767 } |
|
768 |
|
769 Object[] messageFields = { |
|
770 SSLLogger.toString(x509Certs), |
|
771 Utilities.indent(extensions.toString(), " ") |
|
772 }; |
|
773 |
|
774 return messageFormat.format(messageFields); |
|
775 } |
|
776 } |
|
777 |
|
778 /** |
|
779 * The Certificate handshake message for TLS 1.3. |
|
780 */ |
|
781 static final class T13CertificateMessage extends HandshakeMessage { |
|
782 private final byte[] requestContext; |
|
783 private final List<CertificateEntry> certEntries; |
|
784 |
|
785 T13CertificateMessage(HandshakeContext context, |
|
786 byte[] requestContext, X509Certificate[] certificates) |
|
787 throws SSLException, CertificateException { |
|
788 super(context); |
|
789 |
|
790 this.requestContext = requestContext.clone(); |
|
791 this.certEntries = new LinkedList<>(); |
|
792 for (X509Certificate cert : certificates) { |
|
793 // TODO: shall we use the Certificate for the session? |
|
794 byte[] encoded = cert.getEncoded(); |
|
795 SSLExtensions extensions = new SSLExtensions(this); |
|
796 certEntries.add(new CertificateEntry(encoded, extensions)); |
|
797 } |
|
798 } |
|
799 |
|
800 T13CertificateMessage(HandshakeContext handshakeContext, |
|
801 byte[] requestContext, List<CertificateEntry> certificates) { |
|
802 super(handshakeContext); |
|
803 |
|
804 this.requestContext = requestContext.clone(); |
|
805 this.certEntries = certificates; |
|
806 } |
|
807 |
|
808 T13CertificateMessage(HandshakeContext handshakeContext, |
|
809 ByteBuffer m) throws IOException { |
|
810 super(handshakeContext); |
|
811 |
|
812 // struct { |
|
813 // opaque certificate_request_context<0..2^8-1>; |
|
814 // CertificateEntry certificate_list<0..2^24-1>; |
|
815 // } Certificate; |
|
816 if (m.remaining() < 4) { |
|
817 throw new SSLProtocolException( |
|
818 "Invalid Certificate message: " + |
|
819 "insufficient data (length=" + m.remaining() + ")"); |
|
820 } |
|
821 this.requestContext = Record.getBytes8(m); |
|
822 |
|
823 if (m.remaining() < 3) { |
|
824 throw new SSLProtocolException( |
|
825 "Invalid Certificate message: " + |
|
826 "insufficient certificate entries data (length=" + |
|
827 m.remaining() + ")"); |
|
828 } |
|
829 |
|
830 int listLen = Record.getInt24(m); |
|
831 if (listLen != m.remaining()) { |
|
832 throw new SSLProtocolException( |
|
833 "Invalid Certificate message: " + |
|
834 "incorrect list length (length=" + listLen + ")"); |
|
835 } |
|
836 |
|
837 SSLExtension[] enabledExtensions = |
|
838 handshakeContext.sslConfig.getEnabledExtensions( |
|
839 SSLHandshake.CERTIFICATE); |
|
840 List<CertificateEntry> certList = new LinkedList<>(); |
|
841 while (m.hasRemaining()) { |
|
842 // Note: support only X509 CertificateType right now. |
|
843 byte[] encodedCert = Record.getBytes24(m); |
|
844 if (encodedCert.length == 0) { |
|
845 throw new SSLProtocolException( |
|
846 "Invalid Certificate message: empty cert_data"); |
|
847 } |
|
848 |
|
849 SSLExtensions extensions = |
|
850 new SSLExtensions(this, m, enabledExtensions); |
|
851 certList.add(new CertificateEntry(encodedCert, extensions)); |
|
852 } |
|
853 |
|
854 this.certEntries = Collections.unmodifiableList(certList); |
|
855 } |
|
856 |
|
857 @Override |
|
858 public SSLHandshake handshakeType() { |
|
859 return SSLHandshake.CERTIFICATE; |
|
860 } |
|
861 |
|
862 @Override |
|
863 public int messageLength() { |
|
864 int msgLen = 4 + requestContext.length; |
|
865 for (CertificateEntry entry : certEntries) { |
|
866 msgLen += entry.getEncodedSize(); |
|
867 } |
|
868 |
|
869 return msgLen; |
|
870 } |
|
871 |
|
872 @Override |
|
873 public void send(HandshakeOutStream hos) throws IOException { |
|
874 int entryListLen = 0; |
|
875 for (CertificateEntry entry : certEntries) { |
|
876 entryListLen += entry.getEncodedSize(); |
|
877 } |
|
878 |
|
879 hos.putBytes8(requestContext); |
|
880 hos.putInt24(entryListLen); |
|
881 for (CertificateEntry entry : certEntries) { |
|
882 hos.putBytes24(entry.encoded); |
|
883 // Is it an empty extensions? |
|
884 if (entry.extensions.length() == 0) { |
|
885 hos.putInt16(0); |
|
886 } else { |
|
887 entry.extensions.send(hos); |
|
888 } |
|
889 } |
|
890 } |
|
891 |
|
892 @Override |
|
893 public String toString() { |
|
894 MessageFormat messageFormat = new MessageFormat( |
|
895 "\"Certificate\": '{'\n" + |
|
896 " \"certificate_request_context\": \"{0}\",\n" + |
|
897 " \"certificate_list\": [{1}\n]\n" + |
|
898 "'}'", |
|
899 Locale.ENGLISH); |
|
900 |
|
901 StringBuilder builder = new StringBuilder(512); |
|
902 for (CertificateEntry entry : certEntries) { |
|
903 builder.append(entry.toString()); |
|
904 } |
|
905 |
|
906 Object[] messageFields = { |
|
907 Utilities.toHexString(requestContext), |
|
908 Utilities.indent(builder.toString()) |
|
909 }; |
|
910 |
|
911 return messageFormat.format(messageFields); |
|
912 } |
|
913 } |
|
914 |
|
915 /** |
|
916 * The "Certificate" handshake message producer for TLS 1.3. |
|
917 */ |
|
918 private static final |
|
919 class T13CertificateProducer implements HandshakeProducer { |
|
920 // Prevent instantiation of this class. |
|
921 private T13CertificateProducer() { |
|
922 // blank |
|
923 } |
|
924 |
|
925 @Override |
|
926 public byte[] produce(ConnectionContext context, |
|
927 HandshakeMessage message) throws IOException { |
|
928 // The producing happens in handshake context only. |
|
929 HandshakeContext hc = (HandshakeContext)context; |
|
930 if (hc.sslConfig.isClientMode) { |
|
931 return onProduceCertificate( |
|
932 (ClientHandshakeContext)context, message); |
|
933 } else { |
|
934 return onProduceCertificate( |
|
935 (ServerHandshakeContext)context, message); |
|
936 } |
|
937 } |
|
938 |
|
939 private byte[] onProduceCertificate(ServerHandshakeContext shc, |
|
940 HandshakeMessage message) throws IOException { |
|
941 ClientHelloMessage clientHello = (ClientHelloMessage)message; |
|
942 |
|
943 SSLPossession pos = choosePossession(shc, clientHello); |
|
944 if (pos == null) { |
|
945 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
946 "No available authentication scheme"); |
|
947 return null; // make the complier happy |
|
948 } |
|
949 |
|
950 if (!(pos instanceof X509Possession)) { |
|
951 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
952 "No X.509 certificate for server authentication"); |
|
953 } |
|
954 |
|
955 X509Possession x509Possession = (X509Possession)pos; |
|
956 X509Certificate[] localCerts = x509Possession.popCerts; |
|
957 if (localCerts == null || localCerts.length == 0) { |
|
958 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
959 "No X.509 certificate for server authentication"); |
|
960 return null; // make the complier happy |
|
961 } |
|
962 |
|
963 // update the context |
|
964 shc.handshakePossessions.add(x509Possession); |
|
965 shc.handshakeSession.setLocalPrivateKey( |
|
966 x509Possession.popPrivateKey); |
|
967 shc.handshakeSession.setLocalCertificates(localCerts); |
|
968 T13CertificateMessage cm; |
|
969 try { |
|
970 cm = new T13CertificateMessage(shc, (new byte[0]), localCerts); |
|
971 } catch (SSLException | CertificateException ce) { |
|
972 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
973 "Failed to produce server Certificate message", ce); |
|
974 return null; // make the complier happy |
|
975 } |
|
976 |
|
977 // Check the OCSP stapling extensions and attempt |
|
978 // to get responses. If the resulting stapleParams is non |
|
979 // null, it implies that stapling is enabled on the server side. |
|
980 shc.stapleParams = StatusResponseManager.processStapling(shc); |
|
981 shc.staplingActive = (shc.stapleParams != null); |
|
982 |
|
983 // Process extensions for each CertificateEntry. |
|
984 // Since there can be multiple CertificateEntries within a |
|
985 // single CT message, we will pin a specific CertificateEntry |
|
986 // into the ServerHandshakeContext so individual extension |
|
987 // producers know which X509Certificate it is processing in |
|
988 // each call. |
|
989 SSLExtension[] enabledCTExts = shc.sslConfig.getEnabledExtensions( |
|
990 SSLHandshake.CERTIFICATE, |
|
991 Arrays.asList(ProtocolVersion.PROTOCOLS_OF_13)); |
|
992 for (CertificateEntry certEnt : cm.certEntries) { |
|
993 shc.currentCertEntry = certEnt; |
|
994 certEnt.extensions.produce(shc, enabledCTExts); |
|
995 } |
|
996 |
|
997 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
998 SSLLogger.fine("Produced server Certificate message", cm); |
|
999 } |
|
1000 |
|
1001 // Output the handshake message. |
|
1002 cm.write(shc.handshakeOutput); |
|
1003 shc.handshakeOutput.flush(); |
|
1004 |
|
1005 // The handshake message has been delivered. |
|
1006 return null; |
|
1007 } |
|
1008 |
|
1009 private static SSLPossession choosePossession( |
|
1010 HandshakeContext hc, |
|
1011 ClientHelloMessage clientHello) throws IOException { |
|
1012 if (hc.peerRequestedCertSignSchemes == null || |
|
1013 hc.peerRequestedCertSignSchemes.isEmpty()) { |
|
1014 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1015 SSLLogger.warning( |
|
1016 "No signature_algorithms(_cert) in ClientHello"); |
|
1017 } |
|
1018 return null; |
|
1019 } |
|
1020 |
|
1021 Collection<String> checkedKeyTypes = new HashSet<>(); |
|
1022 for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) { |
|
1023 if (checkedKeyTypes.contains(ss.keyAlgorithm)) { |
|
1024 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1025 SSLLogger.warning( |
|
1026 "Unsupported authentication scheme: " + ss.name); |
|
1027 } |
|
1028 continue; |
|
1029 } |
|
1030 |
|
1031 // Don't select a signature scheme unless we will be able to |
|
1032 // produce a CertificateVerify message later |
|
1033 if (SignatureScheme.getPreferableAlgorithm( |
|
1034 hc.peerRequestedSignatureSchemes, |
|
1035 ss, hc.negotiatedProtocol) == null) { |
|
1036 |
|
1037 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1038 SSLLogger.warning( |
|
1039 "Unable to produce CertificateVerify for scheme: " + ss.name); |
|
1040 } |
|
1041 checkedKeyTypes.add(ss.keyAlgorithm); |
|
1042 continue; |
|
1043 } |
|
1044 |
|
1045 SSLAuthentication ka = |
|
1046 X509Authentication.nameOf(ss.keyAlgorithm); |
|
1047 if (ka == null) { |
|
1048 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1049 SSLLogger.warning( |
|
1050 "Unsupported authentication scheme: " + ss.name); |
|
1051 } |
|
1052 checkedKeyTypes.add(ss.keyAlgorithm); |
|
1053 continue; |
|
1054 } |
|
1055 |
|
1056 SSLPossession pos = ka.createPossession(hc); |
|
1057 if (pos == null) { |
|
1058 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1059 SSLLogger.warning( |
|
1060 "Unavailable authentication scheme: " + ss.name); |
|
1061 } |
|
1062 continue; |
|
1063 } |
|
1064 |
|
1065 return pos; |
|
1066 } |
|
1067 |
|
1068 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1069 SSLLogger.warning("No available authentication scheme"); |
|
1070 } |
|
1071 return null; |
|
1072 } |
|
1073 |
|
1074 private byte[] onProduceCertificate(ClientHandshakeContext chc, |
|
1075 HandshakeMessage message) throws IOException { |
|
1076 ClientHelloMessage clientHello = (ClientHelloMessage)message; |
|
1077 SSLPossession pos = choosePossession(chc, clientHello); |
|
1078 X509Certificate[] localCerts; |
|
1079 if (pos == null) { |
|
1080 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1081 SSLLogger.fine("No available client authentication scheme"); |
|
1082 } |
|
1083 localCerts = new X509Certificate[0]; |
|
1084 } else { |
|
1085 chc.handshakePossessions.add(pos); |
|
1086 if (!(pos instanceof X509Possession)) { |
|
1087 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1088 SSLLogger.fine( |
|
1089 "No X.509 certificate for client authentication"); |
|
1090 } |
|
1091 localCerts = new X509Certificate[0]; |
|
1092 } else { |
|
1093 X509Possession x509Possession = (X509Possession)pos; |
|
1094 localCerts = x509Possession.popCerts; |
|
1095 chc.handshakeSession.setLocalPrivateKey( |
|
1096 x509Possession.popPrivateKey); |
|
1097 } |
|
1098 } |
|
1099 |
|
1100 if (localCerts != null && localCerts.length != 0) { |
|
1101 chc.handshakeSession.setLocalCertificates(localCerts); |
|
1102 } else { |
|
1103 chc.handshakeSession.setLocalCertificates(null); |
|
1104 } |
|
1105 |
|
1106 T13CertificateMessage cm; |
|
1107 try { |
|
1108 cm = new T13CertificateMessage( |
|
1109 chc, chc.certRequestContext, localCerts); |
|
1110 } catch (SSLException | CertificateException ce) { |
|
1111 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
1112 "Failed to produce client Certificate message", ce); |
|
1113 return null; // make the complier happy |
|
1114 } |
|
1115 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1116 SSLLogger.fine("Produced client Certificate message", cm); |
|
1117 } |
|
1118 |
|
1119 // Output the handshake message. |
|
1120 cm.write(chc.handshakeOutput); |
|
1121 chc.handshakeOutput.flush(); |
|
1122 |
|
1123 // The handshake message has been delivered. |
|
1124 return null; |
|
1125 } |
|
1126 } |
|
1127 |
|
1128 /** |
|
1129 * The "Certificate" handshake message consumer for TLS 1.3. |
|
1130 */ |
|
1131 private static final class T13CertificateConsumer implements SSLConsumer { |
|
1132 // Prevent instantiation of this class. |
|
1133 private T13CertificateConsumer() { |
|
1134 // blank |
|
1135 } |
|
1136 |
|
1137 @Override |
|
1138 public void consume(ConnectionContext context, |
|
1139 ByteBuffer message) throws IOException { |
|
1140 // The consuming happens in handshake context only. |
|
1141 HandshakeContext hc = (HandshakeContext)context; |
|
1142 |
|
1143 // clean up this consumer |
|
1144 hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); |
|
1145 T13CertificateMessage cm = new T13CertificateMessage(hc, message); |
|
1146 if (hc.sslConfig.isClientMode) { |
|
1147 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1148 SSLLogger.fine( |
|
1149 "Consuming server Certificate handshake message", cm); |
|
1150 } |
|
1151 onConsumeCertificate((ClientHandshakeContext)context, cm); |
|
1152 } else { |
|
1153 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
1154 SSLLogger.fine( |
|
1155 "Consuming client Certificate handshake message", cm); |
|
1156 } |
|
1157 onConsumeCertificate((ServerHandshakeContext)context, cm); |
|
1158 } |
|
1159 } |
|
1160 |
|
1161 private void onConsumeCertificate(ServerHandshakeContext shc, |
|
1162 T13CertificateMessage certificateMessage )throws IOException { |
|
1163 if (certificateMessage.certEntries == null || |
|
1164 certificateMessage.certEntries.isEmpty()) { |
|
1165 if (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED) { |
|
1166 shc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
1167 "Empty client certificate chain"); |
|
1168 } else { |
|
1169 // optional client authentication |
|
1170 return; |
|
1171 } |
|
1172 } |
|
1173 |
|
1174 // check client certificate entries |
|
1175 X509Certificate[] cliCerts = |
|
1176 checkClientCerts(shc, certificateMessage.certEntries); |
|
1177 |
|
1178 // |
|
1179 // update |
|
1180 // |
|
1181 shc.handshakeCredentials.add( |
|
1182 new X509Credentials(cliCerts[0].getPublicKey(), cliCerts)); |
|
1183 shc.handshakeSession.setPeerCertificates(cliCerts); |
|
1184 } |
|
1185 |
|
1186 private void onConsumeCertificate(ClientHandshakeContext chc, |
|
1187 T13CertificateMessage certificateMessage )throws IOException { |
|
1188 if (certificateMessage.certEntries == null || |
|
1189 certificateMessage.certEntries.isEmpty()) { |
|
1190 chc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
1191 "Empty server certificate chain"); |
|
1192 } |
|
1193 |
|
1194 // Each CertificateEntry will have its own set of extensions |
|
1195 // which must be consumed. |
|
1196 SSLExtension[] enabledExtensions = |
|
1197 chc.sslConfig.getEnabledExtensions(SSLHandshake.CERTIFICATE); |
|
1198 for (CertificateEntry certEnt : certificateMessage.certEntries) { |
|
1199 certEnt.extensions.consumeOnLoad(chc, enabledExtensions); |
|
1200 } |
|
1201 |
|
1202 // check server certificate entries |
|
1203 X509Certificate[] srvCerts = |
|
1204 checkServerCerts(chc, certificateMessage.certEntries); |
|
1205 |
|
1206 // |
|
1207 // update |
|
1208 // |
|
1209 chc.handshakeCredentials.add( |
|
1210 new X509Credentials(srvCerts[0].getPublicKey(), srvCerts)); |
|
1211 chc.handshakeSession.setPeerCertificates(srvCerts); |
|
1212 } |
|
1213 |
|
1214 private static X509Certificate[] checkClientCerts( |
|
1215 ServerHandshakeContext shc, |
|
1216 List<CertificateEntry> certEntries) throws IOException { |
|
1217 X509Certificate[] certs = |
|
1218 new X509Certificate[certEntries.size()]; |
|
1219 try { |
|
1220 CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
|
1221 int i = 0; |
|
1222 for (CertificateEntry entry : certEntries) { |
|
1223 certs[i++] = (X509Certificate)cf.generateCertificate( |
|
1224 new ByteArrayInputStream(entry.encoded)); |
|
1225 // TODO: check extensions |
|
1226 } |
|
1227 } catch (CertificateException ce) { |
|
1228 shc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
1229 "Failed to parse server certificates", ce); |
|
1230 } |
|
1231 |
|
1232 // find out the types of client authentication used |
|
1233 String keyAlgorithm = certs[0].getPublicKey().getAlgorithm(); |
|
1234 String authType; |
|
1235 switch (keyAlgorithm) { |
|
1236 case "RSA": |
|
1237 authType = "RSA"; |
|
1238 break; |
|
1239 case "DSA": |
|
1240 authType = "DSA"; |
|
1241 break; |
|
1242 case "EC": |
|
1243 authType = "EC"; |
|
1244 break; |
|
1245 default: |
|
1246 // unknown public key type |
|
1247 authType = "UNKNOWN"; |
|
1248 break; |
|
1249 } |
|
1250 |
|
1251 try { |
|
1252 X509TrustManager tm = shc.sslContext.getX509TrustManager(); |
|
1253 if (tm instanceof X509ExtendedTrustManager) { |
|
1254 if (shc.conContext.transport instanceof SSLEngine) { |
|
1255 SSLEngine engine = (SSLEngine)shc.conContext.transport; |
|
1256 ((X509ExtendedTrustManager)tm).checkClientTrusted( |
|
1257 certs.clone(), |
|
1258 authType, |
|
1259 engine); |
|
1260 } else { |
|
1261 SSLSocket socket = (SSLSocket)shc.conContext.transport; |
|
1262 ((X509ExtendedTrustManager)tm).checkClientTrusted( |
|
1263 certs.clone(), |
|
1264 authType, |
|
1265 socket); |
|
1266 } |
|
1267 } else { |
|
1268 // Unlikely to happen, because we have wrapped the old |
|
1269 // X509TrustManager with the new X509ExtendedTrustManager. |
|
1270 throw new CertificateException( |
|
1271 "Improper X509TrustManager implementation"); |
|
1272 } |
|
1273 |
|
1274 // Once the client certificate chain has been validated, set |
|
1275 // the certificate chain in the TLS session. |
|
1276 shc.handshakeSession.setPeerCertificates(certs); |
|
1277 } catch (CertificateException ce) { |
|
1278 // TODO: A more precise alert should be used. |
|
1279 shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce); |
|
1280 } |
|
1281 |
|
1282 return certs; |
|
1283 } |
|
1284 |
|
1285 private static X509Certificate[] checkServerCerts( |
|
1286 ClientHandshakeContext chc, |
|
1287 List<CertificateEntry> certEntries) throws IOException { |
|
1288 X509Certificate[] certs = |
|
1289 new X509Certificate[certEntries.size()]; |
|
1290 try { |
|
1291 CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
|
1292 int i = 0; |
|
1293 for (CertificateEntry entry : certEntries) { |
|
1294 certs[i++] = (X509Certificate)cf.generateCertificate( |
|
1295 new ByteArrayInputStream(entry.encoded)); |
|
1296 // TODO: check extensions |
|
1297 } |
|
1298 } catch (CertificateException ce) { |
|
1299 chc.conContext.fatal(Alert.BAD_CERTIFICATE, |
|
1300 "Failed to parse server certificates", ce); |
|
1301 } |
|
1302 |
|
1303 // find out the types of client authentication used |
|
1304 /* |
|
1305 String keyAlgorithm = certs[0].getPublicKey().getAlgorithm(); |
|
1306 String authType; |
|
1307 switch (keyAlgorithm) { |
|
1308 case "RSA": |
|
1309 authType = "RSA"; |
|
1310 break; |
|
1311 case "DSA": |
|
1312 authType = "DSA"; |
|
1313 break; |
|
1314 case "EC": |
|
1315 authType = "EC"; |
|
1316 break; |
|
1317 default: |
|
1318 // unknown public key type |
|
1319 authType = "UNKNOWN"; |
|
1320 break; |
|
1321 } |
|
1322 */ |
|
1323 String authType = "UNKNOWN"; |
|
1324 |
|
1325 try { |
|
1326 X509TrustManager tm = chc.sslContext.getX509TrustManager(); |
|
1327 if (tm instanceof X509ExtendedTrustManager) { |
|
1328 if (chc.conContext.transport instanceof SSLEngine) { |
|
1329 SSLEngine engine = (SSLEngine)chc.conContext.transport; |
|
1330 ((X509ExtendedTrustManager)tm).checkServerTrusted( |
|
1331 certs.clone(), |
|
1332 authType, |
|
1333 engine); |
|
1334 } else { |
|
1335 SSLSocket socket = (SSLSocket)chc.conContext.transport; |
|
1336 ((X509ExtendedTrustManager)tm).checkServerTrusted( |
|
1337 certs.clone(), |
|
1338 authType, |
|
1339 socket); |
|
1340 } |
|
1341 } else { |
|
1342 // Unlikely to happen, because we have wrapped the old |
|
1343 // X509TrustManager with the new X509ExtendedTrustManager. |
|
1344 throw new CertificateException( |
|
1345 "Improper X509TrustManager implementation"); |
|
1346 } |
|
1347 |
|
1348 // Once the server certificate chain has been validated, set |
|
1349 // the certificate chain in the TLS session. |
|
1350 chc.handshakeSession.setPeerCertificates(certs); |
|
1351 } catch (CertificateException ce) { |
|
1352 chc.conContext.fatal(getCertificateAlert(chc, ce), ce); |
|
1353 } |
|
1354 |
|
1355 return certs; |
|
1356 } |
|
1357 |
|
1358 /** |
|
1359 * When a failure happens during certificate checking from an |
|
1360 * {@link X509TrustManager}, determine what TLS alert description |
|
1361 * to use. |
|
1362 * |
|
1363 * @param cexc The exception thrown by the {@link X509TrustManager} |
|
1364 * |
|
1365 * @return A byte value corresponding to a TLS alert description number. |
|
1366 */ |
|
1367 private static Alert getCertificateAlert( |
|
1368 ClientHandshakeContext chc, CertificateException cexc) { |
|
1369 // The specific reason for the failure will determine how to |
|
1370 // set the alert description value |
|
1371 Alert alert = Alert.CERTIFICATE_UNKNOWN; |
|
1372 |
|
1373 Throwable baseCause = cexc.getCause(); |
|
1374 if (baseCause instanceof CertPathValidatorException) { |
|
1375 CertPathValidatorException cpve = |
|
1376 (CertPathValidatorException)baseCause; |
|
1377 Reason reason = cpve.getReason(); |
|
1378 if (reason == BasicReason.REVOKED) { |
|
1379 alert = chc.staplingActive ? |
|
1380 Alert.BAD_CERT_STATUS_RESPONSE : |
|
1381 Alert.CERTIFICATE_REVOKED; |
|
1382 } else if ( |
|
1383 reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { |
|
1384 alert = chc.staplingActive ? |
|
1385 Alert.BAD_CERT_STATUS_RESPONSE : |
|
1386 Alert.CERTIFICATE_UNKNOWN; |
|
1387 } |
|
1388 } |
|
1389 |
|
1390 return alert; |
|
1391 } |
|
1392 } |
|
1393 } |