Home | History | Annotate | Download | only in conscrypt
      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.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING;
     20 import static org.conscrypt.NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE;
     21 import static org.conscrypt.NativeConstants.SSL_OP_NO_TICKET;
     22 import static org.conscrypt.NativeConstants.SSL_RECEIVED_SHUTDOWN;
     23 import static org.conscrypt.NativeConstants.SSL_SENT_SHUTDOWN;
     24 import static org.conscrypt.NativeConstants.SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
     25 import static org.conscrypt.NativeConstants.SSL_VERIFY_NONE;
     26 import static org.conscrypt.NativeConstants.SSL_VERIFY_PEER;
     27 
     28 import java.io.FileDescriptor;
     29 import java.io.IOException;
     30 import java.io.UnsupportedEncodingException;
     31 import java.net.SocketException;
     32 import java.security.InvalidKeyException;
     33 import java.security.PrivateKey;
     34 import java.security.PublicKey;
     35 import java.security.cert.CertificateEncodingException;
     36 import java.security.cert.CertificateException;
     37 import java.security.cert.X509Certificate;
     38 import java.util.HashSet;
     39 import java.util.Set;
     40 import java.util.concurrent.locks.ReadWriteLock;
     41 import java.util.concurrent.locks.ReentrantReadWriteLock;
     42 import javax.crypto.SecretKey;
     43 import javax.net.ssl.SSLException;
     44 import javax.net.ssl.SSLHandshakeException;
     45 import javax.net.ssl.X509KeyManager;
     46 import javax.net.ssl.X509TrustManager;
     47 import javax.security.auth.x500.X500Principal;
     48 import org.conscrypt.NativeCrypto.SSLHandshakeCallbacks;
     49 import org.conscrypt.SSLParametersImpl.AliasChooser;
     50 import org.conscrypt.SSLParametersImpl.PSKCallbacks;
     51 
     52 /**
     53  * A utility wrapper that abstracts operations on the underlying native SSL instance.
     54  */
     55 final class NativeSsl {
     56     private final SSLParametersImpl parameters;
     57     private final SSLHandshakeCallbacks handshakeCallbacks;
     58     private final AliasChooser aliasChooser;
     59     private final PSKCallbacks pskCallbacks;
     60     private X509Certificate[] localCertificates;
     61     private final ReadWriteLock lock = new ReentrantReadWriteLock();
     62     private volatile long ssl;
     63 
     64     private NativeSsl(long ssl, SSLParametersImpl parameters,
     65             SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser,
     66             PSKCallbacks pskCallbacks) {
     67         this.ssl = ssl;
     68         this.parameters = parameters;
     69         this.handshakeCallbacks = handshakeCallbacks;
     70         this.aliasChooser = aliasChooser;
     71         this.pskCallbacks = pskCallbacks;
     72     }
     73 
     74     static NativeSsl newInstance(SSLParametersImpl parameters,
     75             SSLHandshakeCallbacks handshakeCallbacks, AliasChooser chooser,
     76             PSKCallbacks pskCallbacks) throws SSLException {
     77         AbstractSessionContext ctx = parameters.getSessionContext();
     78         long ssl = NativeCrypto.SSL_new(ctx.sslCtxNativePointer, ctx);
     79         return new NativeSsl(ssl, parameters, handshakeCallbacks, chooser, pskCallbacks);
     80     }
     81 
     82     BioWrapper newBio() {
     83         try {
     84             return new BioWrapper();
     85         } catch (SSLException e) {
     86             throw new RuntimeException(e);
     87         }
     88     }
     89 
     90     void offerToResumeSession(long sslSessionNativePointer) throws SSLException {
     91         NativeCrypto.SSL_set_session(ssl, this, sslSessionNativePointer);
     92     }
     93 
     94     byte[] getSessionId() {
     95         return NativeCrypto.SSL_session_id(ssl, this);
     96     }
     97 
     98     long getTime() {
     99         return NativeCrypto.SSL_get_time(ssl, this);
    100     }
    101 
    102     long getTimeout() {
    103         return NativeCrypto.SSL_get_timeout(ssl, this);
    104     }
    105 
    106     void setTimeout(long millis) {
    107         NativeCrypto.SSL_set_timeout(ssl, this, millis);
    108     }
    109 
    110     String getCipherSuite() {
    111         return NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_get_current_cipher(ssl, this));
    112     }
    113 
    114     X509Certificate[] getPeerCertificates() throws CertificateException {
    115         byte[][] encoded = NativeCrypto.SSL_get0_peer_certificates(ssl, this);
    116         return encoded == null ? null : SSLUtils.decodeX509CertificateChain(encoded);
    117     }
    118 
    119     X509Certificate[] getLocalCertificates() {
    120         return localCertificates;
    121     }
    122 
    123     byte[] getPeerCertificateOcspData() {
    124         return NativeCrypto.SSL_get_ocsp_response(ssl, this);
    125     }
    126 
    127     byte[] getTlsUnique() {
    128         return NativeCrypto.SSL_get_tls_unique(ssl, this);
    129     }
    130 
    131     byte[] getPeerTlsSctData() {
    132         return NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl, this);
    133     }
    134 
    135     /**
    136      * @see NativeCrypto.SSLHandshakeCallbacks#clientPSKKeyRequested(String, byte[], byte[])
    137      */
    138     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
    139     int clientPSKKeyRequested(String identityHint, byte[] identityBytesOut, byte[] key) {
    140         PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
    141         if (pskKeyManager == null) {
    142             return 0;
    143         }
    144 
    145         String identity = pskCallbacks.chooseClientPSKIdentity(pskKeyManager, identityHint);
    146         // Store identity in NULL-terminated modified UTF-8 representation into ientityBytesOut
    147         byte[] identityBytes;
    148         if (identity == null) {
    149             identity = "";
    150             identityBytes = EmptyArray.BYTE;
    151         } else if (identity.isEmpty()) {
    152             identityBytes = EmptyArray.BYTE;
    153         } else {
    154             try {
    155                 identityBytes = identity.getBytes("UTF-8");
    156             } catch (UnsupportedEncodingException e) {
    157                 throw new RuntimeException("UTF-8 encoding not supported", e);
    158             }
    159         }
    160         if (identityBytes.length + 1 > identityBytesOut.length) {
    161             // Insufficient space in the output buffer
    162             return 0;
    163         }
    164         if (identityBytes.length > 0) {
    165             System.arraycopy(identityBytes, 0, identityBytesOut, 0, identityBytes.length);
    166         }
    167         identityBytesOut[identityBytes.length] = 0;
    168 
    169         SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
    170         byte[] secretKeyBytes = secretKey.getEncoded();
    171         if (secretKeyBytes == null) {
    172             return 0;
    173         } else if (secretKeyBytes.length > key.length) {
    174             // Insufficient space in the output buffer
    175             return 0;
    176         }
    177         System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
    178         return secretKeyBytes.length;
    179     }
    180 
    181     /**
    182      * @see NativeCrypto.SSLHandshakeCallbacks#serverPSKKeyRequested(String, String, byte[])
    183      */
    184     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
    185     int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
    186         PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
    187         if (pskKeyManager == null) {
    188             return 0;
    189         }
    190         SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
    191         byte[] secretKeyBytes = secretKey.getEncoded();
    192         if (secretKeyBytes == null) {
    193             return 0;
    194         } else if (secretKeyBytes.length > key.length) {
    195             return 0;
    196         }
    197         System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
    198         return secretKeyBytes.length;
    199     }
    200 
    201     void chooseClientCertificate(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
    202             throws SSLException, CertificateEncodingException {
    203         Set<String> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes);
    204         String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]);
    205 
    206         X500Principal[] issuers;
    207         if (asn1DerEncodedPrincipals == null) {
    208             issuers = null;
    209         } else {
    210             issuers = new X500Principal[asn1DerEncodedPrincipals.length];
    211             for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
    212                 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
    213             }
    214         }
    215         X509KeyManager keyManager = parameters.getX509KeyManager();
    216         String alias = (keyManager != null)
    217                 ? aliasChooser.chooseClientAlias(keyManager, issuers, keyTypes)
    218                 : null;
    219         setCertificate(alias);
    220     }
    221 
    222     void setCertificate(String alias) throws CertificateEncodingException, SSLException {
    223         if (alias == null) {
    224             return;
    225         }
    226         X509KeyManager keyManager = parameters.getX509KeyManager();
    227         if (keyManager == null) {
    228             return;
    229         }
    230         PrivateKey privateKey = keyManager.getPrivateKey(alias);
    231         if (privateKey == null) {
    232             return;
    233         }
    234         localCertificates = keyManager.getCertificateChain(alias);
    235         if (localCertificates == null) {
    236             return;
    237         }
    238         int numLocalCerts = localCertificates.length;
    239         PublicKey publicKey = (numLocalCerts > 0) ? localCertificates[0].getPublicKey() : null;
    240 
    241         // Encode the local certificates.
    242         byte[][] encodedLocalCerts = new byte[numLocalCerts][];
    243         for (int i = 0; i < numLocalCerts; ++i) {
    244             encodedLocalCerts[i] = localCertificates[i].getEncoded();
    245         }
    246 
    247         // Convert the key so we can access a native reference.
    248         final OpenSSLKey key;
    249         try {
    250             key = OpenSSLKey.fromPrivateKeyForTLSStackOnly(privateKey, publicKey);
    251         } catch (InvalidKeyException e) {
    252             throw new SSLException(e);
    253         }
    254 
    255         // Set the local certs and private key.
    256         NativeCrypto.setLocalCertsAndPrivateKey(ssl, this, encodedLocalCerts, key.getNativeRef());
    257     }
    258 
    259     String getVersion() {
    260         return NativeCrypto.SSL_get_version(ssl, this);
    261     }
    262 
    263     String getRequestedServerName() {
    264         return NativeCrypto.SSL_get_servername(ssl, this);
    265     }
    266 
    267     byte[] getTlsChannelId() throws SSLException {
    268         return NativeCrypto.SSL_get_tls_channel_id(ssl, this);
    269     }
    270 
    271     void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOException {
    272         boolean enableSessionCreation = parameters.getEnableSessionCreation();
    273         if (!enableSessionCreation) {
    274             NativeCrypto.SSL_set_session_creation_enabled(ssl, this, false);
    275         }
    276 
    277         // Allow servers to trigger renegotiation. Some inadvisable server
    278         // configurations cause them to attempt to renegotiate during
    279         // certain protocols.
    280         NativeCrypto.SSL_accept_renegotiations(ssl, this);
    281 
    282         if (isClient()) {
    283             NativeCrypto.SSL_set_connect_state(ssl, this);
    284 
    285             // Configure OCSP and CT extensions for client
    286             NativeCrypto.SSL_enable_ocsp_stapling(ssl, this);
    287             if (parameters.isCTVerificationEnabled(hostname)) {
    288                 NativeCrypto.SSL_enable_signed_cert_timestamps(ssl, this);
    289             }
    290         } else {
    291             NativeCrypto.SSL_set_accept_state(ssl, this);
    292 
    293             // Configure OCSP for server
    294             if (parameters.getOCSPResponse() != null) {
    295                 NativeCrypto.SSL_enable_ocsp_stapling(ssl, this);
    296             }
    297         }
    298 
    299         if (parameters.getEnabledProtocols().length == 0 && parameters.isEnabledProtocolsFiltered) {
    300             throw new SSLHandshakeException("No enabled protocols; "
    301                     + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3
    302                     + " is no longer supported and was filtered from the list");
    303         }
    304         NativeCrypto.setEnabledProtocols(ssl, this, parameters.enabledProtocols);
    305         NativeCrypto.setEnabledCipherSuites(ssl, this, parameters.enabledCipherSuites);
    306 
    307         if (parameters.applicationProtocols.length > 0) {
    308             NativeCrypto.setApplicationProtocols(ssl, this, isClient(), parameters.applicationProtocols);
    309         }
    310         if (!isClient() && parameters.applicationProtocolSelector != null) {
    311             NativeCrypto.setApplicationProtocolSelector(ssl, this, parameters.applicationProtocolSelector);
    312         }
    313 
    314         // setup server certificates and private keys.
    315         // clients will receive a call back to request certificates.
    316         if (!isClient()) {
    317             Set<String> keyTypes = new HashSet<String>();
    318             for (long sslCipherNativePointer : NativeCrypto.SSL_get_ciphers(ssl, this)) {
    319                 String keyType = SSLUtils.getServerX509KeyType(sslCipherNativePointer);
    320                 if (keyType != null) {
    321                     keyTypes.add(keyType);
    322                 }
    323             }
    324             X509KeyManager keyManager = parameters.getX509KeyManager();
    325             if (keyManager != null) {
    326                 for (String keyType : keyTypes) {
    327                     try {
    328                         setCertificate(aliasChooser.chooseServerAlias(keyManager, keyType));
    329                     } catch (CertificateEncodingException e) {
    330                         throw new IOException(e);
    331                     }
    332                 }
    333             }
    334 
    335             NativeCrypto.SSL_set_options(ssl, this, SSL_OP_CIPHER_SERVER_PREFERENCE);
    336 
    337             if (parameters.sctExtension != null) {
    338                 NativeCrypto.SSL_set_signed_cert_timestamp_list(ssl, this, parameters.sctExtension);
    339             }
    340 
    341             if (parameters.ocspResponse != null) {
    342                 NativeCrypto.SSL_set_ocsp_response(ssl, this, parameters.ocspResponse);
    343             }
    344         }
    345 
    346         enablePSKKeyManagerIfRequested();
    347 
    348         if (parameters.useSessionTickets) {
    349             NativeCrypto.SSL_clear_options(ssl, this, SSL_OP_NO_TICKET);
    350         } else {
    351             NativeCrypto.SSL_set_options(
    352                     ssl, this, NativeCrypto.SSL_get_options(ssl, this) | SSL_OP_NO_TICKET);
    353         }
    354 
    355         if (parameters.getUseSni() && AddressUtils.isValidSniHostname(hostname)) {
    356             NativeCrypto.SSL_set_tlsext_host_name(ssl, this, hostname);
    357         }
    358 
    359         // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites
    360         // with TLSv1 and SSLv3).
    361         NativeCrypto.SSL_set_mode(ssl, this, SSL_MODE_CBC_RECORD_SPLITTING);
    362 
    363         setCertificateValidation();
    364         setTlsChannelId(channelIdPrivateKey);
    365     }
    366 
    367     // TODO(nathanmittler): Remove once after we switch to the engine socket.
    368     void doHandshake(FileDescriptor fd, int timeoutMillis)
    369             throws CertificateException, IOException {
    370         lock.readLock().lock();
    371         try {
    372             if (isClosed() || fd == null || !fd.valid()) {
    373                 throw new SocketException("Socket is closed");
    374             }
    375             NativeCrypto.SSL_do_handshake(ssl, this, fd, handshakeCallbacks, timeoutMillis);
    376         } finally {
    377             lock.readLock().unlock();
    378         }
    379     }
    380 
    381     int doHandshake() throws IOException {
    382         lock.readLock().lock();
    383         try {
    384             return NativeCrypto.ENGINE_SSL_do_handshake(ssl, this, handshakeCallbacks);
    385         } finally {
    386             lock.readLock().unlock();
    387         }
    388     }
    389 
    390     // TODO(nathanmittler): Remove once after we switch to the engine socket.
    391     int read(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
    392             throws IOException {
    393         lock.readLock().lock();
    394         try {
    395             if (isClosed() || fd == null || !fd.valid()) {
    396                 throw new SocketException("Socket is closed");
    397             }
    398             return NativeCrypto
    399                     .SSL_read(ssl, this, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
    400         } finally {
    401             lock.readLock().unlock();
    402         }
    403     }
    404 
    405     // TODO(nathanmittler): Remove once after we switch to the engine socket.
    406     void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
    407             throws IOException {
    408         lock.readLock().lock();
    409         try {
    410             if (isClosed() || fd == null || !fd.valid()) {
    411                 throw new SocketException("Socket is closed");
    412             }
    413             NativeCrypto
    414                     .SSL_write(ssl, this, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
    415         } finally {
    416             lock.readLock().unlock();
    417         }
    418     }
    419 
    420     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
    421     private void enablePSKKeyManagerIfRequested() throws SSLException {
    422         // Enable Pre-Shared Key (PSK) key exchange if requested
    423         PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
    424         if (pskKeyManager != null) {
    425             boolean pskEnabled = false;
    426             for (String enabledCipherSuite : parameters.enabledCipherSuites) {
    427                 if ((enabledCipherSuite != null) && (enabledCipherSuite.contains("PSK"))) {
    428                     pskEnabled = true;
    429                     break;
    430                 }
    431             }
    432             if (pskEnabled) {
    433                 if (isClient()) {
    434                     NativeCrypto.set_SSL_psk_client_callback_enabled(ssl, this, true);
    435                 } else {
    436                     NativeCrypto.set_SSL_psk_server_callback_enabled(ssl, this, true);
    437                     String identityHint = pskCallbacks.chooseServerPSKIdentityHint(pskKeyManager);
    438                     NativeCrypto.SSL_use_psk_identity_hint(ssl, this, identityHint);
    439                 }
    440             }
    441         }
    442     }
    443 
    444     private void setTlsChannelId(OpenSSLKey channelIdPrivateKey) throws SSLException {
    445         if (!parameters.channelIdEnabled) {
    446             return;
    447         }
    448 
    449         if (parameters.getUseClientMode()) {
    450             // Client-side TLS Channel ID
    451             if (channelIdPrivateKey == null) {
    452                 throw new SSLHandshakeException("Invalid TLS channel ID key specified");
    453             }
    454             NativeCrypto.SSL_set1_tls_channel_id(ssl, this, channelIdPrivateKey.getNativeRef());
    455         } else {
    456             // Server-side TLS Channel ID
    457             NativeCrypto.SSL_enable_tls_channel_id(ssl, this);
    458         }
    459     }
    460 
    461     private void setCertificateValidation() throws SSLException {
    462         // setup peer certificate verification
    463         if (!isClient()) {
    464             // needing client auth takes priority...
    465             boolean certRequested;
    466             if (parameters.getNeedClientAuth()) {
    467                 NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_PEER
    468                                 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
    469                 certRequested = true;
    470                 // ... over just wanting it...
    471             } else if (parameters.getWantClientAuth()) {
    472                 NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_PEER);
    473                 certRequested = true;
    474                 // ... and we must disable verification if we don't want client auth.
    475             } else {
    476                 NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_NONE);
    477                 certRequested = false;
    478             }
    479 
    480             if (certRequested) {
    481                 X509TrustManager trustManager = parameters.getX509TrustManager();
    482                 X509Certificate[] issuers = trustManager.getAcceptedIssuers();
    483                 if (issuers != null && issuers.length != 0) {
    484                     byte[][] issuersBytes;
    485                     try {
    486                         issuersBytes = SSLUtils.encodeIssuerX509Principals(issuers);
    487                     } catch (CertificateEncodingException e) {
    488                         throw new SSLException("Problem encoding principals", e);
    489                     }
    490                     NativeCrypto.SSL_set_client_CA_list(ssl, this, issuersBytes);
    491                 }
    492             }
    493         }
    494     }
    495 
    496     void interrupt() {
    497         NativeCrypto.SSL_interrupt(ssl, this);
    498     }
    499 
    500     // TODO(nathanmittler): Remove once after we switch to the engine socket.
    501     void shutdown(FileDescriptor fd) throws IOException {
    502         NativeCrypto.SSL_shutdown(ssl, this, fd, handshakeCallbacks);
    503     }
    504 
    505     void shutdown() throws IOException {
    506         NativeCrypto.ENGINE_SSL_shutdown(ssl, this, handshakeCallbacks);
    507     }
    508 
    509     boolean wasShutdownReceived() {
    510         return (NativeCrypto.SSL_get_shutdown(ssl, this) & SSL_RECEIVED_SHUTDOWN) != 0;
    511     }
    512 
    513     boolean wasShutdownSent() {
    514         return (NativeCrypto.SSL_get_shutdown(ssl, this) & SSL_SENT_SHUTDOWN) != 0;
    515     }
    516 
    517     int readDirectByteBuffer(long destAddress, int destLength)
    518             throws IOException, CertificateException {
    519         lock.readLock().lock();
    520         try {
    521             return NativeCrypto.ENGINE_SSL_read_direct(
    522                     ssl, this, destAddress, destLength, handshakeCallbacks);
    523         } finally {
    524             lock.readLock().unlock();
    525         }
    526     }
    527 
    528     int writeDirectByteBuffer(long sourceAddress, int sourceLength) throws IOException {
    529         lock.readLock().lock();
    530         try {
    531             return NativeCrypto.ENGINE_SSL_write_direct(
    532                     ssl, this, sourceAddress, sourceLength, handshakeCallbacks);
    533         } finally {
    534             lock.readLock().unlock();
    535         }
    536     }
    537 
    538     int getPendingReadableBytes() {
    539         return NativeCrypto.SSL_pending_readable_bytes(ssl, this);
    540     }
    541 
    542     int getMaxSealOverhead() {
    543         return NativeCrypto.SSL_max_seal_overhead(ssl, this);
    544     }
    545 
    546     void close() {
    547         lock.writeLock().lock();
    548         try {
    549             if (!isClosed()) {
    550                 long toFree = ssl;
    551                 ssl = 0L;
    552                 NativeCrypto.SSL_free(toFree, this);
    553             }
    554         } finally {
    555             lock.writeLock().unlock();
    556         }
    557     }
    558 
    559     boolean isClosed() {
    560         return ssl == 0L;
    561     }
    562 
    563     int getError(int result) {
    564         return NativeCrypto.SSL_get_error(ssl, this, result);
    565     }
    566 
    567     byte[] getApplicationProtocol() {
    568         return NativeCrypto.getApplicationProtocol(ssl, this);
    569     }
    570 
    571     private boolean isClient() {
    572         return parameters.getUseClientMode();
    573     }
    574 
    575     @Override
    576     protected final void finalize() throws Throwable {
    577         try {
    578             close();
    579         } finally {
    580             super.finalize();
    581         }
    582     }
    583 
    584     /**
    585      * A utility wrapper that abstracts operations on the underlying native BIO instance.
    586      */
    587     final class BioWrapper {
    588         private long bio;
    589 
    590         private BioWrapper() throws SSLException {
    591             this.bio = NativeCrypto.SSL_BIO_new(ssl, NativeSsl.this);
    592         }
    593 
    594         int getPendingWrittenBytes() {
    595             return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio);
    596         }
    597 
    598         int writeDirectByteBuffer(long address, int length) throws IOException {
    599             return NativeCrypto.ENGINE_SSL_write_BIO_direct(
    600                     ssl, NativeSsl.this, bio, address, length, handshakeCallbacks);
    601         }
    602 
    603         int readDirectByteBuffer(long destAddress, int destLength) throws IOException {
    604             return NativeCrypto.ENGINE_SSL_read_BIO_direct(
    605                     ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks);
    606         }
    607 
    608         void close() {
    609             NativeCrypto.BIO_free_all(bio);
    610             bio = 0L;
    611         }
    612     }
    613 }
    614