1/*
2 * Copyright 2013 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.conscrypt;
18
19import java.io.IOException;
20import java.nio.ByteBuffer;
21import java.nio.ReadOnlyBufferException;
22import java.security.cert.CertificateEncodingException;
23import java.security.cert.CertificateException;
24
25import javax.crypto.SecretKey;
26import javax.net.ssl.SSLEngine;
27import javax.net.ssl.SSLEngineResult;
28import javax.net.ssl.SSLEngineResult.HandshakeStatus;
29import javax.net.ssl.SSLEngineResult.Status;
30import javax.net.ssl.SSLException;
31import javax.net.ssl.SSLHandshakeException;
32import javax.net.ssl.SSLSession;
33import javax.net.ssl.X509ExtendedKeyManager;
34import javax.net.ssl.X509KeyManager;
35import javax.net.ssl.X509TrustManager;
36import javax.security.auth.x500.X500Principal;
37
38/**
39 * Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces.
40 */
41public class OpenSSLEngineImpl extends SSLEngine implements NativeCrypto.SSLHandshakeCallbacks,
42        SSLParametersImpl.AliasChooser, SSLParametersImpl.PSKCallbacks {
43    private final SSLParametersImpl sslParameters;
44
45    /**
46     * Protects handshakeStarted and handshakeCompleted.
47     */
48    private final Object stateLock = new Object();
49
50    private static enum EngineState {
51        /**
52         * The {@link OpenSSLSocketImpl} object is constructed, but {@link #beginHandshake()}
53         * has not yet been called.
54         */
55        NEW,
56        /**
57         * {@link #setUseClientMode(boolean)} has been called at least once.
58         */
59        MODE_SET,
60        /**
61         * {@link #beginHandshake()} has been called at least once.
62         */
63        HANDSHAKE_WANTED,
64        /**
65         * Handshake task has been started.
66         */
67        HANDSHAKE_STARTED,
68        /**
69         * Handshake has been completed, but {@link #beginHandshake()} hasn't returned yet.
70         */
71        HANDSHAKE_COMPLETED,
72        /**
73         * {@link #beginHandshake()} has completed but the task hasn't
74         * been called. This is expected behaviour in cut-through mode, where SSL_do_handshake
75         * returns before the handshake is complete. We can now start writing data to the socket.
76         */
77        READY_HANDSHAKE_CUT_THROUGH,
78        /**
79         * {@link #beginHandshake()} has completed and socket is ready to go.
80         */
81        READY,
82        CLOSED_INBOUND,
83        CLOSED_OUTBOUND,
84        /**
85         * Inbound and outbound has been called.
86         */
87        CLOSED,
88    }
89
90    // @GuardedBy("stateLock");
91    private EngineState engineState = EngineState.NEW;
92
93    /**
94     * Protected by synchronizing on stateLock. Starts as 0, set by
95     * startHandshake, reset to 0 on close.
96     */
97    // @GuardedBy("stateLock");
98    private long sslNativePointer;
99
100    /** Used during handshake when {@link #wrap(ByteBuffer, ByteBuffer)} is called. */
101    // TODO: make this use something similar to BIO_s_null() in native code
102    private static OpenSSLBIOSource nullSource = OpenSSLBIOSource.wrap(ByteBuffer.allocate(0));
103
104    /** A BIO sink written to only during handshakes. */
105    private OpenSSLBIOSink handshakeSink;
106
107    /** A BIO sink written to during regular operation. */
108    private final OpenSSLBIOSink localToRemoteSink = OpenSSLBIOSink.create();
109
110    /** Set during startHandshake. */
111    private OpenSSLSessionImpl sslSession;
112
113    /** Used during handshake callbacks. */
114    private OpenSSLSessionImpl handshakeSession;
115
116    /**
117     * Private key for the TLS Channel ID extension. This field is client-side
118     * only. Set during startHandshake.
119     */
120    OpenSSLKey channelIdPrivateKey;
121
122    public OpenSSLEngineImpl(SSLParametersImpl sslParameters) {
123        this.sslParameters = sslParameters;
124    }
125
126    public OpenSSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) {
127        super(host, port);
128        this.sslParameters = sslParameters;
129    }
130
131    @Override
132    public void beginHandshake() throws SSLException {
133        synchronized (stateLock) {
134            if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND
135                    || engineState == EngineState.CLOSED_INBOUND) {
136                throw new IllegalStateException("Engine has already been closed");
137            }
138            if (engineState == EngineState.HANDSHAKE_STARTED) {
139                throw new IllegalStateException("Handshake has already been started");
140            }
141            if (engineState != EngineState.MODE_SET) {
142                throw new IllegalStateException("Client/server mode must be set before handshake");
143            }
144            if (getUseClientMode()) {
145                engineState = EngineState.HANDSHAKE_WANTED;
146            } else {
147                engineState = EngineState.HANDSHAKE_STARTED;
148            }
149        }
150
151        boolean releaseResources = true;
152        try {
153            final AbstractSessionContext sessionContext = sslParameters.getSessionContext();
154            final long sslCtxNativePointer = sessionContext.sslCtxNativePointer;
155            sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
156            sslSession = sslParameters.getSessionToReuse(
157                    sslNativePointer, getPeerHost(), getPeerPort());
158            sslParameters.setSSLParameters(sslCtxNativePointer, sslNativePointer, this, this,
159                    getPeerHost());
160            sslParameters.setCertificateValidation(sslNativePointer);
161            sslParameters.setTlsChannelId(sslNativePointer, channelIdPrivateKey);
162            if (getUseClientMode()) {
163                NativeCrypto.SSL_set_connect_state(sslNativePointer);
164            } else {
165                NativeCrypto.SSL_set_accept_state(sslNativePointer);
166            }
167            handshakeSink = OpenSSLBIOSink.create();
168            releaseResources = false;
169        } catch (IOException e) {
170            // Write CCS errors to EventLog
171            String message = e.getMessage();
172            // Must match error reason string of SSL_R_UNEXPECTED_CCS (in ssl/ssl_err.c)
173            if (message.contains("unexpected CCS")) {
174                String logMessage = String.format("ssl_unexpected_ccs: host=%s", getPeerHost());
175                Platform.logEvent(logMessage);
176            }
177            throw new SSLException(e);
178        } finally {
179            if (releaseResources) {
180                synchronized (stateLock) {
181                    engineState = EngineState.CLOSED;
182                }
183                shutdownAndFreeSslNative();
184            }
185        }
186    }
187
188    @Override
189    public void closeInbound() throws SSLException {
190        synchronized (stateLock) {
191            if (engineState == EngineState.CLOSED) {
192                return;
193            }
194            if (engineState == EngineState.CLOSED_OUTBOUND) {
195                engineState = EngineState.CLOSED;
196            } else {
197                engineState = EngineState.CLOSED_INBOUND;
198            }
199        }
200        // TODO anything else to notify OpenSSL layer?
201    }
202
203    @Override
204    public void closeOutbound() {
205        synchronized (stateLock) {
206            if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) {
207                return;
208            }
209            if (engineState != EngineState.MODE_SET && engineState != EngineState.NEW) {
210                shutdownAndFreeSslNative();
211            }
212            if (engineState == EngineState.CLOSED_INBOUND) {
213                engineState = EngineState.CLOSED;
214            } else {
215                engineState = EngineState.CLOSED_OUTBOUND;
216            }
217        }
218        shutdown();
219    }
220
221    @Override
222    public Runnable getDelegatedTask() {
223        /* This implementation doesn't use any delegated tasks. */
224        return null;
225    }
226
227    @Override
228    public String[] getEnabledCipherSuites() {
229        return sslParameters.getEnabledCipherSuites();
230    }
231
232    @Override
233    public String[] getEnabledProtocols() {
234        return sslParameters.getEnabledProtocols();
235    }
236
237    @Override
238    public boolean getEnableSessionCreation() {
239        return sslParameters.getEnableSessionCreation();
240    }
241
242    @Override
243    public HandshakeStatus getHandshakeStatus() {
244        synchronized (stateLock) {
245            switch (engineState) {
246                case HANDSHAKE_WANTED:
247                    if (getUseClientMode()) {
248                        return HandshakeStatus.NEED_WRAP;
249                    } else {
250                        return HandshakeStatus.NEED_UNWRAP;
251                    }
252                case HANDSHAKE_STARTED:
253                    if (handshakeSink.available() > 0) {
254                        return HandshakeStatus.NEED_WRAP;
255                    } else {
256                        return HandshakeStatus.NEED_UNWRAP;
257                    }
258                case HANDSHAKE_COMPLETED:
259                    if (handshakeSink.available() == 0) {
260                        handshakeSink = null;
261                        engineState = EngineState.READY;
262                        return HandshakeStatus.FINISHED;
263                    } else {
264                        return HandshakeStatus.NEED_WRAP;
265                    }
266                case NEW:
267                case MODE_SET:
268                case CLOSED:
269                case CLOSED_INBOUND:
270                case CLOSED_OUTBOUND:
271                case READY:
272                case READY_HANDSHAKE_CUT_THROUGH:
273                    return HandshakeStatus.NOT_HANDSHAKING;
274                default:
275                    break;
276            }
277            throw new IllegalStateException("Unexpected engine state: " + engineState);
278        }
279    }
280
281    @Override
282    public boolean getNeedClientAuth() {
283        return sslParameters.getNeedClientAuth();
284    }
285
286    @Override
287    public SSLSession getSession() {
288        if (sslSession == null) {
289            return SSLNullSession.getNullSession();
290        }
291        return sslSession;
292    }
293
294    @Override
295    public String[] getSupportedCipherSuites() {
296        return NativeCrypto.getSupportedCipherSuites();
297    }
298
299    @Override
300    public String[] getSupportedProtocols() {
301        return NativeCrypto.getSupportedProtocols();
302    }
303
304    @Override
305    public boolean getUseClientMode() {
306        return sslParameters.getUseClientMode();
307    }
308
309    @Override
310    public boolean getWantClientAuth() {
311        return sslParameters.getWantClientAuth();
312    }
313
314    @Override
315    public boolean isInboundDone() {
316        if (sslNativePointer == 0) {
317            synchronized (stateLock) {
318                return engineState == EngineState.CLOSED
319                        || engineState == EngineState.CLOSED_INBOUND;
320            }
321        }
322        return (NativeCrypto.SSL_get_shutdown(sslNativePointer)
323                & NativeCrypto.SSL_RECEIVED_SHUTDOWN) != 0;
324    }
325
326    @Override
327    public boolean isOutboundDone() {
328        if (sslNativePointer == 0) {
329            synchronized (stateLock) {
330                return engineState == EngineState.CLOSED
331                        || engineState == EngineState.CLOSED_OUTBOUND;
332            }
333        }
334        return (NativeCrypto.SSL_get_shutdown(sslNativePointer)
335                & NativeCrypto.SSL_SENT_SHUTDOWN) != 0;
336    }
337
338    @Override
339    public void setEnabledCipherSuites(String[] suites) {
340        sslParameters.setEnabledCipherSuites(suites);
341    }
342
343    @Override
344    public void setEnabledProtocols(String[] protocols) {
345        sslParameters.setEnabledProtocols(protocols);
346    }
347
348    @Override
349    public void setEnableSessionCreation(boolean flag) {
350        sslParameters.setEnableSessionCreation(flag);
351    }
352
353    @Override
354    public void setNeedClientAuth(boolean need) {
355        sslParameters.setNeedClientAuth(need);
356    }
357
358    @Override
359    public void setUseClientMode(boolean mode) {
360        synchronized (stateLock) {
361            if (engineState != EngineState.MODE_SET && engineState != EngineState.NEW) {
362                throw new IllegalArgumentException(
363                        "Can not change mode after handshake: engineState == " + engineState);
364            }
365            engineState = EngineState.MODE_SET;
366        }
367        sslParameters.setUseClientMode(mode);
368    }
369
370    @Override
371    public void setWantClientAuth(boolean want) {
372        sslParameters.setWantClientAuth(want);
373    }
374
375    private static void checkIndex(int length, int offset, int count) {
376        if (offset < 0) {
377            throw new IndexOutOfBoundsException("offset < 0");
378        } else if (count < 0) {
379            throw new IndexOutOfBoundsException("count < 0");
380        } else if (offset > length) {
381            throw new IndexOutOfBoundsException("offset > length");
382        } else if (offset > length - count) {
383            throw new IndexOutOfBoundsException("offset + count > length");
384        }
385    }
386
387    @Override
388    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length)
389            throws SSLException {
390        if (src == null) {
391            throw new IllegalArgumentException("src == null");
392        } else if (dsts == null) {
393            throw new IllegalArgumentException("dsts == null");
394        }
395        checkIndex(dsts.length, offset, length);
396        int dstRemaining = 0;
397        for (int i = 0; i < dsts.length; i++) {
398            ByteBuffer dst = dsts[i];
399            if (dst == null) {
400                throw new IllegalArgumentException("one of the dst == null");
401            } else if (dst.isReadOnly()) {
402                throw new ReadOnlyBufferException();
403            }
404            if (i >= offset && i < offset + length) {
405                dstRemaining += dst.remaining();
406            }
407        }
408
409        synchronized (stateLock) {
410            // If the inbound direction is closed. we can't send anymore.
411            if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_INBOUND) {
412                return new SSLEngineResult(Status.CLOSED, getHandshakeStatus(), 0, 0);
413            }
414            if (engineState == EngineState.NEW || engineState == EngineState.MODE_SET) {
415                beginHandshake();
416            }
417        }
418
419        // If we haven't completed the handshake yet, just let the caller know.
420        HandshakeStatus handshakeStatus = getHandshakeStatus();
421        if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) {
422            int positionBeforeHandshake = src.position();
423            OpenSSLBIOSource source = OpenSSLBIOSource.wrap(src);
424            long sslSessionCtx = 0L;
425            try {
426                sslSessionCtx = NativeCrypto.SSL_do_handshake_bio(sslNativePointer,
427                        source.getContext(), handshakeSink.getContext(), this, getUseClientMode(),
428                        sslParameters.npnProtocols, sslParameters.alpnProtocols);
429                if (sslSessionCtx != 0) {
430                    if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) {
431                        engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH;
432                    }
433                    sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession,
434                            getPeerHost(), getPeerPort(), true);
435                }
436                int bytesWritten = handshakeSink.position();
437                int bytesConsumed = (src.position() - positionBeforeHandshake);
438                return new SSLEngineResult((bytesConsumed > 0) ? Status.OK : Status.BUFFER_UNDERFLOW,
439                        getHandshakeStatus(), bytesConsumed, bytesWritten);
440            } catch (Exception e) {
441                throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed")
442                        .initCause(e);
443            } finally {
444                if (sslSession == null && sslSessionCtx != 0) {
445                    NativeCrypto.SSL_SESSION_free(sslSessionCtx);
446                }
447                source.release();
448            }
449        } else if (handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) {
450            return new SSLEngineResult(Status.OK, handshakeStatus, 0, 0);
451        }
452
453        if (dstRemaining == 0) {
454            return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
455        }
456
457        ByteBuffer srcDuplicate = src.duplicate();
458        OpenSSLBIOSource source = OpenSSLBIOSource.wrap(srcDuplicate);
459        try {
460            int positionBeforeRead = srcDuplicate.position();
461            int produced = 0;
462            boolean shouldStop = false;
463
464            while (!shouldStop) {
465                ByteBuffer dst = getNextAvailableByteBuffer(dsts, offset, length);
466                if (dst == null) {
467                    shouldStop = true;
468                    continue;
469                }
470                ByteBuffer arrayDst = dst;
471                if (dst.isDirect()) {
472                    arrayDst = ByteBuffer.allocate(dst.remaining());
473                }
474
475                int dstOffset = arrayDst.arrayOffset() + arrayDst.position();
476
477                int internalProduced = NativeCrypto.SSL_read_BIO(sslNativePointer,
478                        arrayDst.array(), dstOffset, dst.remaining(), source.getContext(),
479                        localToRemoteSink.getContext(), this);
480                if (internalProduced <= 0) {
481                    shouldStop = true;
482                    continue;
483                }
484                arrayDst.position(arrayDst.position() + internalProduced);
485                produced += internalProduced;
486                if (dst != arrayDst) {
487                    arrayDst.flip();
488                    dst.put(arrayDst);
489                }
490            }
491
492            int consumed = srcDuplicate.position() - positionBeforeRead;
493            src.position(srcDuplicate.position());
494            return new SSLEngineResult((consumed > 0) ? Status.OK : Status.BUFFER_UNDERFLOW,
495                    getHandshakeStatus(), consumed, produced);
496        } catch (IOException e) {
497            throw new SSLException(e);
498        } finally {
499            source.release();
500        }
501    }
502
503    /** Returns the next non-empty ByteBuffer. */
504    private ByteBuffer getNextAvailableByteBuffer(ByteBuffer[] buffers, int offset, int length) {
505        for (int i = offset; i < length; ++i) {
506            if (buffers[i].remaining() > 0) {
507                return buffers[i];
508            }
509        }
510        return null;
511    }
512
513    @Override
514    public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst)
515            throws SSLException {
516        if (srcs == null) {
517            throw new IllegalArgumentException("srcs == null");
518        } else if (dst == null) {
519            throw new IllegalArgumentException("dst == null");
520        } else if (dst.isReadOnly()) {
521            throw new ReadOnlyBufferException();
522        }
523        for (ByteBuffer src : srcs) {
524            if (src == null) {
525                throw new IllegalArgumentException("one of the src == null");
526            }
527        }
528        checkIndex(srcs.length, offset, length);
529
530        if (dst.remaining() < NativeCrypto.SSL3_RT_MAX_PACKET_SIZE) {
531            return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
532        }
533
534        synchronized (stateLock) {
535            // If the outbound direction is closed. we can't send anymore.
536            if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) {
537                return new SSLEngineResult(Status.CLOSED, getHandshakeStatus(), 0, 0);
538            }
539            if (engineState == EngineState.NEW || engineState == EngineState.MODE_SET) {
540                beginHandshake();
541            }
542        }
543
544        // If we haven't completed the handshake yet, just let the caller know.
545        HandshakeStatus handshakeStatus = getHandshakeStatus();
546        if (handshakeStatus == HandshakeStatus.NEED_WRAP) {
547            if (handshakeSink.available() == 0) {
548                long sslSessionCtx = 0L;
549                try {
550                    sslSessionCtx = NativeCrypto.SSL_do_handshake_bio(sslNativePointer,
551                            nullSource.getContext(), handshakeSink.getContext(), this,
552                            getUseClientMode(), sslParameters.npnProtocols,
553                            sslParameters.alpnProtocols);
554                    if (sslSessionCtx != 0) {
555                        if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) {
556                            engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH;
557                        }
558                        sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession,
559                                getPeerHost(), getPeerPort(), true);
560                    }
561                } catch (Exception e) {
562                    throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed")
563                            .initCause(e);
564                } finally {
565                    if (sslSession == null && sslSessionCtx != 0) {
566                        NativeCrypto.SSL_SESSION_free(sslSessionCtx);
567                    }
568                }
569            }
570            int bytesWritten = writeSinkToByteBuffer(handshakeSink, dst);
571            return new SSLEngineResult(Status.OK, getHandshakeStatus(), 0, bytesWritten);
572        } else if (handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) {
573            return new SSLEngineResult(Status.OK, handshakeStatus, 0, 0);
574        }
575
576        try {
577            int totalRead = 0;
578            byte[] buffer = null;
579
580            for (ByteBuffer src : srcs) {
581                int toRead = src.remaining();
582                if (buffer == null || toRead > buffer.length) {
583                    buffer = new byte[toRead];
584                }
585                /*
586                 * We can't just use .mark() here because the caller might be
587                 * using it.
588                 */
589                src.duplicate().get(buffer, 0, toRead);
590                int numRead = NativeCrypto.SSL_write_BIO(sslNativePointer, buffer, toRead,
591                        localToRemoteSink.getContext(), this);
592                if (numRead > 0) {
593                    src.position(src.position() + numRead);
594                    totalRead += numRead;
595                }
596            }
597
598            return new SSLEngineResult(Status.OK, getHandshakeStatus(), totalRead,
599                    writeSinkToByteBuffer(localToRemoteSink, dst));
600        } catch (IOException e) {
601            throw new SSLException(e);
602        }
603    }
604
605    /** Writes data available in a BIO sink to a ByteBuffer. */
606    private static int writeSinkToByteBuffer(OpenSSLBIOSink sink, ByteBuffer dst) {
607        int toWrite = Math.min(sink.available(), dst.remaining());
608        dst.put(sink.toByteArray(), sink.position(), toWrite);
609        sink.skip(toWrite);
610        return toWrite;
611    }
612
613    @Override
614    public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
615        return sslParameters.clientPSKKeyRequested(identityHint, identity, key, this);
616    }
617
618    @Override
619    public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
620        return sslParameters.serverPSKKeyRequested(identityHint, identity, key, this);
621    }
622
623    @Override
624    public void onSSLStateChange(long sslSessionNativePtr, int type, int val) {
625        synchronized (stateLock) {
626            switch (type) {
627                case NativeCrypto.SSL_CB_HANDSHAKE_DONE:
628                    if (engineState != EngineState.HANDSHAKE_STARTED &&
629                        engineState != EngineState.READY_HANDSHAKE_CUT_THROUGH) {
630                        throw new IllegalStateException("Completed handshake while in mode "
631                                + engineState);
632                    }
633                    engineState = EngineState.HANDSHAKE_COMPLETED;
634                    break;
635                case NativeCrypto.SSL_CB_HANDSHAKE_START:
636                    // For clients, this will allow the NEED_UNWRAP status to be
637                    // returned.
638                    engineState = EngineState.HANDSHAKE_STARTED;
639                    break;
640            }
641        }
642    }
643
644    @Override
645    public void verifyCertificateChain(long sslSessionNativePtr, long[] certRefs,
646            String authMethod) throws CertificateException {
647        try {
648            X509TrustManager x509tm = sslParameters.getX509TrustManager();
649            if (x509tm == null) {
650                throw new CertificateException("No X.509 TrustManager");
651            }
652            if (certRefs == null || certRefs.length == 0) {
653                throw new SSLException("Peer sent no certificate");
654            }
655            OpenSSLX509Certificate[] peerCertChain = new OpenSSLX509Certificate[certRefs.length];
656            for (int i = 0; i < certRefs.length; i++) {
657                peerCertChain[i] = new OpenSSLX509Certificate(certRefs[i]);
658            }
659
660            // Used for verifyCertificateChain callback
661            handshakeSession = new OpenSSLSessionImpl(sslSessionNativePtr, null, peerCertChain,
662                    getPeerHost(), getPeerPort(), null);
663
664            boolean client = sslParameters.getUseClientMode();
665            if (client) {
666                Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, getPeerHost());
667            } else {
668                String authType = peerCertChain[0].getPublicKey().getAlgorithm();
669                x509tm.checkClientTrusted(peerCertChain, authType);
670            }
671        } catch (CertificateException e) {
672            throw e;
673        } catch (Exception e) {
674            throw new CertificateException(e);
675        } finally {
676            // Clear this before notifying handshake completed listeners
677            handshakeSession = null;
678        }
679    }
680
681    @Override
682    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
683            throws CertificateEncodingException, SSLException {
684        sslParameters.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals,
685                sslNativePointer, this);
686    }
687
688    private void shutdown() {
689        try {
690            NativeCrypto.SSL_shutdown_BIO(sslNativePointer, nullSource.getContext(),
691                    localToRemoteSink.getContext(), this);
692        } catch (IOException ignored) {
693            /*
694             * TODO: The RI ignores close failures in SSLSocket, but need to
695             * investigate whether it does for SSLEngine.
696             */
697        }
698    }
699
700    private void shutdownAndFreeSslNative() {
701        try {
702            shutdown();
703        } finally {
704            free();
705        }
706    }
707
708    private void free() {
709        if (sslNativePointer == 0) {
710            return;
711        }
712        NativeCrypto.SSL_free(sslNativePointer);
713        sslNativePointer = 0;
714    }
715
716    @Override
717    protected void finalize() throws Throwable {
718        try {
719            free();
720        } finally {
721            super.finalize();
722        }
723    }
724
725    @Override
726    public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
727        if (keyManager instanceof X509ExtendedKeyManager) {
728            X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager;
729            return ekm.chooseEngineServerAlias(keyType, null, this);
730        } else {
731            return keyManager.chooseServerAlias(keyType, null, null);
732        }
733    }
734
735    @Override
736    public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
737            String[] keyTypes) {
738        if (keyManager instanceof X509ExtendedKeyManager) {
739            X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager;
740            return ekm.chooseEngineClientAlias(keyTypes, issuers, this);
741        } else {
742            return keyManager.chooseClientAlias(keyTypes, issuers, null);
743        }
744    }
745
746    @Override
747    public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
748        return keyManager.chooseServerKeyIdentityHint(this);
749    }
750
751    @Override
752    public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
753        return keyManager.chooseClientKeyIdentity(identityHint, this);
754    }
755
756    @Override
757    public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
758        return keyManager.getKey(identityHint, identity, this);
759    }
760}
761