This is an automated email from the ASF dual-hosted git repository. HTHou pushed a commit to branch codex/fix-encryption-metadata-validation in repository https://gitbox.apache.org/repos/asf/tsfile.git
commit c9ddb5f046639b3162fa2929eae19524cec541b6 Author: HTHou <[email protected]> AuthorDate: Fri Aug 28 15:33:24 2026 +0800 Fix encryption metadata handling --- .../org/apache/tsfile/i18n/messages.properties | 6 +++ .../org/apache/tsfile/i18n/messages_zh.properties | 6 +++ .../org/apache/tsfile/encrypt/EncryptUtils.java | 50 ++++++++++++++++------ .../java/org/apache/tsfile/encrypt/IDecryptor.java | 6 +-- .../java/org/apache/tsfile/encrypt/IEncryptor.java | 6 +-- .../tsfile/file/metadata/TsFileMetadata.java | 3 ++ .../org/apache/tsfile/encrypt/EncryptTest.java | 41 ++++++++++++++++++ 7 files changed, 99 insertions(+), 19 deletions(-) diff --git a/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties b/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties index ffea7c054..11c1db03b 100644 --- a/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties +++ b/java/common/src/main/resources/org/apache/tsfile/i18n/messages.properties @@ -1069,6 +1069,12 @@ error.external.comparator_chain_empty = ComparatorChains must contain at least o # === encrypt === +# EncryptUtils — unsupported encryption type +error.encrypt.type_not_supported = Unsupported encryption type: %1$s + +# EncryptUtils.getSecondKeyFromStr — key exceeds the supported length (length arg) +error.encrypt.key_too_long = Encryption key exceeds the maximum length of %1$s bytes + # IEncryptor.getEncryptor — class not found (class name arg) error.encrypt.encryptor_class_not_found = Get encryptor class failed, class not found: %1$s diff --git a/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties b/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties index 453977c76..a0015bd20 100644 --- a/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties +++ b/java/common/src/main/resources/org/apache/tsfile/i18n/messages_zh.properties @@ -1069,6 +1069,12 @@ error.external.comparator_chain_empty = ComparatorChain 必须至少包含一个 # === encrypt === +# EncryptUtils — unsupported encryption type +error.encrypt.type_not_supported = 不支持的加密类型: %1$s + +# EncryptUtils.getSecondKeyFromStr — key exceeds the supported length (length arg) +error.encrypt.key_too_long = 加密密钥长度超过支持的最大值 %1$s 字节 + # IEncryptor.getEncryptor — class not found (class name arg) error.encrypt.encryptor_class_not_found = 获取 encryptor class 失败,class not found: %1$s diff --git a/java/tsfile/src/main/java/org/apache/tsfile/encrypt/EncryptUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/encrypt/EncryptUtils.java index 42ccec8f7..9dfd9230c 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/encrypt/EncryptUtils.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/encrypt/EncryptUtils.java @@ -44,6 +44,10 @@ public class EncryptUtils { private static final String encryptClassPrefix = "org.apache.tsfile.encrypt."; + private static final int MAX_SECOND_KEY_LENGTH = 1024; + + private static final int MAX_SECOND_KEY_STRING_LENGTH = MAX_SECOND_KEY_LENGTH * 5; + private static volatile String normalKeyStr; private static volatile EncryptParameter encryptParam; @@ -69,16 +73,24 @@ public class EncryptUtils { } public static String getEncryptClass(String encryptType) { - String classNameRegex = "^(\\p{Alpha}\\w*)(\\.\\p{Alpha}\\w+)+$"; - if (IEncrypt.encryptTypeToClassMap.containsKey(encryptType)) { - return IEncrypt.encryptTypeToClassMap.get(encryptType); - } else if (encryptType.matches(classNameRegex)) { - IEncrypt.encryptTypeToClassMap.put(encryptType, encryptType); - return encryptType; - } else { - IEncrypt.encryptTypeToClassMap.put(encryptType, encryptClassPrefix + encryptType); - return encryptClassPrefix + encryptType; + if (encryptType == null || encryptType.isEmpty()) { + throw new EncryptException( + Messages.format("error.encrypt.type_not_supported", String.valueOf(encryptType))); + } + String className = + encryptType.startsWith(encryptClassPrefix) ? encryptType : encryptClassPrefix + encryptType; + IEncrypt.encryptTypeToClassMap.putIfAbsent(encryptType, className); + return className; + } + + static Class<? extends IEncrypt> loadEncryptClass(String encryptType) + throws ClassNotFoundException { + Class<?> encryptClass = + Class.forName(getEncryptClass(encryptType), false, EncryptUtils.class.getClassLoader()); + if (!IEncrypt.class.isAssignableFrom(encryptClass)) { + throw new EncryptException(Messages.format("error.encrypt.type_not_supported", encryptType)); } + return encryptClass.asSubclass(IEncrypt.class); } public static byte[] getEncryptKeyFromToken(String token, byte[] salt) { @@ -291,11 +303,11 @@ public class EncryptUtils { if (IEncrypt.encryptMap.containsKey(className)) { return ((IEncrypt) IEncrypt.encryptMap.get(className).newInstance(dataEncryptKey)); } - Class<?> encryptTypeClass = Class.forName(className); - java.lang.reflect.Constructor<?> constructor = + Class<? extends IEncrypt> encryptTypeClass = loadEncryptClass(encryptType); + java.lang.reflect.Constructor<? extends IEncrypt> constructor = encryptTypeClass.getDeclaredConstructor(byte[].class); IEncrypt.encryptMap.put(className, constructor); - return ((IEncrypt) constructor.newInstance(dataEncryptKey)); + return constructor.newInstance(dataEncryptKey); } catch (ClassNotFoundException e) { throw new EncryptException( Messages.format("error.encrypt.encrypt_class_not_found", encryptType), e); @@ -331,11 +343,23 @@ public class EncryptUtils { } public static byte[] getSecondKeyFromStr(String str) { - String[] strArray = str.split(","); + validateSecondKeyStringLength(str == null ? -1 : str.length()); + String[] strArray = str.split(",", MAX_SECOND_KEY_LENGTH + 1); + if (strArray.length > MAX_SECOND_KEY_LENGTH) { + throw new EncryptException( + Messages.format("error.encrypt.key_too_long", MAX_SECOND_KEY_LENGTH)); + } byte[] key = new byte[strArray.length]; for (int i = 0; i < strArray.length; i++) { key[i] = Byte.parseByte(strArray[i]); } return key; } + + public static void validateSecondKeyStringLength(int length) { + if (length < 0 || length > MAX_SECOND_KEY_STRING_LENGTH) { + throw new EncryptException( + Messages.format("error.encrypt.key_too_long", MAX_SECOND_KEY_LENGTH)); + } + } } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IDecryptor.java b/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IDecryptor.java index b906e5b72..de7735dae 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IDecryptor.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IDecryptor.java @@ -39,11 +39,11 @@ public interface IDecryptor { if (IEncrypt.encryptMap.containsKey(className)) { return ((IEncrypt) IEncrypt.encryptMap.get(className).newInstance(key)).getDecryptor(); } - Class<?> encryptClass = Class.forName(className); - java.lang.reflect.Constructor<?> constructor = + Class<? extends IEncrypt> encryptClass = EncryptUtils.loadEncryptClass(type); + java.lang.reflect.Constructor<? extends IEncrypt> constructor = encryptClass.getDeclaredConstructor(byte[].class); IEncrypt.encryptMap.put(className, constructor); - return ((IEncrypt) constructor.newInstance(key)).getDecryptor(); + return constructor.newInstance(key).getDecryptor(); } catch (ClassNotFoundException e) { throw new EncryptException( Messages.format("error.encrypt.decryptor_class_not_found", type), e); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IEncryptor.java b/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IEncryptor.java index 2c0b6cf2e..7d60ecfc4 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IEncryptor.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/encrypt/IEncryptor.java @@ -39,11 +39,11 @@ public interface IEncryptor { if (IEncrypt.encryptMap.containsKey(className)) { return ((IEncrypt) IEncrypt.encryptMap.get(className).newInstance(key)).getEncryptor(); } - Class<?> encryptClass = Class.forName(className); - java.lang.reflect.Constructor<?> constructor = + Class<? extends IEncrypt> encryptClass = EncryptUtils.loadEncryptClass(type); + java.lang.reflect.Constructor<? extends IEncrypt> constructor = encryptClass.getDeclaredConstructor(byte[].class); IEncrypt.encryptMap.put(className, constructor); - return ((IEncrypt) constructor.newInstance(key)).getEncryptor(); + return constructor.newInstance(key).getEncryptor(); } catch (ClassNotFoundException e) { throw new EncryptException( Messages.format("error.encrypt.encryptor_class_not_found", type), e); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java index 7caae06b2..95759ae20 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java @@ -122,6 +122,9 @@ public class TsFileMetadata { for (int i = 0; i < propertiesSize; i++) { String key = ReadWriteIOUtils.readVarIntString(buffer); int valueSize = ReadWriteForEncodingUtils.readVarInt(buffer); + if ("encryptKey".equals(key) && valueSize >= 0) { + EncryptUtils.validateSecondKeyStringLength(valueSize); + } byte[] value = null; if (valueSize >= 0) { value = new byte[valueSize]; diff --git a/java/tsfile/src/test/java/org/apache/tsfile/encrypt/EncryptTest.java b/java/tsfile/src/test/java/org/apache/tsfile/encrypt/EncryptTest.java index 51b8f8a07..9078a7255 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/encrypt/EncryptTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/encrypt/EncryptTest.java @@ -18,6 +18,7 @@ */ package org.apache.tsfile.encrypt; +import org.apache.tsfile.exception.encrypt.EncryptException; import org.apache.tsfile.file.metadata.enums.EncryptionType; import org.junit.After; @@ -26,9 +27,12 @@ import org.junit.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Collections; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; public class EncryptTest { private final String inputString = "AES, a fast encryptor/decryptor."; @@ -74,6 +78,43 @@ public class EncryptTest { assertEquals(encryptor2.getEncryptionType(), EncryptionType.UNENCRYPTED); } + @Test + public void GetEncryptorDoesNotLoadExternalClassName() { + assertThrows( + EncryptException.class, + () -> + IEncryptor.getEncryptor( + "java.io.ByteArrayInputStream", key.getBytes(StandardCharsets.UTF_8))); + } + + @Test + public void GetEncryptorValidatesEncryptionClassBeforeInstantiation() { + NonEncryptClass.constructorCalled = false; + + assertThrows( + EncryptException.class, + () -> + IEncryptor.getEncryptor( + NonEncryptClass.class.getName(), key.getBytes(StandardCharsets.UTF_8))); + + assertFalse(NonEncryptClass.constructorCalled); + } + + @Test + public void GetSecondKeyFromStrLimitsKeyLength() { + String oversizedKey = String.join(",", Collections.nCopies(1025, "0")); + + assertThrows(EncryptException.class, () -> EncryptUtils.getSecondKeyFromStr(oversizedKey)); + } + + public static class NonEncryptClass { + private static boolean constructorCalled; + + public NonEncryptClass(byte[] key) { + constructorCalled = true; + } + } + @Test public void HexStringTransverse() { byte[] SboxTable = {
