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.Level.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 }