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