VYPR
High severity7.5NVD Advisory· Published Apr 9, 2026· Updated Apr 14, 2026

CVE-2026-29146

CVE-2026-29146

Description

Padding Oracle vulnerability in Apache Tomcat's EncryptInterceptor with default configuration.

This issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.18, from 10.0.0-M1 through 10.1.52, from 9.0.13 through 9..115, from 8.5.38 through 8.5.100, from 7.0.100 through 7.0.109.

Users are recommended to upgrade to version 11.0.19, 10.1.53 and 9.0.116, which fixes the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.tomcat:tomcat-tribesMaven
>= 9.0.13, < 9.0.1169.0.116
org.apache.tomcat:tomcat-tribesMaven
>= 10.1.50, < 10.1.5310.1.53
org.apache.tomcat:tomcat-tribesMaven
>= 11.0.0-M1, < 11.0.2011.0.20
org.apache.tomcat:tomcatMaven
>= 9.0.13, < 9.0.1169.0.116
org.apache.tomcat:tomcatMaven
>= 10.1.50, < 10.1.5310.1.53
org.apache.tomcat:tomcatMaven
>= 11.0.0-M1, < 11.0.2011.0.20
org.apache.tomcat:tomcat-tribesMaven
>= 8.5.38, <= 8.5.100
org.apache.tomcat:tomcatMaven
>= 8.5.38, <= 8.5.100
org.apache.tomcat:tomcat-tribesMaven
>= 7.0.100, <= 7.0.109
org.apache.tomcat:tomcatMaven
>= 7.0.100, <= 7.0.109

Affected products

1
  • cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*
    Range: >=7.0.100,<=7.0.109

Patches

3
607ebc0fa522

Add support for new algorithms provided by JPA providers

