| /* |
| * Copyright 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.conscrypt; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.ReadOnlyBufferException; |
| import java.security.cert.CertificateEncodingException; |
| import java.security.cert.CertificateException; |
| |
| import javax.crypto.SecretKey; |
| import javax.net.ssl.SSLEngine; |
| import javax.net.ssl.SSLEngineResult; |
| import javax.net.ssl.SSLEngineResult.HandshakeStatus; |
| import javax.net.ssl.SSLEngineResult.Status; |
| import javax.net.ssl.SSLException; |
| import javax.net.ssl.SSLHandshakeException; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.X509ExtendedKeyManager; |
| import javax.net.ssl.X509KeyManager; |
| import javax.net.ssl.X509TrustManager; |
| import javax.security.auth.x500.X500Principal; |
| |
| /** |
| * Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces. |
| */ |
| public class OpenSSLEngineImpl extends SSLEngine implements NativeCrypto.SSLHandshakeCallbacks, |
| SSLParametersImpl.AliasChooser, SSLParametersImpl.PSKCallbacks { |
| private final SSLParametersImpl sslParameters; |
| |
| /** |
| * Protects handshakeStarted and handshakeCompleted. |
| */ |
| private final Object stateLock = new Object(); |
| |
| private static enum EngineState { |
| /** |
| * The {@link OpenSSLSocketImpl} object is constructed, but {@link #beginHandshake()} |
| * has not yet been called. |
| */ |
| NEW, |
| /** |
| * {@link #setUseClientMode(boolean)} has been called at least once. |
| */ |
| MODE_SET, |
| /** |
| * {@link #beginHandshake()} has been called at least once. |
| */ |
| HANDSHAKE_WANTED, |
| /** |
| * Handshake task has been started. |
| */ |
| HANDSHAKE_STARTED, |
| /** |
| * Handshake has been completed, but {@link #beginHandshake()} hasn't returned yet. |
| */ |
| HANDSHAKE_COMPLETED, |
| /** |
| * {@link #beginHandshake()} has completed but the task hasn't |
| * been called. This is expected behaviour in cut-through mode, where SSL_do_handshake |
| * returns before the handshake is complete. We can now start writing data to the socket. |
| */ |
| READY_HANDSHAKE_CUT_THROUGH, |
| /** |
| * {@link #beginHandshake()} has completed and socket is ready to go. |
| */ |
| READY, |
| CLOSED_INBOUND, |
| CLOSED_OUTBOUND, |
| /** |
| * Inbound and outbound has been called. |
| */ |
| CLOSED, |
| } |
| |
| // @GuardedBy("stateLock"); |
| private EngineState engineState = EngineState.NEW; |
| |
| /** |
| * Protected by synchronizing on stateLock. Starts as 0, set by |
| * startHandshake, reset to 0 on close. |
| */ |
| // @GuardedBy("stateLock"); |
| private long sslNativePointer; |
| |
| /** Used during handshake when {@link #wrap(ByteBuffer, ByteBuffer)} is called. */ |
| // TODO: make this use something similar to BIO_s_null() in native code |
| private static OpenSSLBIOSource nullSource = OpenSSLBIOSource.wrap(ByteBuffer.allocate(0)); |
| |
| /** A BIO sink written to only during handshakes. */ |
| private OpenSSLBIOSink handshakeSink; |
| |
| /** A BIO sink written to during regular operation. */ |
| private final OpenSSLBIOSink localToRemoteSink = OpenSSLBIOSink.create(); |
| |
| /** Set during startHandshake. */ |
| private OpenSSLSessionImpl sslSession; |
| |
| /** Used during handshake callbacks. */ |
| private OpenSSLSessionImpl handshakeSession; |
| |
| /** |
| * Private key for the TLS Channel ID extension. This field is client-side |
| * only. Set during startHandshake. |
| */ |
| OpenSSLKey channelIdPrivateKey; |
| |
| public OpenSSLEngineImpl(SSLParametersImpl sslParameters) { |
| this.sslParameters = sslParameters; |
| } |
| |
| public OpenSSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) { |
| super(host, port); |
| this.sslParameters = sslParameters; |
| } |
| |
| @Override |
| public void beginHandshake() throws SSLException { |
| synchronized (stateLock) { |
| if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND |
| || engineState == EngineState.CLOSED_INBOUND) { |
| throw new IllegalStateException("Engine has already been closed"); |
| } |
| if (engineState == EngineState.HANDSHAKE_STARTED) { |
| throw new IllegalStateException("Handshake has already been started"); |
| } |
| if (engineState != EngineState.MODE_SET) { |
| throw new IllegalStateException("Client/server mode must be set before handshake"); |
| } |
| if (getUseClientMode()) { |
| engineState = EngineState.HANDSHAKE_WANTED; |
| } else { |
| engineState = EngineState.HANDSHAKE_STARTED; |
| } |
| } |
| |
| boolean releaseResources = true; |
| try { |
| final AbstractSessionContext sessionContext = sslParameters.getSessionContext(); |
| final long sslCtxNativePointer = sessionContext.sslCtxNativePointer; |
| sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer); |
| sslSession = sslParameters.getSessionToReuse( |
| sslNativePointer, getPeerHost(), getPeerPort()); |
| sslParameters.setSSLParameters(sslCtxNativePointer, sslNativePointer, this, this, |
| getPeerHost()); |
| sslParameters.setCertificateValidation(sslNativePointer); |
| sslParameters.setTlsChannelId(sslNativePointer, channelIdPrivateKey); |
| if (getUseClientMode()) { |
| NativeCrypto.SSL_set_connect_state(sslNativePointer); |
| } else { |
| NativeCrypto.SSL_set_accept_state(sslNativePointer); |
| } |
| handshakeSink = OpenSSLBIOSink.create(); |
| releaseResources = false; |
| } catch (IOException e) { |
| // Write CCS errors to EventLog |
| String message = e.getMessage(); |
| // Must match error reason string of SSL_R_UNEXPECTED_CCS (in ssl/ssl_err.c) |
| if (message.contains("unexpected CCS")) { |
| String logMessage = String.format("ssl_unexpected_ccs: host=%s", getPeerHost()); |
| Platform.logEvent(logMessage); |
| } |
| throw new SSLException(e); |
| } finally { |
| if (releaseResources) { |
| synchronized (stateLock) { |
| engineState = EngineState.CLOSED; |
| } |
| shutdownAndFreeSslNative(); |
| } |
| } |
| } |
| |
| @Override |
| public void closeInbound() throws SSLException { |
| synchronized (stateLock) { |
| if (engineState == EngineState.CLOSED) { |
| return; |
| } |
| if (engineState == EngineState.CLOSED_OUTBOUND) { |
| engineState = EngineState.CLOSED; |
| } else { |
| engineState = EngineState.CLOSED_INBOUND; |
| } |
| } |
| // TODO anything else to notify OpenSSL layer? |
| } |
| |
| @Override |
| public void closeOutbound() { |
| synchronized (stateLock) { |
| if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) { |
| return; |
| } |
| if (engineState != EngineState.MODE_SET && engineState != EngineState.NEW) { |
| shutdownAndFreeSslNative(); |
| } |
| if (engineState == EngineState.CLOSED_INBOUND) { |
| engineState = EngineState.CLOSED; |
| } else { |
| engineState = EngineState.CLOSED_OUTBOUND; |
| } |
| } |
| shutdown(); |
| } |
| |
| @Override |
| public Runnable getDelegatedTask() { |
| /* This implementation doesn't use any delegated tasks. */ |
| return null; |
| } |
| |
| @Override |
| public String[] getEnabledCipherSuites() { |
| return sslParameters.getEnabledCipherSuites(); |
| } |
| |
| @Override |
| public String[] getEnabledProtocols() { |
| return sslParameters.getEnabledProtocols(); |
| } |
| |
| @Override |
| public boolean getEnableSessionCreation() { |
| return sslParameters.getEnableSessionCreation(); |
| } |
| |
| @Override |
| public HandshakeStatus getHandshakeStatus() { |
| synchronized (stateLock) { |
| switch (engineState) { |
| case HANDSHAKE_WANTED: |
| if (getUseClientMode()) { |
| return HandshakeStatus.NEED_WRAP; |
| } else { |
| return HandshakeStatus.NEED_UNWRAP; |
| } |
| case HANDSHAKE_STARTED: |
| if (handshakeSink.available() > 0) { |
| return HandshakeStatus.NEED_WRAP; |
| } else { |
| return HandshakeStatus.NEED_UNWRAP; |
| } |
| case HANDSHAKE_COMPLETED: |
| if (handshakeSink.available() == 0) { |
| handshakeSink = null; |
| engineState = EngineState.READY; |
| return HandshakeStatus.FINISHED; |
| } else { |
| return HandshakeStatus.NEED_WRAP; |
| } |
| case NEW: |
| case MODE_SET: |
| case CLOSED: |
| case CLOSED_INBOUND: |
| case CLOSED_OUTBOUND: |
| case READY: |
| case READY_HANDSHAKE_CUT_THROUGH: |
| return HandshakeStatus.NOT_HANDSHAKING; |
| default: |
| break; |
| } |
| throw new IllegalStateException("Unexpected engine state: " + engineState); |
| } |
| } |
| |
| @Override |
| public boolean getNeedClientAuth() { |
| return sslParameters.getNeedClientAuth(); |
| } |
| |
| @Override |
| public SSLSession getSession() { |
| if (sslSession == null) { |
| return SSLNullSession.getNullSession(); |
| } |
| return sslSession; |
| } |
| |
| @Override |
| public String[] getSupportedCipherSuites() { |
| return NativeCrypto.getSupportedCipherSuites(); |
| } |
| |
| @Override |
| public String[] getSupportedProtocols() { |
| return NativeCrypto.getSupportedProtocols(); |
| } |
| |
| @Override |
| public boolean getUseClientMode() { |
| return sslParameters.getUseClientMode(); |
| } |
| |
| @Override |
| public boolean getWantClientAuth() { |
| return sslParameters.getWantClientAuth(); |
| } |
| |
| @Override |
| public boolean isInboundDone() { |
| if (sslNativePointer == 0) { |
| synchronized (stateLock) { |
| return engineState == EngineState.CLOSED |
| || engineState == EngineState.CLOSED_INBOUND; |
| } |
| } |
| return (NativeCrypto.SSL_get_shutdown(sslNativePointer) |
| & NativeCrypto.SSL_RECEIVED_SHUTDOWN) != 0; |
| } |
| |
| @Override |
| public boolean isOutboundDone() { |
| if (sslNativePointer == 0) { |
| synchronized (stateLock) { |
| return engineState == EngineState.CLOSED |
| || engineState == EngineState.CLOSED_OUTBOUND; |
| } |
| } |
| return (NativeCrypto.SSL_get_shutdown(sslNativePointer) |
| & NativeCrypto.SSL_SENT_SHUTDOWN) != 0; |
| } |
| |
| @Override |
| public void setEnabledCipherSuites(String[] suites) { |
| sslParameters.setEnabledCipherSuites(suites); |
| } |
| |
| @Override |
| public void setEnabledProtocols(String[] protocols) { |
| sslParameters.setEnabledProtocols(protocols); |
| } |
| |
| @Override |
| public void setEnableSessionCreation(boolean flag) { |
| sslParameters.setEnableSessionCreation(flag); |
| } |
| |
| @Override |
| public void setNeedClientAuth(boolean need) { |
| sslParameters.setNeedClientAuth(need); |
| } |
| |
| @Override |
| public void setUseClientMode(boolean mode) { |
| synchronized (stateLock) { |
| if (engineState != EngineState.MODE_SET && engineState != EngineState.NEW) { |
| throw new IllegalArgumentException( |
| "Can not change mode after handshake: engineState == " + engineState); |
| } |
| engineState = EngineState.MODE_SET; |
| } |
| sslParameters.setUseClientMode(mode); |
| } |
| |
| @Override |
| public void setWantClientAuth(boolean want) { |
| sslParameters.setWantClientAuth(want); |
| } |
| |
| private static void checkIndex(int length, int offset, int count) { |
| if (offset < 0) { |
| throw new IndexOutOfBoundsException("offset < 0"); |
| } else if (count < 0) { |
| throw new IndexOutOfBoundsException("count < 0"); |
| } else if (offset > length) { |
| throw new IndexOutOfBoundsException("offset > length"); |
| } else if (offset > length - count) { |
| throw new IndexOutOfBoundsException("offset + count > length"); |
| } |
| } |
| |
| @Override |
| public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) |
| throws SSLException { |
| if (src == null) { |
| throw new IllegalArgumentException("src == null"); |
| } else if (dsts == null) { |
| throw new IllegalArgumentException("dsts == null"); |
| } |
| checkIndex(dsts.length, offset, length); |
| int dstRemaining = 0; |
| for (int i = 0; i < dsts.length; i++) { |
| ByteBuffer dst = dsts[i]; |
| if (dst == null) { |
| throw new IllegalArgumentException("one of the dst == null"); |
| } else if (dst.isReadOnly()) { |
| throw new ReadOnlyBufferException(); |
| } |
| if (i >= offset && i < offset + length) { |
| dstRemaining += dst.remaining(); |
| } |
| } |
| |
| synchronized (stateLock) { |
| // If the inbound direction is closed. we can't send anymore. |
| if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_INBOUND) { |
| return new SSLEngineResult(Status.CLOSED, getHandshakeStatus(), 0, 0); |
| } |
| if (engineState == EngineState.NEW || engineState == EngineState.MODE_SET) { |
| beginHandshake(); |
| } |
| } |
| |
| // If we haven't completed the handshake yet, just let the caller know. |
| HandshakeStatus handshakeStatus = getHandshakeStatus(); |
| if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) { |
| OpenSSLBIOSource source = OpenSSLBIOSource.wrap(src); |
| long sslSessionCtx = 0L; |
| try { |
| sslSessionCtx = NativeCrypto.SSL_do_handshake_bio(sslNativePointer, |
| source.getContext(), handshakeSink.getContext(), this, getUseClientMode(), |
| sslParameters.npnProtocols, sslParameters.alpnProtocols); |
| if (sslSessionCtx != 0) { |
| if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) { |
| engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH; |
| } |
| sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession, |
| getPeerHost(), getPeerPort(), true); |
| } |
| int bytesWritten = handshakeSink.position(); |
| return new SSLEngineResult(Status.OK, getHandshakeStatus(), 0, bytesWritten); |
| } catch (Exception e) { |
| throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed") |
| .initCause(e); |
| } finally { |
| if (sslSession == null && sslSessionCtx != 0) { |
| NativeCrypto.SSL_SESSION_free(sslSessionCtx); |
| } |
| source.release(); |
| } |
| } else if (handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) { |
| return new SSLEngineResult(Status.OK, handshakeStatus, 0, 0); |
| } |
| |
| if (dstRemaining == 0) { |
| return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); |
| } |
| |
| ByteBuffer srcDuplicate = src.duplicate(); |
| OpenSSLBIOSource source = OpenSSLBIOSource.wrap(srcDuplicate); |
| try { |
| int positionBeforeRead = srcDuplicate.position(); |
| int produced = 0; |
| boolean shouldStop = false; |
| |
| while (!shouldStop) { |
| ByteBuffer dst = getNextAvailableByteBuffer(dsts, offset, length); |
| if (dst == null) { |
| shouldStop = true; |
| continue; |
| } |
| ByteBuffer arrayDst = dst; |
| if (dst.isDirect()) { |
| arrayDst = ByteBuffer.allocate(dst.remaining()); |
| } |
| |
| int dstOffset = arrayDst.arrayOffset() + arrayDst.position(); |
| |
| int internalProduced = NativeCrypto.SSL_read_BIO(sslNativePointer, |
| arrayDst.array(), dstOffset, dst.remaining(), source.getContext(), |
| localToRemoteSink.getContext(), this); |
| if (internalProduced <= 0) { |
| shouldStop = true; |
| continue; |
| } |
| arrayDst.position(arrayDst.position() + internalProduced); |
| produced += internalProduced; |
| if (dst != arrayDst) { |
| arrayDst.flip(); |
| dst.put(arrayDst); |
| } |
| } |
| |
| int consumed = srcDuplicate.position() - positionBeforeRead; |
| src.position(srcDuplicate.position()); |
| return new SSLEngineResult(Status.OK, getHandshakeStatus(), consumed, produced); |
| } catch (IOException e) { |
| throw new SSLException(e); |
| } finally { |
| source.release(); |
| } |
| } |
| |
| /** Returns the next non-empty ByteBuffer. */ |
| private ByteBuffer getNextAvailableByteBuffer(ByteBuffer[] buffers, int offset, int length) { |
| for (int i = offset; i < length; ++i) { |
| if (buffers[i].remaining() > 0) { |
| return buffers[i]; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) |
| throws SSLException { |
| if (srcs == null) { |
| throw new IllegalArgumentException("srcs == null"); |
| } else if (dst == null) { |
| throw new IllegalArgumentException("dst == null"); |
| } else if (dst.isReadOnly()) { |
| throw new ReadOnlyBufferException(); |
| } |
| for (ByteBuffer src : srcs) { |
| if (src == null) { |
| throw new IllegalArgumentException("one of the src == null"); |
| } |
| } |
| checkIndex(srcs.length, offset, length); |
| |
| if (dst.remaining() < NativeCrypto.SSL3_RT_MAX_PACKET_SIZE) { |
| return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); |
| } |
| |
| synchronized (stateLock) { |
| // If the outbound direction is closed. we can't send anymore. |
| if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) { |
| return new SSLEngineResult(Status.CLOSED, getHandshakeStatus(), 0, 0); |
| } |
| if (engineState == EngineState.NEW || engineState == EngineState.MODE_SET) { |
| beginHandshake(); |
| } |
| } |
| |
| // If we haven't completed the handshake yet, just let the caller know. |
| HandshakeStatus handshakeStatus = getHandshakeStatus(); |
| if (handshakeStatus == HandshakeStatus.NEED_WRAP) { |
| if (handshakeSink.available() == 0) { |
| long sslSessionCtx = 0L; |
| try { |
| sslSessionCtx = NativeCrypto.SSL_do_handshake_bio(sslNativePointer, |
| nullSource.getContext(), handshakeSink.getContext(), this, |
| getUseClientMode(), sslParameters.npnProtocols, |
| sslParameters.alpnProtocols); |
| if (sslSessionCtx != 0) { |
| if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) { |
| engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH; |
| } |
| sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession, |
| getPeerHost(), getPeerPort(), true); |
| } |
| } catch (Exception e) { |
| throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed") |
| .initCause(e); |
| } finally { |
| if (sslSession == null && sslSessionCtx != 0) { |
| NativeCrypto.SSL_SESSION_free(sslSessionCtx); |
| } |
| } |
| } |
| int bytesWritten = writeSinkToByteBuffer(handshakeSink, dst); |
| return new SSLEngineResult(Status.OK, getHandshakeStatus(), 0, bytesWritten); |
| } else if (handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) { |
| return new SSLEngineResult(Status.OK, handshakeStatus, 0, 0); |
| } |
| |
| try { |
| int totalRead = 0; |
| byte[] buffer = null; |
| |
| for (ByteBuffer src : srcs) { |
| int toRead = src.remaining(); |
| if (buffer == null || toRead > buffer.length) { |
| buffer = new byte[toRead]; |
| } |
| /* |
| * We can't just use .mark() here because the caller might be |
| * using it. |
| */ |
| src.duplicate().get(buffer, 0, toRead); |
| int numRead = NativeCrypto.SSL_write_BIO(sslNativePointer, buffer, toRead, |
| localToRemoteSink.getContext(), this); |
| if (numRead > 0) { |
| src.position(src.position() + numRead); |
| totalRead += numRead; |
| } |
| } |
| |
| return new SSLEngineResult(Status.OK, getHandshakeStatus(), totalRead, |
| writeSinkToByteBuffer(localToRemoteSink, dst)); |
| } catch (IOException e) { |
| throw new SSLException(e); |
| } |
| } |
| |
| /** Writes data available in a BIO sink to a ByteBuffer. */ |
| private static int writeSinkToByteBuffer(OpenSSLBIOSink sink, ByteBuffer dst) { |
| int toWrite = Math.min(sink.available(), dst.remaining()); |
| dst.put(sink.toByteArray(), sink.position(), toWrite); |
| sink.skip(toWrite); |
| return toWrite; |
| } |
| |
| @Override |
| public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) { |
| return sslParameters.clientPSKKeyRequested(identityHint, identity, key, this); |
| } |
| |
| @Override |
| public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) { |
| return sslParameters.serverPSKKeyRequested(identityHint, identity, key, this); |
| } |
| |
| @Override |
| public void onSSLStateChange(long sslSessionNativePtr, int type, int val) { |
| synchronized (stateLock) { |
| switch (type) { |
| case NativeCrypto.SSL_CB_HANDSHAKE_DONE: |
| if (engineState != EngineState.HANDSHAKE_STARTED && |
| engineState != EngineState.READY_HANDSHAKE_CUT_THROUGH) { |
| throw new IllegalStateException("Completed handshake while in mode " |
| + engineState); |
| } |
| engineState = EngineState.HANDSHAKE_COMPLETED; |
| break; |
| case NativeCrypto.SSL_CB_HANDSHAKE_START: |
| // For clients, this will allow the NEED_UNWRAP status to be |
| // returned. |
| engineState = EngineState.HANDSHAKE_STARTED; |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public void verifyCertificateChain(long sslSessionNativePtr, long[] certRefs, |
| String authMethod) throws CertificateException { |
| try { |
| X509TrustManager x509tm = sslParameters.getX509TrustManager(); |
| if (x509tm == null) { |
| throw new CertificateException("No X.509 TrustManager"); |
| } |
| if (certRefs == null || certRefs.length == 0) { |
| throw new SSLException("Peer sent no certificate"); |
| } |
| OpenSSLX509Certificate[] peerCertChain = new OpenSSLX509Certificate[certRefs.length]; |
| for (int i = 0; i < certRefs.length; i++) { |
| peerCertChain[i] = new OpenSSLX509Certificate(certRefs[i]); |
| } |
| |
| // Used for verifyCertificateChain callback |
| handshakeSession = new OpenSSLSessionImpl(sslSessionNativePtr, null, peerCertChain, |
| getPeerHost(), getPeerPort(), null); |
| |
| boolean client = sslParameters.getUseClientMode(); |
| if (client) { |
| Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, getPeerHost()); |
| } else { |
| String authType = peerCertChain[0].getPublicKey().getAlgorithm(); |
| x509tm.checkClientTrusted(peerCertChain, authType); |
| } |
| } catch (CertificateException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new CertificateException(e); |
| } finally { |
| // Clear this before notifying handshake completed listeners |
| handshakeSession = null; |
| } |
| } |
| |
| @Override |
| public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) |
| throws CertificateEncodingException, SSLException { |
| sslParameters.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals, |
| sslNativePointer, this); |
| } |
| |
| private void shutdown() { |
| try { |
| NativeCrypto.SSL_shutdown_BIO(sslNativePointer, nullSource.getContext(), |
| localToRemoteSink.getContext(), this); |
| } catch (IOException ignored) { |
| /* |
| * TODO: The RI ignores close failures in SSLSocket, but need to |
| * investigate whether it does for SSLEngine. |
| */ |
| } |
| } |
| |
| private void shutdownAndFreeSslNative() { |
| try { |
| shutdown(); |
| } finally { |
| free(); |
| } |
| } |
| |
| private void free() { |
| if (sslNativePointer == 0) { |
| return; |
| } |
| NativeCrypto.SSL_free(sslNativePointer); |
| sslNativePointer = 0; |
| } |
| |
| @Override |
| protected void finalize() throws Throwable { |
| try { |
| free(); |
| } finally { |
| super.finalize(); |
| } |
| } |
| |
| @Override |
| public String chooseServerAlias(X509KeyManager keyManager, String keyType) { |
| if (keyManager instanceof X509ExtendedKeyManager) { |
| X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager; |
| return ekm.chooseEngineServerAlias(keyType, null, this); |
| } else { |
| return keyManager.chooseServerAlias(keyType, null, null); |
| } |
| } |
| |
| @Override |
| public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers, |
| String[] keyTypes) { |
| if (keyManager instanceof X509ExtendedKeyManager) { |
| X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager; |
| return ekm.chooseEngineClientAlias(keyTypes, issuers, this); |
| } else { |
| return keyManager.chooseClientAlias(keyTypes, issuers, null); |
| } |
| } |
| |
| @Override |
| public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) { |
| return keyManager.chooseServerKeyIdentityHint(this); |
| } |
| |
| @Override |
| public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) { |
| return keyManager.chooseClientKeyIdentity(identityHint, this); |
| } |
| |
| @Override |
| public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) { |
| return keyManager.getKey(identityHint, identity, this); |
| } |
| } |