1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import static org.conscrypt.Preconditions.checkArgument; 20 import static org.conscrypt.Preconditions.checkNotNull; 21 22 import java.io.FileDescriptor; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.OutputStream; 26 import java.net.InetAddress; 27 import java.net.InetSocketAddress; 28 import java.net.Socket; 29 import java.net.SocketAddress; 30 import java.net.SocketException; 31 import java.nio.channels.SocketChannel; 32 import java.security.PrivateKey; 33 import java.util.ArrayList; 34 import java.util.List; 35 import javax.net.ssl.HandshakeCompletedEvent; 36 import javax.net.ssl.HandshakeCompletedListener; 37 import javax.net.ssl.SSLException; 38 import javax.net.ssl.SSLSession; 39 import javax.net.ssl.SSLSocket; 40 41 /** 42 * Abstract base class for all Conscrypt {@link SSLSocket} classes. 43 */ 44 abstract class AbstractConscryptSocket extends SSLSocket { 45 final Socket socket; 46 private final boolean autoClose; 47 48 /** 49 * The peer's DNS hostname if it was supplied during creation. Note that 50 * this may be a raw IP address, so it should be checked before use with 51 * extensions that don't use it like Server Name Indication (SNI). 52 */ 53 private String peerHostname; 54 55 /** 56 * The peer's port if it was supplied during creation. Should only be set if 57 * {@link #peerHostname} is also set. 58 */ 59 private final int peerPort; 60 61 private final PeerInfoProvider peerInfoProvider = new PeerInfoProvider() { 62 @Override 63 String getHostname() { 64 return AbstractConscryptSocket.this.getHostname(); 65 } 66 67 @Override 68 String getHostnameOrIP() { 69 return AbstractConscryptSocket.this.getHostnameOrIP(); 70 } 71 72 @Override 73 int getPort() { 74 return AbstractConscryptSocket.this.getPort(); 75 } 76 }; 77 78 private final List<HandshakeCompletedListener> listeners = 79 new ArrayList<HandshakeCompletedListener>(2); 80 81 /** 82 * Local cache of timeout to avoid getsockopt on every read and 83 * write for non-wrapped sockets. Note that this is not used when delegating 84 * to another socket. 85 */ 86 private int readTimeoutMilliseconds; 87 AbstractConscryptSocket()88 AbstractConscryptSocket() throws IOException { 89 this.socket = this; 90 this.peerHostname = null; 91 this.peerPort = -1; 92 this.autoClose = false; 93 } 94 AbstractConscryptSocket(String hostname, int port)95 AbstractConscryptSocket(String hostname, int port) throws IOException { 96 super(hostname, port); 97 this.socket = this; 98 this.peerHostname = hostname; 99 this.peerPort = port; 100 this.autoClose = false; 101 } 102 AbstractConscryptSocket(InetAddress address, int port)103 AbstractConscryptSocket(InetAddress address, int port) throws IOException { 104 super(address, port); 105 this.socket = this; 106 this.peerHostname = null; 107 this.peerPort = -1; 108 this.autoClose = false; 109 } 110 AbstractConscryptSocket(String hostname, int port, InetAddress clientAddress, int clientPort)111 AbstractConscryptSocket(String hostname, int port, InetAddress clientAddress, int clientPort) 112 throws IOException { 113 super(hostname, port, clientAddress, clientPort); 114 this.socket = this; 115 this.peerHostname = hostname; 116 this.peerPort = port; 117 this.autoClose = false; 118 } 119 AbstractConscryptSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort)120 AbstractConscryptSocket(InetAddress address, int port, InetAddress clientAddress, 121 int clientPort) throws IOException { 122 super(address, port, clientAddress, clientPort); 123 this.socket = this; 124 this.peerHostname = null; 125 this.peerPort = -1; 126 this.autoClose = false; 127 } 128 AbstractConscryptSocket(Socket socket, String hostname, int port, boolean autoClose)129 AbstractConscryptSocket(Socket socket, String hostname, int port, boolean autoClose) 130 throws IOException { 131 this.socket = checkNotNull(socket, "socket"); 132 this.peerHostname = hostname; 133 this.peerPort = port; 134 this.autoClose = autoClose; 135 } 136 137 @Override connect(SocketAddress endpoint)138 public final void connect(SocketAddress endpoint) throws IOException { 139 connect(endpoint, 0); 140 } 141 142 /** 143 * Try to extract the peer's hostname if it's available from the endpoint address. 144 */ 145 @Override connect(SocketAddress endpoint, int timeout)146 public final void connect(SocketAddress endpoint, int timeout) throws IOException { 147 if (peerHostname == null && endpoint instanceof InetSocketAddress) { 148 peerHostname = 149 Platform.getHostStringFromInetSocketAddress((InetSocketAddress) endpoint); 150 } 151 152 if (isDelegating()) { 153 socket.connect(endpoint, timeout); 154 } else { 155 super.connect(endpoint, timeout); 156 } 157 } 158 159 @Override bind(SocketAddress bindpoint)160 public void bind(SocketAddress bindpoint) throws IOException { 161 if (isDelegating()) { 162 socket.bind(bindpoint); 163 } else { 164 super.bind(bindpoint); 165 } 166 } 167 168 @Override 169 @SuppressWarnings("UnsynchronizedOverridesSynchronized") close()170 public void close() throws IOException { 171 if (isDelegating()) { 172 if (autoClose && !socket.isClosed()) { 173 socket.close(); 174 } 175 } else { 176 if (!super.isClosed()) { 177 super.close(); 178 } 179 } 180 } 181 182 @Override getInetAddress()183 public InetAddress getInetAddress() { 184 if (isDelegating()) { 185 return socket.getInetAddress(); 186 } 187 return super.getInetAddress(); 188 } 189 190 @Override getLocalAddress()191 public InetAddress getLocalAddress() { 192 if (isDelegating()) { 193 return socket.getLocalAddress(); 194 } 195 return super.getLocalAddress(); 196 } 197 198 @Override getLocalPort()199 public int getLocalPort() { 200 if (isDelegating()) { 201 return socket.getLocalPort(); 202 } 203 return super.getLocalPort(); 204 } 205 206 @Override getRemoteSocketAddress()207 public SocketAddress getRemoteSocketAddress() { 208 if (isDelegating()) { 209 return socket.getRemoteSocketAddress(); 210 } 211 return super.getRemoteSocketAddress(); 212 } 213 214 @Override getLocalSocketAddress()215 public SocketAddress getLocalSocketAddress() { 216 if (isDelegating()) { 217 return socket.getLocalSocketAddress(); 218 } 219 return super.getLocalSocketAddress(); 220 } 221 222 @Override getPort()223 public final int getPort() { 224 if (isDelegating()) { 225 return socket.getPort(); 226 } 227 228 if (peerPort != -1) { 229 // Return the port that has been explicitly set in the constructor. 230 return peerPort; 231 } 232 return super.getPort(); 233 } 234 235 @Override addHandshakeCompletedListener(HandshakeCompletedListener listener)236 public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { 237 checkArgument(listener != null, "Provided listener is null"); 238 listeners.add(listener); 239 } 240 241 @Override removeHandshakeCompletedListener(HandshakeCompletedListener listener)242 public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { 243 checkArgument(listener != null, "Provided listener is null"); 244 if (!listeners.remove(listener)) { 245 throw new IllegalArgumentException("Provided listener is not registered"); 246 } 247 } 248 249 /* @Override */ getFileDescriptor$()250 public FileDescriptor getFileDescriptor$() { 251 if (isDelegating()) { 252 return Platform.getFileDescriptor(socket); 253 } 254 return Platform.getFileDescriptorFromSSLSocket(this); 255 } 256 257 @Override 258 @SuppressWarnings("UnsynchronizedOverridesSynchronized") setSoTimeout(int readTimeoutMilliseconds)259 public final void setSoTimeout(int readTimeoutMilliseconds) throws SocketException { 260 if (isDelegating()) { 261 socket.setSoTimeout(readTimeoutMilliseconds); 262 } else { 263 super.setSoTimeout(readTimeoutMilliseconds); 264 this.readTimeoutMilliseconds = readTimeoutMilliseconds; 265 } 266 } 267 268 @Override 269 @SuppressWarnings("UnsynchronizedOverridesSynchronized") getSoTimeout()270 public final int getSoTimeout() throws SocketException { 271 if (isDelegating()) { 272 return socket.getSoTimeout(); 273 } 274 return readTimeoutMilliseconds; 275 } 276 277 @Override sendUrgentData(int data)278 public final void sendUrgentData(int data) throws IOException { 279 throw new SocketException("Method sendUrgentData() is not supported."); 280 } 281 282 @Override setOOBInline(boolean on)283 public final void setOOBInline(boolean on) throws SocketException { 284 throw new SocketException("Method setOOBInline() is not supported."); 285 } 286 287 @Override getOOBInline()288 public boolean getOOBInline() throws SocketException { 289 return false; 290 } 291 292 @Override getChannel()293 public SocketChannel getChannel() { 294 // TODO(nmittler): Support channels? 295 return null; 296 } 297 298 @Override getInputStream()299 public InputStream getInputStream() throws IOException { 300 if (isDelegating()) { 301 return socket.getInputStream(); 302 } 303 return super.getInputStream(); 304 } 305 306 @Override getOutputStream()307 public OutputStream getOutputStream() throws IOException { 308 if (isDelegating()) { 309 return socket.getOutputStream(); 310 } 311 return super.getOutputStream(); 312 } 313 314 @Override setTcpNoDelay(boolean on)315 public void setTcpNoDelay(boolean on) throws SocketException { 316 if (isDelegating()) { 317 socket.setTcpNoDelay(on); 318 } else { 319 super.setTcpNoDelay(on); 320 } 321 } 322 323 @Override getTcpNoDelay()324 public boolean getTcpNoDelay() throws SocketException { 325 if (isDelegating()) { 326 return socket.getTcpNoDelay(); 327 } 328 return super.getTcpNoDelay(); 329 } 330 331 @Override setSoLinger(boolean on, int linger)332 public void setSoLinger(boolean on, int linger) throws SocketException { 333 if (isDelegating()) { 334 socket.setSoLinger(on, linger); 335 } else { 336 super.setSoLinger(on, linger); 337 } 338 } 339 340 @Override getSoLinger()341 public int getSoLinger() throws SocketException { 342 if (isDelegating()) { 343 return socket.getSoLinger(); 344 } 345 return super.getSoLinger(); 346 } 347 348 @Override 349 @SuppressWarnings("UnsynchronizedOverridesSynchronized") setSendBufferSize(int size)350 public void setSendBufferSize(int size) throws SocketException { 351 if (isDelegating()) { 352 socket.setSendBufferSize(size); 353 } else { 354 super.setSendBufferSize(size); 355 } 356 } 357 358 @Override 359 @SuppressWarnings("UnsynchronizedOverridesSynchronized") getSendBufferSize()360 public int getSendBufferSize() throws SocketException { 361 if (isDelegating()) { 362 return socket.getSendBufferSize(); 363 } 364 return super.getSendBufferSize(); 365 } 366 367 @Override 368 @SuppressWarnings("UnsynchronizedOverridesSynchronized") setReceiveBufferSize(int size)369 public void setReceiveBufferSize(int size) throws SocketException { 370 if (isDelegating()) { 371 socket.setReceiveBufferSize(size); 372 } else { 373 super.setReceiveBufferSize(size); 374 } 375 } 376 377 @Override 378 @SuppressWarnings("UnsynchronizedOverridesSynchronized") getReceiveBufferSize()379 public int getReceiveBufferSize() throws SocketException { 380 if (isDelegating()) { 381 return socket.getReceiveBufferSize(); 382 } 383 return super.getReceiveBufferSize(); 384 } 385 386 @Override setKeepAlive(boolean on)387 public void setKeepAlive(boolean on) throws SocketException { 388 if (isDelegating()) { 389 socket.setKeepAlive(on); 390 } else { 391 super.setKeepAlive(on); 392 } 393 } 394 395 @Override getKeepAlive()396 public boolean getKeepAlive() throws SocketException { 397 if (isDelegating()) { 398 return socket.getKeepAlive(); 399 } 400 return super.getKeepAlive(); 401 } 402 403 @Override setTrafficClass(int tc)404 public void setTrafficClass(int tc) throws SocketException { 405 if (isDelegating()) { 406 socket.setTrafficClass(tc); 407 } else { 408 super.setTrafficClass(tc); 409 } 410 } 411 412 @Override getTrafficClass()413 public int getTrafficClass() throws SocketException { 414 if (isDelegating()) { 415 return socket.getTrafficClass(); 416 } 417 return super.getTrafficClass(); 418 } 419 420 @Override setReuseAddress(boolean on)421 public void setReuseAddress(boolean on) throws SocketException { 422 if (isDelegating()) { 423 socket.setReuseAddress(on); 424 } else { 425 super.setReuseAddress(on); 426 } 427 } 428 429 @Override getReuseAddress()430 public boolean getReuseAddress() throws SocketException { 431 if (isDelegating()) { 432 return socket.getReuseAddress(); 433 } 434 return super.getReuseAddress(); 435 } 436 437 @Override shutdownInput()438 public void shutdownInput() throws IOException { 439 if (isDelegating()) { 440 socket.shutdownInput(); 441 } else { 442 super.shutdownInput(); 443 } 444 } 445 446 @Override shutdownOutput()447 public void shutdownOutput() throws IOException { 448 if (isDelegating()) { 449 socket.shutdownOutput(); 450 } else { 451 super.shutdownOutput(); 452 } 453 } 454 455 @Override isConnected()456 public boolean isConnected() { 457 if (isDelegating()) { 458 return socket.isConnected(); 459 } 460 return super.isConnected(); 461 } 462 463 @Override isBound()464 public boolean isBound() { 465 if (isDelegating()) { 466 return socket.isBound(); 467 } 468 return super.isBound(); 469 } 470 471 @Override isClosed()472 public boolean isClosed() { 473 if (isDelegating()) { 474 return socket.isClosed(); 475 } 476 return super.isClosed(); 477 } 478 479 @Override isInputShutdown()480 public boolean isInputShutdown() { 481 if (isDelegating()) { 482 return socket.isInputShutdown(); 483 } 484 return super.isInputShutdown(); 485 } 486 487 @Override isOutputShutdown()488 public boolean isOutputShutdown() { 489 if (isDelegating()) { 490 return socket.isOutputShutdown(); 491 } 492 return super.isOutputShutdown(); 493 } 494 495 @Override setPerformancePreferences(int connectionTime, int latency, int bandwidth)496 public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { 497 if (isDelegating()) { 498 socket.setPerformancePreferences(connectionTime, latency, bandwidth); 499 } else { 500 super.setPerformancePreferences(connectionTime, latency, bandwidth); 501 } 502 } 503 504 @Override toString()505 public String toString() { 506 StringBuilder builder = new StringBuilder("SSL socket over "); 507 if (isDelegating()) { 508 builder.append(socket.toString()); 509 } else { 510 builder.append(super.toString()); 511 } 512 return builder.toString(); 513 } 514 515 /** 516 * Returns the hostname that was supplied during socket creation. No DNS resolution is 517 * attempted before returning the hostname. 518 */ getHostname()519 String getHostname() { 520 return peerHostname; 521 } 522 523 /** 524 * This method enables Server Name Indication 525 * 526 * @param hostname the desired SNI hostname, or null to disable 527 */ setHostname(String hostname)528 void setHostname(String hostname) { 529 peerHostname = hostname; 530 } 531 532 /** 533 * For the purposes of an SSLSession, we want a way to represent the supplied hostname 534 * or the IP address in a textual representation. We do not want to perform reverse DNS 535 * lookups on this address. 536 */ getHostnameOrIP()537 String getHostnameOrIP() { 538 if (peerHostname != null) { 539 return peerHostname; 540 } 541 542 InetAddress peerAddress = getInetAddress(); 543 if (peerAddress != null) { 544 return Platform.getOriginalHostNameFromInetAddress(peerAddress); 545 } 546 547 return null; 548 } 549 550 /** 551 * Note write timeouts are not part of the javax.net.ssl.SSLSocket API 552 */ setSoWriteTimeout(int writeTimeoutMilliseconds)553 void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException { 554 throw new SocketException("Method setSoWriteTimeout() is not supported."); 555 } 556 557 /** 558 * Note write timeouts are not part of the javax.net.ssl.SSLSocket API 559 */ getSoWriteTimeout()560 int getSoWriteTimeout() throws SocketException { 561 return 0; 562 } 563 564 /** 565 * Set the handshake timeout on this socket. This timeout is specified in 566 * milliseconds and will be used only during the handshake process. 567 */ setHandshakeTimeout(int handshakeTimeoutMilliseconds)568 void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException { 569 throw new SocketException("Method setHandshakeTimeout() is not supported."); 570 } 571 checkOpen()572 final void checkOpen() throws SocketException { 573 if (isClosed()) { 574 throw new SocketException("Socket is closed"); 575 } 576 } 577 peerInfoProvider()578 final PeerInfoProvider peerInfoProvider() { 579 return peerInfoProvider; 580 } 581 582 /** 583 * Called by {@link #notifyHandshakeCompletedListeners()} to get the currently active session. 584 * Unlike {@link #getSession()}, this method must not block. 585 */ getActiveSession()586 abstract SSLSession getActiveSession(); 587 setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector)588 abstract void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector); 589 notifyHandshakeCompletedListeners()590 final void notifyHandshakeCompletedListeners() { 591 List<HandshakeCompletedListener> listenersCopy = new ArrayList<>(listeners); 592 if (!listenersCopy.isEmpty()) { 593 // notify the listeners 594 HandshakeCompletedEvent event = new HandshakeCompletedEvent(this, getActiveSession()); 595 for (HandshakeCompletedListener listener : listenersCopy) { 596 try { 597 listener.handshakeCompleted(event); 598 } catch (RuntimeException e) { 599 // The RI runs the handlers in a separate thread, 600 // which we do not. But we try to preserve their 601 // behavior of logging a problem and not killing 602 // the handshaking thread just because a listener 603 // has a problem. 604 Thread thread = Thread.currentThread(); 605 thread.getUncaughtExceptionHandler().uncaughtException(thread, e); 606 } 607 } 608 } 609 } 610 isDelegating()611 private boolean isDelegating() { 612 // Checking for null to handle the case of calling virtual methods in the super class 613 // constructor. 614 return socket != null && socket != this; 615 } 616 /* @Override */ 617 @SuppressWarnings("MissingOverride") // For compilation with Java 6. getHandshakeSession()618 public abstract SSLSession getHandshakeSession(); 619 620 /** 621 * This method enables session ticket support. 622 * 623 * @param useSessionTickets True to enable session tickets 624 */ setUseSessionTickets(boolean useSessionTickets)625 abstract void setUseSessionTickets(boolean useSessionTickets); 626 627 /** 628 * Enables/disables TLS Channel ID for this server socket. 629 * 630 * <p>This method needs to be invoked before the handshake starts. 631 * 632 * @throws IllegalStateException if this is a client socket or if the handshake has already 633 * started. 634 */ setChannelIdEnabled(boolean enabled)635 abstract void setChannelIdEnabled(boolean enabled); 636 637 /** 638 * Gets the TLS Channel ID for this server socket. Channel ID is only available once the 639 * handshake completes. 640 * 641 * @return channel ID or {@code null} if not available. 642 * 643 * @throws IllegalStateException if this is a client socket or if the handshake has not yet 644 * completed. 645 * @throws SSLException if channel ID is available but could not be obtained. 646 */ getChannelId()647 abstract byte[] getChannelId() throws SSLException; 648 649 /** 650 * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket. 651 * 652 * <p>This method needs to be invoked before the handshake starts. 653 * 654 * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables 655 * TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST 656 * P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1). 657 * 658 * @throws IllegalStateException if this is a server socket or if the handshake has already 659 * started. 660 */ setChannelIdPrivateKey(PrivateKey privateKey)661 abstract void setChannelIdPrivateKey(PrivateKey privateKey); 662 663 /** 664 * Returns null always for backward compatibility. 665 * @deprecated NPN is not supported 666 */ 667 @Deprecated getNpnSelectedProtocol()668 byte[] getNpnSelectedProtocol() { 669 return null; 670 } 671 672 /** 673 * This method does nothing and is kept for backward compatibility. 674 * @deprecated NPN is not supported 675 */ 676 @Deprecated setNpnProtocols(byte[] npnProtocols)677 void setNpnProtocols(byte[] npnProtocols) {} 678 679 /** 680 * Returns the protocol agreed upon by client and server, or {@code null} if 681 * no protocol was agreed upon. 682 * 683 * @deprecated use {@link #getApplicationProtocol()} instead. 684 */ 685 @Deprecated getAlpnSelectedProtocol()686 abstract byte[] getAlpnSelectedProtocol(); 687 688 /** 689 * Sets the list of ALPN protocols. This method internally converts the protocols to their 690 * wire-format form. 691 * 692 * @param alpnProtocols the list of ALPN protocols 693 * @deprecated use {@link #setApplicationProtocols(String[])} instead. 694 */ 695 @Deprecated setAlpnProtocols(String[] alpnProtocols)696 abstract void setAlpnProtocols(String[] alpnProtocols); 697 698 /** 699 * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of 700 * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings). 701 * Requires that all strings be encoded with US-ASCII. 702 * 703 * @param alpnProtocols the encoded form of the ALPN protocol list 704 * @deprecated Use {@link #setApplicationProtocols(String[])} instead. 705 */ 706 @Deprecated setAlpnProtocols(byte[] alpnProtocols)707 abstract void setAlpnProtocols(byte[] alpnProtocols); 708 709 /** 710 * Sets the list of ALPN protocols. 711 * 712 * @param protocols the list of ALPN protocols 713 */ 714 @SuppressWarnings("MissingOverride") // For compiling pre Java 9. setApplicationProtocols(String[] protocols)715 abstract void setApplicationProtocols(String[] protocols); 716 717 /** 718 * Returns the list of supported ALPN protocols. 719 */ 720 @SuppressWarnings("MissingOverride") // For compiling pre Java 9. getApplicationProtocols()721 abstract String[] getApplicationProtocols(); 722 723 @SuppressWarnings("MissingOverride") // For compiling pre Java 9. getApplicationProtocol()724 public abstract String getApplicationProtocol(); 725 726 @SuppressWarnings("MissingOverride") // For compiling pre Java 9. getHandshakeApplicationProtocol()727 public abstract String getHandshakeApplicationProtocol(); 728 729 /** 730 * Sets an application-provided ALPN protocol selector. If provided, this will override 731 * the list of protocols set by {@link #setApplicationProtocols(String[])}. 732 */ setApplicationProtocolSelector(ApplicationProtocolSelector selector)733 abstract void setApplicationProtocolSelector(ApplicationProtocolSelector selector); 734 735 /** 736 * Returns the tls-unique channel binding value for this connection, per RFC 5929. This 737 * will return {@code null} if there is no such value available, such as if the handshake 738 * has not yet completed or this connection is closed. 739 */ getTlsUnique()740 abstract byte[] getTlsUnique(); 741 742 /** 743 * Exports a value derived from the TLS master secret as described in RFC 5705. 744 * 745 * @param label the label to use in calculating the exported value. This must be 746 * an ASCII-only string. 747 * @param context the application-specific context value to use in calculating the 748 * exported value. This may be {@code null} to use no application context, which is 749 * treated differently than an empty byte array. 750 * @param length the number of bytes of keying material to return. 751 * @return a value of the specified length, or {@code null} if the handshake has not yet 752 * completed or the connection has been closed. 753 * @throws SSLException if the value could not be exported. 754 */ exportKeyingMaterial(String label, byte[] context, int length)755 abstract byte[] exportKeyingMaterial(String label, byte[] context, int length) 756 throws SSLException; 757 } 758