1 /*
2 * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27 package sun.net.www.protocol.https;
28
29 import java.io.IOException;
30 import java.io.UnsupportedEncodingException;
31 import java.io.PrintStream;
32 import java.io.BufferedOutputStream;
33 import java.net.InetAddress;
34 import java.net.Socket;
35 import java.net.SocketException;
36 import java.net.URL;
37 import java.net.UnknownHostException;
38 import java.net.InetSocketAddress;
39 import java.net.Proxy;
40 import java.security.Principal;
41 import java.security.cert.*;
42 import java.util.StringTokenizer;
43 import java.util.Vector;
44 import java.security.AccessController;
45
46 import javax.security.auth.x500.X500Principal;
47
48 import javax.net.ssl.*;
49 import sun.net.www.http.HttpClient;
50 import sun.net.www.protocol.http.HttpURLConnection;
51 import sun.security.action.*;
52
53 import sun.security.util.HostnameChecker;
54 import sun.security.ssl.SSLSocketImpl;
55
56 import sun.util.logging.PlatformLogger;
57 import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
58
59
60 /**
61 * This class provides HTTPS client URL support, building on the standard
62 * "sun.net.www" HTTP protocol handler. HTTPS is the same protocol as HTTP,
63 * but differs in the transport layer which it uses: <UL>
64 *
65 * <LI>There's a <em>Secure Sockets Layer</em> between TCP
66 * and the HTTP protocol code.
67 *
68 * <LI>It uses a different default TCP port.
69 *
70 * <LI>It doesn't use application level proxies, which can see and
71 * manipulate HTTP user level data, compromising privacy. It uses
72 * low level tunneling instead, which hides HTTP protocol and data
73 * from all third parties. (Traffic analysis is still possible).
74 *
75 * <LI>It does basic server authentication, to protect
76 * against "URL spoofing" attacks. This involves deciding
77 * whether the X.509 certificate chain identifying the server
78 * is trusted, and verifying that the name of the server is
79 * found in the certificate. (The application may enable an
80 * anonymous SSL cipher suite, and such checks are not done
81 * for anonymous ciphers.)
82 *
83 * <LI>It exposes key SSL session attributes, specifically the
84 * cipher suite in use and the server's X509 certificates, to
85 * application software which knows about this protocol handler.
86 *
87 * </UL>
88 *
89 * <P> System properties used include: <UL>
90 *
91 * <LI><em>https.proxyHost</em> ... the host supporting SSL
92 * tunneling using the conventional CONNECT syntax
93 *
94 * <LI><em>https.proxyPort</em> ... port to use on proxyHost
95 *
96 * <LI><em>https.cipherSuites</em> ... comma separated list of
97 * SSL cipher suite names to enable.
98 *
99 * <LI><em>http.nonProxyHosts</em> ...
100 *
101 * </UL>
102 *
103 * @author David Brownell
104 * @author Bill Foote
105 */
106
107 // final for export control reasons (access to APIs); remove with care
108 final class HttpsClient extends HttpClient
109 implements HandshakeCompletedListener
110 {
111 // STATIC STATE and ACCESSORS THERETO
112
113 // HTTPS uses a different default port number than HTTP.
114 private static final int httpsPortNumber = 443;
115
116 // default HostnameVerifier class canonical name
117 private static final String defaultHVCanonicalName =
118 "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier";
119
120 /** Returns the default HTTPS port (443) */
121 @Override
122 protected int getDefaultPort() { return httpsPortNumber; }
123
124 private HostnameVerifier hv;
125 private SSLSocketFactory sslSocketFactory;
126
127 // HttpClient.proxyDisabled will always be false, because we don't
128 // use an application-level HTTP proxy. We might tunnel through
129 // our http proxy, though.
130
131
132 // INSTANCE DATA
133
134 // last negotiated SSL session
135 private SSLSession session;
136
137 private String [] getCipherSuites() {
138 //
139 // If ciphers are assigned, sort them into an array.
140 //
141 String ciphers [];
142 String cipherString = AccessController.doPrivileged(
143 new GetPropertyAction("https.cipherSuites"));
144
145 if (cipherString == null || "".equals(cipherString)) {
146 ciphers = null;
147 } else {
148 StringTokenizer tokenizer;
149 Vector<String> v = new Vector<String>();
150
151 tokenizer = new StringTokenizer(cipherString, ",");
152 while (tokenizer.hasMoreTokens())
153 v.addElement(tokenizer.nextToken());
154 ciphers = new String [v.size()];
155 for (int i = 0; i < ciphers.length; i++)
156 ciphers [i] = v.elementAt(i);
157 }
158 return ciphers;
159 }
160
161 private String [] getProtocols() {
162 //
163 // If protocols are assigned, sort them into an array.
164 //
165 String protocols [];
166 String protocolString = AccessController.doPrivileged(
167 new GetPropertyAction("https.protocols"));
168
169 if (protocolString == null || "".equals(protocolString)) {
170 protocols = null;
171 } else {
172 StringTokenizer tokenizer;
173 Vector<String> v = new Vector<String>();
174
175 tokenizer = new StringTokenizer(protocolString, ",");
176 while (tokenizer.hasMoreTokens())
177 v.addElement(tokenizer.nextToken());
178 protocols = new String [v.size()];
179 for (int i = 0; i < protocols.length; i++) {
180 protocols [i] = v.elementAt(i);
181 }
182 }
183 return protocols;
184 }
185
186 private String getUserAgent() {
187 String userAgent = java.security.AccessController.doPrivileged(
188 new sun.security.action.GetPropertyAction("https.agent"));
189 if (userAgent == null || userAgent.length() == 0) {
190 userAgent = "JSSE";
191 }
192 return userAgent;
193 }
194
195 // should remove once HttpClient.newHttpProxy is putback
196 private static Proxy newHttpProxy(String proxyHost, int proxyPort) {
197 InetSocketAddress saddr = null;
198 final String phost = proxyHost;
199 final int pport = proxyPort < 0 ? httpsPortNumber : proxyPort;
200 try {
201 saddr = java.security.AccessController.doPrivileged(new
202 java.security.PrivilegedExceptionAction<InetSocketAddress>() {
203 public InetSocketAddress run() {
204 return new InetSocketAddress(phost, pport);
205 }});
206 } catch (java.security.PrivilegedActionException pae) {
207 }
208 return new Proxy(Proxy.Type.HTTP, saddr);
209 }
210
211 // CONSTRUCTOR, FACTORY
212
213
214 /**
215 * Create an HTTPS client URL. Traffic will be tunneled through any
216 * intermediate nodes rather than proxied, so that confidentiality
217 * of data exchanged can be preserved. However, note that all the
218 * anonymous SSL flavors are subject to "person-in-the-middle"
219 * attacks against confidentiality. If you enable use of those
220 * flavors, you may be giving up the protection you get through
221 * SSL tunneling.
222 *
223 * Use New to get new HttpsClient. This constructor is meant to be
224 * used only by New method. New properly checks for URL spoofing.
225 *
226 * @param URL https URL with which a connection must be established
227 */
228 private HttpsClient(SSLSocketFactory sf, URL url)
229 throws IOException
230 {
231 // HttpClient-level proxying is always disabled,
232 // because we override doConnect to do tunneling instead.
233 this(sf, url, (String)null, -1);
234 }
235
236 /**
237 * Create an HTTPS client URL. Traffic will be tunneled through
238 * the specified proxy server.
239 */
240 HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)
241 throws IOException {
242 this(sf, url, proxyHost, proxyPort, -1);
243 }
244
245 /**
246 * Create an HTTPS client URL. Traffic will be tunneled through
247 * the specified proxy server, with a connect timeout
248 */
249 HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,
250 int connectTimeout)
251 throws IOException {
252 this(sf, url,
253 (proxyHost == null? null:
254 HttpsClient.newHttpProxy(proxyHost, proxyPort)),
255 connectTimeout);
256 }
257
258 /**
259 * Same as previous constructor except using a Proxy
260 */
261 HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,
262 int connectTimeout)
263 throws IOException {
264 this.proxy = proxy;
265 setSSLSocketFactory(sf);
266 this.proxyDisabled = true;
267
268 this.host = url.getHost();
269 this.url = url;
270 port = url.getPort();
271 if (port == -1) {
272 port = getDefaultPort();
273 }
274 setConnectTimeout(connectTimeout);
275 openServer();
276 }
277
278
279 // This code largely ripped off from HttpClient.New, and
280 // it uses the same keepalive cache.
281
282 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
283 HttpURLConnection httpuc)
284 throws IOException {
285 return HttpsClient.New(sf, url, hv, true, httpuc);
286 }
287
288 /** See HttpClient for the model for this method. */
289 static HttpClient New(SSLSocketFactory sf, URL url,
290 HostnameVerifier hv, boolean useCache,
291 HttpURLConnection httpuc) throws IOException {
292 return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc);
293 }
294
295 /**
296 * Get a HTTPS client to the URL. Traffic will be tunneled through
297 * the specified proxy server.
298 */
299 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
300 String proxyHost, int proxyPort,
301 HttpURLConnection httpuc) throws IOException {
302 return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc);
303 }
304
305 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
306 String proxyHost, int proxyPort, boolean useCache,
307 HttpURLConnection httpuc)
308 throws IOException {
309 return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1,
310 httpuc);
311 }
312
313 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
314 String proxyHost, int proxyPort, boolean useCache,
315 int connectTimeout, HttpURLConnection httpuc)
316 throws IOException {
317
318 return HttpsClient.New(sf, url, hv,
319 (proxyHost == null? null :
320 HttpsClient.newHttpProxy(proxyHost, proxyPort)),
321 useCache, connectTimeout, httpuc);
322 }
323
324 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
325 Proxy p, boolean useCache,
326 int connectTimeout, HttpURLConnection httpuc)
327 throws IOException
328 {
329 if (p == null) {
330 p = Proxy.NO_PROXY;
331 }
332 HttpsClient ret = null;
333 if (useCache) {
334 /* see if one's already around */
335 ret = (HttpsClient) kac.get(url, sf);
336 if (ret != null && httpuc != null &&
337 httpuc.streaming() &&
338 httpuc.getRequestMethod() == "POST") {
339 if (!ret.available())
340 ret = null;
341 }
342
343 if (ret != null) {
344 if ((ret.proxy != null && ret.proxy.equals(p)) ||
345 (ret.proxy == null && p == null)) {
346 synchronized (ret) {
347 ret.cachedHttpClient = true;
348 assert ret.inCache;
349 ret.inCache = false;
350 if (httpuc != null && ret.needsTunneling())
351 httpuc.setTunnelState(TUNNELING);
352 PlatformLogger logger = HttpURLConnection.getHttpLogger();
353 if (logger.isLoggable(PlatformLogger.FINEST)) {
354 logger.finest("KeepAlive stream retrieved from the cache, " + ret);
355 }
356 }
357 } else {
358 // We cannot return this connection to the cache as it's
359 // KeepAliveTimeout will get reset. We simply close the connection.
360 // This should be fine as it is very rare that a connection
361 // to the same host will not use the same proxy.
362 synchronized(ret) {
363 ret.inCache = false;
364 ret.closeServer();
365 }
366 ret = null;
367 }
368 }
369 }
370 if (ret == null) {
371 ret = new HttpsClient(sf, url, p, connectTimeout);
372 } else {
373 SecurityManager security = System.getSecurityManager();
374 if (security != null) {
375 if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
376 security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
377 } else {
378 security.checkConnect(url.getHost(), url.getPort());
379 }
380 }
381 ret.url = url;
382 }
383 ret.setHostnameVerifier(hv);
384
385 return ret;
386 }
387
388 // METHODS
389 void setHostnameVerifier(HostnameVerifier hv) {
390 this.hv = hv;
391 }
392
393 void setSSLSocketFactory(SSLSocketFactory sf) {
394 sslSocketFactory = sf;
395 }
396
397 SSLSocketFactory getSSLSocketFactory() {
398 return sslSocketFactory;
399 }
400
401 /**
402 * The following method, createSocket, is defined in NetworkClient
403 * and overridden here so that the socket facroty is used to create
404 * new sockets.
405 */
406 @Override
407 protected Socket createSocket() throws IOException {
408 try {
409 return sslSocketFactory.createSocket();
410 } catch (SocketException se) {
411 //
412 // bug 6771432
413 // javax.net.SocketFactory throws a SocketException with an
414 // UnsupportedOperationException as its cause to indicate that
415 // unconnected sockets have not been implemented.
416 //
417 Throwable t = se.getCause();
418 if (t != null && t instanceof UnsupportedOperationException) {
419 return super.createSocket();
420 } else {
421 throw se;
422 }
423 }
424 }
425
426
427 @Override
428 public boolean needsTunneling() {
429 return (proxy != null && proxy.type() != Proxy.Type.DIRECT
430 && proxy.type() != Proxy.Type.SOCKS);
431 }
432
433 @Override
434 public void afterConnect() throws IOException, UnknownHostException {
435 if (!isCachedConnection()) {
436 SSLSocket s = null;
437 SSLSocketFactory factory = sslSocketFactory;
438 try {
439 if (!(serverSocket instanceof SSLSocket)) {
440 s = (SSLSocket)factory.createSocket(serverSocket,
441 host, port, true);
442 } else {
443 s = (SSLSocket)serverSocket;
444 if (s instanceof SSLSocketImpl) {
445 ((SSLSocketImpl)s).setHost(host);
446 }
447 }
448 } catch (IOException ex) {
449 // If we fail to connect through the tunnel, try it
450 // locally, as a last resort. If this doesn't work,
451 // throw the original exception.
452 try {
453 s = (SSLSocket)factory.createSocket(host, port);
454 } catch (IOException ignored) {
455 throw ex;
456 }
457 }
458
459 //
460 // Force handshaking, so that we get any authentication.
461 // Register a handshake callback so our session state tracks any
462 // later session renegotiations.
463 //
464 String [] protocols = getProtocols();
465 String [] ciphers = getCipherSuites();
466 if (protocols != null) {
467 s.setEnabledProtocols(protocols);
468 }
469 if (ciphers != null) {
470 s.setEnabledCipherSuites(ciphers);
471 }
472 s.addHandshakeCompletedListener(this);
473
474 // We have two hostname verification approaches. One is in
475 // SSL/TLS socket layer, where the algorithm is configured with
476 // SSLParameters.setEndpointIdentificationAlgorithm(), and the
477 // hostname verification is done by X509ExtendedTrustManager when
478 // the algorithm is "HTTPS". The other one is in HTTPS layer,
479 // where the algorithm is customized by
480 // HttpsURLConnection.setHostnameVerifier(), and the hostname
481 // verification is done by HostnameVerifier when the default
482 // rules for hostname verification fail.
483 //
484 // The relationship between two hostname verification approaches
485 // likes the following:
486 //
487 // | EIA algorithm
488 // +----------------------------------------------
489 // | null | HTTPS | LDAP/other |
490 // -------------------------------------------------------------
491 // | |1 |2 |3 |
492 // HNV | default | Set HTTPS EIA | use EIA | HTTPS |
493 // |--------------------------------------------------------
494 // | non - |4 |5 |6 |
495 // | default | HTTPS/HNV | use EIA | HTTPS/HNV |
496 // -------------------------------------------------------------
497 //
498 // Abbreviation:
499 // EIA: the endpoint identification algorithm in SSL/TLS
500 // socket layer
501 // HNV: the hostname verification object in HTTPS layer
502 // Notes:
503 // case 1. default HNV and EIA is null
504 // Set EIA as HTTPS, hostname check done in SSL/TLS
505 // layer.
506 // case 2. default HNV and EIA is HTTPS
507 // Use existing EIA, hostname check done in SSL/TLS
508 // layer.
509 // case 3. default HNV and EIA is other than HTTPS
510 // Use existing EIA, EIA check done in SSL/TLS
511 // layer, then do HTTPS check in HTTPS layer.
512 // case 4. non-default HNV and EIA is null
513 // No EIA, no EIA check done in SSL/TLS layer, then do
514 // HTTPS check in HTTPS layer using HNV as override.
515 // case 5. non-default HNV and EIA is HTTPS
516 // Use existing EIA, hostname check done in SSL/TLS
517 // layer. No HNV override possible. We will review this
518 // decision and may update the architecture for JDK 7.
519 // case 6. non-default HNV and EIA is other than HTTPS
520 // Use existing EIA, EIA check done in SSL/TLS layer,
521 // then do HTTPS check in HTTPS layer as override.
522 boolean needToCheckSpoofing = true;
523 String identification =
524 s.getSSLParameters().getEndpointIdentificationAlgorithm();
525 if (identification != null && identification.length() != 0) {
526 if (identification.equalsIgnoreCase("HTTPS")) {
527 // Do not check server identity again out of SSLSocket,
528 // the endpoint will be identified during TLS handshaking
529 // in SSLSocket.
530 needToCheckSpoofing = false;
531 } // else, we don't understand the identification algorithm,
532 // need to check URL spoofing here.
533 } else {
534 boolean isDefaultHostnameVerifier = false;
535
536 // We prefer to let the SSLSocket do the spoof checks, but if
537 // the application has specified a HostnameVerifier (HNV),
538 // we will always use that.
539 if (hv != null) {
540 String canonicalName = hv.getClass().getCanonicalName();
541 if (canonicalName != null &&
542 canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) {
543 isDefaultHostnameVerifier = true;
544 }
545 } else {
546 // Unlikely to happen! As the behavior is the same as the
547 // default hostname verifier, so we prefer to let the
548 // SSLSocket do the spoof checks.
549 isDefaultHostnameVerifier = true;
550 }
551
552 if (isDefaultHostnameVerifier) {
553 // If the HNV is the default from HttpsURLConnection, we
554 // will do the spoof checks in SSLSocket.
555 SSLParameters paramaters = s.getSSLParameters();
556 paramaters.setEndpointIdentificationAlgorithm("HTTPS");
557 s.setSSLParameters(paramaters);
558
559 needToCheckSpoofing = false;
560 }
561 }
562
563 s.startHandshake();
564 session = s.getSession();
565 // change the serverSocket and serverOutput
566 serverSocket = s;
567 try {
568 serverOutput = new PrintStream(
569 new BufferedOutputStream(serverSocket.getOutputStream()),
570 false, encoding);
571 } catch (UnsupportedEncodingException e) {
572 throw new InternalError(encoding+" encoding not found");
573 }
574
575 // check URL spoofing if it has not been checked under handshaking
576 if (needToCheckSpoofing) {
577 checkURLSpoofing(hv);
578 }
579 } else {
580 // if we are reusing a cached https session,
581 // we don't need to do handshaking etc. But we do need to
582 // set the ssl session
583 session = ((SSLSocket)serverSocket).getSession();
584 }
585 }
586
587 // Server identity checking is done according to RFC 2818: HTTP over TLS
588 // Section 3.1 Server Identity
589 private void checkURLSpoofing(HostnameVerifier hostnameVerifier)
590 throws IOException {
591 //
592 // Get authenticated server name, if any
593 //
594 String host = url.getHost();
595
596 // if IPv6 strip off the "[]"
597 if (host != null && host.startsWith("[") && host.endsWith("]")) {
598 host = host.substring(1, host.length()-1);
599 }
600
601 Certificate[] peerCerts = null;
602 String cipher = session.getCipherSuite();
603 try {
604 HostnameChecker checker = HostnameChecker.getInstance(
605 HostnameChecker.TYPE_TLS);
606
607 // Use ciphersuite to determine whether Kerberos is present.
608 if (cipher.startsWith("TLS_KRB5")) {
609 if (!HostnameChecker.match(host, getPeerPrincipal())) {
610 throw new SSLPeerUnverifiedException("Hostname checker" +
611 " failed for Kerberos");
612 }
613 } else { // X.509
614
615 // get the subject's certificate
616 peerCerts = session.getPeerCertificates();
617
618 X509Certificate peerCert;
619 if (peerCerts[0] instanceof
620 java.security.cert.X509Certificate) {
621 peerCert = (java.security.cert.X509Certificate)peerCerts[0];
622 } else {
623 throw new SSLPeerUnverifiedException("");
624 }
625 checker.match(host, peerCert);
626 }
627
628 // if it doesn't throw an exception, we passed. Return.
629 return;
630
631 } catch (SSLPeerUnverifiedException e) {
632
633 //
634 // client explicitly changed default policy and enabled
635 // anonymous ciphers; we can't check the standard policy
636 //
637 // ignore
638 } catch (java.security.cert.CertificateException cpe) {
639 // ignore
640 }
641
642 if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) {
643 return;
644 } else if ((hostnameVerifier != null) &&
645 (hostnameVerifier.verify(host, session))) {
646 return;
647 }
648
649 serverSocket.close();
650 session.invalidate();
651
652 throw new IOException("HTTPS hostname wrong: should be <"
653 + url.getHost() + ">");
654 }
655
656 @Override
657 protected void putInKeepAliveCache() {
658 if (inCache) {
659 assert false : "Duplicate put to keep alive cache";
660 return;
661 }
662 inCache = true;
663 kac.put(url, sslSocketFactory, this);
664 }
665
666 /*
667 * Close an idle connection to this URL (if it exists in the cache).
668 */
669 @Override
670 public void closeIdleConnection() {
671 HttpClient http = kac.get(url, sslSocketFactory);
672 if (http != null) {
673 http.closeServer();
674 }
675 }
676
677 /**
678 * Returns the cipher suite in use on this connection.
679 */
680 String getCipherSuite() {
681 return session.getCipherSuite();
682 }
683
684 /**
685 * Returns the certificate chain the client sent to the
686 * server, or null if the client did not authenticate.
687 */
688 public java.security.cert.Certificate [] getLocalCertificates() {
689 return session.getLocalCertificates();
690 }
691
692 /**
693 * Returns the certificate chain with which the server
694 * authenticated itself, or throw a SSLPeerUnverifiedException
695 * if the server did not authenticate.
696 */
697 java.security.cert.Certificate [] getServerCertificates()
698 throws SSLPeerUnverifiedException
699 {
700 return session.getPeerCertificates();
701 }
702
703 /**
704 * Returns the X.509 certificate chain with which the server
705 * authenticated itself, or null if the server did not authenticate.
706 */
707 javax.security.cert.X509Certificate [] getServerCertificateChain()
708 throws SSLPeerUnverifiedException
709 {
710 return session.getPeerCertificateChain();
711 }
712
713 /**
714 * Returns the principal with which the server authenticated
715 * itself, or throw a SSLPeerUnverifiedException if the
716 * server did not authenticate.
717 */
718 Principal getPeerPrincipal()
719 throws SSLPeerUnverifiedException
720 {
721 Principal principal;
722 try {
723 principal = session.getPeerPrincipal();
724 } catch (AbstractMethodError e) {
725 // if the provider does not support it, fallback to peer certs.
726 // return the X500Principal of the end-entity cert.
727 java.security.cert.Certificate[] certs =
728 session.getPeerCertificates();
729 principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
730 }
731 return principal;
732 }
733
734 /**
735 * Returns the principal the client sent to the
736 * server, or null if the client did not authenticate.
737 */
738 Principal getLocalPrincipal()
739 {
740 Principal principal;
741 try {
742 principal = session.getLocalPrincipal();
743 } catch (AbstractMethodError e) {
744 principal = null;
745 // if the provider does not support it, fallback to local certs.
746 // return the X500Principal of the end-entity cert.
747 java.security.cert.Certificate[] certs =
748 session.getLocalCertificates();
749 if (certs != null) {
750 principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
751 }
752 }
753 return principal;
754 }
755
756 /**
757 * This method implements the SSL HandshakeCompleted callback,
758 * remembering the resulting session so that it may be queried
759 * for the current cipher suite and peer certificates. Servers
760 * sometimes re-initiate handshaking, so the session in use on
761 * a given connection may change. When sessions change, so may
762 * peer identities and cipher suites.
763 */
764 public void handshakeCompleted(HandshakeCompletedEvent event)
765 {
766 session = event.getSession();
767 }
768
769 /**
770 * @return the proxy host being used for this client, or null
771 * if we're not going through a proxy
772 */
773 @Override
774 public String getProxyHostUsed() {
775 if (!needsTunneling()) {
776 return null;
777 } else {
778 return super.getProxyHostUsed();
779 }
780 }
781
782 /**
783 * @return the proxy port being used for this client. Meaningless
784 * if getProxyHostUsed() gives null.
785 */
786 @Override
787 public int getProxyPortUsed() {
788 return (proxy == null || proxy.type() == Proxy.Type.DIRECT ||
789 proxy.type() == Proxy.Type.SOCKS)? -1:
790 ((InetSocketAddress)proxy.address()).getPort();
791 }
792 }
--- EOF ---