1 /* 2 * Copyright (c) 2001, 2015, 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 // CONSTRUCTOR, FACTORY 196 197 198 /** 199 * Create an HTTPS client URL. Traffic will be tunneled through any 200 * intermediate nodes rather than proxied, so that confidentiality 201 * of data exchanged can be preserved. However, note that all the 202 * anonymous SSL flavors are subject to "person-in-the-middle" 203 * attacks against confidentiality. If you enable use of those 204 * flavors, you may be giving up the protection you get through 205 * SSL tunneling. 206 * 207 * Use New to get new HttpsClient. This constructor is meant to be 208 * used only by New method. New properly checks for URL spoofing. 209 * 210 * @param URL https URL with which a connection must be established 211 */ 212 private HttpsClient(SSLSocketFactory sf, URL url) 213 throws IOException 214 { 215 // HttpClient-level proxying is always disabled, 216 // because we override doConnect to do tunneling instead. 217 this(sf, url, (String)null, -1); 218 } 219 220 /** 221 * Create an HTTPS client URL. Traffic will be tunneled through 222 * the specified proxy server. 223 */ 224 HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort) 225 throws IOException { 226 this(sf, url, proxyHost, proxyPort, -1); 227 } 228 229 /** 230 * Create an HTTPS client URL. Traffic will be tunneled through 231 * the specified proxy server, with a connect timeout 232 */ 233 HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort, 234 int connectTimeout) 235 throws IOException { 236 this(sf, url, 237 (proxyHost == null? null: 238 HttpClient.newHttpProxy(proxyHost, proxyPort, "https")), 239 connectTimeout); 240 } 241 242 /** 243 * Same as previous constructor except using a Proxy 244 */ 245 HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy, 246 int connectTimeout) 247 throws IOException { 248 PlatformLogger logger = HttpURLConnection.getHttpLogger(); 249 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 250 logger.finest("Creating new HttpsClient with url:" + url + " and proxy:" + proxy + 251 " with connect timeout:" + connectTimeout); 252 } 253 this.proxy = proxy; 254 setSSLSocketFactory(sf); 255 this.proxyDisabled = true; 256 257 this.host = url.getHost(); 258 this.url = url; 259 port = url.getPort(); 260 if (port == -1) { 261 port = getDefaultPort(); 262 } 263 setConnectTimeout(connectTimeout); 264 openServer(); 265 } 266 267 268 // This code largely ripped off from HttpClient.New, and 269 // it uses the same keepalive cache. 270 271 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, 272 HttpURLConnection httpuc) 273 throws IOException { 274 return HttpsClient.New(sf, url, hv, true, httpuc); 275 } 276 277 /** See HttpClient for the model for this method. */ 278 static HttpClient New(SSLSocketFactory sf, URL url, 279 HostnameVerifier hv, boolean useCache, 280 HttpURLConnection httpuc) throws IOException { 281 return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc); 282 } 283 284 /** 285 * Get a HTTPS client to the URL. Traffic will be tunneled through 286 * the specified proxy server. 287 */ 288 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, 289 String proxyHost, int proxyPort, 290 HttpURLConnection httpuc) throws IOException { 291 return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc); 292 } 293 294 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, 295 String proxyHost, int proxyPort, boolean useCache, 296 HttpURLConnection httpuc) 297 throws IOException { 298 return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1, 299 httpuc); 300 } 301 302 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, 303 String proxyHost, int proxyPort, boolean useCache, 304 int connectTimeout, HttpURLConnection httpuc) 305 throws IOException { 306 307 return HttpsClient.New(sf, url, hv, 308 (proxyHost == null? null : 309 HttpClient.newHttpProxy(proxyHost, proxyPort, "https")), 310 useCache, connectTimeout, httpuc); 311 } 312 313 static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, 314 Proxy p, boolean useCache, 315 int connectTimeout, HttpURLConnection httpuc) 316 throws IOException 317 { 318 if (p == null) { 319 p = Proxy.NO_PROXY; 320 } 321 PlatformLogger logger = HttpURLConnection.getHttpLogger(); 322 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 323 logger.finest("Looking for HttpClient for URL " + url + 324 " and proxy value of " + p); 325 } 326 HttpsClient ret = null; 327 if (useCache) { 328 /* see if one's already around */ 329 ret = (HttpsClient) kac.get(url, sf); 330 if (ret != null && httpuc != null && 331 httpuc.streaming() && 332 httpuc.getRequestMethod() == "POST") { 333 if (!ret.available()) 334 ret = null; 335 } 336 337 if (ret != null) { 338 if ((ret.proxy != null && ret.proxy.equals(p)) || 339 (ret.proxy == null && p == Proxy.NO_PROXY)) { 340 synchronized (ret) { 341 ret.cachedHttpClient = true; 342 assert ret.inCache; 343 ret.inCache = false; 344 if (httpuc != null && ret.needsTunneling()) 345 httpuc.setTunnelState(TUNNELING); 346 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 347 logger.finest("KeepAlive stream retrieved from the cache, " + ret); 348 } 349 } 350 } else { 351 // We cannot return this connection to the cache as it's 352 // KeepAliveTimeout will get reset. We simply close the connection. 353 // This should be fine as it is very rare that a connection 354 // to the same host will not use the same proxy. 355 synchronized(ret) { 356 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 357 logger.finest("Not returning this connection to cache: " + ret); 358 } 359 ret.inCache = false; 360 ret.closeServer(); 361 } 362 ret = null; 363 } 364 } 365 } 366 if (ret == null) { 367 ret = new HttpsClient(sf, url, p, connectTimeout); 368 } else { 369 SecurityManager security = System.getSecurityManager(); 370 if (security != null) { 371 if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) { 372 security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort()); 373 } else { 374 security.checkConnect(url.getHost(), url.getPort()); 375 } 376 } 377 ret.url = url; 378 } 379 ret.setHostnameVerifier(hv); 380 381 return ret; 382 } 383 384 // METHODS 385 void setHostnameVerifier(HostnameVerifier hv) { 386 this.hv = hv; 387 } 388 389 void setSSLSocketFactory(SSLSocketFactory sf) { 390 sslSocketFactory = sf; 391 } 392 393 SSLSocketFactory getSSLSocketFactory() { 394 return sslSocketFactory; 395 } 396 397 /** 398 * The following method, createSocket, is defined in NetworkClient 399 * and overridden here so that the socket facroty is used to create 400 * new sockets. 401 */ 402 @Override 403 protected Socket createSocket() throws IOException { 404 try { 405 return sslSocketFactory.createSocket(); 406 } catch (SocketException se) { 407 // 408 // bug 6771432 409 // javax.net.SocketFactory throws a SocketException with an 410 // UnsupportedOperationException as its cause to indicate that 411 // unconnected sockets have not been implemented. 412 // 413 Throwable t = se.getCause(); 414 if (t != null && t instanceof UnsupportedOperationException) { 415 return super.createSocket(); 416 } else { 417 throw se; 418 } 419 } 420 } 421 422 423 @Override 424 public boolean needsTunneling() { 425 return (proxy != null && proxy.type() != Proxy.Type.DIRECT 426 && proxy.type() != Proxy.Type.SOCKS); 427 } 428 429 @Override 430 public void afterConnect() throws IOException, UnknownHostException { 431 if (!isCachedConnection()) { 432 SSLSocket s = null; 433 SSLSocketFactory factory = sslSocketFactory; 434 try { 435 if (!(serverSocket instanceof SSLSocket)) { 436 s = (SSLSocket)factory.createSocket(serverSocket, 437 host, port, true); 438 } else { 439 s = (SSLSocket)serverSocket; 440 if (s instanceof SSLSocketImpl) { 441 ((SSLSocketImpl)s).setHost(host); 442 } 443 } 444 } catch (IOException ex) { 445 // If we fail to connect through the tunnel, try it 446 // locally, as a last resort. If this doesn't work, 447 // throw the original exception. 448 try { 449 s = (SSLSocket)factory.createSocket(host, port); 450 } catch (IOException ignored) { 451 throw ex; 452 } 453 } 454 455 // 456 // Force handshaking, so that we get any authentication. 457 // Register a handshake callback so our session state tracks any 458 // later session renegotiations. 459 // 460 String [] protocols = getProtocols(); 461 String [] ciphers = getCipherSuites(); 462 if (protocols != null) { 463 s.setEnabledProtocols(protocols); 464 } 465 if (ciphers != null) { 466 s.setEnabledCipherSuites(ciphers); 467 } 468 s.addHandshakeCompletedListener(this); 469 470 // We have two hostname verification approaches. One is in 471 // SSL/TLS socket layer, where the algorithm is configured with 472 // SSLParameters.setEndpointIdentificationAlgorithm(), and the 473 // hostname verification is done by X509ExtendedTrustManager when 474 // the algorithm is "HTTPS". The other one is in HTTPS layer, 475 // where the algorithm is customized by 476 // HttpsURLConnection.setHostnameVerifier(), and the hostname 477 // verification is done by HostnameVerifier when the default 478 // rules for hostname verification fail. 479 // 480 // The relationship between two hostname verification approaches 481 // likes the following: 482 // 483 // | EIA algorithm 484 // +---------------------------------------------- 485 // | null | HTTPS | LDAP/other | 486 // ------------------------------------------------------------- 487 // | |1 |2 |3 | 488 // HNV | default | Set HTTPS EIA | use EIA | HTTPS | 489 // |-------------------------------------------------------- 490 // | non - |4 |5 |6 | 491 // | default | HTTPS/HNV | use EIA | HTTPS/HNV | 492 // ------------------------------------------------------------- 493 // 494 // Abbreviation: 495 // EIA: the endpoint identification algorithm in SSL/TLS 496 // socket layer 497 // HNV: the hostname verification object in HTTPS layer 498 // Notes: 499 // case 1. default HNV and EIA is null 500 // Set EIA as HTTPS, hostname check done in SSL/TLS 501 // layer. 502 // case 2. default HNV and EIA is HTTPS 503 // Use existing EIA, hostname check done in SSL/TLS 504 // layer. 505 // case 3. default HNV and EIA is other than HTTPS 506 // Use existing EIA, EIA check done in SSL/TLS 507 // layer, then do HTTPS check in HTTPS layer. 508 // case 4. non-default HNV and EIA is null 509 // No EIA, no EIA check done in SSL/TLS layer, then do 510 // HTTPS check in HTTPS layer using HNV as override. 511 // case 5. non-default HNV and EIA is HTTPS 512 // Use existing EIA, hostname check done in SSL/TLS 513 // layer. No HNV override possible. We will review this 514 // decision and may update the architecture for JDK 7. 515 // case 6. non-default HNV and EIA is other than HTTPS 516 // Use existing EIA, EIA check done in SSL/TLS layer, 517 // then do HTTPS check in HTTPS layer as override. 518 boolean needToCheckSpoofing = true; 519 String identification = 520 s.getSSLParameters().getEndpointIdentificationAlgorithm(); 521 if (identification != null && identification.length() != 0) { 522 if (identification.equalsIgnoreCase("HTTPS")) { 523 // Do not check server identity again out of SSLSocket, 524 // the endpoint will be identified during TLS handshaking 525 // in SSLSocket. 526 needToCheckSpoofing = false; 527 } // else, we don't understand the identification algorithm, 528 // need to check URL spoofing here. 529 } else { 530 boolean isDefaultHostnameVerifier = false; 531 532 // We prefer to let the SSLSocket do the spoof checks, but if 533 // the application has specified a HostnameVerifier (HNV), 534 // we will always use that. 535 if (hv != null) { 536 String canonicalName = hv.getClass().getCanonicalName(); 537 if (canonicalName != null && 538 canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) { 539 isDefaultHostnameVerifier = true; 540 } 541 } else { 542 // Unlikely to happen! As the behavior is the same as the 543 // default hostname verifier, so we prefer to let the 544 // SSLSocket do the spoof checks. 545 isDefaultHostnameVerifier = true; 546 } 547 548 if (isDefaultHostnameVerifier) { 549 // If the HNV is the default from HttpsURLConnection, we 550 // will do the spoof checks in SSLSocket. 551 SSLParameters paramaters = s.getSSLParameters(); 552 paramaters.setEndpointIdentificationAlgorithm("HTTPS"); 553 s.setSSLParameters(paramaters); 554 555 needToCheckSpoofing = false; 556 } 557 } 558 559 s.startHandshake(); 560 session = s.getSession(); 561 // change the serverSocket and serverOutput 562 serverSocket = s; 563 try { 564 serverOutput = new PrintStream( 565 new BufferedOutputStream(serverSocket.getOutputStream()), 566 false, encoding); 567 } catch (UnsupportedEncodingException e) { 568 throw new InternalError(encoding+" encoding not found"); 569 } 570 571 // check URL spoofing if it has not been checked under handshaking 572 if (needToCheckSpoofing) { 573 checkURLSpoofing(hv); 574 } 575 } else { 576 // if we are reusing a cached https session, 577 // we don't need to do handshaking etc. But we do need to 578 // set the ssl session 579 session = ((SSLSocket)serverSocket).getSession(); 580 } 581 } 582 583 // Server identity checking is done according to RFC 2818: HTTP over TLS 584 // Section 3.1 Server Identity 585 private void checkURLSpoofing(HostnameVerifier hostnameVerifier) 586 throws IOException { 587 // 588 // Get authenticated server name, if any 589 // 590 String host = url.getHost(); 591 592 // if IPv6 strip off the "[]" 593 if (host != null && host.startsWith("[") && host.endsWith("]")) { 594 host = host.substring(1, host.length()-1); 595 } 596 597 Certificate[] peerCerts = null; 598 String cipher = session.getCipherSuite(); 599 try { 600 HostnameChecker checker = HostnameChecker.getInstance( 601 HostnameChecker.TYPE_TLS); 602 603 // Use ciphersuite to determine whether Kerberos is present. 604 if (cipher.startsWith("TLS_KRB5")) { 605 if (!HostnameChecker.match(host, getPeerPrincipal())) { 606 throw new SSLPeerUnverifiedException("Hostname checker" + 607 " failed for Kerberos"); 608 } 609 } else { // X.509 610 611 // get the subject's certificate 612 peerCerts = session.getPeerCertificates(); 613 614 X509Certificate peerCert; 615 if (peerCerts[0] instanceof 616 java.security.cert.X509Certificate) { 617 peerCert = (java.security.cert.X509Certificate)peerCerts[0]; 618 } else { 619 throw new SSLPeerUnverifiedException(""); 620 } 621 checker.match(host, peerCert); 622 } 623 624 // if it doesn't throw an exception, we passed. Return. 625 return; 626 627 } catch (SSLPeerUnverifiedException e) { 628 629 // 630 // client explicitly changed default policy and enabled 631 // anonymous ciphers; we can't check the standard policy 632 // 633 // ignore 634 } catch (java.security.cert.CertificateException cpe) { 635 // ignore 636 } 637 638 if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) { 639 return; 640 } else if ((hostnameVerifier != null) && 641 (hostnameVerifier.verify(host, session))) { 642 return; 643 } 644 645 serverSocket.close(); 646 session.invalidate(); 647 648 throw new IOException("HTTPS hostname wrong: should be <" 649 + url.getHost() + ">"); 650 } 651 652 @Override 653 protected void putInKeepAliveCache() { 654 if (inCache) { 655 assert false : "Duplicate put to keep alive cache"; 656 return; 657 } 658 inCache = true; 659 kac.put(url, sslSocketFactory, this); 660 } 661 662 /* 663 * Close an idle connection to this URL (if it exists in the cache). 664 */ 665 @Override 666 public void closeIdleConnection() { 667 HttpClient http = kac.get(url, sslSocketFactory); 668 if (http != null) { 669 http.closeServer(); 670 } 671 } 672 673 /** 674 * Returns the cipher suite in use on this connection. 675 */ 676 String getCipherSuite() { 677 return session.getCipherSuite(); 678 } 679 680 /** 681 * Returns the certificate chain the client sent to the 682 * server, or null if the client did not authenticate. 683 */ 684 public java.security.cert.Certificate [] getLocalCertificates() { 685 return session.getLocalCertificates(); 686 } 687 688 /** 689 * Returns the certificate chain with which the server 690 * authenticated itself, or throw a SSLPeerUnverifiedException 691 * if the server did not authenticate. 692 */ 693 java.security.cert.Certificate [] getServerCertificates() 694 throws SSLPeerUnverifiedException 695 { 696 return session.getPeerCertificates(); 697 } 698 699 /** 700 * Returns the principal with which the server authenticated 701 * itself, or throw a SSLPeerUnverifiedException if the 702 * server did not authenticate. 703 */ 704 Principal getPeerPrincipal() 705 throws SSLPeerUnverifiedException 706 { 707 Principal principal; 708 try { 709 principal = session.getPeerPrincipal(); 710 } catch (AbstractMethodError e) { 711 // if the provider does not support it, fallback to peer certs. 712 // return the X500Principal of the end-entity cert. 713 java.security.cert.Certificate[] certs = 714 session.getPeerCertificates(); 715 principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); 716 } 717 return principal; 718 } 719 720 /** 721 * Returns the principal the client sent to the 722 * server, or null if the client did not authenticate. 723 */ 724 Principal getLocalPrincipal() 725 { 726 Principal principal; 727 try { 728 principal = session.getLocalPrincipal(); 729 } catch (AbstractMethodError e) { 730 principal = null; 731 // if the provider does not support it, fallback to local certs. 732 // return the X500Principal of the end-entity cert. 733 java.security.cert.Certificate[] certs = 734 session.getLocalCertificates(); 735 if (certs != null) { 736 principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); 737 } 738 } 739 return principal; 740 } 741 742 /** 743 * This method implements the SSL HandshakeCompleted callback, 744 * remembering the resulting session so that it may be queried 745 * for the current cipher suite and peer certificates. Servers 746 * sometimes re-initiate handshaking, so the session in use on 747 * a given connection may change. When sessions change, so may 748 * peer identities and cipher suites. 749 */ 750 public void handshakeCompleted(HandshakeCompletedEvent event) 751 { 752 session = event.getSession(); 753 } 754 755 /** 756 * @return the proxy host being used for this client, or null 757 * if we're not going through a proxy 758 */ 759 @Override 760 public String getProxyHostUsed() { 761 if (!needsTunneling()) { 762 return null; 763 } else { 764 return super.getProxyHostUsed(); 765 } 766 } 767 768 /** 769 * @return the proxy port being used for this client. Meaningless 770 * if getProxyHostUsed() gives null. 771 */ 772 @Override 773 public int getProxyPortUsed() { 774 return (proxy == null || proxy.type() == Proxy.Type.DIRECT || 775 proxy.type() == Proxy.Type.SOCKS)? -1: 776 ((InetSocketAddress)proxy.address()).getPort(); 777 } 778 }