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/11/25 15:13:29 UTC

svn commit: r1847414 - /tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java

Author: schultz
Date: Sun Nov 25 15:13:29 2018
New Revision: 1847414

URL: http://svn.apache.org/viewvc?rev=1847414&view=rev
Log:
Re-factor code into separate EncryptionManager class; prep for additional features.
No functional change.

Modified:
    tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java

Modified: 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=1847414&r1=1847413&r2=1847414&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java (original)
+++ tomcat/trunk/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java Sun Nov 25 15:13:29 2018
@@ -62,23 +62,8 @@ public class EncryptInterceptor extends
     private byte[] encryptionKeyBytes;
     private String encryptionKeyString;
 
-    /**
-     * This is the name of the core encryption algorithm e.g. AES.
-     */
-    private String algorithmName;
-    /**
-     * The size of the initialization vector to use for encryption. This is
-     * often, but not always, the same as the block size.
-     */
-    private int ivSize;
-    /**
-     * This is the name of the provider which will not change after
-     * a call to {@link #start}.
-     */
-    private String providerNameInternal;
-    private SecretKeySpec secretKey;
-    private ConcurrentLinkedQueue<Cipher> cipherPool;
-    private ConcurrentLinkedQueue<SecureRandom> randomPool;
+
+    private EncryptionManager encryptionManager;
 
     public EncryptInterceptor() {
     }
