You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2015/06/18 19:13:41 UTC
svn commit: r1686279 [2/2] -
/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,1312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.CertificateException;
+import javax.security.cert.X509Certificate;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jni.Buffer;
+import org.apache.tomcat.jni.Pool;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.util.buf.ByteBufferUtils;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Implements a {@link SSLEngine} using
+ * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL
+ * BIO abstractions</a>.
+ */
+public final class OpenSSLEngine extends SSLEngine {
+
+ private static final Log logger = LogFactory.getLog(OpenSSLEngine.class);
+ private static final StringManager sm = StringManager.getManager(OpenSSLEngine.class);
+
+ private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
+ private static final SSLException ENGINE_CLOSED = new SSLException(sm.getString("engine.engineClosed"));
+ private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException(sm.getString("engine.renegociationUnsupported"));
+ private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException(sm.getString("engine.oversizedPacket"));
+
+ private static final Set<String> AVAILABLE_CIPHER_SUITES;
+
+ static {
+ final Set<String> availableCipherSuites = new LinkedHashSet<String>(128);
+ final long aprPool = Pool.create(0);
+ try {
+ final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
+ try {
+ SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL);
+ SSLContext.setCipherSuite(sslCtx, "ALL");
+ final long ssl = SSL.newSSL(sslCtx, true);
+ try {
+ for (String c: SSL.getCiphers(ssl)) {
+ // Filter out bad input.
+ if (c == null || c.length() == 0 || availableCipherSuites.contains(c)) {
+ continue;
+ }
+ availableCipherSuites.add(CipherSuiteConverter.toJava(c, "ALL"));
+ }
+ } finally {
+ SSL.freeSSL(ssl);
+ }
+ } finally {
+ SSLContext.free(sslCtx);
+ }
+ } catch (Exception e) {
+ logger.warn(sm.getString("engine.ciphersFailure"), e);
+ } finally {
+ Pool.destroy(aprPool);
+ }
+ AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites);
+ }
+
+ static {
+ ENGINE_CLOSED.setStackTrace(new StackTraceElement[0]);
+ RENEGOTIATION_UNSUPPORTED.setStackTrace(new StackTraceElement[0]);
+ ENCRYPTED_PACKET_OVERSIZED.setStackTrace(new StackTraceElement[0]);
+ DESTROYED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(OpenSSLEngine.class, "destroyed");
+ SESSION_UPDATER = AtomicReferenceFieldUpdater.newUpdater(OpenSSLEngine.class, SSLSession.class, "session");
+ }
+
+ private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
+ private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024;
+ private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
+
+ // Protocols
+ protected static final int VERIFY_DEPTH = 10;
+
+ private static final String[] SUPPORTED_PROTOCOLS = {
+ Constants.SSL_PROTO_SSLv2Hello,
+ Constants.SSL_PROTO_SSLv2,
+ Constants.SSL_PROTO_SSLv3,
+ Constants.SSL_PROTO_TLSv1,
+ Constants.SSL_PROTO_TLSv1_1,
+ Constants.SSL_PROTO_TLSv1_2
+ };
+ private static final Set<String> SUPPORTED_PROTOCOLS_SET = new HashSet<String>(Arrays.asList(SUPPORTED_PROTOCOLS));
+
+ // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
+ static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
+
+ static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
+
+ enum ClientAuthMode {
+ NONE,
+ OPTIONAL,
+ REQUIRE,
+ }
+
+ private static final AtomicIntegerFieldUpdater<OpenSSLEngine> DESTROYED_UPDATER;
+ private static final AtomicReferenceFieldUpdater<OpenSSLEngine, SSLSession> SESSION_UPDATER;
+
+ private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
+
+ private static final long EMPTY_ADDR = Buffer.address(ByteBuffer.allocate(0));
+
+ // OpenSSL state
+ private long ssl;
+ private long networkBIO;
+
+ /**
+ * 0 - not accepted, 1 - accepted implicitly via wrap()/unwrap(), 2 -
+ * accepted explicitly via beginHandshake() call
+ */
+ private int accepted;
+ private boolean handshakeFinished;
+ private boolean receivedShutdown;
+ @SuppressWarnings("UnusedDeclaration")
+ private volatile int destroyed;
+
+ // Use an invalid cipherSuite until the handshake is completed
+ // See http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html#getSession()
+ private volatile String cipher;
+ private volatile String applicationProtocol;
+
+ // We store this outside of the SslSession so we not need to create an instance during verifyCertificates(...)
+ private volatile Certificate[] peerCerts;
+ private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
+
+ // SSL Engine status variables
+ private boolean isInboundDone;
+ private boolean isOutboundDone;
+ private boolean engineClosed;
+
+ private final boolean clientMode;
+ private final String fallbackApplicationProtocol;
+ private final OpenSSLSessionContext sessionContext;
+
+ @SuppressWarnings("unused")
+ private volatile SSLSession session;
+
+ /**
+ * Creates a new instance
+ *
+ * @param sslCtx an OpenSSL {@code SSL_CTX} object
+ * @param alloc the {@link ByteBufAllocator} that will be used by this
+ * engine
+ * @param clientMode {@code true} if this is used for clients, {@code false}
+ * otherwise
+ * @param sessionContext the {@link OpenSslSessionContext} this
+ * {@link SSLEngine} belongs to.
+ */
+ OpenSSLEngine(long sslCtx, String fallbackApplicationProtocol,
+ boolean clientMode, OpenSSLSessionContext sessionContext) {
+ if (sslCtx == 0) {
+ throw new IllegalArgumentException(sm.getString("engine.noSSLContext"));
+ }
+ ssl = SSL.newSSL(sslCtx, !clientMode);
+ networkBIO = SSL.makeNetworkBIO(ssl);
+ this.fallbackApplicationProtocol = fallbackApplicationProtocol;
+ this.clientMode = clientMode;
+ this.sessionContext = sessionContext;
+ }
+
+ /**
+ * Destroys this engine.
+ */
+ public synchronized void shutdown() {
+ if (DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
+ SSL.freeSSL(ssl);
+ SSL.freeBIO(networkBIO);
+ ssl = networkBIO = 0;
+
+ // internal errors can cause shutdown without marking the engine closed
+ isInboundDone = isOutboundDone = engineClosed = true;
+ }
+ }
+
+ /**
+ * Write plaintext data to the OpenSSL internal BIO
+ *
+ * Calling this function with src.remaining == 0 is undefined.
+ */
+ private int writePlaintextData(final ByteBuffer src) {
+ final int pos = src.position();
+ final int limit = src.limit();
+ final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
+ final int sslWrote;
+
+ if (src.isDirect()) {
+ final long addr = Buffer.address(src) + pos;
+ sslWrote = SSL.writeToSSL(ssl, addr, len);
+ if (sslWrote > 0) {
+ src.position(pos + sslWrote);
+ return sslWrote;
+ }
+ } else {
+ ByteBuffer buf = ByteBuffer.allocateDirect(len);
+ try {
+ final long addr = memoryAddress(buf);
+
+ src.limit(pos + len);
+
+ buf.put(src);
+ src.limit(limit);
+
+ sslWrote = SSL.writeToSSL(ssl, addr, len);
+ if (sslWrote > 0) {
+ src.position(pos + sslWrote);
+ return sslWrote;
+ } else {
+ src.position(pos);
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ throw new IllegalStateException(sm.getString("engine.writeToSSLFailed", sslWrote));
+ }
+
+ /**
+ * Write encrypted data to the OpenSSL network BIO.
+ */
+ private int writeEncryptedData(final ByteBuffer src) {
+ final int pos = src.position();
+ final int len = src.remaining();
+ if (src.isDirect()) {
+ final long addr = Buffer.address(src) + pos;
+ final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
+ if (netWrote >= 0) {
+ src.position(pos + netWrote);
+ return netWrote;
+ }
+ } else {
+ ByteBuffer buf = ByteBuffer.allocateDirect(len);
+ try {
+ final long addr = memoryAddress(buf);
+
+ buf.put(src);
+
+ final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
+ if (netWrote >= 0) {
+ src.position(pos + netWrote);
+ return netWrote;
+ } else {
+ src.position(pos);
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Read plaintext data from the OpenSSL internal BIO
+ */
+ private int readPlaintextData(final ByteBuffer dst) {
+ if (dst.isDirect()) {
+ final int pos = dst.position();
+ final long addr = Buffer.address(dst) + pos;
+ final int len = dst.limit() - pos;
+ final int sslRead = SSL.readFromSSL(ssl, addr, len);
+ if (sslRead > 0) {
+ dst.position(pos + sslRead);
+ return sslRead;
+ }
+ } else {
+ final int pos = dst.position();
+ final int limit = dst.limit();
+ final int len = Math.min(MAX_ENCRYPTED_PACKET_LENGTH, limit - pos);
+ final ByteBuffer buf = ByteBuffer.allocateDirect(len);
+ try {
+ final long addr = memoryAddress(buf);
+
+ final int sslRead = SSL.readFromSSL(ssl, addr, len);
+ if (sslRead > 0) {
+ buf.limit(sslRead);
+ dst.limit(pos + sslRead);
+ dst.put(buf);
+ dst.limit(limit);
+ return sslRead;
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Read encrypted data from the OpenSSL network BIO
+ */
+ private int readEncryptedData(final ByteBuffer dst, final int pending) {
+ if (dst.isDirect() && dst.remaining() >= pending) {
+ final int pos = dst.position();
+ final long addr = Buffer.address(dst) + pos;
+ final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
+ if (bioRead > 0) {
+ dst.position(pos + bioRead);
+ return bioRead;
+ }
+ } else {
+ final ByteBuffer buf = ByteBuffer.allocateDirect(pending);
+ try {
+ final long addr = memoryAddress(buf);
+
+ final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
+ if (bioRead > 0) {
+ buf.limit(bioRead);
+ int oldLimit = dst.limit();
+ dst.limit(dst.position() + bioRead);
+ dst.put(buf);
+ dst.limit(oldLimit);
+ return bioRead;
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ public synchronized SSLEngineResult wrap(final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
+
+ // Check to make sure the engine has not been closed
+ if (destroyed != 0) {
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
+ }
+
+ // Throw required runtime exceptions
+ if (srcs == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBuffer"));
+ }
+ if (dst == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBuffer"));
+ }
+
+ if (offset >= srcs.length || offset + length > srcs.length) {
+ throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", offset, length, srcs.length));
+ }
+
+ if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ // Prepare OpenSSL to work in server mode and receive handshake
+ if (accepted == 0) {
+ beginHandshakeImplicitly();
+ }
+
+ // In handshake or close_notify stages, check if call to wrap was made
+ // without regard to the handshake status.
+ SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+
+ if ((!handshakeFinished || engineClosed) && handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+ return new SSLEngineResult(getEngineStatus(), SSLEngineResult.HandshakeStatus.NEED_UNWRAP, 0, 0);
+ }
+
+ int bytesProduced = 0;
+ int pendingNet;
+
+ // Check for pending data in the network BIO
+ pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
+ if (pendingNet > 0) {
+ // Do we have enough room in dst to write encrypted data?
+ int capacity = dst.remaining();
+ if (capacity < pendingNet) {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, handshakeStatus, 0, bytesProduced);
+ }
+
+ // Write the pending data from the network BIO into the dst buffer
+ try {
+ bytesProduced += readEncryptedData(dst, pendingNet);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ // If isOuboundDone is set, then the data from the network BIO
+ // was the close_notify message -- we are not required to wait
+ // for the receipt the peer's close_notify message -- shutdown.
+ if (isOutboundDone) {
+ shutdown();
+ }
+
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
+ }
+
+ // There was no pending data in the network BIO -- encrypt any application data
+ int bytesConsumed = 0;
+ int endOffset = offset + length;
+ for (int i = offset; i < endOffset; ++i) {
+ final ByteBuffer src = srcs[i];
+ if (src == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBufferInArray"));
+ }
+ while (src.hasRemaining()) {
+
+ // Write plaintext application data to the SSL engine
+ try {
+ bytesConsumed += writePlaintextData(src);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ // Check to see if the engine wrote data into the network BIO
+ pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
+ if (pendingNet > 0) {
+ // Do we have enough room in dst to write encrypted data?
+ int capacity = dst.remaining();
+ if (capacity < pendingNet) {
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
+ }
+
+ // Write the pending data from the network BIO into the dst buffer
+ try {
+ bytesProduced += readEncryptedData(dst, pendingNet);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ }
+ }
+ }
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ }
+
+ @Override
+ public synchronized SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException {
+ // Check to make sure the engine has not been closed
+ if (destroyed != 0) {
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
+ }
+
+ // Throw requried runtime exceptions
+ if (src == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBuffer"));
+ }
+ if (dsts == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBuffer"));
+ }
+ if (offset >= dsts.length || offset + length > dsts.length) {
+ throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", offset, length, dsts.length));
+ }
+
+ int capacity = 0;
+ final int endOffset = offset + length;
+ for (int i = offset; i < endOffset; i++) {
+ ByteBuffer dst = dsts[i];
+ if (dst == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBufferInArray"));
+ }
+ if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+ capacity += dst.remaining();
+ }
+
+ // Prepare OpenSSL to work in server mode and receive handshake
+ if (accepted == 0) {
+ beginHandshakeImplicitly();
+ }
+
+ // In handshake or close_notify stages, check if call to unwrap was made
+ // without regard to the handshake status.
+ SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+ if ((!handshakeFinished || engineClosed) && handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ return new SSLEngineResult(getEngineStatus(), SSLEngineResult.HandshakeStatus.NEED_WRAP, 0, 0);
+ }
+
+ int len = src.remaining();
+
+ // protect against protocol overflow attack vector
+ if (len > MAX_ENCRYPTED_PACKET_LENGTH) {
+ isInboundDone = true;
+ isOutboundDone = true;
+ engineClosed = true;
+ shutdown();
+ throw ENCRYPTED_PACKET_OVERSIZED;
+ }
+
+ // Write encrypted data to network BIO
+ int bytesConsumed = -1;
+ try {
+ int written = writeEncryptedData(src);
+ if (written >= 0) {
+ if (bytesConsumed == -1) {
+ bytesConsumed = written;
+ } else {
+ bytesConsumed += written;
+ }
+ }
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+ if (bytesConsumed >= 0) {
+ int lastPrimingReadResult = SSL.readFromSSL(ssl, EMPTY_ADDR, 0); // priming read
+ // check if SSL_read returned <= 0. In this case we need to check the error and see if it was something
+ // fatal.
+ if (lastPrimingReadResult <= 0) {
+ // Check for OpenSSL errors caused by the priming read
+ long error = SSL.getLastErrorNumber();
+ if (error != SSL.SSL_ERROR_NONE) {
+ String err = SSL.getErrorString(error);
+ if (logger.isDebugEnabled()) {
+ logger.debug(sm.getString("engine.readFromSSLFailed", error, lastPrimingReadResult, err));
+ }
+ // There was an internal error -- shutdown
+ shutdown();
+ throw new SSLException(err);
+ }
+ }
+ } else {
+ // Reset to 0 as -1 is used to signal that nothing was written and no priming read needs to be done
+ bytesConsumed = 0;
+ }
+
+ // There won't be any application data until we're done handshaking
+ //
+ // We first check handshakeFinished to eliminate the overhead of extra JNI call if possible.
+ int pendingApp = (handshakeFinished || SSL.isInInit(ssl) == 0) ? SSL.pendingReadableBytesInSSL(ssl) : 0;
+ int bytesProduced = 0;
+
+ if (pendingApp > 0) {
+ // Do we have enough room in dsts to write decrypted data?
+ if (capacity < pendingApp) {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, 0);
+ }
+
+ // Write decrypted data to dsts buffers
+ int idx = offset;
+ while (idx < endOffset) {
+ ByteBuffer dst = dsts[idx];
+ if (!dst.hasRemaining()) {
+ idx++;
+ continue;
+ }
+
+ if (pendingApp <= 0) {
+ break;
+ }
+
+ int bytesRead;
+ try {
+ bytesRead = readPlaintextData(dst);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ if (bytesRead == 0) {
+ break;
+ }
+
+ bytesProduced += bytesRead;
+ pendingApp -= bytesRead;
+
+ if (!dst.hasRemaining()) {
+ idx++;
+ }
+ }
+ }
+
+ // Check to see if we received a close_notify message from the peer
+ if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) {
+ receivedShutdown = true;
+ closeOutbound();
+ closeInbound();
+ }
+ if (bytesProduced == 0 && bytesConsumed == 0) {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
+ } else {
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ }
+ }
+
+ @Override
+ public Runnable getDelegatedTask() {
+ // Currently, we do not delegate SSL computation tasks
+ // TODO: in the future, possibly create tasks to do encrypt / decrypt async
+ return null;
+ }
+
+ @Override
+ public synchronized void closeInbound() throws SSLException {
+ if (isInboundDone) {
+ return;
+ }
+
+ isInboundDone = true;
+ engineClosed = true;
+
+ shutdown();
+
+ if (accepted != 0 && !receivedShutdown) {
+ throw new SSLException(sm.getString("engine.inboundClose"));
+ }
+ }
+
+ @Override
+ public synchronized boolean isInboundDone() {
+ return isInboundDone || engineClosed;
+ }
+
+ @Override
+ public synchronized void closeOutbound() {
+ if (isOutboundDone) {
+ return;
+ }
+
+ isOutboundDone = true;
+ engineClosed = true;
+
+ if (accepted != 0 && destroyed == 0) {
+ int mode = SSL.getShutdown(ssl);
+ if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
+ SSL.shutdownSSL(ssl);
+ }
+ } else {
+ // engine closing before initial handshake
+ shutdown();
+ }
+ }
+
+ @Override
+ public synchronized boolean isOutboundDone() {
+ return isOutboundDone;
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ Set<String> availableCipherSuites = AVAILABLE_CIPHER_SUITES;
+ return availableCipherSuites.toArray(new String[availableCipherSuites.size()]);
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ String[] enabled = SSL.getCiphers(ssl);
+ if (enabled == null) {
+ return new String[0];
+ } else {
+ for (int i = 0; i < enabled.length; i++) {
+ String mapped = toJavaCipherSuite(enabled[i]);
+ if (mapped != null) {
+ enabled[i] = mapped;
+ }
+ }
+ return enabled;
+ }
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] cipherSuites) {
+ if (cipherSuites == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullCipherSuite"));
+ }
+ final StringBuilder buf = new StringBuilder();
+ for (String cipherSuite : cipherSuites) {
+ if (cipherSuite == null) {
+ break;
+ }
+ String converted = CipherSuiteConverter.toOpenSsl(cipherSuite);
+ if (converted != null) {
+ cipherSuite = converted;
+ }
+ if (!AVAILABLE_CIPHER_SUITES.contains(cipherSuite)) {
+ logger.debug(sm.getString("engine.unsupportedCipher", cipherSuite, converted));
+ }
+
+ buf.append(cipherSuite);
+ buf.append(':');
+ }
+
+ if (buf.length() == 0) {
+ throw new IllegalArgumentException(sm.getString("engine.emptyCipherSuite"));
+ }
+ buf.setLength(buf.length() - 1);
+
+ final String cipherSuiteSpec = buf.toString();
+ try {
+ SSL.setCipherSuites(ssl, cipherSuiteSpec);
+ } catch (Exception e) {
+ throw new IllegalStateException(sm.getString("engine.failedCipherSuite", cipherSuiteSpec), e);
+ }
+ }
+
+ @Override
+ public String[] getSupportedProtocols() {
+ return SUPPORTED_PROTOCOLS.clone();
+ }
+
+ @Override
+ public String[] getEnabledProtocols() {
+ List<String> enabled = new ArrayList<String>();
+ // Seems like there is no way to explict disable SSLv2Hello in openssl so it is always enabled
+ enabled.add(Constants.SSL_PROTO_SSLv2Hello);
+ int opts = SSL.getOptions(ssl);
+ if ((opts & SSL.SSL_OP_NO_TLSv1) == 0) {
+ enabled.add(Constants.SSL_PROTO_TLSv1);
+ }
+ if ((opts & SSL.SSL_OP_NO_TLSv1_1) == 0) {
+ enabled.add(Constants.SSL_PROTO_TLSv1_1);
+ }
+ if ((opts & SSL.SSL_OP_NO_TLSv1_2) == 0) {
+ enabled.add(Constants.SSL_PROTO_TLSv1_2);
+ }
+ if ((opts & SSL.SSL_OP_NO_SSLv2) == 0) {
+ enabled.add(Constants.SSL_PROTO_SSLv2);
+ }
+ if ((opts & SSL.SSL_OP_NO_SSLv3) == 0) {
+ enabled.add(Constants.SSL_PROTO_SSLv3);
+ }
+ int size = enabled.size();
+ if (size == 0) {
+ return new String[0];
+ } else {
+ return enabled.toArray(new String[size]);
+ }
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ if (protocols == null) {
+ // This is correct from the API docs
+ throw new IllegalArgumentException();
+ }
+ boolean sslv2 = false;
+ boolean sslv3 = false;
+ boolean tlsv1 = false;
+ boolean tlsv1_1 = false;
+ boolean tlsv1_2 = false;
+ for (String p : protocols) {
+ if (!SUPPORTED_PROTOCOLS_SET.contains(p)) {
+ throw new IllegalArgumentException(sm.getString("engine.unsupportedProtocol", p));
+ }
+ if (p.equals(Constants.SSL_PROTO_SSLv2)) {
+ sslv2 = true;
+ } else if (p.equals(Constants.SSL_PROTO_SSLv3)) {
+ sslv3 = true;
+ } else if (p.equals(Constants.SSL_PROTO_TLSv1)) {
+ tlsv1 = true;
+ } else if (p.equals(Constants.SSL_PROTO_TLSv1_1)) {
+ tlsv1_1 = true;
+ } else if (p.equals(Constants.SSL_PROTO_TLSv1_2)) {
+ tlsv1_2 = true;
+ }
+ }
+ // Enable all and then disable what we not want
+ SSL.setOptions(ssl, SSL.SSL_OP_ALL);
+
+ if (!sslv2) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv2);
+ }
+ if (!sslv3) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv3);
+ }
+ if (!tlsv1) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1);
+ }
+ if (!tlsv1_1) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_1);
+ }
+ if (!tlsv1_2) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2);
+ }
+ }
+
+ private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
+ byte[][] chain = SSL.getPeerCertChain(ssl);
+ byte[] clientCert;
+ if (!clientMode) {
+ // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
+ // We use SSL_get_peer_certificate to get it in this case and add it to our array later.
+ //
+ // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
+ clientCert = SSL.getPeerCertificate(ssl);
+ } else {
+ clientCert = null;
+ }
+
+ if (chain == null && clientCert == null) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ int len = 0;
+ if (chain != null) {
+ len += chain.length;
+ }
+
+ int i = 0;
+ Certificate[] peerCerts;
+ if (clientCert != null) {
+ len++;
+ peerCerts = new Certificate[len];
+ peerCerts[i++] = new OpenSslX509Certificate(clientCert);
+ } else {
+ peerCerts = new Certificate[len];
+ }
+ if (chain != null) {
+ int a = 0;
+ for (; i < peerCerts.length; i++) {
+ peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
+ }
+ }
+ return peerCerts;
+ }
+
+ @Override
+ public SSLSession getSession() {
+ // A other methods on SSLEngine are thread-safe we also need to make this thread-safe...
+ SSLSession session = this.session;
+ if (session == null) {
+ session = new SSLSession() {
+ // SSLSession implementation seems to not need to be thread-safe so no need for volatile etc.
+ private X509Certificate[] x509PeerCerts;
+
+ // lazy init for memory reasons
+ private Map<String, Object> values;
+
+ @Override
+ public byte[] getId() {
+ // We don't cache that to keep memory usage to a minimum.
+ byte[] id = SSL.getSessionId(ssl);
+ if (id == null) {
+ // The id should never be null, if it was null then the SESSION itself was not valid.
+ throw new IllegalStateException(sm.getString("engine.noSession"));
+ }
+ return id;
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ return sessionContext;
+ }
+
+ @Override
+ public long getCreationTime() {
+ // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
+ return SSL.getTime(ssl) * 1000L;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ // TODO: Add proper implementation
+ return getCreationTime();
+ }
+
+ @Override
+ public void invalidate() {
+ // NOOP
+ }
+
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ if (name == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullName"));
+ }
+ if (value == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullValue"));
+ }
+ Map<String, Object> values = this.values;
+ if (values == null) {
+ // Use size of 2 to keep the memory overhead small
+ values = this.values = new HashMap<String, Object>(2);
+ }
+ Object old = values.put(name, value);
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
+ }
+ notifyUnbound(old, name);
+ }
+
+ @Override
+ public Object getValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullName"));
+ }
+ if (values == null) {
+ return null;
+ }
+ return values.get(name);
+ }
+
+ @Override
+ public void removeValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullName"));
+ }
+ Map<String, Object> values = this.values;
+ if (values == null) {
+ return;
+ }
+ Object old = values.remove(name);
+ notifyUnbound(old, name);
+ }
+
+ @Override
+ public String[] getValueNames() {
+ Map<String, Object> values = this.values;
+ if (values == null || values.isEmpty()) {
+ return new String[0];
+ }
+ return values.keySet().toArray(new String[values.size()]);
+ }
+
+ private void notifyUnbound(Object value, String name) {
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
+ }
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ // these are lazy created to reduce memory overhead
+ Certificate[] c = peerCerts;
+ if (c == null) {
+ if (SSL.isInInit(ssl) != 0) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ c = peerCerts = initPeerCertChain();
+ }
+ return c;
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ // TODO: Find out how to get these
+ return EMPTY_CERTIFICATES;
+ }
+
+ @Override
+ public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+ // these are lazy created to reduce memory overhead
+ X509Certificate[] c = x509PeerCerts;
+ if (c == null) {
+ if (SSL.isInInit(ssl) != 0) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ byte[][] chain = SSL.getPeerCertChain(ssl);
+ if (chain == null) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ X509Certificate[] peerCerts = new X509Certificate[chain.length];
+ for (int i = 0; i < peerCerts.length; i++) {
+ try {
+ peerCerts[i] = X509Certificate.getInstance(chain[i]);
+ } catch (CertificateException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ c = x509PeerCerts = peerCerts;
+ }
+ return c;
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ Certificate[] peer = getPeerCertificates();
+ if (peer == null || peer.length == 0) {
+ return null;
+ }
+ return principal(peer);
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ Certificate[] local = getLocalCertificates();
+ if (local == null || local.length == 0) {
+ return null;
+ }
+ return principal(local);
+ }
+
+ private Principal principal(Certificate[] certs) {
+ return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ if (!handshakeFinished) {
+ return INVALID_CIPHER;
+ }
+ if (cipher == null) {
+ String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
+ if (c != null) {
+ cipher = c;
+ }
+ }
+ return cipher;
+ }
+
+ @Override
+ public String getProtocol() {
+ String applicationProtocol = OpenSSLEngine.this.applicationProtocol;
+ if (applicationProtocol == null) {
+ applicationProtocol = SSL.getNextProtoNegotiated(ssl);
+ if (applicationProtocol == null) {
+ applicationProtocol = fallbackApplicationProtocol;
+ }
+ if (applicationProtocol != null) {
+ OpenSSLEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
+ } else {
+ OpenSSLEngine.this.applicationProtocol = applicationProtocol = "";
+ }
+ }
+ String version = SSL.getVersion(ssl);
+ if (applicationProtocol.isEmpty()) {
+ return version;
+ } else {
+ return version + ':' + applicationProtocol;
+ }
+ }
+
+ @Override
+ public String getPeerHost() {
+ return null;
+ }
+
+ @Override
+ public int getPeerPort() {
+ return 0;
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ return MAX_ENCRYPTED_PACKET_LENGTH;
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ return MAX_PLAINTEXT_LENGTH;
+ }
+ };
+
+ if (!SESSION_UPDATER.compareAndSet(this, null, session)) {
+ // Was lazy created in the meantime so get the current reference.
+ session = this.session;
+ }
+ }
+
+ return session;
+ }
+
+ @Override
+ public synchronized void beginHandshake() throws SSLException {
+ if (engineClosed || destroyed != 0) {
+ throw ENGINE_CLOSED;
+ }
+ switch (accepted) {
+ case 0:
+ handshake();
+ accepted = 2;
+ break;
+ case 1:
+ // A user did not start handshake by calling this method by him/herself,
+ // but handshake has been started already by wrap() or unwrap() implicitly.
+ // Because it's the user's first time to call this method, it is unfair to
+ // raise an exception. From the user's standpoint, he or she never asked
+ // for renegotiation.
+
+ accepted = 2; // Next time this method is invoked by the user, we should raise an exception.
+ break;
+ case 2:
+ throw RENEGOTIATION_UNSUPPORTED;
+ default:
+ throw new Error();
+ }
+ }
+
+ private void beginHandshakeImplicitly() throws SSLException {
+ if (engineClosed || destroyed != 0) {
+ throw ENGINE_CLOSED;
+ }
+
+ if (accepted == 0) {
+ handshake();
+ accepted = 1;
+ }
+ }
+
+ private void handshake() throws SSLException {
+ int code = SSL.doHandshake(ssl);
+ if (code <= 0) {
+ // Check for OpenSSL errors caused by the handshake
+ long error = SSL.getLastErrorNumber();
+ if (error != SSL.SSL_ERROR_NONE) {
+ String err = SSL.getErrorString(error);
+ if (logger.isDebugEnabled()) {
+ logger.debug(sm.getString("engine.handshakeFailure", err));
+ }
+ // There was an internal error -- shutdown
+ shutdown();
+ throw new SSLException(err);
+ }
+ } else {
+ // if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update
+ // handshakeFinished directly and so eliminate uncessary calls to SSL.isInInit(...)
+ handshakeFinished = true;
+ }
+ }
+
+ private static long memoryAddress(ByteBuffer buf) {
+ return Buffer.address(buf);
+ }
+
+ private SSLEngineResult.Status getEngineStatus() {
+ return engineClosed ? SSLEngineResult.Status.CLOSED : SSLEngineResult.Status.OK;
+ }
+
+ @Override
+ public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
+ if (accepted == 0 || destroyed != 0) {
+ return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+ }
+
+ // Check if we are in the initial handshake phase
+ if (!handshakeFinished) {
+ // There is pending data in the network BIO -- call wrap
+ if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
+ return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+ }
+
+ // No pending data to be sent to the peer
+ // Check to see if we have finished handshaking
+ if (SSL.isInInit(ssl) == 0) {
+ handshakeFinished = true;
+ return SSLEngineResult.HandshakeStatus.FINISHED;
+ }
+
+ // No pending data and still handshaking
+ // Must be waiting on the peer to send more data
+ return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+ }
+
+ // Check if we are in the shutdown phase
+ if (engineClosed) {
+ // Waiting to send the close_notify message
+ if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
+ return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+ }
+
+ // Must be waiting to receive the close_notify message
+ return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+ }
+
+ return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+ }
+
+ /**
+ * Converts the specified OpenSSL cipher suite to the Java cipher suite.
+ */
+ private String toJavaCipherSuite(String openSslCipherSuite) {
+ if (openSslCipherSuite == null) {
+ return null;
+ }
+
+ String prefix = toJavaCipherSuitePrefix(SSL.getVersion(ssl));
+ return CipherSuiteConverter.toJava(openSslCipherSuite, prefix);
+ }
+
+ /**
+ * Converts the protocol version string returned by
+ * {@link SSL#getVersion(long)} to protocol family string.
+ */
+ private static String toJavaCipherSuitePrefix(String protocolVersion) {
+ final char c;
+ if (protocolVersion == null || protocolVersion.length() == 0) {
+ c = 0;
+ } else {
+ c = protocolVersion.charAt(0);
+ }
+
+ switch (c) {
+ case 'T':
+ return "TLS";
+ case 'S':
+ return "SSL";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ @Override
+ public void setUseClientMode(boolean clientMode) {
+ if (clientMode != this.clientMode) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ return clientMode;
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean b) {
+ setClientAuth(b ? ClientAuthMode.REQUIRE : ClientAuthMode.NONE);
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ return clientAuth == ClientAuthMode.REQUIRE;
+ }
+
+ @Override
+ public void setWantClientAuth(boolean b) {
+ setClientAuth(b ? ClientAuthMode.OPTIONAL : ClientAuthMode.NONE);
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ return clientAuth == ClientAuthMode.OPTIONAL;
+ }
+
+ private void setClientAuth(ClientAuthMode mode) {
+ if (clientMode) {
+ return;
+ }
+ synchronized (this) {
+ if (clientAuth == mode) {
+ // No need to issue any JNI calls if the mode is the same
+ return;
+ }
+ switch (mode) {
+ case NONE:
+ SSL.setVerify(ssl, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
+ break;
+ case REQUIRE:
+ SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRE, VERIFY_DEPTH);
+ break;
+ case OPTIONAL:
+ SSL.setVerify(ssl, SSL.SSL_CVERIFY_OPTIONAL, VERIFY_DEPTH);
+ break;
+ }
+ clientAuth = mode;
+ }
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean b) {
+ if (b) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ return false;
+ }
+
+ @Override
+ @SuppressWarnings("FinalizeDeclaration")
+ protected void finalize() throws Throwable {
+ super.finalize();
+ // Call shutdown as the user may have created the OpenSslEngine and not used it at all.
+ shutdown();
+ }
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SSLUtil;
+import org.apache.tomcat.util.net.jsse.JSSESupport;
+
+public class OpenSSLImplementation extends SSLImplementation {
+
+ public static final String IMPLEMENTATION_NAME = "org.apache.tomcat.util.net.openssl.OpenSSLImplementation";
+
+ @Override
+ public String getImplementationName() {
+ return "OpenSSl";
+ }
+
+ @Override
+ public SSLSupport getSSLSupport(SSLSession session) {
+ return new JSSESupport(session);
+ }
+
+ @Override
+ public SSLUtil getSSLUtil(SSLHostConfig sslHostConfig) {
+ return new OpenSSLUtil(sslHostConfig);
+ }
+
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import java.io.File;
+
+import javax.net.ssl.KeyManager;
+
+import org.apache.tomcat.util.res.StringManager;
+
+public class OpenSSLKeyManager implements KeyManager{
+
+ private static final StringManager sm = StringManager.getManager(OpenSSLKeyManager.class);
+
+ private File certificateChain;
+ public File getCertificateChain() { return certificateChain; }
+ public void setCertificateChain(File certificateChain) { this.certificateChain = certificateChain; }
+
+ private File privateKey;
+ public File getPrivateKey() { return privateKey; }
+ public void setPrivateKey(File privateKey) { this.privateKey = privateKey; }
+
+ OpenSSLKeyManager(String certChainFile, String keyFile) {
+ if (certChainFile == null) {
+ throw new IllegalArgumentException(sm.getString("keyManager.nullCertificateChain"));
+ }
+ if (keyFile == null) {
+ throw new IllegalArgumentException(sm.getString("keyManager.nullPrivateKey"));
+ }
+ this.certificateChain = new File(certChainFile);
+ this.privateKey = new File(keyFile);
+ }
+
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tomcat.util.net.Constants;
+
+/**
+ * Get SSL protocols in the right preference order
+ */
+public class OpenSSLProtocols {
+
+ private List<String> openSSLProtocols = new ArrayList<>();
+
+ public OpenSSLProtocols(String preferredJSSEProto) {
+ Collections.addAll(openSSLProtocols, Constants.SSL_PROTO_TLSv1_2,
+ Constants.SSL_PROTO_TLSv1_1, Constants.SSL_PROTO_TLSv1,
+ Constants.SSL_PROTO_SSLv3, Constants.SSL_PROTO_SSLv2);
+ if(openSSLProtocols.contains(preferredJSSEProto)) {
+ openSSLProtocols.remove(preferredJSSEProto);
+ openSSLProtocols.add(0, preferredJSSEProto);
+ }
+ }
+
+ public String[] getProtocols() {
+ return openSSLProtocols.toArray(new String[openSSLProtocols.size()]);
+ }
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLServerSessionContext.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLServerSessionContext.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLServerSessionContext.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLServerSessionContext.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLContext;
+
+
+/**
+ * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side.
+ */
+public final class OpenSSLServerSessionContext extends OpenSSLSessionContext {
+ OpenSSLServerSessionContext(long context) {
+ super(context);
+ }
+
+ @Override
+ public void setSessionTimeout(int seconds) {
+ if (seconds < 0) {
+ throw new IllegalArgumentException();
+ }
+ SSLContext.setSessionCacheTimeout(context, seconds);
+ }
+
+ @Override
+ public int getSessionTimeout() {
+ return (int) SSLContext.getSessionCacheTimeout(context);
+ }
+
+ @Override
+ public void setSessionCacheSize(int size) {
+ if (size < 0) {
+ throw new IllegalArgumentException();
+ }
+ SSLContext.setSessionCacheSize(context, size);
+ }
+
+ @Override
+ public int getSessionCacheSize() {
+ return (int) SSLContext.getSessionCacheSize(context);
+ }
+
+ @Override
+ public void setSessionCacheEnabled(boolean enabled) {
+ long mode = enabled ? SSL.SSL_SESS_CACHE_SERVER : SSL.SSL_SESS_CACHE_OFF;
+ SSLContext.setSessionCacheMode(context, mode);
+ }
+
+ @Override
+ public boolean isSessionCacheEnabled() {
+ return SSLContext.getSessionCacheMode(context) == SSL.SSL_SESS_CACHE_SERVER;
+ }
+
+ /**
+ * Set the context within which session be reused (server side only)
+ * See <a href="http://www.openssl.org/docs/ssl/SSL_CTX_set_session_id_context.html">
+ * man SSL_CTX_set_session_id_context</a>
+ *
+ * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name
+ * of the application and/or the hostname and/or service name
+ * @return {@code true} if success, {@code false} otherwise.
+ */
+ public boolean setSessionIdContext(byte[] sidCtx) {
+ return SSLContext.setSessionIdContext(context, sidCtx);
+ }
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * OpenSSL specific {@link SSLSessionContext} implementation.
+ */
+public abstract class OpenSSLSessionContext implements SSLSessionContext {
+ private static final StringManager sm = StringManager.getManager(OpenSSLSessionContext.class);
+ private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
+
+ private final OpenSSLSessionStats stats;
+ final long context;
+
+ OpenSSLSessionContext(long context) {
+ this.context = context;
+ stats = new OpenSSLSessionStats(context);
+ }
+
+ @Override
+ public SSLSession getSession(byte[] bytes) {
+ return null;
+ }
+
+ @Override
+ public Enumeration<byte[]> getIds() {
+ return EMPTY;
+ }
+
+ /**
+ * Sets the SSL session ticket keys of this context.
+ */
+ public void setTicketKeys(byte[] keys) {
+ if (keys == null) {
+ throw new IllegalArgumentException(sm.getString("sessionContext.nullTicketKeys"));
+ }
+ SSLContext.setSessionTicketKeys(context, keys);
+ }
+
+ /**
+ * Enable or disable caching of SSL sessions.
+ */
+ public abstract void setSessionCacheEnabled(boolean enabled);
+
+ /**
+ * Return {@code true} if caching of SSL sessions is enabled, {@code false} otherwise.
+ */
+ public abstract boolean isSessionCacheEnabled();
+
+ /**
+ * Returns the stats of this context.
+ */
+ public OpenSSLSessionStats stats() {
+ return stats;
+ }
+
+ private static final class EmptyEnumeration implements Enumeration<byte[]> {
+ @Override
+ public boolean hasMoreElements() {
+ return false;
+ }
+
+ @Override
+ public byte[] nextElement() {
+ throw new NoSuchElementException();
+ }
+ }
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import org.apache.tomcat.jni.SSLContext;
+
+/**
+ * Stats exposed by an OpenSSL session context.
+ *
+ * @see <a href="https://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html"><code>SSL_CTX_sess_number</code></a>
+ */
+public final class OpenSSLSessionStats {
+
+ private final long context;
+
+ OpenSSLSessionStats(long context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns the current number of sessions in the internal session cache.
+ */
+ public long number() {
+ return SSLContext.sessionNumber(context);
+ }
+
+ /**
+ * Returns the number of started SSL/TLS handshakes in client mode.
+ */
+ public long connect() {
+ return SSLContext.sessionConnect(context);
+ }
+
+ /**
+ * Returns the number of successfully established SSL/TLS sessions in client mode.
+ */
+ public long connectGood() {
+ return SSLContext.sessionConnectGood(context);
+ }
+
+ /**
+ * Returns the number of start renegotiations in client mode.
+ */
+ public long connectRenegotiate() {
+ return SSLContext.sessionConnectRenegotiate(context);
+ }
+
+ /**
+ * Returns the number of started SSL/TLS handshakes in server mode.
+ */
+ public long accept() {
+ return SSLContext.sessionAccept(context);
+ }
+
+ /**
+ * Returns the number of successfully established SSL/TLS sessions in server mode.
+ */
+ public long acceptGood() {
+ return SSLContext.sessionAcceptGood(context);
+ }
+
+ /**
+ * Returns the number of start renegotiations in server mode.
+ */
+ public long acceptRenegotiate() {
+ return SSLContext.sessionAcceptRenegotiate(context);
+ }
+
+ /**
+ * Returns the number of successfully reused sessions. In client mode, a session set with {@code SSL_set_session}
+ * successfully reused is counted as a hit. In server mode, a session successfully retrieved from internal or
+ * external cache is counted as a hit.
+ */
+ public long hits() {
+ return SSLContext.sessionHits(context);
+ }
+
+ /**
+ * Returns the number of successfully retrieved sessions from the external session cache in server mode.
+ */
+ public long cbHits() {
+ return SSLContext.sessionCbHits(context);
+ }
+
+ /**
+ * Returns the number of sessions proposed by clients that were not found in the internal session cache
+ * in server mode.
+ */
+ public long misses() {
+ return SSLContext.sessionMisses(context);
+ }
+
+ /**
+ * Returns the number of sessions proposed by clients and either found in the internal or external session cache
+ * in server mode, but that were invalid due to timeout. These sessions are not included in the {@link #hits()}
+ * count.
+ */
+ public long timeouts() {
+ return SSLContext.sessionTimeouts(context);
+ }
+
+ /**
+ * Returns the number of sessions that were removed because the maximum session cache size was exceeded.
+ */
+ public long cacheFull() {
+ return SSLContext.sessionCacheFull(context);
+ }
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import java.util.List;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.TrustManager;
+
+import org.apache.tomcat.util.net.SSLContext;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLUtil;
+
+public class OpenSSLUtil implements SSLUtil {
+
+ private final SSLHostConfig sslHostConfig;
+
+ private String[] enabledProtocols = null;
+ private String[] enabledCiphers = null;
+
+ public OpenSSLUtil(SSLHostConfig sslHostConfig) {
+ this.sslHostConfig = sslHostConfig;
+ }
+
+ @Override
+ public SSLContext createSSLContext() throws Exception {
+ return new OpenSSLContext(sslHostConfig);
+ }
+
+ @Override
+ public KeyManager[] getKeyManagers() throws Exception {
+ KeyManager[] managers = {
+ new OpenSSLKeyManager(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateFile()),
+ SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateKeyFile()))
+ };
+ return managers;
+ }
+
+ @Override
+ public TrustManager[] getTrustManagers() throws Exception {
+ return null;
+ }
+
+ @Override
+ public void configureSessionContext(SSLSessionContext sslSessionContext) {
+ // do nothing. configuration is done in the init phase
+ }
+
+ @Override
+ public String[] getEnableableCiphers(SSLContext context) {
+ if (enabledCiphers == null) {
+ List<String> enabledCiphersList = ((OpenSSLContext) context).getCiphers();
+ enabledCiphers = enabledCiphersList.toArray(new String[enabledCiphersList.size()]);
+ }
+ return enabledCiphers;
+ }
+
+ @Override
+ public String[] getEnableableProtocols(SSLContext context) {
+ if (enabledProtocols == null) {
+ enabledProtocols = new OpenSSLProtocols(((OpenSSLContext) context).getEnabledProtocol()).getProtocols();
+ }
+ return enabledProtocols;
+ }
+
+}
Added: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java?rev=1686279&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java Thu Jun 18 17:13:40 2015
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net.openssl;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+final class OpenSslX509Certificate extends X509Certificate {
+
+ private final byte[] bytes;
+ private X509Certificate wrapped;
+
+ public OpenSslX509Certificate(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
+ unwrap().checkValidity();
+ }
+
+ @Override
+ public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
+ unwrap().checkValidity(date);
+ }
+
+ @Override
+ public int getVersion() {
+ return unwrap().getVersion();
+ }
+
+ @Override
+ public BigInteger getSerialNumber() {
+ return unwrap().getSerialNumber();
+ }
+
+ @Override
+ public Principal getIssuerDN() {
+ return unwrap().getIssuerDN();
+ }
+
+ @Override
+ public Principal getSubjectDN() {
+ return unwrap().getSubjectDN();
+ }
+
+ @Override
+ public Date getNotBefore() {
+ return unwrap().getNotBefore();
+ }
+
+ @Override
+ public Date getNotAfter() {
+ return unwrap().getNotAfter();
+ }
+
+ @Override
+ public byte[] getTBSCertificate() throws CertificateEncodingException {
+ return unwrap().getTBSCertificate();
+ }
+
+ @Override
+ public byte[] getSignature() {
+ return unwrap().getSignature();
+ }
+
+ @Override
+ public String getSigAlgName() {
+ return unwrap().getSigAlgName();
+ }
+
+ @Override
+ public String getSigAlgOID() {
+ return unwrap().getSigAlgOID();
+ }
+
+ @Override
+ public byte[] getSigAlgParams() {
+ return unwrap().getSigAlgParams();
+ }
+
+ @Override
+ public boolean[] getIssuerUniqueID() {
+ return unwrap().getIssuerUniqueID();
+ }
+
+ @Override
+ public boolean[] getSubjectUniqueID() {
+ return unwrap().getSubjectUniqueID();
+ }
+
+ @Override
+ public boolean[] getKeyUsage() {
+ return unwrap().getKeyUsage();
+ }
+
+ @Override
+ public int getBasicConstraints() {
+ return unwrap().getBasicConstraints();
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return bytes.clone();
+ }
+
+ @Override
+ public void verify(PublicKey key)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException {
+ unwrap().verify(key);
+ }
+
+ @Override
+ public void verify(PublicKey key, String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException {
+ unwrap().verify(key, sigProvider);
+ }
+
+ @Override
+ public String toString() {
+ return unwrap().toString();
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return unwrap().getPublicKey();
+ }
+
+ @Override
+ public boolean hasUnsupportedCriticalExtension() {
+ return unwrap().hasUnsupportedCriticalExtension();
+ }
+
+ @Override
+ public Set<String> getCriticalExtensionOIDs() {
+ return unwrap().getCriticalExtensionOIDs();
+ }
+
+ @Override
+ public Set<String> getNonCriticalExtensionOIDs() {
+ return unwrap().getNonCriticalExtensionOIDs();
+ }
+
+ @Override
+ public byte[] getExtensionValue(String oid) {
+ return unwrap().getExtensionValue(oid);
+ }
+
+ private X509Certificate unwrap() {
+ X509Certificate wrapped = this.wrapped;
+ if (wrapped == null) {
+ try {
+ wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY.generateCertificate(
+ new ByteArrayInputStream(bytes));
+ } catch (CertificateException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return wrapped;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org