You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by sc...@apache.org on 2018/10/29 18:11:57 UTC
svn commit: r1845157 - in /tomcat/trunk:
java/org/apache/catalina/tribes/group/interceptors/
test/org/apache/catalina/tribes/group/interceptors/ webapps/docs/
webapps/docs/config/
Author: schultz
Date: Mon Oct 29 18:11:57 2018
New Revision: 1845157
URL: http://svn.apache.org/viewvc?rev=1845157&view=rev
Log:
Add EncryptInterceptor for clustering.
Added:
tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java
tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java
tomcat/trunk/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java
Modified:
tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
tomcat/trunk/webapps/docs/changelog.xml
tomcat/trunk/webapps/docs/config/cluster-interceptor.xml
Added: tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java?rev=1845157&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java Mon Oct 29 18:11:57 2018
@@ -0,0 +1,356 @@
+/*
+ * 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.catalina.tribes.group.interceptors;
+
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.HexUtils;
+
+/**
+ * Adds encryption using a pre-shared key.
+ *
+ * The length of the key (in bytes) must be acceptable for the encryption
+ * algorithm being used. For example, for AES, you must use a key of either
+ * 16 bytes (128 bits, 24 bytes 192 bits), or 32 bytes (256 bits).
+ *
+ * You can supply the raw key bytes by calling {@link #setEncryptionKey(byte[])}
+ * or the hex-encoded binary bytes by calling
+ * {@link #setEncryptionKey(String)}.
+ */
+public class EncryptInterceptor extends ChannelInterceptorBase implements EncryptInterceptorMBean {
+
+ private static final Log log = LogFactory.getLog(EncryptInterceptor.class);
+ protected static final StringManager sm = StringManager.getManager(EncryptInterceptor.class);
+
+ private static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding";
+
+ private String providerName;
+ private String encryptionAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM;
+ private byte[] encryptionKeyBytes;
+
+ private Cipher encryptionCipher;
+ private Cipher decryptionCipher;
+
+ public EncryptInterceptor() {
+ }
+
+ @Override
+ public void start(int svc) throws ChannelException {
+ if(Channel.SND_TX_SEQ == (svc & Channel.SND_TX_SEQ)) {
+ try {
+ initCiphers();
+ } catch (GeneralSecurityException gse) {
+ log.fatal(sm.getString("encryptInterceptor.init.failed"));
+ throw new ChannelException(sm.getString("encryptInterceptor.init.failed"), gse);
+ }
+ }
+
+ super.start(svc);
+ }
+
+ @Override
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload)
+ throws ChannelException {
+ try {
+ byte[] data = msg.getMessage().getBytes();
+
+ // See #encrypt(byte[]) for an explanation of the return value
+ byte[][] bytes = encrypt(data);
+
+ XByteBuffer xbb = msg.getMessage();
+
+ // Completely replace the message
+ xbb.setLength(0);
+ xbb.append(bytes[0], 0, bytes[0].length);
+ xbb.append(bytes[1], 0, bytes[1].length);
+
+ super.sendMessage(destination, msg, payload);
+
+ } catch (IllegalBlockSizeException ibse) {
+ log.error(sm.getString("encryptInterceptor.encrypt.failed"));
+ throw new ChannelException(ibse);
+ } catch (BadPaddingException bpe) {
+ log.error(sm.getString("encryptInterceptor.encrypt.failed"));
+ throw new ChannelException(bpe);
+ }
+ }
+
+ @Override
+ public void messageReceived(ChannelMessage msg) {
+ try {
+ byte[] data = msg.getMessage().getBytes();
+
+ data = decrypt(data);
+
+ // Remove the decrypted IV/nonce block from the front of the message
+ int blockSize = getDecryptionCipher().getBlockSize();
+ int trimmedSize = data.length - blockSize;
+ if(trimmedSize < 0) {
+ log.error(sm.getString("encryptInterceptor.decrypt.error.short-message"));
+ throw new IllegalStateException(sm.getString("encryptInterceptor.decrypt.error.short-message"));
+ }
+
+ XByteBuffer xbb = msg.getMessage();
+
+ // Completely replace the message with the decrypted one
+ xbb.setLength(0);
+ xbb.append(data, blockSize, data.length - blockSize);
+
+ super.messageReceived(msg);
+ } catch (IllegalBlockSizeException ibse) {
+ log.error(sm.getString("encryptInterceptor.decrypt.failed"), ibse);
+ } catch (BadPaddingException bpe) {
+ log.error(sm.getString("encryptInterceptor.decrypt.failed"), bpe);
+ }
+ }
+
+ /**
+ * Sets the encryption algorithm to be used for encrypting and decrypting
+ * channel messages. You must specify the <code>algorithm/mode/padding</code>.
+ * Information on what standard algorithm names are, please see
+ * {@link https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html}.
+ *
+ * Default is <code>AES/CBC/PKCS5Padding</code>.
+ *
+ * @param algorithm The algorithm to use.
+ */
+ public void setEncryptionAlgorithm(String algorithm) {
+ if(null == getEncryptionAlgorithm())
+ throw new IllegalStateException(sm.getString("encryptInterceptor.algorithm.required"));
+
+ int pos = algorithm.indexOf('/');
+ if(pos < 0)
+ throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.required"));
+ pos = algorithm.indexOf('/', pos + 1);
+ if(pos < 0)
+ throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.required"));
+
+ encryptionAlgorithm = algorithm;
+ }
+
+ /**
+ * Gets the encryption algorithm being used to encrypt and decrypt channel
+ * messages.
+ *
+ * @return The algorithm being used, including the algorithm mode and padding.
+ */
+ public String getEncryptionAlgorithm() {
+ return encryptionAlgorithm;
+ }
+
+ /**
+ * Sets the encryption key for encryption and decryption. The length of the
+ * key must be appropriate for the algorithm being used.
+ *
+ * @param key The encryption key.
+ */
+ public void setEncryptionKey(byte[] key) {
+ if(null == key)
+ key = null;
+ else
+ encryptionKeyBytes = key.clone();
+ }
+
+ /**
+ * Gets the encryption key being used for encryption and decryption.
+ * The key is encoded using hex-encoding where e.g. the byte <code>0xab</code>
+ * will be shown as "ab". The length of the string in characters will
+ * be twice the length of the key in bytes.
+ *
+ * @return The encryption key.
+ */
+ public void setEncryptionKey(String keyBytes) {
+ if(null == keyBytes)
+ setEncryptionKey((byte[])null);
+ else
+ setEncryptionKey(HexUtils.fromHexString(keyBytes.trim()));
+ }
+
+ /**
+ * Gets the encryption key being used for encryption and decryption.
+ *
+ * @return The encryption key.
+ */
+ public byte[] getEncryptionKey() {
+ byte[] key = getEncryptionKeyInternal();
+
+ if(null != key)
+ key = key.clone();
+
+ return key;
+ }
+
+ private byte[] getEncryptionKeyInternal() {
+ return encryptionKeyBytes;
+ }
+
+ /**
+ * Sets the JCA provider name used for cryptographic activities.
+ *
+ * Default is the JVM platform default.
+ *
+ * @param provider The name of the JCA provider.
+ */
+ public void setProviderName(String provider) {
+ providerName = provider;
+ }
+
+ /**
+ * Gets the JCA provider name used for cryptographic activities.
+ *
+ * Default is the JVM platform default.
+ *
+ * @return The name of the JCA provider.
+ */
+ public String getProviderName() {
+ return providerName;
+ }
+
+ private void initCiphers() throws GeneralSecurityException {
+ if(null == getEncryptionKey())
+ throw new IllegalStateException(sm.getString("encryptInterceptor.key.required"));
+
+ String algorithm = getEncryptionAlgorithm();
+
+ String mode = getAlgorithmMode(algorithm);
+
+ if(!"CBC".equalsIgnoreCase(mode))
+ throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.requires-cbc-mode", mode));
+
+ Cipher cipher;
+
+ String providerName = getProviderName();
+ if(null == providerName) {
+ cipher = Cipher.getInstance(algorithm);
+ } else {
+ cipher = Cipher.getInstance(algorithm, getProviderName());
+ }
+
+ byte[] iv = new byte[cipher.getBlockSize()];
+
+ // Always use a random IV For cipher setup.
+ // The recipient doesn't need the (matching) IV because we will always
+ // pre-pad messages with the IV as a nonce.
+ new SecureRandom().nextBytes(iv);
+
+ IvParameterSpec IV = new IvParameterSpec(iv);
+
+ // If this is a cipher transform of the form ALGO/MODE/PAD,
+ // take just the algorithm part.
+ int pos = algorithm.indexOf('/');
+
+ String bareAlgorithm;
+ if(pos >= 0) {
+ bareAlgorithm = algorithm.substring(0, pos);
+ } else {
+ bareAlgorithm = algorithm;
+ }
+
+ SecretKeySpec encryptionKey = new SecretKeySpec(getEncryptionKey(), bareAlgorithm);
+
+ cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, IV);
+
+ encryptionCipher = cipher;
+
+ if(null == providerName) {
+ cipher = Cipher.getInstance(algorithm);
+ } else {
+ cipher = Cipher.getInstance(algorithm, getProviderName());
+ }
+
+ cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv));
+
+ decryptionCipher = cipher;
+ }
+
+ private Cipher getEncryptionCipher() {
+ return encryptionCipher;
+ }
+
+ private Cipher getDecryptionCipher() {
+ return decryptionCipher;
+ }
+
+ private static String getAlgorithmMode(String algorithm) {
+ int start = algorithm.indexOf('/');
+ if(start < 0)
+ throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.required"));
+ int end = algorithm.indexOf('/', start + 1);
+ if(start < 0)
+ throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.required"));
+
+ return algorithm.substring(start + 1, end);
+ }
+
+ /**
+ * Encrypts the input <code>bytes</code> into two separate byte arrays:
+ * one for the initial block (which will be the encrypted random IV)
+ * and the second one containing the actual encrypted payload.
+ *
+ * This method returns a pair of byte arrays instead of a single
+ * concatenated one to reduce the number of byte buffers created
+ * and copied during the whole operation -- including message re-building.
+ *
+ * @param bytes The data to encrypt.
+ *
+ * @return The encrypted IV block in [0] and the encrypted data in [1].
+ *
+ * @throws GeneralSecurityException If there is a problem performing the encryption.
+ */
+ private byte[][] encrypt(byte[] bytes) throws IllegalBlockSizeException, BadPaddingException {
+ Cipher cipher = getEncryptionCipher();
+
+ // Adding the IV to the beginning of the encrypted data
+ byte[] iv = cipher.getIV();
+
+ byte[][] data = new byte[2][];
+ data[0] = cipher.update(iv, 0, iv.length);
+ data[1] = cipher.doFinal(bytes);
+
+ return data;
+ }
+
+ /**
+ * Decrypts the input <code>bytes</code>.
+ *
+ * @param bytes The data to decrypt.
+ *
+ * @return The decrypted data.
+ *
+ * @throws GeneralSecurityException If there is a problem performing the decryption.
+ */
+ private byte[] decrypt(byte[] bytes) throws IllegalBlockSizeException, BadPaddingException {
+ return getDecryptionCipher().doFinal(bytes);
+ }
+}
Added: tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java?rev=1845157&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java Mon Oct 29 18:11:57 2018
@@ -0,0 +1,31 @@
+/*
+ * 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.catalina.tribes.group.interceptors;
+
+public interface EncryptInterceptorMBean {
+
+ // Config
+ public int getOptionFlag();
+ public void setOptionFlag(int optionFlag);
+
+ public void setEncryptionAlgorithm(String algorithm);
+ public String getEncryptionAlgorithm();
+ public void setEncryptionKey(byte[] key);
+ public byte[] getEncryptionKey();
+ public void setProviderName(String provider);
+ public String getProviderName();
+}
Modified: tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties?rev=1845157&r1=1845156&r2=1845157&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties [UTF-8] (original)
+++ tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties [UTF-8] Mon Oct 29 18:11:57 2018
@@ -73,4 +73,11 @@ throughputInterceptor.report=ThroughputI
\n\tRx Speed:{8} MB/sec (since 1st msg)\
\n\tReceived:{9} MB]\n
twoPhaseCommitInterceptor.originalMessage.missing=Received a confirmation, but original message is missing. Id:[{0}]
-twoPhaseCommitInterceptor.heartbeat.failed=Unable to perform heartbeat on the TwoPhaseCommit interceptor.
\ No newline at end of file
+twoPhaseCommitInterceptor.heartbeat.failed=Unable to perform heartbeat on the TwoPhaseCommit interceptor.
+encryptInterceptor.init.failed=Failed to initialize EncryptInterceptor
+encryptInterceptor.encrypt.failed=Failed to encrypt message
+encryptInterceptor.decrypt.failed=Failed to decrypt message
+encryptInterceptor.decrypt.error.short-message=Failed to decrypt message: premature end-of-message
+encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/CBC/PKCS5Padding
+encryptInterceptor.key.required=Encryption key is required
+encryptInterceptor.algorithm.requires-cbc-mode=EncryptInterceptor only supports CBC cipher modes, not [{0}]
\ No newline at end of file
Added: tomcat/trunk/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java?rev=1845157&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java (added)
+++ tomcat/trunk/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java Mon Oct 29 18:11:57 2018
@@ -0,0 +1,191 @@
+package org.apache.catalina.tribes.group.interceptors;
+
+import static org.junit.Assert.*;
+
+import java.nio.charset.StandardCharsets;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelInterceptor;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the EncryptInterceptor.
+ *
+ * Many of the tests in this class use strings as input and output, even
+ * though the interceptor actually operates on byte arrays. This is done
+ * for readability for the tests and their outputs.
+ */
+public class TestEncryptInterceptor {
+ private static final String encryptionKey128 = HexUtils.toHexString("cafebabedeadbeef".getBytes(StandardCharsets.UTF_8));
+ private static final String encryptionKey192 = HexUtils.toHexString("cafebabedeadbeefbeefcafe".getBytes(StandardCharsets.UTF_8));
+ private static final String encryptionKey256 = HexUtils.toHexString("cafebabedeadbeefcafebabedeadbeef".getBytes(StandardCharsets.UTF_8));
+
+ EncryptInterceptor src;
+ EncryptInterceptor dest;
+
+ @Before
+ public void setup() {
+ src = new EncryptInterceptor();
+ src.setEncryptionKey(encryptionKey128);
+
+ dest = new EncryptInterceptor();
+ dest.setEncryptionKey(encryptionKey128);
+
+ src.setNext(new PipedInterceptor(dest));
+ dest.setPrevious(new ValueCaptureInterceptor());
+ }
+
+ @Test
+ public void testBasic() throws Exception {
+ src.start(Channel.SND_TX_SEQ);
+ dest.start(Channel.SND_TX_SEQ);
+
+ String testInput = "The quick brown fox jumps over the lazy dog.";
+
+ assertEquals("Basic roundtrip failed",
+ testInput,
+ roundTrip(testInput, src, dest));
+ }
+
+ @Test
+ public void testTinyPayload() throws Exception {
+ src.start(Channel.SND_TX_SEQ);
+ dest.start(Channel.SND_TX_SEQ);
+
+ String testInput = "x";
+
+ assertEquals("Tiny payload roundtrip failed",
+ testInput,
+ roundTrip(testInput, src, dest));
+ }
+
+ @Test
+ public void testHugePayload() throws Exception {
+ src.start(Channel.SND_TX_SEQ);
+ dest.start(Channel.SND_TX_SEQ);
+
+ byte[] bytes = new byte[1073741824]; // 1MiB, all zeros
+
+ assertArrayEquals("Tiny payload roundtrip failed",
+ bytes,
+ roundTrip(bytes, src, dest));
+ }
+
+ @Test
+ public void testCustomProvider() throws Exception {
+ src.setProviderName("SunJCE"); // Explicitly set the provider name
+ dest.setProviderName("SunJCE");
+ src.start(Channel.SND_TX_SEQ);
+ dest.start(Channel.SND_TX_SEQ);
+
+ String testInput = "The quick brown fox jumps over the lazy dog.";
+
+ assertEquals("Failed to set custom provider name",
+ testInput,
+ roundTrip(testInput, src, dest));
+ }
+
+ @Test
+ public void test192BitKey() throws Exception {
+ src.setEncryptionKey(encryptionKey192);
+ dest.setEncryptionKey(encryptionKey192);
+ src.start(Channel.SND_TX_SEQ);
+ dest.start(Channel.SND_TX_SEQ);
+
+ String testInput = "The quick brown fox jumps over the lazy dog.";
+
+ assertEquals("Failed to set custom provider name",
+ testInput,
+ roundTrip(testInput, src, dest));
+ }
+
+ @Test
+ public void test256BitKey() throws Exception {
+ src.setEncryptionKey(encryptionKey256);
+ dest.setEncryptionKey(encryptionKey256);
+ src.start(Channel.SND_TX_SEQ);
+ dest.start(Channel.SND_TX_SEQ);
+
+ String testInput = "The quick brown fox jumps over the lazy dog.";
+
+ assertEquals("Failed to set custom provider name",
+ testInput,
+ roundTrip(testInput, src, dest));
+ }
+
+ /**
+ * Actually go through the interceptor's send/receive message methods.
+ */
+ private static String roundTrip(String input, EncryptInterceptor src, EncryptInterceptor dest) throws Exception {
+ byte[] bytes = input.getBytes("UTF-8");
+
+ bytes = roundTrip(bytes, src, dest);
+
+ return new String(((ValueCaptureInterceptor)dest.getPrevious()).getValue(), "UTF-8");
+ }
+
+ /**
+ * Actually go through the interceptor's send/receive message methods.
+ */
+ private static byte[] roundTrip(byte[] input, EncryptInterceptor src, EncryptInterceptor dest) throws Exception {
+ ChannelData msg = new ChannelData(false);
+ msg.setMessage(new XByteBuffer(input, false));
+ src.sendMessage(null, msg, null);
+
+ return ((ValueCaptureInterceptor)dest.getPrevious()).getValue();
+ }
+
+ /**
+ * Interceptor that delivers directly to a destination.
+ */
+ private static class PipedInterceptor
+ extends ChannelInterceptorBase
+ {
+ private ChannelInterceptor dest;
+
+ public PipedInterceptor(ChannelInterceptor dest) {
+ if(null == dest)
+ throw new IllegalArgumentException("Destination must not be null");
+
+ this.dest = dest;
+ }
+
+ @Override
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload)
+ throws ChannelException {
+ dest.messageReceived(msg);
+ }
+ }
+
+ /**
+ * Interceptor that simply captures the latest message sent to or received by it.
+ */
+ private static class ValueCaptureInterceptor
+ extends ChannelInterceptorBase
+ {
+ private byte[] value;
+
+ @Override
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload)
+ throws ChannelException {
+ value = msg.getMessage().getBytes();
+ }
+
+ @Override
+ public void messageReceived(ChannelMessage msg) {
+ value = msg.getMessage().getBytes();
+ }
+
+ public byte[] getValue() {
+ return value;
+ }
+ }
+}
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1845157&r1=1845156&r2=1845157&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Mon Oct 29 18:11:57 2018
@@ -131,6 +131,12 @@
the <code>Endpoint</code> rather than a local field that could end up
out of sync. (markt)
</scode>
+ <add>
+ Add EncryptInterceptor to the portfolio of available clustering
+ interceptors. This adds symmetric encryption of session data
+ to Tomcat clustering regardless of the type of cluster manager
+ or membership being used. (schultz)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: tomcat/trunk/webapps/docs/config/cluster-interceptor.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/cluster-interceptor.xml?rev=1845157&r1=1845156&r2=1845157&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/cluster-interceptor.xml (original)
+++ tomcat/trunk/webapps/docs/config/cluster-interceptor.xml Mon Oct 29 18:11:57 2018
@@ -36,7 +36,7 @@
<section name="Introduction">
<p>
Apache Tribes supports an interceptor architecture to intercept both messages and membership notifications.
- This architecture allows decoupling of logic and opens the way for some very kewl feature add ons.
+ This architecture allows decoupling of logic and opens the way for some very useful feature add ons.
</p>
</section>
@@ -54,6 +54,7 @@
<li><code>org.apache.catalina.tribes.group.interceptors.FragmentationInterceptor</code></li>
<li><code>org.apache.catalina.tribes.group.interceptors.GzipInterceptor</code></li>
<li><code>org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor</code></li>
+ <li><code>org.apache.catalina.tribes.group.interceptors.EncryptInterceptor</code></li>
</ul>
</section>
@@ -196,6 +197,33 @@
</attribute>
</attributes>
</subsection>
+ <subsection name="org.apache.catalina.tribes.group.interceptors.EncryptInterceptor Attributes">
+ <p>
+ The EncryptInterceptor adds encryption to the channel messages carrying
+ session data between nodes. Added in Tomcat 9.0.13.
+ </p>
+ <attributes>
+ <attribute name="encryptionAlgorithm" required="false">
+ The encryption algorithm to be used, including the mode and padding. Please see
+ <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html</a>
+ for the standard JCA names that can be used.
+
+ The <i>mode</i> is currently required to be <code>CBC</code>.
+
+ The length of the key will specify the flavor of the encryption algorithm
+ to be used, if applicable (e.g. AES-128 versus AES-256).
+
+ The default algorithm is <code>AES/CBC/PKCS5Padding</code>.
+ </attribute>
+ <attribute name="encryptionKey" required="true">
+ The key to be used with the encryption algorithm.
+
+ The key should be specified as hex-encoded bytes of the appropriate
+ length for the algorithm (e.g. 16 bytes / 32 characters / 128 bits for
+ AES-128, 32 bytes / 64 characters / 256 bits for AES-256, etc.).
+ </attribute>
+ </attributes>
+ </subsection>
</section>
<section name="Nested Components">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org