@@ -87,7 +72,9 @@ public class EncryptInterceptor extends
     public void start(int svc) throws ChannelException {
         if(Channel.SND_TX_SEQ == (svc & Channel.SND_TX_SEQ)) {
             try {
-                initInternal();
+                encryptionManager = createEncryptionManager(getEncryptionAlgorithm(),
+                        getEncryptionKeyInternal(),
+                        getProviderName());
             } catch (GeneralSecurityException gse) {
                 throw new ChannelException(sm.getString("encryptInterceptor.init.failed"), gse);
             }
@@ -99,9 +86,7 @@ public class EncryptInterceptor extends
     @Override
     public void stop(int svc) throws ChannelException {
         if(Channel.SND_TX_SEQ == (svc & Channel.SND_TX_SEQ)) {
-            // Individual Cipher and SecureRandom objects need no explicit teardown
-            cipherPool.clear();
-            randomPool.clear();
+            encryptionManager.shutdown();
         }
 
         super.stop(svc);
@@ -114,7 +99,7 @@ public class EncryptInterceptor extends
             byte[] data = msg.getMessage().getBytes();
 
             // See #encrypt(byte[]) for an explanation of the return value
-            byte[][] bytes = encrypt(data);
+            byte[][] bytes = encryptionManager.encrypt(data);
 
             XByteBuffer xbb = msg.getMessage();
 
@@ -136,7 +121,7 @@ public class EncryptInterceptor extends
         try {
             byte[] data = msg.getMessage().getBytes();
 
-            data = decrypt(data);
+            data = encryptionManager.decrypt(data);
 
             XByteBuffer xbb = msg.getMessage();
 
@@ -270,45 +255,56 @@ public class EncryptInterceptor extends
         return providerName;
     }
 
-    private void setSecretKey(SecretKeySpec secretKey) {
-        this.secretKey = secretKey;
-    }
+    // Copied from org.apache.tomcat.util.buf.HexUtils
 
-    private SecretKeySpec getSecretKey() {
-        return secretKey;
-    }
+    private static final int[] DEC = {
+        00, 01, 02, 03, 04, 05, 06, 07,  8,  9, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15,
+    };
 
-    private void setAlgorithmName(String algorithm) {
-        algorithmName = algorithm;
-    }
 
-    private String getAlgorithmName() {
-        return algorithmName;
+    private static int getDec(int index) {
+        // Fast for correct values, slower for incorrect ones
+        try {
+            return DEC[index - '0'];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            return -1;
+        }
     }
 
-    private void setIVSize(int size) {
-        ivSize = size;
-    }
 
-    private int getIVSize() {
-        return ivSize;
-    }
+    private static byte[] fromHexString(String input) {
+        if (input == null) {
+            return null;
+        }
 
-    private void setProviderNameInternal(String providerName) {
-        providerNameInternal = providerName;
-    }
+        if ((input.length() & 1) == 1) {
+            // Odd number of characters
+            throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.oddDigits"));
+        }
 
-    private String getProviderNameInternal() {
-        return providerNameInternal;
+        char[] inputChars = input.toCharArray();
+        byte[] result = new byte[input.length() >> 1];
+        for (int i = 0; i < result.length; i++) {
+            int upperNibble = getDec(inputChars[2*i]);
+            int lowerNibble =  getDec(inputChars[2*i + 1]);
+            if (upperNibble < 0 || lowerNibble < 0) {
+                // Non hex character
+                throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.nonHex"));
+            }
+            result[i] = (byte) ((upperNibble << 4) + lowerNibble);
+        }
+        return result;
     }
 
-    private void initInternal()
+    private static EncryptionManager createEncryptionManager(String algorithm,
+            byte[] encryptionKey, String providerName)
         throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
-        if(null == getEncryptionKey())
+        if(null == encryptionKey)
             throw new IllegalStateException(sm.getString("encryptInterceptor.key.required"));
 
-        String algorithm = getEncryptionAlgorithm();
-
         String algorithmName;
         String algorithmMode;
 
@@ -332,178 +328,216 @@ public class EncryptInterceptor extends
 
         // Note: ECB is not an appropriate mode for secure communications.
         if(!("CBC".equalsIgnoreCase(algorithmMode)
-             || "OFB".equalsIgnoreCase(algorithmMode)
-             || "CFB".equalsIgnoreCase(algorithmMode)))
+                || "OFB".equalsIgnoreCase(algorithmMode)
+                || "CFB".equalsIgnoreCase(algorithmMode)))
             throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported-mode", algorithmMode));
 
-        setAlgorithmName(algorithm);
-        setProviderNameInternal(getProviderName());
-        setSecretKey(new SecretKeySpec(getEncryptionKeyInternal(), algorithmName));
-
-        cipherPool = new ConcurrentLinkedQueue<>();
-        Cipher cipher = createCipher();
-        setIVSize(cipher.getBlockSize());
-        cipherPool.offer(cipher);
-        randomPool = new ConcurrentLinkedQueue<>();
-    }
+        EncryptionManager mgr = new EncryptionManager(algorithm,
+                new SecretKeySpec(encryptionKey, algorithmName),
+                providerName);
+
+        return mgr;
+    }
+
+    private static class EncryptionManager {
+        /**
+         * The fully-specified algorithm e.g. AES/CBC/PKCS5Padding.
+         */
+        private String algorithm;
+
+        /**
+         * The size of the initialization vector to use for encryption. This is
+         * often, but not always, the same as the block size.
+         */
+        private int ivSize;
+
+        /**
+         * The cryptographic provider name.
+         */
+        private String providerName;
+
+        /**
+         * The secret key to use for encryption and decryption operations.
+         */
+        private SecretKeySpec secretKey;
+
+        /**
+         * A pool of Cipher objects. Ciphers are expensive to create, but not
+         * to re-initialize, so we use a pool of them which grows as necessary.
+         */
+        private ConcurrentLinkedQueue<Cipher> cipherPool;
+
+        /**
+         * A pool of SecureRandom objects. Each encrypt operation requires access
+         * to a source of randomness. SecureRandom is thread-safe, but sharing a
+         * single instance will likely be a bottleneck.
+         */
+        private ConcurrentLinkedQueue<SecureRandom> randomPool;
+
+        public EncryptionManager(String algorithm, SecretKeySpec secretKey, String providerName)
+            throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
+            setAlgorithm(algorithm);
+            setProviderName(providerName);
+            setSecretKey(secretKey);
+
+            cipherPool = new ConcurrentLinkedQueue<>();
+            Cipher cipher = createCipher();
+            setIVSize(cipher.getBlockSize());
+            cipherPool.offer(cipher);
+            randomPool = new ConcurrentLinkedQueue<>();
+        }
 
-    private Cipher createCipher()
-        throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
-        String providerName = getProviderNameInternal();
+        public void shutdown() {
+            // Individual Cipher and SecureRandom objects need no explicit teardown
+            cipherPool.clear();
+            randomPool.clear();
+        }
 
-        if(null == providerName) {
-            return Cipher.getInstance(getAlgorithmName());
-        } else {
-            return Cipher.getInstance(getAlgorithmName(), providerName);
+        private void setAlgorithm(String algorithm) {
+            this.algorithm = algorithm;
         }
-    }
 
-    private Cipher getCipher() throws GeneralSecurityException {
-        Cipher cipher = cipherPool.poll();
+        private String getAlgorithm() {
+            return algorithm;
+        }
 
-        if(null == cipher) {
-            cipher = createCipher();
+        private void setSecretKey(SecretKeySpec secretKey) {
+            this.secretKey = secretKey;
         }
 
-        return cipher;
-    }
+        private SecretKeySpec getSecretKey() {
+            return secretKey;
+        }
 
-    private void returnCipher(Cipher cipher) {
-        cipherPool.offer(cipher);
-    }
+        private void setIVSize(int size) {
+            ivSize = size;
+        }
 
-    private SecureRandom getRandom() {
-        SecureRandom random = randomPool.poll();
+        private int getIVSize() {
+            return ivSize;
+        }
 
-        if(null == random) {
-            random = new SecureRandom();
+        private void setProviderName(String provider) {
+            providerName = provider;
         }
 
-        return random;
-    }
+        private String getProviderName() {
+            return providerName;
+        }
 
-    private void returnRandom(SecureRandom random) {
-        randomPool.offer(random);
-    }
+        private Cipher createCipher()
+            throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
+            String providerName = getProviderName();
 
-    /**
-     * Encrypts the input <code>bytes</code> into two separate byte arrays:
-     * one for the random initialization vector (IV) used for this message,
-     * 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 IV in [0] and the encrypted data in [1].
-     *
-     * @throws GeneralSecurityException If the input data cannot be encrypted.
-     */
-    private byte[][] encrypt(byte[] bytes) throws GeneralSecurityException {
-        Cipher cipher = null;
-        SecureRandom random = null;
-        byte[] iv = new byte[getIVSize()];
+            if(null == providerName) {
+                return Cipher.getInstance(getAlgorithm());
+            } else {
+                return Cipher.getInstance(getAlgorithm(), providerName);
+            }
+        }
 
-        try {
-            random = getRandom();
+        private Cipher getCipher() throws GeneralSecurityException {
+            Cipher cipher = cipherPool.poll();
 
-            // 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.
-            random.nextBytes(iv);
-
-            IvParameterSpec IV = new IvParameterSpec(iv);
-
-            cipher = getCipher();
-            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), IV);
-
-            // Prepend the IV to the beginning of the encrypted data
-            byte[][] data = new byte[2][];
-            data[0] = iv;
-            data[1] = cipher.doFinal(bytes);
-
-            return data;
-        } finally {
-            if(null != cipher)
-                returnCipher(cipher);
-            if(null != random)
-                returnRandom(random);
-        }
-    }
+            if(null == cipher) {
+                cipher = createCipher();
+            }
 
-    /**
-     * Decrypts the input <code>bytes</code>.
-     *
-     * @param bytes The data to decrypt.
-     *
-     * @return The decrypted data.
-     *
-     * @throws GeneralSecurityException If the input data cannot be decrypted.
-     */
-    private byte[] decrypt(byte[] bytes) throws GeneralSecurityException {
-        Cipher cipher = null;
+            return cipher;
+        }
 
-        int ivSize = getIVSize();
-        // Use first part of incoming data as IV
-        IvParameterSpec IV = new IvParameterSpec(bytes, 0, ivSize);
+        private void returnCipher(Cipher cipher) {
+            cipherPool.offer(cipher);
+        }
 
-        try {
-            cipher = getCipher();
+        private SecureRandom getRandom() {
+            SecureRandom random = randomPool.poll();
 
-            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), IV);
+            if(null == random) {
+                random = new SecureRandom();
+            }
 
-            // Decrypt remainder of the message.
-            return cipher.doFinal(bytes, ivSize, bytes.length - ivSize);
-        } finally {
-            if(null != cipher)
-                returnCipher(cipher);
+            return random;
         }