https://github.com/apache/tomcatMark ThomasMar 13, 2026via ghsa
10 files changed · +231 85
  • java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java+50 14 modified
    @@ -21,6 +21,7 @@
     import java.security.NoSuchProviderException;
     import java.security.SecureRandom;
     import java.security.spec.AlgorithmParameterSpec;
    +import java.util.Locale;
     import java.util.concurrent.ConcurrentLinkedQueue;
     
     import javax.crypto.Cipher;
    @@ -41,7 +42,6 @@
     import org.apache.juli.logging.Log;
     import org.apache.juli.logging.LogFactory;
     
    -
     /**
      * 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
    @@ -139,17 +139,18 @@ public void messageReceived(ChannelMessage msg) {
                 xbb.clear();
                 xbb.append(data, 0, data.length);
     
    -            super.messageReceived(msg);
             } catch (GeneralSecurityException gse) {
                 log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);
             }
    +        super.messageReceived(msg);
         }
     
         /**
          * Sets the encryption algorithm to be used for encrypting and decrypting channel messages. You must specify the
          * <code>algorithm/mode/padding</code>. Information on standard algorithm names may be found in the
          * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">Java
    -     * documentation</a>. Default is <code>AES/CBC/PKCS5Padding</code>.
    +     * documentation</a>. Default is <code>AES/CBC/PKCS5Padding</code> for backwards compatibility but it is recommended
    +     * that <code>AES/GCM/NoPadding</code> is used.
          *
          * @param algorithm The algorithm to use.
          */
    @@ -313,33 +314,68 @@ private static BaseEncryptionManager createEncryptionManager(String algorithm, b
     
             String algorithmName;
             String algorithmMode;
    +        String algorithmPadding;
     
    -        // We need to break-apart the algorithm name e.g. AES/CBC/PKCS5Padding
    +        // We need to break-apart the algorithm name e.g. AES/GCM/NoPadding
             // take just the algorithm part.
             int pos = algorithm.indexOf('/');
     
             if (pos >= 0) {
    -            algorithmName = algorithm.substring(0, pos);
    +            algorithmName = algorithm.substring(0, pos).toUpperCase(Locale.ENGLISH);
                 int pos2 = algorithm.indexOf('/', pos + 1);
     
                 if (pos2 >= 0) {
    -                algorithmMode = algorithm.substring(pos + 1, pos2);
    +                algorithmMode = algorithm.substring(pos + 1, pos2).toUpperCase(Locale.ENGLISH);
    +                algorithmPadding = algorithm.substring(pos2 + 1).toUpperCase(Locale.ENGLISH);
                 } else {
    -                algorithmMode = "CBC";
    +                algorithmMode = "GCM";
    +                algorithmPadding = "NOPADDING";
                 }
             } else {
                 algorithmName = algorithm;
    -            algorithmMode = "CBC";
    +            algorithmMode = "GCM";
    +            algorithmPadding = "NOPADDING";
             }
     
    -        if ("GCM".equalsIgnoreCase(algorithmMode)) {
    +        /*
    +         * Limit the cipher algorithm modes available. The limits are based on the cipher algorithm modes listed in the
    +         * Java Standard Names documentation. Those modes that are not appropriate or provide no protection are blocked.
    +         * Where there are performance or security concerns regarding a mode, a warning is logged. Unrecognised modes,
    +         * such as those provided by custom JCA providers are allowed but will be rejected if there is no JCA provider
    +         * to support them.
    +         */
    +        if ("NONE".equals(algorithmMode) || "ECB".equals(algorithmMode) || "PCBC".equals(algorithmMode) ||
    +                "CTS".equals(algorithmMode) || "KW".equals(algorithmMode) || "KWP".equals(algorithmMode) ||
    +                "CTR".equals(algorithmMode) ||
    +                ("CBC".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding)) ||
    +                ("CFB".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding)) ||
    +                ("GCM".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("OFB".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding))) {
    +            // Insecure, unsuitable or unsupported
    +            throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported", algorithm));
    +
    +        } else if (("CBC".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("CFB".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("OFB".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding))) {
    +            // Supported but not recommended as more secure modes are available
    +            log.warn(sm.getString("encryptInterceptor.algorithm.switch", algorithm));
    +
    +        } else if (algorithmMode.startsWith("CFB") || algorithmMode.startsWith("OFB")) {
    +            // Using a non-default block size. Not supported as insecure and/or inefficient.
    +            throw new IllegalArgumentException(
    +                    sm.getString("encryptInterceptor.algorithm.unsupported", algorithm));
    +
    +        } else if ("GCM".equalsIgnoreCase(algorithmMode) && "NOPADDING".equals(algorithmPadding)) {
    +            // Needs a specialised encryption manager to handle the differences between GCM and other modes
                 return new GCMEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
    -        } else if ("CBC".equalsIgnoreCase(algorithmMode) || "OFB".equalsIgnoreCase(algorithmMode) ||
    -                "CFB".equalsIgnoreCase(algorithmMode)) {
    +        }
    +
    +        // Use the default encryption manager
    +        try {
                 return new BaseEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
    -        } else {
    -            throw new IllegalArgumentException(
    -                    sm.getString("encryptInterceptor.algorithm.unsupported-mode", algorithmMode));
    +        } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException ex) {
    +            throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported", algorithmMode),
    +                    ex);
             }
         }
     
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=Le membre [{0}] a été refusé dans le c
     domainFilterInterceptor.message.refused=Le message reçu du cluster [{0}] a été refusé
     
     encryptInterceptor.algorithm.required=Un algorithme de cryptage est requis, avec une spécification complète telle que AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=L''EncryptInterceptor ne supporte pas le mode de chiffrage de bloc [{0}]
     encryptInterceptor.decrypt.error.short-message=Echec du décryptage du message : fin de message prématuré
     encryptInterceptor.decrypt.failed=Echec de décryptage du message
     encryptInterceptor.encrypt.failed=Erreur de cryptage du message
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=メンバーはクラスター [{0}] へ
     domainFilterInterceptor.message.refused=クラスター [{0}] から受信したメッセージは拒否されました。
     
     encryptInterceptor.algorithm.required=暗号化アルゴリズムが必要です。完全指定。 AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptorはブロック暗号モード [{0}]をサポートしていません。
     encryptInterceptor.decrypt.error.short-message=メッセージの復号に失敗: メッセージの末尾が途切れています
     encryptInterceptor.decrypt.failed=メッセージの復号に失敗しました。
     encryptInterceptor.encrypt.failed=メッセージを暗号化できません。
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ko.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=멤버 [{0}]이(가) 클러스터에 참
     domainFilterInterceptor.message.refused=클러스터 [{0}](으)로부터 받은 메시지가 거부되었습니다.
     
     encryptInterceptor.algorithm.required=암호화 알고리즘을 완전하게 지정해야 합니다. 예) AES/CBC/PKCS5Padding.
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor가 블록 cipher 모드 [{0}]을(를) 지원하지 않습니다.
     encryptInterceptor.decrypt.error.short-message=메시지를 해독하지 못했습니다: 메시지가 너무 일찍 끝났습니다 (premature end-of-message).
     encryptInterceptor.decrypt.failed=메시지를 해독하지 못했습니다.
     encryptInterceptor.encrypt.failed=메시지를 암호화하지 못했습니다.
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties+3 2 modified
    @@ -19,8 +19,9 @@
     domainFilterInterceptor.member.refused=Member [{0}] was refused to join cluster
     domainFilterInterceptor.message.refused=Received message from cluster[{0}] was refused.
     
    -encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor does not support block cipher mode [{0}]
    +encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/GCM/NoPadding
    +encryptInterceptor.algorithm.switch=The EncryptInterceptor is using the algorithm [{0}]. It is recommended to switch to using AES/GCM/NoPadding.
    +encryptInterceptor.algorithm.unsupported=EncryptInterceptor does not support algorithm [{0}]
     encryptInterceptor.decrypt.error.short-message=Failed to decrypt message: premature end-of-message
     encryptInterceptor.decrypt.failed=Failed to decrypt message
     encryptInterceptor.encrypt.failed=Failed to encrypt message
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=成员被拒绝加入集群 cluster[{0}]
     domainFilterInterceptor.message.refused=从集群[{0}]中接收的消息被拒绝
     
     encryptInterceptor.algorithm.required=加密算法是必需的,充分说明,例如AES / CBC / PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor不支持分组密码模式[{0}]
     encryptInterceptor.decrypt.error.short-message=解密消息失败: 结尾消息提前结束
     encryptInterceptor.decrypt.failed=无法解密信息
     encryptInterceptor.encrypt.failed=无法加密信息
    
  • test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptorAlgorithms.java+162 0 added
    @@ -0,0 +1,162 @@
    +/*
    + * 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.util.ArrayList;
    +import java.util.Collection;
    +import java.util.List;
    +
    +import org.junit.Assert;
    +import org.junit.Test;
    +import org.junit.runner.RunWith;
    +import org.junit.runners.Parameterized;
    +import org.junit.runners.Parameterized.Parameter;
    +import org.junit.runners.Parameterized.Parameters;
    +
    +import org.apache.catalina.tribes.Channel;
    +
    +@RunWith(Parameterized.class)
    +public class TestEncryptInterceptorAlgorithms extends EncryptionInterceptorBaseTest {
    +
    +    @Parameters(name = "{index} {0}/{1}/{2}")
    +    public static Collection<Object[]> inputs() {
    +
    +        List<Object[]> result = new ArrayList<>();
    +        // Covers all cipher algorithm modes currently listed in Java Standard Names
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "NONE", "NoPadding", Boolean.FALSE});
    +        // Not supported - Insecure - Padding makes no sense if there is no encryption
    +        result.add(new Object[] { "AES", "NONE", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "CBC", "NoPadding", Boolean.FALSE});
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "CBC", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - JCA provider doesn't included it
    +        result.add(new Object[] { "AES", "CCM", "NoPadding", Boolean.FALSE});
    +        // Not supported - JCA provider doesn't included it - CCM doesn't need (support?) padding
    +        result.add(new Object[] { "AES", "CCM", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "CFB", "NoPadding", Boolean.FALSE});
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "CFB", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - Insecure and/or slow
    +        result.add(new Object[] { "AES", "CFB8", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB8", "PKCS5Padding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB16", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB16", "PKCS5Padding", Boolean.FALSE});
    +        // large block sizes not tested but will be rejected as well
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "CTR", "NoPadding", Boolean.FALSE});
    +        // Not supported - Configuration not recommended
    +        result.add(new Object[] { "AES", "CTR", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - has minimum length
    +        result.add(new Object[] { "AES", "CTS", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CTS", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "ECB", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "ECB", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Default for Tomcat 12 onwards
    +        result.add(new Object[] { "AES", "GCM", "NoPadding", Boolean.TRUE});
    +        // Not supported - GCM doesn't need (support?) padding
    +        result.add(new Object[] { "AES", "GCM", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - KW not appropriate for encrypting cluster messages
    +        result.add(new Object[] { "AES", "KW", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "KW", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - KWP not appropriate for encrypting cluster messages
    +        result.add(new Object[] { "AES", "KWP", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "KWP", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "OFB", "NoPadding", Boolean.FALSE});
    +
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "OFB", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - Insecure and/or slow
    +        result.add(new Object[] { "AES", "OFB8", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB8", "PKCS5Padding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB16", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB16", "PKCS5Padding", Boolean.FALSE});
    +        // large block sizes not tested but will be rejected as well
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "PCBC", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "PCBC", "PKCS5Padding", Boolean.FALSE});
    +
    +        return result;
    +    }
    +
    +    @Parameter(0)
    +    public String algorithm;
    +
    +    @Parameter(1)
    +    public String mode;
    +
    +    @Parameter(2)
    +    public String padding;
    +
    +    @Parameter(3)
    +    public boolean shouldSucceed;
    +
    +    @Test
    +    public void testAlgorithm() throws Exception {
    +        if (shouldSucceed) {
    +            doTestShouldSucceed();
    +        } else {
    +            doTestShouldNotSucceed();
    +        }
    +    }
    +
    +    private void doTestShouldSucceed() throws Exception {
    +        String transformation = String.format("%s/%s/%s", algorithm, mode, padding);
    +
    +        src.setEncryptionAlgorithm(transformation);
    +        src.start(Channel.SND_TX_SEQ);
    +        dest.setEncryptionAlgorithm(transformation);
    +        dest.start(Channel.SND_TX_SEQ);
    +
    +        String testInput = "The quick brown fox jumps over the lazy dog.";
    +
    +        Assert.assertEquals("Failed in " + transformation + " mode",
    +                     testInput,
    +                     roundTrip(testInput, src, dest));
    +    }
    +
    +    private void doTestShouldNotSucceed() throws Exception {
    +        try {
    +            String transformation = String.format("%s/%s/%s", algorithm, mode, padding);
    +            src.setEncryptionAlgorithm(transformation);
    +            src.start(Channel.SND_TX_SEQ);
    +
    +            // start() should trigger IllegalArgumentException
    +            Assert.fail(transformation + " mode is not being refused");
    +        } catch (IllegalArgumentException iae) {
    +            // Expected
    +        }
    +    }
    +}
    
  • test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java+0 58 modified
    @@ -160,64 +160,6 @@ public void test256BitKey() throws Exception {
                          roundTrip(testInput, src, dest));
         }
     
    -    @Test
    -    public void testOFB() throws Exception {
    -        src.setEncryptionAlgorithm("AES/OFB/PKCS5Padding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/OFB/PKCS5Padding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in OFB mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    @Test
    -    public void testCFB() throws Exception {
    -        src.setEncryptionAlgorithm("AES/CFB/PKCS5Padding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/CFB/PKCS5Padding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in CFB mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    @Test
    -    public void testGCM() throws Exception {
    -        src.setEncryptionAlgorithm("AES/GCM/NoPadding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/GCM/NoPadding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in GCM mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    /*
    -     * ECB mode isn't supported because it's insecure.
    -     */
    -    @Test
    -    public void testECB() throws Exception {
    -        try {
    -            src.setEncryptionAlgorithm("AES/ECB/PKCS5Padding");
    -            src.start(Channel.SND_TX_SEQ);
    -
    -            // start() should trigger IllegalArgumentException
    -            Assert.fail("ECB mode is not being refused");
    -        } catch (IllegalArgumentException iae) {
    -            // Expected
    -        }
    -    }
    -
         @Test
         public void testViaFile() throws Exception {
             src.start(Channel.SND_TX_SEQ);
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -246,6 +246,10 @@
             <bug>69970</bug>: Support raw IPv6 in Kubernetes membership provider
             for the service host. (remm)
           </fix>
    +      <add>
    +        Add support for new algorithms provided by JPA providers to the
    +        <code>EncyptInterceptor</code>. (markt)
    +      </add>
         </changelog>
       </subsection>
       <subsection name="WebSocket">
    
  • webapps/docs/config/cluster-interceptor.xml+12 7 modified
    @@ -214,18 +214,23 @@
        </p>
        <attributes>
          <attribute name="encryptionAlgorithm" required="false">
    -       The encryption algorithm to be used, including the mode and padding. Please see
    +       <p>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.
    +       for the standard JCA names that can be used. Algorithms from other JCA
    +       providers are also supported.</p>
     
    -       EncryptInterceptor currently supports the following
    +       <p>The EncryptInterceptor currently supports the following standard
            <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">block-cipher modes</a>:
    -       CBC, OFB, CFB, and GCM.
    +       CBC, CFB, OFB and GCM. Of these, it is recommended that GCM is always
    +       used.</p>
     
    -       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).
    +       <p>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).</p>
     
    -       The default algorithm is <code>AES/CBC/PKCS5Padding</code>.
    +       <p>The default algorithm is <code>AES/CBC/PKCS5Padding</code> for
    +       backwards compatability but it is recommended that
    +       <code>AES/GCM/NoPadding</code> is used.</p>
          </attribute>
          <attribute name="encryptionKey" required="true">
            The key to be used with the encryption algorithm.
    
