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