-    }
-
 
-    // Copied from org.apache.tomcat.util.buf.HexUtils
+        private void returnRandom(SecureRandom random) {
+            randomPool.offer(random);
+        }
 
-    private static final int[] DEC = {
-        00, 01, 02, 03, 04, 05, 06, 07,  8,  9, -1, -1, -1, -1, -1, -1,
-        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-        -1, 10, 11, 12, 13, 14, 15,
-    };
+        /**
+         * Encrypts the input <code>bytes</code> into two separate byte arrays:
+         * one for the random initialization vector (IV) used for this message,
+         * 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 IV in [0] and the encrypted data in [1].
+         *
+         * @throws GeneralSecurityException If the input data cannot be encrypted.
+         */
+        private byte[][] encrypt(byte[] bytes) throws GeneralSecurityException {
+            Cipher cipher = null;
+            SecureRandom random = null;
+            byte[] iv = new byte[getIVSize()];
 
+            try {
+                random = getRandom();
 
-    private static int getDec(int index) {
-        // Fast for correct values, slower for incorrect ones
-        try {
-            return DEC[index - '0'];
-        } catch (ArrayIndexOutOfBoundsException ex) {
-            return -1;
+                // 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.
+                random.nextBytes(iv);
+
+                IvParameterSpec IV = new IvParameterSpec(iv);
+
+                cipher = getCipher();
+                cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), IV);
+
+                // Prepend the IV to the beginning of the encrypted data
+                byte[][] data = new byte[2][];
+                data[0] = iv;
+                data[1] = cipher.doFinal(bytes);
+
+                return data;
+            } finally {
+                if(null != cipher)
+                    returnCipher(cipher);
+                if(null != random)
+                    returnRandom(random);
+            }
         }
