OpenSSLSocketImpl.java revision e688a4123f165ed2905878e312b074b8c825d119
1/* 2 * Copyright (C) 2007 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 17package org.apache.harmony.xnet.provider.jsse; 18 19import java.io.IOException; 20import java.io.InputStream; 21import java.io.OutputStream; 22import java.io.OutputStreamWriter; 23import java.net.InetAddress; 24import java.net.InetSocketAddress; 25import java.net.Socket; 26import java.net.SocketException; 27import java.security.cert.Certificate; 28import java.security.cert.CertificateException; 29import java.security.cert.X509Certificate; 30import java.security.interfaces.RSAPublicKey; 31import java.util.ArrayList; 32import java.util.concurrent.atomic.AtomicInteger; 33import java.util.logging.Level; 34import java.util.logging.Logger; 35 36import javax.net.ssl.HandshakeCompletedEvent; 37import javax.net.ssl.HandshakeCompletedListener; 38import javax.net.ssl.SSLException; 39import javax.net.ssl.SSLHandshakeException; 40import javax.net.ssl.SSLPeerUnverifiedException; 41import javax.net.ssl.SSLSession; 42 43import org.apache.harmony.security.provider.cert.X509CertImpl; 44 45/** 46 * Implementation of the class OpenSSLSocketImpl 47 * based on OpenSSL. The JNI native interface for some methods 48 * of this this class are defined in the file: 49 * org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp 50 * 51 * This class only supports SSLv3 and TLSv1. This should be documented elsewhere 52 * later, for example in the package.html or a separate reference document. 53 */ 54public class OpenSSLSocketImpl 55 extends javax.net.ssl.SSLSocket 56 implements NativeCrypto.CertificateChainVerifier, NativeCrypto.HandshakeCompletedCallback { 57 private int sslNativePointer; 58 private InputStream is; 59 private OutputStream os; 60 private final Object handshakeLock = new Object(); 61 private final Object readLock = new Object(); 62 private final Object writeLock = new Object(); 63 private SSLParameters sslParameters; 64 private String[] enabledProtocols; 65 private String[] enabledCipherSuites; 66 private OpenSSLSessionImpl sslSession; 67 private Socket socket; 68 private boolean autoClose; 69 private boolean handshakeStarted = false; 70 71 /** 72 * Not set to true until the update from native that tells us the 73 * full handshake is complete, since SSL_do_handshake can return 74 * before the handshake is completely done due to 75 * handshake_cutthrough support. 76 */ 77 private boolean handshakeCompleted = false; 78 79 private ArrayList<HandshakeCompletedListener> listeners; 80 private int timeout = 0; 81 // BEGIN android-added 82 private int handshakeTimeout = -1; // -1 = same as timeout; 0 = infinite 83 // END android-added 84 private InetSocketAddress address; 85 86 private static final AtomicInteger instanceCount = new AtomicInteger(0); 87 88 public static int getInstanceCount() { 89 return instanceCount.get(); 90 } 91 92 private static void updateInstanceCount(int amount) { 93 instanceCount.addAndGet(amount); 94 } 95 96 /** 97 * Class constructor with 1 parameter 98 * 99 * @param sslParameters Parameters for the SSL 100 * context 101 * @throws IOException if network fails 102 */ 103 protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException { 104 super(); 105 init(sslParameters); 106 } 107 108 /** 109 * Create an OpenSSLSocketImpl from an OpenSSLServerSocketImpl 110 * 111 * @param sslParameters Parameters for the SSL 112 * context 113 * @throws IOException if network fails 114 */ 115 protected OpenSSLSocketImpl(SSLParameters sslParameters, 116 String[] enabledProtocols, 117 String[] enabledCipherSuites) throws IOException { 118 super(); 119 init(sslParameters, enabledProtocols, enabledCipherSuites); 120 } 121 122 /** 123 * Class constructor with 3 parameters 124 * 125 * @throws IOException if network fails 126 * @throws java.net.UnknownHostException host not defined 127 */ 128 protected OpenSSLSocketImpl(String host, int port, 129 SSLParameters sslParameters) 130 throws IOException { 131 super(host, port); 132 init(sslParameters); 133 } 134 135 /** 136 * Class constructor with 3 parameters: 1st is InetAddress 137 * 138 * @throws IOException if network fails 139 * @throws java.net.UnknownHostException host not defined 140 */ 141 protected OpenSSLSocketImpl(InetAddress address, int port, 142 SSLParameters sslParameters) 143 throws IOException { 144 super(address, port); 145 init(sslParameters); 146 } 147 148 149 /** 150 * Class constructor with 5 parameters: 1st is host 151 * 152 * @throws IOException if network fails 153 * @throws java.net.UnknownHostException host not defined 154 */ 155 protected OpenSSLSocketImpl(String host, int port, InetAddress clientAddress, 156 int clientPort, SSLParameters sslParameters) 157 throws IOException { 158 super(host, port, clientAddress, clientPort); 159 init(sslParameters); 160 } 161 162 /** 163 * Class constructor with 5 parameters: 1st is InetAddress 164 * 165 * @throws IOException if network fails 166 * @throws java.net.UnknownHostException host not defined 167 */ 168 protected OpenSSLSocketImpl(InetAddress address, int port, 169 InetAddress clientAddress, int clientPort, SSLParameters sslParameters) 170 throws IOException { 171 super(address, port, clientAddress, clientPort); 172 init(sslParameters); 173 } 174 175 /** 176 * Constructor with 5 parameters: 1st is socket. Enhances an existing socket 177 * with SSL functionality. 178 * 179 * @throws IOException if network fails 180 */ 181 protected OpenSSLSocketImpl(Socket socket, String host, int port, 182 boolean autoClose, SSLParameters sslParameters) throws IOException { 183 super(); 184 this.socket = socket; 185 this.timeout = socket.getSoTimeout(); 186 this.address = new InetSocketAddress(host, port); 187 this.autoClose = autoClose; 188 init(sslParameters); 189 } 190 191 /** 192 * Initialize the SSL socket and set the certificates for the 193 * future handshaking. 194 */ 195 private void init(SSLParameters sslParameters) throws IOException { 196 init(sslParameters, 197 NativeCrypto.getSupportedProtocols(), 198 NativeCrypto.getDefaultCipherSuites()); 199 } 200 201 /** 202 * Initialize the SSL socket and set the certificates for the 203 * future handshaking. 204 */ 205 private void init(SSLParameters sslParameters, 206 String[] enabledProtocols, 207 String[] enabledCipherSuites) throws IOException { 208 this.sslParameters = sslParameters; 209 this.enabledProtocols = enabledProtocols; 210 this.enabledCipherSuites = enabledCipherSuites; 211 updateInstanceCount(1); 212 } 213 214 /** 215 * Gets the suitable session reference from the session cache container. 216 * 217 * @return OpenSSLSessionImpl 218 */ 219 private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) { 220 if (super.getInetAddress() == null || 221 super.getInetAddress().getHostAddress() == null || 222 super.getInetAddress().getHostName() == null) { 223 return null; 224 } 225 return (OpenSSLSessionImpl) sessionContext.getSession( 226 super.getInetAddress().getHostName(), 227 super.getPort()); 228 } 229 230 /** 231 * Ensures that logger is lazily loaded. The outer class seems to load 232 * before logging is ready. 233 */ 234 static class LoggerHolder { 235 static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName()); 236 } 237 238 /** 239 * Starts a TLS/SSL handshake on this connection using some native methods 240 * from the OpenSSL library. It can negotiate new encryption keys, change 241 * cipher suites, or initiate a new session. The certificate chain is 242 * verified if the correspondent property in java.Security is set. All 243 * listeners are notified at the end of the TLS/SSL handshake. 244 * 245 * @throws <code>IOException</code> if network fails 246 */ 247 public void startHandshake() throws IOException { 248 startHandshake(true); 249 } 250 251 /** 252 * Perform the handshake 253 * @param full If true, disable handshake cutthrough for a fully synchronous handshake 254 */ 255 public synchronized void startHandshake(boolean full) throws IOException { 256 synchronized (handshakeLock) { 257 if (!handshakeStarted) { 258 handshakeStarted = true; 259 } else { 260 return; 261 } 262 } 263 264 this.sslNativePointer = NativeCrypto.SSL_new(sslParameters); 265 // TODO move more code out of NativeCrypto.SSL_new 266 NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols); 267 NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites); 268 269 boolean enableSessionCreation = sslParameters.getEnableSessionCreation(); 270 if (!enableSessionCreation) { 271 NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer, 272 enableSessionCreation); 273 } 274 275 boolean client = sslParameters.getUseClientMode(); 276 277 AbstractSessionContext sessionContext; 278 OpenSSLSessionImpl session; 279 if (client) { 280 // look for client session to reuse 281 ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext(); 282 sessionContext = clientSessionContext; 283 session = getCachedClientSession(clientSessionContext); 284 if (session != null) { 285 NativeCrypto.SSL_set_session(sslNativePointer, session.sslSessionNativePointer); 286 } 287 } else { 288 sessionContext = sslParameters.getServerSessionContext(); 289 session = null; 290 } 291 292 // setup peer certificate verification 293 if (client) { 294 // TODO support for anonymous cipher would require us to conditionally use SSL_VERIFY_NONE 295 } else { 296 // needing client auth takes priority... 297 if (sslParameters.getNeedClientAuth()) { 298 NativeCrypto.SSL_set_verify(sslNativePointer, 299 NativeCrypto.SSL_VERIFY_PEER| 300 NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT| 301 NativeCrypto.SSL_VERIFY_CLIENT_ONCE); 302 // ... over just wanting it... 303 } else if (sslParameters.getWantClientAuth()) { 304 NativeCrypto.SSL_set_verify(sslNativePointer, 305 NativeCrypto.SSL_VERIFY_PEER| 306 NativeCrypto.SSL_VERIFY_CLIENT_ONCE); 307 } 308 // ... and it defaults properly so we don't need call SSL_set_verify in the common case. 309 } 310 311 if (client && full) { 312 // we want to do a full synchronous handshake, so turn off cutthrough 313 NativeCrypto.SSL_clear_mode(sslNativePointer, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); 314 } 315 316 // BEGIN android-added 317 // Temporarily use a different timeout for the handshake process 318 int savedTimeout = timeout; 319 if (handshakeTimeout >= 0) { 320 setSoTimeout(handshakeTimeout); 321 } 322 // END android-added 323 324 325 Socket socket = this.socket != null ? this.socket : this; 326 int sslSessionNativePointer; 327 try { 328 sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer, socket, this, this, timeout, client); 329 } catch (CertificateException e) { 330 throw new SSLPeerUnverifiedException(e.getMessage()); 331 } 332 byte[] sessionId = OpenSSLSessionImpl.getId(sslSessionNativePointer); 333 sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId); 334 if (sslSession != null) { 335 session.lastAccessedTime = System.currentTimeMillis(); 336 LoggerHolder.logger.fine("Reused cached session for " 337 + getInetAddress() + "."); 338 OpenSSLSessionImpl.freeImpl(sslSessionNativePointer); 339 } else { 340 if (!enableSessionCreation) { 341 // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled 342 throw new IllegalStateException("SSL Session may not be created"); 343 } 344 byte[][] localCertificatesBytes = NativeCrypto.SSL_get_certificate(sslNativePointer); 345 X509Certificate[] localCertificates; 346 if (localCertificatesBytes == null) { 347 localCertificates = null; 348 } else { 349 localCertificates = new X509Certificate[localCertificatesBytes.length]; 350 for (int i = 0; i < localCertificatesBytes.length; i++) { 351 try { 352 // TODO do not go through PEM decode, DER encode, DER decode 353 localCertificates[i] 354 = new X509CertImpl( 355 javax.security.cert.X509Certificate.getInstance( 356 localCertificatesBytes[i]).getEncoded()); 357 } catch (javax.security.cert.CertificateException e) { 358 throw new IOException("Problem decoding local certificate", e); 359 } 360 } 361 } 362 363 if (address == null) { 364 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates, 365 super.getInetAddress().getHostName(), 366 super.getPort(), sessionContext); 367 } else { 368 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates, 369 address.getHostName(), address.getPort(), 370 sessionContext); 371 } 372 // putSession will be done later in handshakeCompleted() callback 373 if (handshakeCompleted) { 374 sessionContext.putSession(sslSession); 375 } 376 LoggerHolder.logger.fine("Created new session for " 377 + getInetAddress().getHostName() + "."); 378 } 379 380 // BEGIN android-added 381 // Restore the original timeout now that the handshake is complete 382 if (handshakeTimeout >= 0) { 383 setSoTimeout(savedTimeout); 384 } 385 // END android-added 386 387 // notifyHandshakeCompletedListeners will be done later in handshakeCompleted() callback 388 if (handshakeCompleted) { 389 notifyHandshakeCompletedListeners(); 390 } 391 392 } 393 394 /** 395 * Implementation of NativeCrypto.HandshakeCompletedCallback 396 * invoked via JNI from info_callback 397 */ 398 public void handshakeCompleted() { 399 handshakeCompleted = true; 400 401 // If sslSession is null, the handshake was completed during 402 // the call to NativeCrypto.SSL_do_handshake and not during a 403 // later read operation. That means we do not need to fixup 404 // the SSLSession and session cache or notify 405 // HandshakeCompletedListeners, it will be done in 406 // startHandshake. 407 if (sslSession == null) { 408 return; 409 } 410 411 // reset session id from the native pointer and update the 412 // appropriate cache. 413 sslSession.resetId(); 414 AbstractSessionContext sessionContext = 415 (sslParameters.getUseClientMode()) 416 ? sslParameters.getClientSessionContext() 417 : sslParameters.getServerSessionContext(); 418 sessionContext.putSession(sslSession); 419 420 // let listeners know we are finally done 421 notifyHandshakeCompletedListeners(); 422 } 423 424 private void notifyHandshakeCompletedListeners() { 425 if (listeners != null && !listeners.isEmpty()) { 426 // notify the listeners 427 HandshakeCompletedEvent event = 428 new HandshakeCompletedEvent(this, sslSession); 429 for (HandshakeCompletedListener listener : listeners) { 430 try { 431 listener.handshakeCompleted(event); 432 } catch (RuntimeException e) { 433 // The RI runs the handlers in a separate thread, 434 // which we do not. But we try to preserve their 435 // behavior of logging a problem and not killing 436 // the handshaking thread just because a listener 437 // has a problem. 438 Thread thread = Thread.currentThread(); 439 thread.getUncaughtExceptionHandler().uncaughtException(thread, e); 440 } 441 } 442 } 443 } 444 445 /** 446 * Implementation of NativeCrypto.CertificateChainVerifier. 447 * 448 * @param bytes An array of certficates in PEM encode bytes 449 * @param authMethod auth algorithm name 450 * 451 * @throws CertificateException if the certificate is untrusted 452 */ 453 @SuppressWarnings("unused") 454 public void verifyCertificateChain(byte[][] bytes, String authMethod) throws CertificateException { 455 try { 456 if (bytes == null || bytes.length == 0) { 457 throw new SSLException("Peer sent no certificate"); 458 } 459 X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length]; 460 for (int i = 0; i < bytes.length; i++) { 461 peerCertificateChain[i] = 462 new X509CertImpl(javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded()); 463 } 464 boolean client = sslParameters.getUseClientMode(); 465 if (client) { 466 sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, authMethod); 467 } else { 468 sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, authMethod); 469 } 470 471 } catch (CertificateException e) { 472 throw e; 473 } catch (Exception e) { 474 throw new RuntimeException(e); 475 } 476 } 477 478 /** 479 * Returns an input stream for this SSL socket using native calls to the 480 * OpenSSL library. 481 * 482 * @return: an input stream for reading bytes from this socket. 483 * @throws: <code>IOException</code> if an I/O error occurs when creating 484 * the input stream, the socket is closed, the socket is not 485 * connected, or the socket input has been shutdown. 486 */ 487 public InputStream getInputStream() throws IOException { 488 synchronized(this) { 489 if (is == null) { 490 is = new SSLInputStream(); 491 } 492 493 return is; 494 } 495 } 496 497 /** 498 * Returns an output stream for this SSL socket using native calls to the 499 * OpenSSL library. 500 * 501 * @return an output stream for writing bytes to this socket. 502 * @throws <code>IOException</code> if an I/O error occurs when creating 503 * the output stream, or no connection to the socket exists. 504 */ 505 public OutputStream getOutputStream() throws IOException { 506 synchronized(this) { 507 if (os == null) { 508 os = new SSLOutputStream(); 509 } 510 511 return os; 512 } 513 } 514 515 /** 516 * This method is not supported for this SSLSocket implementation 517 * because reading from an SSLSocket may involve writing to the 518 * network. 519 */ 520 public void shutdownInput() throws IOException { 521 throw new UnsupportedOperationException(); 522 } 523 524 /** 525 * This method is not supported for this SSLSocket implementation 526 * because writing to an SSLSocket may involve reading from the 527 * network. 528 */ 529 public void shutdownOutput() throws IOException { 530 throw new UnsupportedOperationException(); 531 } 532 533 /** 534 * Reads with the native SSL_read function from the encrypted data stream 535 * @return -1 if error or the end of the stream is reached. 536 */ 537 private native int nativeread(int sslNativePointer, int timeout) throws IOException; 538 private native int nativeread(int sslNativePointer, byte[] b, int off, int len, int timeout) throws IOException; 539 540 /** 541 * This inner class provides input data stream functionality 542 * for the OpenSSL native implementation. It is used to 543 * read data received via SSL protocol. 544 */ 545 private class SSLInputStream extends InputStream { 546 SSLInputStream() throws IOException { 547 /** 548 /* Note: When startHandshake() throws an exception, no 549 * SSLInputStream object will be created. 550 */ 551 OpenSSLSocketImpl.this.startHandshake(false); 552 } 553 554 /** 555 * Reads one byte. If there is no data in the underlying buffer, 556 * this operation can block until the data will be 557 * available. 558 * @return read value. 559 * @throws <code>IOException</code> 560 */ 561 public int read() throws IOException { 562 synchronized(readLock) { 563 return OpenSSLSocketImpl.this.nativeread(sslNativePointer, timeout); 564 } 565 } 566 567 /** 568 * Method acts as described in spec for superclass. 569 * @see java.io.InputStream#read(byte[],int,int) 570 */ 571 public int read(byte[] b, int off, int len) throws IOException { 572 synchronized(readLock) { 573 return OpenSSLSocketImpl.this.nativeread(sslNativePointer, b, off, len, timeout); 574 } 575 } 576 } 577 578 /** 579 * Writes with the native SSL_write function to the encrypted data stream. 580 */ 581 private native void nativewrite(int sslNativePointer, int b) throws IOException; 582 private native void nativewrite(int sslNativePointer, byte[] b, int off, int len) throws IOException; 583 584 /** 585 * This inner class provides output data stream functionality 586 * for the OpenSSL native implementation. It is used to 587 * write data according to the encryption parameters given in SSL context. 588 */ 589 private class SSLOutputStream extends OutputStream { 590 SSLOutputStream() throws IOException { 591 /** 592 /* Note: When startHandshake() throws an exception, no 593 * SSLOutputStream object will be created. 594 */ 595 OpenSSLSocketImpl.this.startHandshake(false); 596 } 597 598 /** 599 * Method acts as described in spec for superclass. 600 * @see java.io.OutputStream#write(int) 601 */ 602 public void write(int b) throws IOException { 603 synchronized(writeLock) { 604 OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b); 605 } 606 } 607 608 /** 609 * Method acts as described in spec for superclass. 610 * @see java.io.OutputStream#write(byte[],int,int) 611 */ 612 public void write(byte[] b, int start, int len) throws IOException { 613 synchronized(writeLock) { 614 OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b, start, len); 615 } 616 } 617 } 618 619 620 /** 621 * The SSL session used by this connection is returned. The SSL session 622 * determines which cipher suite should be used by all connections within 623 * that session and which identities have the session's client and server. 624 * This method starts the SSL handshake. 625 * @return the SSLSession. 626 * @throws <code>IOException</code> if the handshake fails 627 */ 628 public SSLSession getSession() { 629 try { 630 startHandshake(true); 631 } catch (IOException e) { 632 // return an invalid session with 633 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" 634 return SSLSessionImpl.NULL_SESSION; 635 } 636 return sslSession; 637 } 638 639 /** 640 * Registers a listener to be notified that a SSL handshake 641 * was successfully completed on this connection. 642 * @throws <code>IllegalArgumentException</code> if listener is null. 643 */ 644 public void addHandshakeCompletedListener( 645 HandshakeCompletedListener listener) { 646 if (listener == null) { 647 throw new IllegalArgumentException("Provided listener is null"); 648 } 649 if (listeners == null) { 650 listeners = new ArrayList(); 651 } 652 listeners.add(listener); 653 } 654 655 /** 656 * The method removes a registered listener. 657 * @throws IllegalArgumentException if listener is null or not registered 658 */ 659 public void removeHandshakeCompletedListener( 660 HandshakeCompletedListener listener) { 661 if (listener == null) { 662 throw new IllegalArgumentException("Provided listener is null"); 663 } 664 if (listeners == null) { 665 throw new IllegalArgumentException( 666 "Provided listener is not registered"); 667 } 668 if (!listeners.remove(listener)) { 669 throw new IllegalArgumentException( 670 "Provided listener is not registered"); 671 } 672 } 673 674 /** 675 * Returns true if new SSL sessions may be established by this socket. 676 * 677 * @return true if the session may be created; false if a session already 678 * exists and must be resumed. 679 */ 680 public boolean getEnableSessionCreation() { 681 return sslParameters.getEnableSessionCreation(); 682 } 683 684 /** 685 * Set a flag for the socket to inhibit or to allow the creation of a new 686 * SSL sessions. If the flag is set to false, and there are no actual 687 * sessions to resume, then there will be no successful handshaking. 688 * 689 * @param flag true if session may be created; false 690 * if a session already exists and must be resumed. 691 */ 692 public void setEnableSessionCreation(boolean flag) { 693 sslParameters.setEnableSessionCreation(flag); 694 } 695 696 /** 697 * The names of the cipher suites which could be used by the SSL connection 698 * are returned. 699 * @return an array of cipher suite names 700 */ 701 public String[] getSupportedCipherSuites() { 702 return NativeCrypto.getSupportedCipherSuites(); 703 } 704 705 /** 706 * The names of the cipher suites that are in use in the actual the SSL 707 * connection are returned. 708 * 709 * @return an array of cipher suite names 710 */ 711 public String[] getEnabledCipherSuites() { 712 return enabledCipherSuites.clone(); 713 } 714 715 /** 716 * This method enables the cipher suites listed by 717 * getSupportedCipherSuites(). 718 * 719 * @param suites names of all the cipher suites to 720 * put on use 721 * @throws IllegalArgumentException when one or more of the 722 * ciphers in array suites are not supported, or when the array 723 * is null. 724 */ 725 public void setEnabledCipherSuites(String[] suites) { 726 enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites); 727 } 728 729 /** 730 * The names of the protocols' versions that may be used on this SSL 731 * connection. 732 * @return an array of protocols names 733 */ 734 public String[] getSupportedProtocols() { 735 return NativeCrypto.getSupportedProtocols(); 736 } 737 738 /** 739 * The names of the protocols' versions that are in use on this SSL 740 * connection. 741 * 742 * @return an array of protocols names 743 */ 744 @Override 745 public String[] getEnabledProtocols() { 746 return enabledProtocols.clone(); 747 } 748 749 /** 750 * This method enables the protocols' versions listed by 751 * getSupportedProtocols(). 752 * 753 * @param protocols The names of all the protocols to put on use 754 * 755 * @throws IllegalArgumentException when one or more of the names in the 756 * array are not supported, or when the array is null. 757 */ 758 @Override 759 public synchronized void setEnabledProtocols(String[] protocols) { 760 enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols); 761 } 762 763 /** 764 * This method gives true back if the SSL socket is set to client mode. 765 * 766 * @return true if the socket should do the handshaking as client. 767 */ 768 public boolean getUseClientMode() { 769 return sslParameters.getUseClientMode(); 770 } 771 772 /** 773 * This method set the actual SSL socket to client mode. 774 * 775 * @param mode true if the socket starts in client 776 * mode 777 * @throws IllegalArgumentException if mode changes during 778 * handshake. 779 */ 780 public synchronized void setUseClientMode(boolean mode) { 781 if (handshakeStarted) { 782 throw new IllegalArgumentException( 783 "Could not change the mode after the initial handshake has begun."); 784 } 785 sslParameters.setUseClientMode(mode); 786 } 787 788 /** 789 * Returns true if the SSL socket requests client's authentication. Relevant 790 * only for server sockets! 791 * 792 * @return true if client authentication is desired, false if not. 793 */ 794 public boolean getWantClientAuth() { 795 return sslParameters.getWantClientAuth(); 796 } 797 798 /** 799 * Returns true if the SSL socket needs client's authentication. Relevant 800 * only for server sockets! 801 * 802 * @return true if client authentication is desired, false if not. 803 */ 804 public boolean getNeedClientAuth() { 805 return sslParameters.getNeedClientAuth(); 806 } 807 808 /** 809 * Sets the SSL socket to use client's authentication. Relevant only for 810 * server sockets! 811 * 812 * @param need true if client authentication is 813 * desired, false if not. 814 */ 815 public void setNeedClientAuth(boolean need) { 816 sslParameters.setNeedClientAuth(need); 817 } 818 819 /** 820 * Sets the SSL socket to use client's authentication. Relevant only for 821 * server sockets! Notice that in contrast to setNeedClientAuth(..) this 822 * method will continue the negotiation if the client decide not to send 823 * authentication credentials. 824 * 825 * @param want true if client authentication is 826 * desired, false if not. 827 */ 828 public void setWantClientAuth(boolean want) { 829 sslParameters.setWantClientAuth(want); 830 } 831 832 /** 833 * This method is not supported for SSLSocket implementation. 834 */ 835 public void sendUrgentData(int data) throws IOException { 836 throw new SocketException( 837 "Method sendUrgentData() is not supported."); 838 } 839 840 /** 841 * This method is not supported for SSLSocket implementation. 842 */ 843 public void setOOBInline(boolean on) throws SocketException { 844 throw new SocketException( 845 "Methods sendUrgentData, setOOBInline are not supported."); 846 } 847 848 /** 849 * Set the read timeout on this socket. The SO_TIMEOUT option, is specified 850 * in milliseconds. The read operation will block indefinitely for a zero 851 * value. 852 * 853 * @param timeout the read timeout value 854 * @throws SocketException if an error occurs setting the option 855 */ 856 public synchronized void setSoTimeout(int timeout) throws SocketException { 857 super.setSoTimeout(timeout); 858 this.timeout = timeout; 859 } 860 861 // BEGIN android-added 862 /** 863 * Set the handshake timeout on this socket. This timeout is specified in 864 * milliseconds and will be used only during the handshake process. 865 * 866 * @param timeout the handshake timeout value 867 */ 868 public synchronized void setHandshakeTimeout(int timeout) throws SocketException { 869 this.handshakeTimeout = timeout; 870 } 871 // END android-added 872 873 private native void nativeinterrupt(int sslNativePointer) throws IOException; 874 private native void nativeclose(int sslNativePointer) throws IOException; 875 876 /** 877 * Closes the SSL socket. Once closed, a socket is not available for further 878 * use anymore under any circumstance. A new socket must be created. 879 * 880 * @throws <code>IOException</code> if an I/O error happens during the 881 * socket's closure. 882 */ 883 public void close() throws IOException { 884 // TODO: Close SSL sockets using a background thread so they close 885 // gracefully. 886 887 synchronized (handshakeLock) { 888 if (!handshakeStarted) { 889 // prevent further attemps to start handshake 890 handshakeStarted = true; 891 892 synchronized (this) { 893 free(); 894 895 if (socket != null) { 896 if (autoClose && !socket.isClosed()) socket.close(); 897 } else { 898 if (!super.isClosed()) super.close(); 899 } 900 } 901 902 return; 903 } 904 } 905 906 nativeinterrupt(sslNativePointer); 907 908 synchronized (this) { 909 synchronized (writeLock) { 910 synchronized (readLock) { 911 912 IOException pendingException = null; 913 914 // Shut down the SSL connection, per se. 915 try { 916 if (handshakeStarted) { 917 nativeclose(sslNativePointer); 918 } 919 } catch (IOException ex) { 920 /* 921 * Note the exception at this point, but try to continue 922 * to clean the rest of this all up before rethrowing. 923 */ 924 pendingException = ex; 925 } 926 927 /* 928 * Even if the above call failed, it is still safe to free 929 * the native structs, and we need to do so lest we leak 930 * memory. 931 */ 932 free(); 933 934 if (socket != null) { 935 if (autoClose && !socket.isClosed()) 936 socket.close(); 937 } else { 938 if (!super.isClosed()) 939 super.close(); 940 } 941 942 if (pendingException != null) { 943 throw pendingException; 944 } 945 } 946 } 947 } 948 } 949 950 private void free() { 951 if (sslNativePointer == 0) { 952 return; 953 } 954 NativeCrypto.SSL_free(sslNativePointer); 955 sslNativePointer = 0; 956 } 957 958 protected void finalize() throws IOException { 959 /* 960 * Just worry about our own state. Notably we do not try and 961 * close anything. The SocketImpl, either our own 962 * PlainSocketImpl, or the Socket we are wrapping, will do 963 * that. This might mean we do not properly SSL_shutdown, but 964 * if you want to do that, properly close the socket yourself. 965 * 966 * The reason why we don't try to SSL_shutdown, is that there 967 * can be a race between finalizers where the PlainSocketImpl 968 * finalizer runs first and closes the socket. However, in the 969 * meanwhile, the underlying file descriptor could be reused 970 * for another purpose. If we call SSL_shutdown, the 971 * underlying socket BIOs still have the old file descriptor 972 * and will write the close notify to some unsuspecting 973 * reader. 974 */ 975 updateInstanceCount(-1); 976 free(); 977 } 978 979 /** 980 * Verifies an RSA signature. Conceptually, this method doesn't really 981 * belong here, but due to its native code being closely tied to OpenSSL 982 * (just like the rest of this class), we put it here for the time being. 983 * This also solves potential problems with native library initialization. 984 * 985 * @param message The message to verify 986 * @param signature The signature to verify 987 * @param algorithm The hash/sign algorithm to use, i.e. "RSA-SHA1" 988 * @param key The RSA public key to use 989 * @return true if the verification succeeds, false otherwise 990 */ 991 public static boolean verifySignature(byte[] message, byte[] signature, String algorithm, RSAPublicKey key) { 992 byte[] modulus = key.getModulus().toByteArray(); 993 byte[] exponent = key.getPublicExponent().toByteArray(); 994 995 return nativeverifysignature(message, signature, algorithm, modulus, exponent) == 1; 996 } 997 998 private static native int nativeverifysignature(byte[] message, byte[] signature, 999 String algorithm, byte[] modulus, byte[] exponent); 1000} 1001