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