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