0112ed22abfc

Add support for new algorithms provided by JPA providers

https://github.com/apache/tomcatMark ThomasMar 13, 2026via ghsa
10 files changed · +231 85
  • java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java+50 14 modified
    @@ -21,6 +21,7 @@
     import java.security.NoSuchProviderException;
     import java.security.SecureRandom;
     import java.security.spec.AlgorithmParameterSpec;
    +import java.util.Locale;
     import java.util.concurrent.ConcurrentLinkedQueue;
     
     import javax.crypto.Cipher;
    @@ -41,7 +42,6 @@
     import org.apache.juli.logging.Log;
     import org.apache.juli.logging.LogFactory;
     
    -
     /**
      * 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
    @@ -139,17 +139,18 @@ public void messageReceived(ChannelMessage msg) {
                 xbb.clear();
                 xbb.append(data, 0, data.length);
     
    -            super.messageReceived(msg);
             } catch (GeneralSecurityException gse) {
                 log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);
             }
    +        super.messageReceived(msg);
         }
     
         /**
          * Sets the encryption algorithm to be used for encrypting and decrypting channel messages. You must specify the
          * <code>algorithm/mode/padding</code>. Information on standard algorithm names may be found in the
          * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">Java
    -     * documentation</a>. Default is <code>AES/CBC/PKCS5Padding</code>.
    +     * documentation</a>. Default is <code>AES/CBC/PKCS5Padding</code> for backwards compatibility but it is recommended
    +     * that <code>AES/GCM/NoPadding</code> is used.
          *
          * @param algorithm The algorithm to use.
          */
    @@ -313,33 +314,68 @@ private static BaseEncryptionManager createEncryptionManager(String algorithm, b
     
             String algorithmName;
             String algorithmMode;
    +        String algorithmPadding;
     
    -        // We need to break-apart the algorithm name e.g. AES/CBC/PKCS5Padding
    +        // We need to break-apart the algorithm name e.g. AES/GCM/NoPadding
             // take just the algorithm part.
             int pos = algorithm.indexOf('/');
     
             if (pos >= 0) {
    -            algorithmName = algorithm.substring(0, pos);
    +            algorithmName = algorithm.substring(0, pos).toUpperCase(Locale.ENGLISH);
                 int pos2 = algorithm.indexOf('/', pos + 1);
     
                 if (pos2 >= 0) {
    -                algorithmMode = algorithm.substring(pos + 1, pos2);
    +                algorithmMode = algorithm.substring(pos + 1, pos2).toUpperCase(Locale.ENGLISH);
    +                algorithmPadding = algorithm.substring(pos2 + 1).toUpperCase(Locale.ENGLISH);
                 } else {
    -                algorithmMode = "CBC";
    +                algorithmMode = "GCM";
    +                algorithmPadding = "NOPADDING";
                 }
             } else {
                 algorithmName = algorithm;
    -            algorithmMode = "CBC";
    +            algorithmMode = "GCM";
    +            algorithmPadding = "NOPADDING";
             }
     
    -        if ("GCM".equalsIgnoreCase(algorithmMode)) {
    +        /*
    +         * Limit the cipher algorithm modes available. The limits are based on the cipher algorithm modes listed in the
    +         * Java Standard Names documentation. Those modes that are not appropriate or provide no protection are blocked.
    +         * Where there are performance or security concerns regarding a mode, a warning is logged. Unrecognised modes,
    +         * such as those provided by custom JCA providers are allowed but will be rejected if there is no JCA provider
    +         * to support them.
    +         */
    +        if ("NONE".equals(algorithmMode) || "ECB".equals(algorithmMode) || "PCBC".equals(algorithmMode) ||
    +                "CTS".equals(algorithmMode) || "KW".equals(algorithmMode) || "KWP".equals(algorithmMode) ||
    +                "CTR".equals(algorithmMode) ||
    +                ("CBC".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding)) ||
    +                ("CFB".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding)) ||
    +                ("GCM".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("OFB".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding))) {
    +            // Insecure, unsuitable or unsupported
    +            throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported", algorithm));
    +
    +        } else if (("CBC".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("CFB".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("OFB".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding))) {
    +            // Supported but not recommended as more secure modes are available
    +            log.warn(sm.getString("encryptInterceptor.algorithm.switch", algorithm));
    +
    +        } else if (algorithmMode.startsWith("CFB") || algorithmMode.startsWith("OFB")) {
    +            // Using a non-default block size. Not supported as insecure and/or inefficient.
    +            throw new IllegalArgumentException(
    +                    sm.getString("encryptInterceptor.algorithm.unsupported", algorithm));
    +
    +        } else if ("GCM".equalsIgnoreCase(algorithmMode) && "NOPADDING".equals(algorithmPadding)) {
    +            // Needs a specialised encryption manager to handle the differences between GCM and other modes
                 return new GCMEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
    -        } else if ("CBC".equalsIgnoreCase(algorithmMode) || "OFB".equalsIgnoreCase(algorithmMode) ||
    -                "CFB".equalsIgnoreCase(algorithmMode)) {
    +        }
    +
    +        // Use the default encryption manager
    +        try {
                 return new BaseEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
    -        } else {
    -            throw new IllegalArgumentException(
    -                    sm.getString("encryptInterceptor.algorithm.unsupported-mode", algorithmMode));
    +        } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException ex) {
    +            throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported", algorithmMode),
    +                    ex);
             }
         }
     
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=Le membre [{0}] a été refusé dans le c
     domainFilterInterceptor.message.refused=Le message reçu du cluster [{0}] a été refusé
     
     encryptInterceptor.algorithm.required=Un algorithme de cryptage est requis, avec une spécification complète telle que AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=L''EncryptInterceptor ne supporte pas le mode de chiffrage de bloc [{0}]
     encryptInterceptor.decrypt.error.short-message=Echec du décryptage du message : fin de message prématuré
     encryptInterceptor.decrypt.failed=Echec de décryptage du message
     encryptInterceptor.encrypt.failed=Erreur de cryptage du message
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=メンバーはクラスター [{0}] へ
     domainFilterInterceptor.message.refused=クラスター [{0}] から受信したメッセージは拒否されました。
     
     encryptInterceptor.algorithm.required=暗号化アルゴリズムが必要です。完全指定。 AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptorはブロック暗号モード [{0}]をサポートしていません。
     encryptInterceptor.decrypt.error.short-message=メッセージの復号に失敗: メッセージの末尾が途切れています
     encryptInterceptor.decrypt.failed=メッセージの復号に失敗しました。
     encryptInterceptor.encrypt.failed=メッセージを暗号化できません。
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ko.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=멤버 [{0}]이(가) 클러스터에 참
     domainFilterInterceptor.message.refused=클러스터 [{0}](으)로부터 받은 메시지가 거부되었습니다.
     
     encryptInterceptor.algorithm.required=암호화 알고리즘을 완전하게 지정해야 합니다. 예) AES/CBC/PKCS5Padding.
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor가 블록 cipher 모드 [{0}]을(를) 지원하지 않습니다.
     encryptInterceptor.decrypt.error.short-message=메시지를 해독하지 못했습니다: 메시지가 너무 일찍 끝났습니다 (premature end-of-message).
     encryptInterceptor.decrypt.failed=메시지를 해독하지 못했습니다.
     encryptInterceptor.encrypt.failed=메시지를 암호화하지 못했습니다.
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties+3 2 modified
    @@ -19,8 +19,9 @@
     domainFilterInterceptor.member.refused=Member [{0}] was refused to join cluster
     domainFilterInterceptor.message.refused=Received message from cluster[{0}] was refused.
     
    -encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor does not support block cipher mode [{0}]
    +encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/GCM/NoPadding
    +encryptInterceptor.algorithm.switch=The EncryptInterceptor is using the algorithm [{0}]. It is recommended to switch to using AES/GCM/NoPadding.
    +encryptInterceptor.algorithm.unsupported=EncryptInterceptor does not support algorithm [{0}]
     encryptInterceptor.decrypt.error.short-message=Failed to decrypt message: premature end-of-message
     encryptInterceptor.decrypt.failed=Failed to decrypt message
     encryptInterceptor.encrypt.failed=Failed to encrypt message
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=成员被拒绝加入集群 cluster[{0}]
     domainFilterInterceptor.message.refused=从集群[{0}]中接收的消息被拒绝
     
     encryptInterceptor.algorithm.required=加密算法是必需的,充分说明,例如AES / CBC / PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor不支持分组密码模式[{0}]
     encryptInterceptor.decrypt.error.short-message=解密消息失败: 结尾消息提前结束
     encryptInterceptor.decrypt.failed=无法解密信息
     encryptInterceptor.encrypt.failed=无法加密信息
    
  • test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptorAlgorithms.java+162 0 added
    @@ -0,0 +1,162 @@
    +/*
    + * 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.util.ArrayList;
    +import java.util.Collection;
    +import java.util.List;
    +
    +import org.junit.Assert;
    +import org.junit.Test;
    +import org.junit.runner.RunWith;
    +import org.junit.runners.Parameterized;
    +import org.junit.runners.Parameterized.Parameter;
    +import org.junit.runners.Parameterized.Parameters;
    +
    +import org.apache.catalina.tribes.Channel;
    +
    +@RunWith(Parameterized.class)
    +public class TestEncryptInterceptorAlgorithms extends EncryptionInterceptorBaseTest {
    +
    +    @Parameters(name = "{index} {0}/{1}/{2}")
    +    public static Collection<Object[]> inputs() {
    +
    +        List<Object[]> result = new ArrayList<>();
    +        // Covers all cipher algorithm modes currently listed in Java Standard Names
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "NONE", "NoPadding", Boolean.FALSE});
    +        // Not supported - Insecure - Padding makes no sense if there is no encryption
    +        result.add(new Object[] { "AES", "NONE", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "CBC", "NoPadding", Boolean.FALSE});
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "CBC", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - JCA provider doesn't included it
    +        result.add(new Object[] { "AES", "CCM", "NoPadding", Boolean.FALSE});
    +        // Not supported - JCA provider doesn't included it - CCM doesn't need (support?) padding
    +        result.add(new Object[] { "AES", "CCM", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "CFB", "NoPadding", Boolean.FALSE});
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "CFB", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - Insecure and/or slow
    +        result.add(new Object[] { "AES", "CFB8", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB8", "PKCS5Padding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB16", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB16", "PKCS5Padding", Boolean.FALSE});
    +        // large block sizes not tested but will be rejected as well
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "CTR", "NoPadding", Boolean.FALSE});
    +        // Not supported - Configuration not recommended
    +        result.add(new Object[] { "AES", "CTR", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - has minimum length
    +        result.add(new Object[] { "AES", "CTS", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CTS", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "ECB", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "ECB", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Default for Tomcat 12 onwards
    +        result.add(new Object[] { "AES", "GCM", "NoPadding", Boolean.TRUE});
    +        // Not supported - GCM doesn't need (support?) padding
    +        result.add(new Object[] { "AES", "GCM", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - KW not appropriate for encrypting cluster messages
    +        result.add(new Object[] { "AES", "KW", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "KW", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - KWP not appropriate for encrypting cluster messages
    +        result.add(new Object[] { "AES", "KWP", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "KWP", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "OFB", "NoPadding", Boolean.FALSE});
    +
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "OFB", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - Insecure and/or slow
    +        result.add(new Object[] { "AES", "OFB8", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB8", "PKCS5Padding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB16", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB16", "PKCS5Padding", Boolean.FALSE});
    +        // large block sizes not tested but will be rejected as well
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "PCBC", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "PCBC", "PKCS5Padding", Boolean.FALSE});
    +
    +        return result;
    +    }
    +
    +    @Parameter(0)
    +    public String algorithm;
    +
    +    @Parameter(1)
    +    public String mode;
    +
    +    @Parameter(2)
    +    public String padding;
    +
    +    @Parameter(3)
    +    public boolean shouldSucceed;
    +
    +    @Test
    +    public void testAlgorithm() throws Exception {
    +        if (shouldSucceed) {
    +            doTestShouldSucceed();
    +        } else {
    +            doTestShouldNotSucceed();
    +        }
    +    }
    +
    +    private void doTestShouldSucceed() throws Exception {
    +        String transformation = String.format("%s/%s/%s", algorithm, mode, padding);
    +
    +        src.setEncryptionAlgorithm(transformation);
    +        src.start(Channel.SND_TX_SEQ);
    +        dest.setEncryptionAlgorithm(transformation);
    +        dest.start(Channel.SND_TX_SEQ);
    +
    +        String testInput = "The quick brown fox jumps over the lazy dog.";
    +
    +        Assert.assertEquals("Failed in " + transformation + " mode",
    +                     testInput,
    +                     roundTrip(testInput, src, dest));
    +    }
    +
    +    private void doTestShouldNotSucceed() throws Exception {
    +        try {
    +            String transformation = String.format("%s/%s/%s", algorithm, mode, padding);
    +            src.setEncryptionAlgorithm(transformation);
    +            src.start(Channel.SND_TX_SEQ);
    +
    +            // start() should trigger IllegalArgumentException
    +            Assert.fail(transformation + " mode is not being refused");
    +        } catch (IllegalArgumentException iae) {
    +            // Expected
    +        }
    +    }
    +}
    
  • test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java+0 58 modified
    @@ -160,64 +160,6 @@ public void test256BitKey() throws Exception {
                          roundTrip(testInput, src, dest));
         }
     
    -    @Test
    -    public void testOFB() throws Exception {
    -        src.setEncryptionAlgorithm("AES/OFB/PKCS5Padding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/OFB/PKCS5Padding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in OFB mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    @Test
    -    public void testCFB() throws Exception {
    -        src.setEncryptionAlgorithm("AES/CFB/PKCS5Padding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/CFB/PKCS5Padding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in CFB mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    @Test
    -    public void testGCM() throws Exception {
    -        src.setEncryptionAlgorithm("AES/GCM/NoPadding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/GCM/NoPadding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in GCM mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    /*
    -     * ECB mode isn't supported because it's insecure.
    -     */
    -    @Test
    -    public void testECB() throws Exception {
    -        try {
    -            src.setEncryptionAlgorithm("AES/ECB/PKCS5Padding");
    -            src.start(Channel.SND_TX_SEQ);
    -
    -            // start() should trigger IllegalArgumentException
    -            Assert.fail("ECB mode is not being refused");
    -        } catch (IllegalArgumentException iae) {
    -            // Expected
    -        }
    -    }
    -
         @Test
         public void testViaFile() throws Exception {
             src.start(Channel.SND_TX_SEQ);
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -250,6 +250,10 @@
             <bug>69970</bug>: Support raw IPv6 in Kubernetes membership provider
             for the service host. (remm)
           </fix>
    +      <add>
    +        Add support for new algorithms provided by JPA providers to the
    +        <code>EncyptInterceptor</code>. (markt)
    +      </add>
         </changelog>
       </subsection>
       <subsection name="WebSocket">
    
  • webapps/docs/config/cluster-interceptor.xml+12 7 modified
    @@ -214,18 +214,23 @@
        </p>
        <attributes>
          <attribute name="encryptionAlgorithm" required="false">
    -       The encryption algorithm to be used, including the mode and padding. Please see
    +       <p>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.
    +       for the standard JCA names that can be used. Algorithms from other JCA
    +       providers are also supported.</p>
     
    -       EncryptInterceptor currently supports the following
    +       <p>The EncryptInterceptor currently supports the following standard
            <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">block-cipher modes</a>:
    -       CBC, OFB, CFB, and GCM.
    +       CBC, CFB, OFB and GCM. Of these, it is recommended that GCM is always
    +       used.</p>
     
    -       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).
    +       <p>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).</p>
     
    -       The default algorithm is <code>AES/CBC/PKCS5Padding</code>.
    +       <p>The default algorithm is <code>AES/CBC/PKCS5Padding</code> for
    +       backwards compatability but it is recommended that
    +       <code>AES/GCM/NoPadding</code> is used.</p>
          </attribute>
          <attribute name="encryptionKey" required="true">
            The key to be used with the encryption algorithm.
    
