From 145709fd760d826771c04209b773907486171d86 Mon Sep 17 00:00:00 2001 From: heavyrian2012 Date: Mon, 24 Apr 2023 11:51:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81AES256=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- broker/config/wildfirechat.conf | 3 ++ .../java/io/moquette/BrokerConstants.java | 2 + .../persistence/MemoryMessagesStore.java | 2 +- .../main/java/io/moquette/server/Server.java | 16 ++---- .../io/moquette/spi/impl/security/AES.java | 54 +++++++++++++------ .../src/main/resources/wildfirechat.conf | 3 ++ 6 files changed, 50 insertions(+), 30 deletions(-) diff --git a/broker/config/wildfirechat.conf b/broker/config/wildfirechat.conf index f3856491..a9ef4777 100755 --- a/broker/config/wildfirechat.conf +++ b/broker/config/wildfirechat.conf @@ -65,6 +65,9 @@ token.key testim ##是否拒绝空签名的android客户端, 默认为true。当有旧的客户端需要兼容时改成false,当所有都是新客户端时,改成true。 #connect.reject_empty_signature true +##是否使用AES256加密。默认客户端和服务器都是AES128加密,如果需要开启AES256,需要客户端和IM服务同时开通。 +#encrypt.use_aes256 true + ##首次登录,是否接收之前的历史消息。 ##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉; ##1接收,会接收 message.max_queue 配置的条数的历史消息 diff --git a/broker/src/main/java/io/moquette/BrokerConstants.java b/broker/src/main/java/io/moquette/BrokerConstants.java index 8820904b..8f5c5c30 100755 --- a/broker/src/main/java/io/moquette/BrokerConstants.java +++ b/broker/src/main/java/io/moquette/BrokerConstants.java @@ -29,6 +29,8 @@ public final class BrokerConstants { public static final String HTTP_SERVER_PORT = "http_port"; public static final String HTTP_LOCAL_PORT = "local_port"; + public static final String ENCRYPT_AES256 = "encrypt.use_aes256"; + public static final String HTTP_ADMIN_PORT = "http.admin.port"; public static final String DEFAULT_MOQUETTE_STORE_MAP_DB_FILENAME = "moquette_store.mapdb"; public static final String DEFAULT_PERSISTENT_PATH = System.getProperty("user.dir") + File.separator diff --git a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java index 914ddedb..436137e8 100755 --- a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java +++ b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java @@ -3492,7 +3492,7 @@ public class MemoryMessagesStore implements IMessagesStore { } private String ensureSecretLength(String secret) { - while ( secret.length() < 16) { + while ( secret.length() < AES.keyLen) { secret += "w"; secret += secret; } diff --git a/broker/src/main/java/io/moquette/server/Server.java b/broker/src/main/java/io/moquette/server/Server.java index 9471aaa8..6d0b3017 100755 --- a/broker/src/main/java/io/moquette/server/Server.java +++ b/broker/src/main/java/io/moquette/server/Server.java @@ -232,18 +232,6 @@ public class Server { handlers = Collections.emptyList(); } DBUtil.init(config); - String strKey = "0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F"; - String[] strs = strKey.split(","); - if(strs.length != 16) { - LOG.error("Key error, it length should be 16"); - } - byte[] keys = new byte[16]; - for (int i = 0; i < 16; i++) { - keys[i] = (byte)(Integer.parseInt(strs[i].replace("0X", "").replace("0x", ""), 16)); - } - - - AES.init(keys); LOG.info("Starting Moquette Server. MQTT message interceptors={}", getInterceptorIds(handlers)); @@ -393,9 +381,13 @@ public class Server { longPort = config.getProperty(BrokerConstants.PORT_PROPERTY_NAME); shortPort = config.getProperty(BrokerConstants.HTTP_SERVER_PORT); + boolean aes256 = Boolean.parseBoolean(config.getProperty(ENCRYPT_AES256, "false")); MessageShardingUtil.setNodeId(1); Tokenor.setKey(config.getProperty(BrokerConstants.TOKEN_SECRET_KEY)); + + AES.useAes256(aes256); + String expirTimeStr = config.getProperty(TOKEN_EXPIRE_TIME); if (!StringUtil.isNullOrEmpty(expirTimeStr)) { try { diff --git a/common/src/main/java/io/moquette/spi/impl/security/AES.java b/common/src/main/java/io/moquette/spi/impl/security/AES.java index ad4d83e8..d8f036a8 100755 --- a/common/src/main/java/io/moquette/spi/impl/security/AES.java +++ b/common/src/main/java/io/moquette/spi/impl/security/AES.java @@ -5,6 +5,7 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; import java.util.Base64; import javax.crypto.BadPaddingException; @@ -18,38 +19,46 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class AES { - private static byte[] aes_key= {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F}; - public static void init(byte[] secret) { - if (secret != null && secret.length == 16) { - aes_key = new byte[16]; - for (int i = 0; i < 16; i++) { - aes_key[i] = secret[i]; - } - } else { - System.out.println("Error int key error, secret incorrect"); - } - } + public static int keyLen = 16; + private final static int ivLen = 16; + private static byte[] aes_key= {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x3A,0x1F,0x28,0x39,0x4F,0x52,0x68,0x79,0x71,0x73,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F}; public static byte[] AESEncrypt(String sSrc, String userKey) { return AESEncrypt(sSrc.getBytes(), userKey); } + public static void useAes256(boolean aes256) { + if(aes256) + keyLen = 32; + else + keyLen = 16; + } + public static byte[] AESEncrypt(byte[] tobeencrypdata, byte[] aesKey) { if (aesKey == null) { System.out.print("Key为空null"); return null; } // 判断Key是否为16位 - if (aesKey.length != 16) { + if (aesKey.length < keyLen) { System.out.print("Key长度不是16位"); return null; } + if(aesKey.length > keyLen) { + aesKey = Arrays.copyOfRange(aesKey, 0, keyLen); + } + byte[] ivKeys; + if(aesKey.length == ivLen) { + ivKeys = aesKey; + } else { + ivKeys = Arrays.copyOfRange(aesKey, 0, ivLen); + } try { SecretKeySpec skeySpec = new SecretKeySpec(aesKey, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式" - IvParameterSpec iv = new IvParameterSpec(aesKey);//使用CBC模式,需要一个向量iv,可增加加密算法的强度 + IvParameterSpec iv = new IvParameterSpec(ivKeys);//使用CBC模式,需要一个向量iv,可增加加密算法的强度 cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); //2018.1.1 0:0:0 以来的小时数 @@ -101,8 +110,8 @@ public class AES { } private static byte[] convertUserKey(String userKey) { - byte[] key = new byte[16]; - for (int i = 0; i < 16; i++) { + byte[] key = new byte[keyLen]; + for (int i = 0; i < keyLen; i++) { key[i] = (byte) (userKey.charAt(i) & 0xFF); } return key; @@ -128,14 +137,25 @@ public class AES { aesKey = aes_key; } // 判断Key是否为16位 - if (aesKey.length != 16) { + if (aesKey.length < keyLen) { System.out.print("Key长度不是16位"); return null; } + if(aesKey.length > keyLen) { + aesKey = Arrays.copyOfRange(aesKey, 0, keyLen); + } + + byte[] ivKeys; + if(aesKey.length == ivLen) { + ivKeys = aesKey; + } else { + ivKeys = Arrays.copyOfRange(aesKey, 0, ivLen); + } + SecretKeySpec skeySpec = new SecretKeySpec(aesKey, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - IvParameterSpec iv = new IvParameterSpec(aesKey); + IvParameterSpec iv = new IvParameterSpec(ivKeys); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); try { byte[] original = cipher.doFinal(sSrc); diff --git a/distribution/src/main/resources/wildfirechat.conf b/distribution/src/main/resources/wildfirechat.conf index a1cc26cf..b030e362 100755 --- a/distribution/src/main/resources/wildfirechat.conf +++ b/distribution/src/main/resources/wildfirechat.conf @@ -63,6 +63,9 @@ token.key testim ##是否拒绝空签名的android客户端, 默认为true。当有旧的客户端需要兼容时改成false,当所有都是新客户端时,改成true。 #connect.reject_empty_signature true +##是否使用AES256加密。默认客户端和服务器都是AES128加密,如果需要开启AES256,需要客户端和IM服务同时开通。 +#encrypt.use_aes256 true + ##首次登录,是否接收之前的历史消息。 ##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉; ##1接收,会接收 message.max_queue 配置的条数的历史消息