-    }
 
+        /**
+         * Decrypts the input <code>bytes</code>.
+         *
+         * @param bytes The data to decrypt.
+         *
+         * @return The decrypted data.
+         *
+         * @throws GeneralSecurityException If the input data cannot be decrypted.
+         */
+        private byte[] decrypt(byte[] bytes) throws GeneralSecurityException {
+            Cipher cipher = null;
+
+            int ivSize = getIVSize();
+            // Use first part of incoming data as IV
+            IvParameterSpec IV = new IvParameterSpec(bytes, 0, ivSize);
 
-    private static byte[] fromHexString(String input) {
-        if (input == null) {
-            return null;
-        }
+            try {
+                cipher = getCipher();
 
-        if ((input.length() & 1) == 1) {
-            // Odd number of characters
-            throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.oddDigits"));
-        }
+                cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), IV);
 
-        char[] inputChars = input.toCharArray();
-        byte[] result = new byte[input.length() >> 1];
-        for (int i = 0; i < result.length; i++) {
-            int upperNibble = getDec(inputChars[2*i]);
-            int lowerNibble =  getDec(inputChars[2*i + 1]);
-            if (upperNibble < 0 || lowerNibble < 0) {
-                // Non hex character
-                throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.nonHex"));
+                // Decrypt remainder of the message.
+                return cipher.doFinal(bytes, ivSize, bytes.length - ivSize);
+            } finally {
+                if(null != cipher)
+                    returnCipher(cipher);
             }
-            result[i] = (byte) ((upperNibble << 4) + lowerNibble);
         }
-        return result;
     }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org