6d955cceca84

Add support for new algorithms provided by JPA providers

https://github.com/apache/tomcatMark ThomasMar 13, 2026via ghsa
10 files changed · +231 85
  • java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java+50 14 modified
    @@ -22,6 +22,7 @@
     import java.security.NoSuchProviderException;
     import java.security.SecureRandom;
     import java.security.spec.AlgorithmParameterSpec;
    +import java.util.Locale;
     import java.util.concurrent.ConcurrentLinkedQueue;
     
     import javax.crypto.Cipher;
    @@ -42,7 +43,6 @@
     import org.apache.juli.logging.Log;
     import org.apache.juli.logging.LogFactory;
     
    -
     /**
      * 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
    @@ -140,17 +140,18 @@ public void messageReceived(ChannelMessage msg) {
                 xbb.clear();
                 xbb.append(data, 0, data.length);
     
    -            super.messageReceived(msg);
             } catch (GeneralSecurityException gse) {
                 log.error(sm.getString("encryptInterceptor.decrypt.failed"), gse);
             }
    +        super.messageReceived(msg);
         }
     
         /**
          * Sets the encryption algorithm to be used for encrypting and decrypting channel messages. You must specify the
          * <code>algorithm/mode/padding</code>. Information on standard algorithm names may be found in the
          * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">Java
    -     * documentation</a>. Default is <code>AES/CBC/PKCS5Padding</code>.
    +     * documentation</a>. Default is <code>AES/CBC/PKCS5Padding</code> for backwards compatibility but it is recommended
    +     * that <code>AES/GCM/NoPadding</code> is used.
          *
          * @param algorithm The algorithm to use.
          */
    @@ -314,33 +315,68 @@ private static BaseEncryptionManager createEncryptionManager(String algorithm, b
     
             String algorithmName;
             String algorithmMode;
    +        String algorithmPadding;
     
    -        // We need to break-apart the algorithm name e.g. AES/CBC/PKCS5Padding
    +        // We need to break-apart the algorithm name e.g. AES/GCM/NoPadding
             // take just the algorithm part.
             int pos = algorithm.indexOf('/');
     
             if (pos >= 0) {
    -            algorithmName = algorithm.substring(0, pos);
    +            algorithmName = algorithm.substring(0, pos).toUpperCase(Locale.ENGLISH);
                 int pos2 = algorithm.indexOf('/', pos + 1);
     
                 if (pos2 >= 0) {
    -                algorithmMode = algorithm.substring(pos + 1, pos2);
    +                algorithmMode = algorithm.substring(pos + 1, pos2).toUpperCase(Locale.ENGLISH);
    +                algorithmPadding = algorithm.substring(pos2 + 1).toUpperCase(Locale.ENGLISH);
                 } else {
    -                algorithmMode = "CBC";
    +                algorithmMode = "GCM";
    +                algorithmPadding = "NOPADDING";
                 }
             } else {
                 algorithmName = algorithm;
    -            algorithmMode = "CBC";
    +            algorithmMode = "GCM";
    +            algorithmPadding = "NOPADDING";
             }
     
    -        if ("GCM".equalsIgnoreCase(algorithmMode)) {
    +        /*
    +         * Limit the cipher algorithm modes available. The limits are based on the cipher algorithm modes listed in the
    +         * Java Standard Names documentation. Those modes that are not appropriate or provide no protection are blocked.
    +         * Where there are performance or security concerns regarding a mode, a warning is logged. Unrecognised modes,
    +         * such as those provided by custom JCA providers are allowed but will be rejected if there is no JCA provider
    +         * to support them.
    +         */
    +        if ("NONE".equals(algorithmMode) || "ECB".equals(algorithmMode) || "PCBC".equals(algorithmMode) ||
    +                "CTS".equals(algorithmMode) || "KW".equals(algorithmMode) || "KWP".equals(algorithmMode) ||
    +                "CTR".equals(algorithmMode) ||
    +                ("CBC".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding)) ||
    +                ("CFB".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding)) ||
    +                ("GCM".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("OFB".equals(algorithmMode) && "NOPADDING".equals(algorithmPadding))) {
    +            // Insecure, unsuitable or unsupported
    +            throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported", algorithm));
    +
    +        } else if (("CBC".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("CFB".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding)) ||
    +                ("OFB".equals(algorithmMode) && "PKCS5PADDING".equals(algorithmPadding))) {
    +            // Supported but not recommended as more secure modes are available
    +            log.warn(sm.getString("encryptInterceptor.algorithm.switch", algorithm));
    +
    +        } else if (algorithmMode.startsWith("CFB") || algorithmMode.startsWith("OFB")) {
    +            // Using a non-default block size. Not supported as insecure and/or inefficient.
    +            throw new IllegalArgumentException(
    +                    sm.getString("encryptInterceptor.algorithm.unsupported", algorithm));
    +
    +        } else if ("GCM".equalsIgnoreCase(algorithmMode) && "NOPADDING".equals(algorithmPadding)) {
    +            // Needs a specialised encryption manager to handle the differences between GCM and other modes
                 return new GCMEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
    -        } else if ("CBC".equalsIgnoreCase(algorithmMode) || "OFB".equalsIgnoreCase(algorithmMode) ||
    -                "CFB".equalsIgnoreCase(algorithmMode)) {
    +        }
    +
    +        // Use the default encryption manager
    +        try {
                 return new BaseEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
    -        } else {
    -            throw new IllegalArgumentException(
    -                    sm.getString("encryptInterceptor.algorithm.unsupported-mode", algorithmMode));
    +        } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException ex) {
    +            throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported", algorithmMode),
    +                    ex);
             }
         }
     
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=Le membre [{0}] a été refusé dans le c
     domainFilterInterceptor.message.refused=Le message reçu du cluster [{0}] a été refusé
     
     encryptInterceptor.algorithm.required=Un algorithme de cryptage est requis, avec une spécification complète telle que AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=L''EncryptInterceptor ne supporte pas le mode de chiffrage de bloc [{0}]
     encryptInterceptor.decrypt.error.short-message=Echec du décryptage du message : fin de message prématuré
     encryptInterceptor.decrypt.failed=Echec de décryptage du message
     encryptInterceptor.encrypt.failed=Erreur de cryptage du message
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=メンバーはクラスター [{0}] へ
     domainFilterInterceptor.message.refused=クラスター [{0}] から受信したメッセージは拒否されました。
     
     encryptInterceptor.algorithm.required=暗号化アルゴリズムが必要です。完全指定。 AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptorはブロック暗号モード [{0}]をサポートしていません。
     encryptInterceptor.decrypt.error.short-message=メッセージの復号に失敗: メッセージの末尾が途切れています
     encryptInterceptor.decrypt.failed=メッセージの復号に失敗しました。
     encryptInterceptor.encrypt.failed=メッセージを暗号化できません。
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ko.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=멤버 [{0}]이(가) 클러스터에 참
     domainFilterInterceptor.message.refused=클러스터 [{0}](으)로부터 받은 메시지가 거부되었습니다.
     
     encryptInterceptor.algorithm.required=암호화 알고리즘을 완전하게 지정해야 합니다. 예) AES/CBC/PKCS5Padding.
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor가 블록 cipher 모드 [{0}]을(를) 지원하지 않습니다.
     encryptInterceptor.decrypt.error.short-message=메시지를 해독하지 못했습니다: 메시지가 너무 일찍 끝났습니다 (premature end-of-message).
     encryptInterceptor.decrypt.failed=메시지를 해독하지 못했습니다.
     encryptInterceptor.encrypt.failed=메시지를 암호화하지 못했습니다.
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties+3 2 modified
    @@ -19,8 +19,9 @@
     domainFilterInterceptor.member.refused=Member [{0}] was refused to join cluster
     domainFilterInterceptor.message.refused=Received message from cluster[{0}] was refused.
     
    -encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/CBC/PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor does not support block cipher mode [{0}]
    +encryptInterceptor.algorithm.required=Encryption algorithm is required, fully-specified e.g. AES/GCM/NoPadding
    +encryptInterceptor.algorithm.switch=The EncryptInterceptor is using the algorithm [{0}]. It is recommended to switch to using AES/GCM/NoPadding.
    +encryptInterceptor.algorithm.unsupported=EncryptInterceptor does not support algorithm [{0}]
     encryptInterceptor.decrypt.error.short-message=Failed to decrypt message: premature end-of-message
     encryptInterceptor.decrypt.failed=Failed to decrypt message
     encryptInterceptor.encrypt.failed=Failed to encrypt message
    
  • java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties+0 1 modified
    @@ -20,7 +20,6 @@ domainFilterInterceptor.member.refused=成员被拒绝加入集群 cluster[{0}]
     domainFilterInterceptor.message.refused=从集群[{0}]中接收的消息被拒绝
     
     encryptInterceptor.algorithm.required=加密算法是必需的,充分说明,例如AES / CBC / PKCS5Padding
    -encryptInterceptor.algorithm.unsupported-mode=EncryptInterceptor不支持分组密码模式[{0}]
     encryptInterceptor.decrypt.error.short-message=解密消息失败: 结尾消息提前结束
     encryptInterceptor.decrypt.failed=无法解密信息
     encryptInterceptor.encrypt.failed=无法加密信息
    
  • test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptorAlgorithms.java+162 0 added
    @@ -0,0 +1,162 @@
    +/*
    + * 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.util.ArrayList;
    +import java.util.Collection;
    +import java.util.List;
    +
    +import org.junit.Assert;
    +import org.junit.Test;
    +import org.junit.runner.RunWith;
    +import org.junit.runners.Parameterized;
    +import org.junit.runners.Parameterized.Parameter;
    +import org.junit.runners.Parameterized.Parameters;
    +
    +import org.apache.catalina.tribes.Channel;
    +
    +@RunWith(Parameterized.class)
    +public class TestEncryptInterceptorAlgorithms extends EncryptionInterceptorBaseTest {
    +
    +    @Parameters(name = "{index} {0}/{1}/{2}")
    +    public static Collection<Object[]> inputs() {
    +
    +        List<Object[]> result = new ArrayList<>();
    +        // Covers all cipher algorithm modes currently listed in Java Standard Names
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "NONE", "NoPadding", Boolean.FALSE});
    +        // Not supported - Insecure - Padding makes no sense if there is no encryption
    +        result.add(new Object[] { "AES", "NONE", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "CBC", "NoPadding", Boolean.FALSE});
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "CBC", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - JCA provider doesn't included it
    +        result.add(new Object[] { "AES", "CCM", "NoPadding", Boolean.FALSE});
    +        // Not supported - JCA provider doesn't included it - CCM doesn't need (support?) padding
    +        result.add(new Object[] { "AES", "CCM", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "CFB", "NoPadding", Boolean.FALSE});
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "CFB", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - Insecure and/or slow
    +        result.add(new Object[] { "AES", "CFB8", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB8", "PKCS5Padding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB16", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CFB16", "PKCS5Padding", Boolean.FALSE});
    +        // large block sizes not tested but will be rejected as well
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "CTR", "NoPadding", Boolean.FALSE});
    +        // Not supported - Configuration not recommended
    +        result.add(new Object[] { "AES", "CTR", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - has minimum length
    +        result.add(new Object[] { "AES", "CTS", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "CTS", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "ECB", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "ECB", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Default for Tomcat 12 onwards
    +        result.add(new Object[] { "AES", "GCM", "NoPadding", Boolean.TRUE});
    +        // Not supported - GCM doesn't need (support?) padding
    +        result.add(new Object[] { "AES", "GCM", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - KW not appropriate for encrypting cluster messages
    +        result.add(new Object[] { "AES", "KW", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "KW", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - KWP not appropriate for encrypting cluster messages
    +        result.add(new Object[] { "AES", "KWP", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "KWP", "PKCS5Padding", Boolean.FALSE});
    +
    +        // Not supported - NoPadding requires fixed block size and cluster messages are variable length
    +        result.add(new Object[] { "AES", "OFB", "NoPadding", Boolean.FALSE});
    +
    +        // Supported but not recommended - possible security issues in some configurations - backwards compatibility
    +        result.add(new Object[] { "AES", "OFB", "PKCS5Padding", Boolean.TRUE});
    +
    +        // Not supported - Insecure and/or slow
    +        result.add(new Object[] { "AES", "OFB8", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB8", "PKCS5Padding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB16", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "OFB16", "PKCS5Padding", Boolean.FALSE});
    +        // large block sizes not tested but will be rejected as well
    +
    +        // Not supported - Insecure
    +        result.add(new Object[] { "AES", "PCBC", "NoPadding", Boolean.FALSE});
    +        result.add(new Object[] { "AES", "PCBC", "PKCS5Padding", Boolean.FALSE});
    +
    +        return result;
    +    }
    +
    +    @Parameter(0)
    +    public String algorithm;
    +
    +    @Parameter(1)
    +    public String mode;
    +
    +    @Parameter(2)
    +    public String padding;
    +
    +    @Parameter(3)
    +    public boolean shouldSucceed;
    +
    +    @Test
    +    public void testAlgorithm() throws Exception {
    +        if (shouldSucceed) {
    +            doTestShouldSucceed();
    +        } else {
    +            doTestShouldNotSucceed();
    +        }
    +    }
    +
    +    private void doTestShouldSucceed() throws Exception {
    +        String transformation = String.format("%s/%s/%s", algorithm, mode, padding);
    +
    +        src.setEncryptionAlgorithm(transformation);
    +        src.start(Channel.SND_TX_SEQ);
    +        dest.setEncryptionAlgorithm(transformation);
    +        dest.start(Channel.SND_TX_SEQ);
    +
    +        String testInput = "The quick brown fox jumps over the lazy dog.";
    +
    +        Assert.assertEquals("Failed in " + transformation + " mode",
    +                     testInput,
    +                     roundTrip(testInput, src, dest));
    +    }
    +
    +    private void doTestShouldNotSucceed() throws Exception {
    +        try {
    +            String transformation = String.format("%s/%s/%s", algorithm, mode, padding);
    +            src.setEncryptionAlgorithm(transformation);
    +            src.start(Channel.SND_TX_SEQ);
    +
    +            // start() should trigger IllegalArgumentException
    +            Assert.fail(transformation + " mode is not being refused");
    +        } catch (IllegalArgumentException iae) {
    +            // Expected
    +        }
    +    }
    +}
    
  • test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java+0 58 modified
    @@ -160,64 +160,6 @@ public void test256BitKey() throws Exception {
                          roundTrip(testInput, src, dest));
         }
     
    -    @Test
    -    public void testOFB() throws Exception {
    -        src.setEncryptionAlgorithm("AES/OFB/PKCS5Padding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/OFB/PKCS5Padding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in OFB mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    @Test
    -    public void testCFB() throws Exception {
    -        src.setEncryptionAlgorithm("AES/CFB/PKCS5Padding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/CFB/PKCS5Padding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in CFB mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    @Test
    -    public void testGCM() throws Exception {
    -        src.setEncryptionAlgorithm("AES/GCM/NoPadding");
    -        src.start(Channel.SND_TX_SEQ);
    -        dest.setEncryptionAlgorithm("AES/GCM/NoPadding");
    -        dest.start(Channel.SND_TX_SEQ);
    -
    -        String testInput = "The quick brown fox jumps over the lazy dog.";
    -
    -        Assert.assertEquals("Failed in GCM mode",
    -                     testInput,
    -                     roundTrip(testInput, src, dest));
    -    }
    -
    -    /*
    -     * ECB mode isn't supported because it's insecure.
    -     */
    -    @Test
    -    public void testECB() throws Exception {
    -        try {
    -            src.setEncryptionAlgorithm("AES/ECB/PKCS5Padding");
    -            src.start(Channel.SND_TX_SEQ);
    -
    -            // start() should trigger IllegalArgumentException
    -            Assert.fail("ECB mode is not being refused");
    -        } catch (IllegalArgumentException iae) {
    -            // Expected
    -        }
    -    }
    -
         @Test
         public void testViaFile() throws Exception {
             src.start(Channel.SND_TX_SEQ);
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -246,6 +246,10 @@
             <bug>69970</bug>: Support raw IPv6 in Kubernetes membership provider
             for the service host. (remm)
           </fix>
    +      <add>
    +        Add support for new algorithms provided by JPA providers to the
    +        <code>EncyptInterceptor</code>. (markt)
    +      </add>
         </changelog>
       </subsection>
       <subsection name="WebSocket">
    
  • webapps/docs/config/cluster-interceptor.xml+12 7 modified
    @@ -214,18 +214,23 @@
        </p>
        <attributes>
          <attribute name="encryptionAlgorithm" required="false">
    -       The encryption algorithm to be used, including the mode and padding. Please see
    +       <p>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.
    +       for the standard JCA names that can be used. Algorithms from other JCA
    +       providers are also supported.</p>
     
    -       EncryptInterceptor currently supports the following
    +       <p>The EncryptInterceptor currently supports the following standard
            <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">block-cipher modes</a>:
    -       CBC, OFB, CFB, and GCM.
    +       CBC, CFB, OFB and GCM. Of these, it is recommended that GCM is always
    +       used.</p>
     
    -       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).
    +       <p>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).</p>
     
    -       The default algorithm is <code>AES/CBC/PKCS5Padding</code>.
    +       <p>The default algorithm is <code>AES/CBC/PKCS5Padding</code> for
    +       backwards compatability but it is recommended that
    +       <code>AES/GCM/NoPadding</code> is used.</p>
          </attribute>
          <attribute name="encryptionKey" required="true">
            The key to be used with the encryption algorithm.
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.