You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2021/07/19 01:27:30 UTC

[incubator-doris] branch master updated: [Feature] Support data encrypt/decrypt (#6115)

This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 5de79ec  [Feature] Support data encrypt/decrypt (#6115)
5de79ec is described below

commit 5de79ec3f0c6d9835e07381565b83b4f2d89deb3
Author: qiye <ji...@gmail.com>
AuthorDate: Mon Jul 19 09:27:08 2021 +0800

    [Feature] Support data encrypt/decrypt (#6115)
    
    Add support for data encryption/decryption.
---
 docs/.vuepress/sidebar/en.js                       |   3 +
 docs/.vuepress/sidebar/zh-CN.js                    |   3 +
 .../Data Definition/CREATE ENCRYPTKEY.md           |  80 ++++++++++++
 .../Data Definition/DROP ENCRYPTKEY.md             |  55 +++++++++
 .../Data Definition/SHOW ENCRYPTKEYS.md            |  68 +++++++++++
 .../Data Definition/CREATE ENCRYPTKEY.md           |  80 ++++++++++++
 .../Data Definition/DROP ENCRYPTKEY.md             |  55 +++++++++
 .../Data Definition/SHOW ENCRYPTKEYS.md            |  68 +++++++++++
 fe/fe-core/src/main/cup/sql_parser.cup             |  33 ++++-
 .../java/org/apache/doris/analysis/Analyzer.java   |   2 +
 .../doris/analysis/CreateEncryptKeyStmt.java       |  88 +++++++++++++
 .../apache/doris/analysis/DropEncryptKeyStmt.java  |  65 ++++++++++
 .../org/apache/doris/analysis/EncryptKeyName.java  | 136 +++++++++++++++++++++
 .../org/apache/doris/analysis/EncryptKeyRef.java   | 102 ++++++++++++++++
 .../apache/doris/analysis/ShowEncryptKeysStmt.java | 103 ++++++++++++++++
 .../org/apache/doris/analysis/StringLiteral.java   |   2 +-
 .../java/org/apache/doris/catalog/Database.java    |  82 +++++++++++++
 .../apache/doris/catalog/DatabaseEncryptKey.java   |  59 +++++++++
 .../java/org/apache/doris/catalog/EncryptKey.java  |  77 ++++++++++++
 .../org/apache/doris/catalog/EncryptKeyHelper.java |  69 +++++++++++
 .../apache/doris/catalog/EncryptKeySearchDesc.java |  68 +++++++++++
 .../org/apache/doris/common/FeMetaVersion.java     |   4 +-
 .../org/apache/doris/journal/JournalEntity.java    |  12 ++
 .../java/org/apache/doris/persist/EditLog.java     |  21 ++++
 .../org/apache/doris/persist/OperationType.java    |   2 +
 .../main/java/org/apache/doris/qe/DdlExecutor.java |   7 ++
 .../java/org/apache/doris/qe/ShowExecutor.java     |  44 +++++++
 .../doris/rewrite/RewriteEncryptKeyRule.java       |  48 ++++++++
 fe/fe-core/src/main/jflex/sql_scanner.flex         |   2 +
 .../doris/analysis/ShowEncryptKeysStmtTest.java    |  79 ++++++++++++
 .../apache/doris/catalog/CreateEncryptKeyTest.java |  94 ++++++++++++++
 31 files changed, 1608 insertions(+), 3 deletions(-)

diff --git a/docs/.vuepress/sidebar/en.js b/docs/.vuepress/sidebar/en.js
index 36105a6..04d5ee4 100644
--- a/docs/.vuepress/sidebar/en.js
+++ b/docs/.vuepress/sidebar/en.js
@@ -457,6 +457,7 @@ module.exports = [
               "BACKUP",
               "CANCEL ALTER",
               "CANCEL BACKUP",
+              "CREATE ENCRYPTKEY",
               "CANCEL RESTORE",
               "CREATE DATABASE",
               "CREATE INDEX",
@@ -467,6 +468,7 @@ module.exports = [
               "CREATE VIEW",
               "Colocate Join",
               "DROP DATABASE",
+              "DROP ENCRYPTKEY",
               "DROP INDEX",
               "DROP MATERIALIZED VIEW",
               "DROP REPOSITORY",
@@ -475,6 +477,7 @@ module.exports = [
               "HLL",
               "RECOVER",
               "RESTORE",
+              "SHOW ENCRYPTKEYS",
               "TRUNCATE TABLE",
               "create-function",
               "drop-function",
diff --git a/docs/.vuepress/sidebar/zh-CN.js b/docs/.vuepress/sidebar/zh-CN.js
index deb5c81..6de5ccb 100644
--- a/docs/.vuepress/sidebar/zh-CN.js
+++ b/docs/.vuepress/sidebar/zh-CN.js
@@ -464,6 +464,7 @@ module.exports = [
               "CANCEL BACKUP",
               "CANCEL RESTORE",
               "CREATE DATABASE",
+              "CREATE ENCRYPTKEY",
               "CREATE INDEX",
               "CREATE MATERIALIZED VIEW",
               "CREATE REPOSITORY",
@@ -472,6 +473,7 @@ module.exports = [
               "CREATE TABLE",
               "CREATE VIEW",
               "DROP DATABASE",
+              "DROP ENCRYPTKEY",
               "DROP INDEX",
               "DROP MATERIALIZED VIEW",
               "DROP REPOSITORY",
@@ -481,6 +483,7 @@ module.exports = [
               "HLL",
               "RECOVER",
               "RESTORE",
+              "SHOW ENCRYPTKEYS",
               "SHOW RESOURCES",
               "TRUNCATE TABLE",
               "create-function",
diff --git a/docs/en/sql-reference/sql-statements/Data Definition/CREATE ENCRYPTKEY.md b/docs/en/sql-reference/sql-statements/Data Definition/CREATE ENCRYPTKEY.md
new file mode 100644
index 0000000..77928cb
--- /dev/null
+++ b/docs/en/sql-reference/sql-statements/Data Definition/CREATE ENCRYPTKEY.md	
@@ -0,0 +1,80 @@
+---
+{
+    "title": "CREATE ENCRYPTKEY",
+    "language": "en"
+}
+---
+
+<!-- 
+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.
+-->
+
+# CREATE ENCRYPTKEY
+
+## Description
+
+### Syntax
+
+```
+CREATE ENCRYPTKEY key_name
+    AS "key_string"
+```
+
+### Parameters
+
+> `key_name`: The name of the key to be created, which can include the name of the database. For example: `db1.my_key`.
+>
+> `key_string`: The string to create the key
+
+This statement creates a custom key. Executing this command requires the user to have the `ADMIN` privileges.
+
+If the database name is included in `key_name`, then this custom key will be created in the corresponding database, otherwise this function will be created in the database where the current session is located. The name of the new key cannot be the same as the key that already exists in the corresponding database, otherwise the creation will fail.
+
+## Example
+
+1. Create a custom key
+
+    ```
+    CREATE ENCRYPTKEY my_key as "ABCD123456789";
+    ```
+
+2. Using a custom key
+
+    To use a custom key, add the keyword `KEY`/`key` before the key name, separated from `key_name` by a space.
+    
+    ```
+    mysql> SELECT HEX(AES_ENCRYPT("Doris is Great", KEY my_key));
+    +------------------------------------------------+
+    | hex(aes_encrypt('Doris is Great', key my_key)) |
+    +------------------------------------------------+
+    | D26DB38579D6A343350EDDC6F2AD47C6               |
+    +------------------------------------------------+
+    1 row in set (0.02 sec)
+
+    mysql> SELECT AES_DECRYPT(UNHEX('D26DB38579D6A343350EDDC6F2AD47C6'), KEY my_key);
+    +--------------------------------------------------------------------+
+    | aes_decrypt(unhex('D26DB38579D6A343350EDDC6F2AD47C6'), key my_key) |
+    +--------------------------------------------------------------------+
+    | Doris is Great                                                     |
+    +--------------------------------------------------------------------+
+    1 row in set (0.01 sec)
+    ```
+	
+## Keyword
+
+    CREATE,ENCRYPTKEY
diff --git a/docs/en/sql-reference/sql-statements/Data Definition/DROP ENCRYPTKEY.md b/docs/en/sql-reference/sql-statements/Data Definition/DROP ENCRYPTKEY.md
new file mode 100644
index 0000000..352258f
--- /dev/null
+++ b/docs/en/sql-reference/sql-statements/Data Definition/DROP ENCRYPTKEY.md	
@@ -0,0 +1,55 @@
+---
+{
+    "title": "DROP ENCRYPTKEY",
+    "language": "en"
+}
+---
+
+<!-- 
+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.
+-->
+
+# DROP ENCRYPTKEY
+
+## Description
+
+### Syntax
+
+```
+DROP ENCRYPTKEY key_name
+```
+
+### Parameters
+
+> `key_name`: To delete the name of the key, you can include the name of the database. For example: `db1.my_key`.
+
+Delete a custom key. A key can be deleted only if its name is identical.
+
+Executing this command requires the user to have the `ADMIN` privileges.
+
+## example
+
+1. Delete a key.
+
+```
+DROP ENCRYPTKEY my_key;
+```
+
+## keyword
+
+    DROP,ENCRYPTKEY
diff --git a/docs/en/sql-reference/sql-statements/Data Definition/SHOW ENCRYPTKEYS.md b/docs/en/sql-reference/sql-statements/Data Definition/SHOW ENCRYPTKEYS.md
new file mode 100644
index 0000000..2473f98
--- /dev/null
+++ b/docs/en/sql-reference/sql-statements/Data Definition/SHOW ENCRYPTKEYS.md	
@@ -0,0 +1,68 @@
+---
+{
+    "title": "SHOW ENCRYPTKEYS",
+    "language": "en"
+}
+---
+
+<!-- 
+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.
+-->
+
+# SHOW ENCRYPTKEYS
+
+## Description
+
+### Syntax
+
+```
+SHOW ENCRYPTKEYS [IN|FROM db] [LIKE 'key_pattern']
+```
+
+### Parameters
+
+>`db`: the name of the database to query
+>`key_pattern`: parameter used to filter key names  
+
+View all custom keys under the database. If the user specifies a database, then view the corresponding database, otherwise query the current session's database directly.
+
+You need to have `ADMIN` privileges for this database.
+
+## Example
+
+    ```
+    mysql> SHOW ENCRYPTKEYS;
+    +-------------------+-------------------+
+    | EncryptKey Name   | EncryptKey String |
+    +-------------------+-------------------+
+    | example_db.my_key | ABCD123456789     |
+    +-------------------+-------------------+
+    1 row in set (0.00 sec)
+
+    mysql> SHOW ENCRYPTKEYS FROM example_db LIKE "%my%";
+    +-------------------+-------------------+
+    | EncryptKey Name   | EncryptKey String |
+    +-------------------+-------------------+
+    | example_db.my_key | ABCD123456789     |
+    +-------------------+-------------------+
+    1 row in set (0.00 sec)
+    ```
+
+## keyword
+
+    SHOW,ENCRYPTKEYS
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Definition/CREATE ENCRYPTKEY.md b/docs/zh-CN/sql-reference/sql-statements/Data Definition/CREATE ENCRYPTKEY.md
new file mode 100644
index 0000000..1faec7c
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Definition/CREATE ENCRYPTKEY.md	
@@ -0,0 +1,80 @@
+---
+{
+    "title": "CREATE ENCRYPTKEY",
+    "language": "zh-CN"
+}
+---
+
+<!-- 
+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.
+-->
+
+# CREATE ENCRYPTKEY
+
+## Description
+
+### Syntax
+
+```
+CREATE ENCRYPTKEY key_name
+    AS "key_string"
+```
+
+### Parameters
+
+> `key_name`: 要创建密钥的名字, 可以包含数据库的名字。比如:`db1.my_key`。
+>
+> `key_string`: 要创建密钥的字符串
+
+此语句创建一个自定义密钥。执行此命令需要用户拥有 `ADMIN` 权限。
+
+如果 `key_name` 中包含了数据库名字,那么这个自定义密钥会创建在对应的数据库中,否则这个函数将会创建在当前会话所在的数据库。新密钥的名字不能够与对应数据库中已存在的密钥相同,否则会创建失败。
+
+## Example
+
+1. 创建一个自定义密钥
+
+	```
+	CREATE ENCRYPTKEY my_key AS "ABCD123456789";
+	```
+	
+2. 使用自定义密钥
+
+    使用自定义密钥需在密钥前添加关键字 `KEY`/`key`,与 `key_name` 空格隔开。
+    
+    ```
+    mysql> SELECT HEX(AES_ENCRYPT("Doris is Great", KEY my_key));
+    +------------------------------------------------+
+    | hex(aes_encrypt('Doris is Great', key my_key)) |
+    +------------------------------------------------+
+    | D26DB38579D6A343350EDDC6F2AD47C6               |
+    +------------------------------------------------+
+    1 row in set (0.02 sec)
+
+    mysql> SELECT AES_DECRYPT(UNHEX('D26DB38579D6A343350EDDC6F2AD47C6'), KEY my_key);
+    +--------------------------------------------------------------------+
+    | aes_decrypt(unhex('D26DB38579D6A343350EDDC6F2AD47C6'), key my_key) |
+    +--------------------------------------------------------------------+
+    | Doris is Great                                                     |
+    +--------------------------------------------------------------------+
+    1 row in set (0.01 sec)
+    ```
+
+## Keyword
+
+    CREATE,ENCRYPTKEY
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Definition/DROP ENCRYPTKEY.md b/docs/zh-CN/sql-reference/sql-statements/Data Definition/DROP ENCRYPTKEY.md
new file mode 100644
index 0000000..c0f0787
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Definition/DROP ENCRYPTKEY.md	
@@ -0,0 +1,55 @@
+---
+{
+    "title": "DROP ENCRYPTKEY",
+    "language": "zh-CN"
+}
+---
+
+<!-- 
+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.
+-->
+
+# DROP ENCRYPTKEY
+
+## Description
+
+### Syntax
+
+```
+DROP ENCRYPTKEY key_name
+```
+
+### Parameters
+
+> `key_name`: 要删除密钥的名字, 可以包含数据库的名字。比如:`db1.my_key`。
+
+删除一个自定义密钥。密钥的名字完全一致才能够被删除。
+
+执行此命令需要用户拥有 `ADMIN` 权限。
+
+## example
+
+1. 删除掉一个密钥
+
+```
+DROP ENCRYPTKEY my_key;
+```
+
+## keyword
+
+    DROP,ENCRYPTKEY
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Definition/SHOW ENCRYPTKEYS.md b/docs/zh-CN/sql-reference/sql-statements/Data Definition/SHOW ENCRYPTKEYS.md
new file mode 100644
index 0000000..54f09b2
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Definition/SHOW ENCRYPTKEYS.md	
@@ -0,0 +1,68 @@
+---
+{
+    "title": "SHOW ENCRYPTKEYS",
+    "language": "zh-CN"
+}
+---
+
+<!-- 
+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.
+-->
+
+# SHOW ENCRYPTKEYS
+
+## Description
+
+### Syntax
+
+```
+SHOW ENCRYPTKEYS [IN|FROM db] [LIKE 'key_pattern']
+```
+
+### Parameters
+
+>`db`: 要查询的数据库名字
+>`key_pattern`: 用来过滤密钥名称的参数  
+
+查看数据库下所有的自定义的密钥。如果用户指定了数据库,那么查看对应数据库的,否则直接查询当前会话所在数据库。
+
+需要对这个数据库拥有 `ADMIN` 权限
+
+## Example
+
+    ```
+    mysql> SHOW ENCRYPTKEYS;
+    +-------------------+-------------------+
+    | EncryptKey Name   | EncryptKey String |
+    +-------------------+-------------------+
+    | example_db.my_key | ABCD123456789     |
+    +-------------------+-------------------+
+    1 row in set (0.00 sec)
+
+    mysql> SHOW ENCRYPTKEYS FROM example_db LIKE "%my%";
+    +-------------------+-------------------+
+    | EncryptKey Name   | EncryptKey String |
+    +-------------------+-------------------+
+    | example_db.my_key | ABCD123456789     |
+    +-------------------+-------------------+
+    1 row in set (0.00 sec)
+    ```
+
+## keyword
+
+    SHOW,ENCRYPTKEYS
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup
index c8bef94..caa9ade 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -240,7 +240,7 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALL, KW_ALTER, KW_A
     KW_CONFIG, KW_CONNECTION, KW_CONNECTION_ID, KW_CONSISTENT, KW_CONVERT, KW_COUNT, KW_CREATE, KW_CROSS, KW_CUBE, KW_CURRENT, KW_CURRENT_USER,
     KW_DATA, KW_DATABASE, KW_DATABASES, KW_DATE, KW_DATETIME, KW_DAY, KW_DECIMAL, KW_DECOMMISSION, KW_DEFAULT, KW_DESC, KW_DESCRIBE,
     KW_DELETE, KW_DISTINCT, KW_DISTINCTPC, KW_DISTINCTPCSA, KW_DISTRIBUTED, KW_DISTRIBUTION, KW_DYNAMIC, KW_BUCKETS, KW_DIV, KW_DOUBLE, KW_DROP, KW_DROPP, KW_DUPLICATE,
-    KW_ELSE, KW_ENABLE, KW_END, KW_ENGINE, KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, KW_EXCEPT, KW_EXCLUDE,
+    KW_ELSE, KW_ENABLE, KW_ENCRYPTKEY, KW_ENCRYPTKEYS, KW_END, KW_ENGINE, KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, KW_EXCEPT, KW_EXCLUDE,
     KW_EXISTS, KW_EXPORT, KW_EXTERNAL, KW_EXTRACT,
     KW_FALSE, KW_FEATURE, KW_FOLLOWER, KW_FOLLOWING, KW_FREE, KW_FROM, KW_FILE, KW_FILTER, KW_FIRST, KW_FLOAT, KW_FOR, KW_FORCE, KW_FORMAT, KW_FRONTEND, KW_FRONTENDS, KW_FULL, KW_FUNCTION, KW_FUNCTIONS,
     KW_GLOBAL, KW_GRANT, KW_GRANTS, KW_GRAPH, KW_GROUP, KW_GROUPING,
@@ -350,6 +350,7 @@ nonterminal ClusterName cluster_name;
 nonterminal ClusterName des_cluster_name;
 nonterminal TableName table_name;
 nonterminal FunctionName function_name;
+nonterminal EncryptKeyName encryptkey_name;
 nonterminal Expr pre_filter_clause;
 nonterminal Expr where_clause;
 nonterminal Expr delete_on_clause;
@@ -1208,6 +1209,11 @@ create_stmt ::=
     {:
         RESULT = new CreateResourceStmt(isExternal, resourceName, properties);
     :}
+    /* encryptkey */
+    | KW_CREATE KW_ENCRYPTKEY encryptkey_name:keyName KW_AS STRING_LITERAL:keyString
+    {:
+        RESULT = new CreateEncryptKeyStmt(keyName, keyString);
+    :}
     ;
 
 opt_aggregate ::=
@@ -1839,6 +1845,10 @@ drop_stmt ::=
     {:
         RESULT = new DropResourceStmt(resourceName);
     :}
+    | KW_DROP KW_ENCRYPTKEY encryptkey_name:keyName
+    {:
+        RESULT = new DropEncryptKeyStmt(keyName);
+    :}
     ;
 
 // Recover statement
@@ -2610,6 +2620,10 @@ show_param ::=
     {:
         RESULT = new ShowQueryProfileStmt(queryIdPath);
     :}
+    | KW_ENCRYPTKEYS opt_db:dbName opt_wild_where
+    {:
+        RESULT = new ShowEncryptKeysStmt(dbName, parser.wild);
+    :}
     ;
 
 opt_tmp ::=
@@ -3654,6 +3668,17 @@ table_name ::=
     {: RESULT = new TableName(db, tbl); :}
     ;
 
+encryptkey_name ::=
+    ident:name
+    {:
+        RESULT = new EncryptKeyName(name);
+    :}
+    | ident:db DOT ident:name
+    {:
+        RESULT = new EncryptKeyName(db, name);
+    :}
+    ;
+
 function_name ::=
     type_function_name:fn
     {: RESULT = new FunctionName(null, fn); :}
@@ -4367,6 +4392,8 @@ non_pred_expr ::=
   {: RESULT = new BoolLiteral(false); :}
   | KW_CONVERT LPAREN expr:e COMMA type_def:targetType RPAREN
   {: RESULT = new CastExpr(targetType, e); :}
+  | KW_KEY encryptkey_name:name
+  {: RESULT = new EncryptKeyRef(name); :}
   ;
 
 expr_pipe_list ::=
@@ -4973,6 +5000,10 @@ keyword ::=
     {: RESULT = id; :}
     | KW_ISOLATION:id
     {: RESULT = id; :}
+    | KW_ENCRYPTKEY:id
+    {: RESULT = id; :}
+    | KW_ENCRYPTKEYS:id
+    {: RESULT = id; :}
     | KW_LABEL:id
     {: RESULT = id; :}
     | KW_LAST:id
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
index deb8413..9030377 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
@@ -41,6 +41,7 @@ import org.apache.doris.rewrite.ExprRewriteRule;
 import org.apache.doris.rewrite.ExprRewriter;
 import org.apache.doris.rewrite.ExtractCommonFactorsRule;
 import org.apache.doris.rewrite.FoldConstantsRule;
+import org.apache.doris.rewrite.RewriteEncryptKeyRule;
 import org.apache.doris.rewrite.RewriteFromUnixTimeRule;
 import org.apache.doris.rewrite.NormalizeBinaryPredicatesRule;
 import org.apache.doris.rewrite.SimplifyInvalidDateBinaryPredicatesDateRule;
@@ -268,6 +269,7 @@ public class Analyzer {
             rules.add(FoldConstantsRule.INSTANCE);
             rules.add(RewriteFromUnixTimeRule.INSTANCE);
             rules.add(SimplifyInvalidDateBinaryPredicatesDateRule.INSTANCE);
+            rules.add(RewriteEncryptKeyRule.INSTANCE);
             List<ExprRewriteRule> onceRules = Lists.newArrayList();
             onceRules.add(ExtractCommonFactorsRule.INSTANCE);
             exprRewriter_ = new ExprRewriter(rules, onceRules);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateEncryptKeyStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateEncryptKeyStmt.java
new file mode 100644
index 0000000..1cbfcf0
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateEncryptKeyStmt.java
@@ -0,0 +1,88 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.EncryptKey;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+
+import com.google.common.base.Strings;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+/**
+ * create a encryptKey
+ *
+ * The syntax is:
+ * CREATE ENCRYPTKEY key_name
+ *     AS "key_string";
+ * `key_name`: The name of the key to be created, which can include the name of the database. For example: `db1.my_key`.
+ * `key_string`: The string to create the key
+ *
+ * for example:
+ *     CREATE ENCRYPTKEY test.key1 AS "beijing";
+ */
+public class CreateEncryptKeyStmt extends DdlStmt{
+    private final EncryptKeyName encryptKeyName;
+    private final String keyString;
+    private EncryptKey encryptKey;
+
+    public CreateEncryptKeyStmt(EncryptKeyName encryptKeyName, String keyString) {
+        this.encryptKeyName = encryptKeyName;
+        this.keyString = keyString;
+    }
+
+    public EncryptKeyName getEncryptKeyName() {
+        return encryptKeyName;
+    }
+
+    public String getKeyString() {
+        return keyString;
+    }
+
+    public EncryptKey getEncryptKey() {
+        return encryptKey;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws AnalysisException, UserException {
+        super.analyze(analyzer);
+
+        // check operation privilege
+        if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) {
+            ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN");
+        }
+
+        encryptKeyName.analyze(analyzer);
+        if (Strings.isNullOrEmpty(keyString)) {
+            throw new AnalysisException("keyString can not be null or empty string.");
+        }
+        encryptKey = new EncryptKey(encryptKeyName, keyString);
+    }
+
+    @Override
+    public String toSql() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("CREATE ENCRYPTKEY ")
+                .append(encryptKeyName.getKeyName()).append(" AS \"").append(keyString).append("\"");
+        return stringBuilder.toString();
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/DropEncryptKeyStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropEncryptKeyStmt.java
new file mode 100644
index 0000000..9ecd6db
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropEncryptKeyStmt.java
@@ -0,0 +1,65 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.EncryptKeySearchDesc;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+public class DropEncryptKeyStmt extends DdlStmt {
+    private final EncryptKeyName encryptKeyName;
+    private EncryptKeySearchDesc encryptKeySearchDesc;
+
+    public DropEncryptKeyStmt(EncryptKeyName encryptKeyName) {
+        this.encryptKeyName = encryptKeyName;
+    }
+
+    public EncryptKeyName getEncryptKeyName() {
+        return encryptKeyName;
+    }
+
+    public EncryptKeySearchDesc getEncryptKeysSearchDesc() {
+        return encryptKeySearchDesc;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws AnalysisException, UserException {
+        super.analyze(analyzer);
+
+        // check operation privilege
+        if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) {
+            ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN");
+        }
+
+        // analyze encryptkey name
+        encryptKeyName.analyze(analyzer);
+        encryptKeySearchDesc = new EncryptKeySearchDesc(encryptKeyName);
+    }
+
+    @Override
+    public String toSql() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("DROP ENCRYPTKEY ").append(encryptKeyName.getKeyName());
+        return stringBuilder.toString();
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/EncryptKeyName.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/EncryptKeyName.java
new file mode 100644
index 0000000..737c515
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/EncryptKeyName.java
@@ -0,0 +1,136 @@
+// 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.doris.analysis;
+
+import org.apache.doris.cluster.ClusterNamespace;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.FeNameFormat;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import com.google.common.base.Strings;
+import com.google.gson.annotations.SerializedName;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+public class EncryptKeyName implements Writable {
+    private static final Logger LOG = LogManager.getLogger(EncryptKeyName.class);
+
+    @SerializedName(value = "db")
+    private String db;
+    @SerializedName(value = "keyName")
+    private String keyName;
+
+    public EncryptKeyName(String db, String keyName) {
+        this.db = db;
+        this.keyName = keyName.toLowerCase();
+        if (db != null) {
+            this.db = db.toLowerCase();
+        }
+    }
+
+    public EncryptKeyName(String keyName) {
+        this.db = null;
+        this.keyName = keyName.toLowerCase();
+    }
+
+    public void analyze(Analyzer analyzer) throws AnalysisException {
+        FeNameFormat.checkCommonName("EncryptKey", keyName);
+        if (db == null) {
+            db = analyzer.getDefaultDb();
+            if (Strings.isNullOrEmpty(db)) {
+                ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR);
+            }
+        } else {
+            if (Strings.isNullOrEmpty(analyzer.getClusterName())) {
+                ErrorReport.reportAnalysisException(ErrorCode.ERR_CLUSTER_NAME_NULL);
+            }
+            db = ClusterNamespace.getFullName(analyzer.getClusterName(), db);
+        }
+    }
+
+    public String getDb() {
+        return db;
+    }
+
+    public String getKeyName() {
+        return keyName;
+    }
+
+    @Override
+    public String toString() {
+        if (db == null) {
+            return keyName;
+        }
+        return ClusterNamespace.getNameFromFullName(db) + "." + keyName;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        String json = GsonUtils.GSON.toJson(this);
+        Text.writeString(out, json);
+    }
+
+    public static EncryptKeyName read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        return GsonUtils.GSON.fromJson(json, EncryptKeyName.class);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof EncryptKeyName)) {
+            return false;
+        }
+        EncryptKeyName o = (EncryptKeyName) obj;
+        if ((db == null || o.db == null) && (db != o.db)) {
+            if (db == null && o.db != null) {
+                return false;
+            }
+            if (db != null && o.db == null) {
+                return false;
+            }
+            if (!db.equalsIgnoreCase(o.db)) {
+                return false;
+            }
+        }
+        return keyName.equalsIgnoreCase(o.keyName);
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * Objects.hashCode(db) + Objects.hashCode(keyName);
+    }
+
+    public String toSql() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("KEY ");
+        if (db != null) {
+            sb.append(ClusterNamespace.getNameFromFullName(db)).append(".");
+        }
+        sb.append(keyName);
+        return sb.toString();
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/EncryptKeyRef.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/EncryptKeyRef.java
new file mode 100644
index 0000000..4e2f881
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/EncryptKeyRef.java
@@ -0,0 +1,102 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Database;
+import org.apache.doris.catalog.EncryptKey;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.cluster.ClusterNamespace;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.thrift.TExprNode;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import com.google.common.base.Strings;
+
+public class EncryptKeyRef extends Expr {
+    private static final Logger LOG = LogManager.getLogger(EncryptKeyRef.class);
+    private EncryptKeyName encryptKeyName;
+    private EncryptKey encryptKey;
+
+    public EncryptKeyRef(EncryptKeyName encryptKeyName) {
+        super();
+        this.encryptKeyName = encryptKeyName;
+        this.type = Type.VARCHAR;
+    }
+
+    protected EncryptKeyRef(EncryptKeyRef other) {
+        super(other);
+        this.encryptKeyName = other.encryptKeyName;
+        this.encryptKey = other.encryptKey;
+    }
+
+    public EncryptKey getEncryptKey() {
+        return encryptKey;
+    }
+
+    private void analyzeEncryptKey(Analyzer analyzer) throws AnalysisException {
+        String dbName = encryptKeyName.getDb();
+        if (Strings.isNullOrEmpty(dbName)) {
+            dbName = analyzer.getDefaultDb();
+        }
+        if ("".equals(dbName)) {
+            ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR);
+        } else {
+            dbName = ClusterNamespace.getFullName(analyzer.getClusterName(), dbName);
+            Database database = analyzer.getCatalog().getDb(dbName);
+            if (null == database) {
+                ErrorReport.reportAnalysisException(ErrorCode.ERR_BAD_DB_ERROR, dbName);
+            }
+
+            EncryptKey encryptKey = database.getEncryptKey(encryptKeyName.getKeyName());
+            if (encryptKey != null) {
+                this.encryptKey = encryptKey;
+            } else {
+                throw new AnalysisException("Can not found encryptKey: " + encryptKeyName.toString());
+            }
+        }
+
+    }
+
+    @Override
+    protected void analyzeImpl(Analyzer analyzer) throws AnalysisException {
+        // analyze encryptKey name
+        encryptKeyName.analyze(analyzer);
+        // analyze encryptKey
+        analyzeEncryptKey(analyzer);
+    }
+
+    @Override
+    protected String toSqlImpl() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(encryptKeyName.toSql());
+        return sb.toString();
+    }
+
+    @Override
+    protected void toThrift(TExprNode msg) {
+        // no operation
+    }
+
+    @Override
+    public Expr clone() {
+        return new EncryptKeyRef(this);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowEncryptKeysStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowEncryptKeysStmt.java
new file mode 100644
index 0000000..c2c3a10
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowEncryptKeysStmt.java
@@ -0,0 +1,103 @@
+// 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.doris.analysis;
+
+import com.google.common.base.Strings;
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.cluster.ClusterNamespace;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ShowResultSetMetaData;
+
+public class ShowEncryptKeysStmt extends ShowStmt{
+    private static final ShowResultSetMetaData META_DATA =
+            ShowResultSetMetaData.builder()
+                    .addColumn(new Column("EncryptKey Name", ScalarType.createVarchar(20)))
+                    .addColumn(new Column("EncryptKey String", ScalarType.createVarchar(1024)))
+                    .build();
+
+    private String dbName;
+    private String wild;
+
+    public ShowEncryptKeysStmt(String dbName, String wild) {
+        this.dbName = dbName;
+        this.wild = wild;
+    }
+
+    public String getDbName() {
+        return dbName;
+    }
+
+    public String getWild() {
+        return wild;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws AnalysisException, UserException {
+        super.analyze(analyzer);
+        if (Strings.isNullOrEmpty(dbName)) {
+            dbName = analyzer.getDefaultDb();
+            if (Strings.isNullOrEmpty(dbName)) {
+                ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR);
+            }
+        } else {
+            dbName = ClusterNamespace.getFullName(getClusterName(), dbName);
+        }
+
+        // must check after analyze dbName, for case dbName is null.
+        if (!Catalog.getCurrentCatalog().getAuth().checkDbPriv(ConnectContext.get(), dbName, PrivPredicate.ADMIN)) {
+            ErrorReport.reportAnalysisException(
+                    ErrorCode.ERR_DB_ACCESS_DENIED, ConnectContext.get().getQualifiedUser(), dbName);
+        }
+
+    }
+
+    public boolean like(String str) {
+        str = str.toLowerCase();
+        return str.matches(wild.replace(".", "\\.").replace("?", ".").replace("%", ".*").toLowerCase());
+    }
+
+    @Override
+    public String toSql() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("SHOW ENCRYPTKEYS FROM ");
+        if (!Strings.isNullOrEmpty(dbName)) {
+            stringBuilder.append("`").append(dbName).append("` ");
+        }
+        if (wild != null) {
+            stringBuilder.append("LIKE ").append("`").append(wild).append("`");
+        }
+        return stringBuilder.toString();
+    }
+
+    @Override
+    public String toString() {
+        return toSql();
+    }
+
+    @Override
+    public ShowResultSetMetaData getMetaData() {
+        return META_DATA;
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
index ab202e7..2db36f9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
@@ -45,7 +45,7 @@ public class StringLiteral extends LiteralExpr {
     private String value;
     // Means the converted session variable need to be cast to int, such as "cast 'STRICT_TRANS_TABLES' to Integer".
     private String beConverted = "";
-    
+
     public StringLiteral() {
         super();
         type = Type.VARCHAR;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java
index 2226cd1..1d70067 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java
@@ -88,6 +88,8 @@ public class Database extends MetaObject implements Writable {
 
     // user define function
     private ConcurrentMap<String, ImmutableList<Function>> name2Function = Maps.newConcurrentMap();
+    // user define encryptKey for current db
+    private DatabaseEncryptKey dbEncryptKey;
 
     private volatile long dataQuotaBytes;
 
@@ -118,6 +120,7 @@ public class Database extends MetaObject implements Writable {
         this.dbState = DbState.NORMAL;
         this.attachDbName = "";
         this.clusterName = "";
+        this.dbEncryptKey = new DatabaseEncryptKey();
     }
 
     public void readLock() {
@@ -512,6 +515,9 @@ public class Database extends MetaObject implements Writable {
             }
         }
 
+        // write encryptKeys
+        dbEncryptKey.write(out);
+
         out.writeLong(replicaQuotaSize);
     }
 
@@ -557,6 +563,11 @@ public class Database extends MetaObject implements Writable {
             }
         }
 
+        // read encryptKeys
+        if (Catalog.getCurrentCatalogJournalVersion() >= FeMetaVersion.VERSION_102) {
+            dbEncryptKey = DatabaseEncryptKey.read(in);
+        }
+
         if (Catalog.getCurrentCatalogJournalVersion() >= FeMetaVersion.VERSION_81) {
             replicaQuotaSize = in.readLong();
         } else {
@@ -737,4 +748,75 @@ public class Database extends MetaObject implements Writable {
     public boolean isInfoSchemaDb() {
         return ClusterNamespace.getNameFromFullName(fullQualifiedName).equalsIgnoreCase(InfoSchemaDb.DATABASE_NAME);
     }
+
+    public synchronized void addEncryptKey(EncryptKey encryptKey) throws UserException {
+        addEncryptKeyImpl(encryptKey, false);
+        Catalog.getCurrentCatalog().getEditLog().logAddEncryptKey(encryptKey);
+    }
+
+    public synchronized void replayAddEncryptKey(EncryptKey encryptKey) {
+        try {
+            addEncryptKeyImpl(encryptKey, true);
+        } catch (UserException e) {
+            Preconditions.checkArgument(false);
+        }
+    }
+
+    private void addEncryptKeyImpl(EncryptKey encryptKey, boolean isReplay) throws UserException {
+        String keyName = encryptKey.getEncryptKeyName().getKeyName();
+        EncryptKey existKey = dbEncryptKey.getName2EncryptKey().get(keyName);
+        if (!isReplay) {
+            if (existKey != null) {
+                if (existKey.isIdentical(encryptKey)) {
+                    throw new UserException("encryptKey [" + existKey.getEncryptKeyName().toString() + "] already exists");
+                }
+            }
+        }
+
+        dbEncryptKey.getName2EncryptKey().put(keyName, encryptKey);
+    }
+
+    public synchronized void dropEncryptKey(EncryptKeySearchDesc encryptKeySearchDesc) throws UserException {
+        dropEncryptKeyImpl(encryptKeySearchDesc);
+        Catalog.getCurrentCatalog().getEditLog().logDropEncryptKey(encryptKeySearchDesc);
+    }
+
+    public synchronized void replayDropEncryptKey(EncryptKeySearchDesc encryptKeySearchDesc) {
+        try {
+            dropEncryptKeyImpl(encryptKeySearchDesc);
+        } catch (UserException e) {
+            Preconditions.checkArgument(false);
+        }
+    }
+
+    private void dropEncryptKeyImpl(EncryptKeySearchDesc encryptKeySearchDesc) throws UserException {
+        String keyName = encryptKeySearchDesc.getKeyEncryptKeyName().getKeyName();
+        EncryptKey existKey = dbEncryptKey.getName2EncryptKey().get(keyName);
+        if (existKey == null) {
+            throw new UserException("Unknown encryptKey, encryptKey=" + encryptKeySearchDesc.toString());
+        }
+        boolean isFound = false;
+        if (encryptKeySearchDesc.isIdentical(existKey)) {
+            isFound = true;
+        }
+        if (!isFound) {
+            throw new UserException("Unknown encryptKey, encryptKey=" + encryptKeySearchDesc.toString());
+        }
+        dbEncryptKey.getName2EncryptKey().remove(keyName);
+    }
+
+    public synchronized List<EncryptKey> getEncryptKeys() {
+        List<EncryptKey> encryptKeys = Lists.newArrayList();
+        for (Map.Entry<String, EncryptKey> entry : dbEncryptKey.getName2EncryptKey().entrySet()) {
+            encryptKeys.add(entry.getValue());
+        }
+        return encryptKeys;
+    }
+
+    public synchronized EncryptKey getEncryptKey(String keyName) {
+        if (dbEncryptKey.getName2EncryptKey().containsKey(keyName)) {
+            return dbEncryptKey.getName2EncryptKey().get(keyName);
+        }
+        return null;
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/DatabaseEncryptKey.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/DatabaseEncryptKey.java
new file mode 100644
index 0000000..eafe2b0
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/DatabaseEncryptKey.java
@@ -0,0 +1,59 @@
+// 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.doris.catalog;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import com.google.common.collect.Maps;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * user define encryptKey in current db.
+ */
+public class DatabaseEncryptKey implements Writable {
+    private static final Logger LOG = LogManager.getLogger(DatabaseEncryptKey.class);
+    // user define encryptKey
+    // keyName -> encryptKey
+    @SerializedName(value = "name2EncryptKey")
+    private ConcurrentMap<String, EncryptKey> name2EncryptKey = Maps.newConcurrentMap();
+
+    public ConcurrentMap<String, EncryptKey> getName2EncryptKey() {
+        return name2EncryptKey;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        String json = GsonUtils.GSON.toJson(this);
+        Text.writeString(out, json);
+    }
+
+    public static DatabaseEncryptKey read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        return GsonUtils.GSON.fromJson(json, DatabaseEncryptKey.class);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKey.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKey.java
new file mode 100644
index 0000000..e9f2786
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKey.java
@@ -0,0 +1,77 @@
+// 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.doris.catalog;
+
+import org.apache.doris.analysis.EncryptKeyName;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+
+import com.google.common.collect.Lists;
+import com.google.gson.annotations.SerializedName;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.List;
+
+public class EncryptKey implements Writable {
+    @SerializedName(value = "encryptKeyName")
+    private EncryptKeyName encryptKeyName;
+    @SerializedName(value = "keyString")
+    private String keyString;
+
+    public EncryptKey(EncryptKeyName encryptKeyname, String keyString) {
+        this.encryptKeyName = encryptKeyname;
+        this.keyString = keyString;
+    }
+
+    public EncryptKeyName getEncryptKeyName() {
+        return encryptKeyName;
+    }
+
+    public String getKeyString() {
+        return keyString;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        String json = GsonUtils.GSON.toJson(this);
+        Text.writeString(out, json);
+    }
+
+    public static EncryptKey read(DataInput input) throws IOException {
+        String json = Text.readString(input);
+        return GsonUtils.GSON.fromJson(json, EncryptKey.class);
+    }
+
+    public boolean isIdentical(EncryptKey other) {
+        if (encryptKeyName.equals(other.getEncryptKeyName())) {
+            return true;
+        }
+        return false;
+    }
+
+    public List<Comparable> getInfo() {
+        List<Comparable> row = Lists.newArrayList();
+        row.add(encryptKeyName.toString());
+        row.add(keyString);
+
+        return row;
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKeyHelper.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKeyHelper.java
new file mode 100644
index 0000000..b555f98
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKeyHelper.java
@@ -0,0 +1,69 @@
+// 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.doris.catalog;
+
+import org.apache.doris.analysis.CreateEncryptKeyStmt;
+import org.apache.doris.analysis.DropEncryptKeyStmt;
+import org.apache.doris.analysis.EncryptKeyName;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+// helper class for encryptKeys, create and drop
+public class EncryptKeyHelper {
+    private static final Logger LOG = LogManager.getLogger(EncryptKeyHelper.class);
+
+    public static void createEncryptKey(CreateEncryptKeyStmt stmt) throws UserException {
+        EncryptKeyName name = stmt.getEncryptKeyName();
+        Database db = Catalog.getCurrentCatalog().getDb(name.getDb());
+        if (db == null) {
+            ErrorReport.reportDdlException(ErrorCode.ERR_BAD_DB_ERROR, name.getDb());
+        }
+        db.addEncryptKey(stmt.getEncryptKey());
+    }
+
+    public static void replayCreateEncryptKey(EncryptKey encryptKey) {
+        String dbName = encryptKey.getEncryptKeyName().getDb();
+        Database db = Catalog.getCurrentCatalog().getDb(dbName);
+        if (db == null) {
+            throw new Error("unknown database when replay log, db=" + dbName);
+        }
+        db.replayAddEncryptKey(encryptKey);
+    }
+
+    public static void dropEncryptKey(DropEncryptKeyStmt stmt) throws UserException {
+        EncryptKeyName name = stmt.getEncryptKeyName();
+        Database db = Catalog.getCurrentCatalog().getDb(name.getDb());
+        if (db == null) {
+            ErrorReport.reportDdlException(ErrorCode.ERR_BAD_DB_ERROR, name.getDb());
+        }
+        db.dropEncryptKey(stmt.getEncryptKeysSearchDesc());
+    }
+
+    public static void replayDropEncryptKey(EncryptKeySearchDesc encryptKeySearchDesc) {
+        String dbName = encryptKeySearchDesc.getKeyEncryptKeyName().getDb();
+        Database db = Catalog.getCurrentCatalog().getDb(dbName);
+        if (db == null) {
+            throw new Error("unknown database when replay log, db=" + dbName);
+        }
+        db.replayDropEncryptKey(encryptKeySearchDesc);
+    }
+
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKeySearchDesc.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKeySearchDesc.java
new file mode 100644
index 0000000..48ea4ac
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/EncryptKeySearchDesc.java
@@ -0,0 +1,68 @@
+// 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.doris.catalog;
+
+import org.apache.doris.analysis.EncryptKeyName;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+// used to search a EncryptKey
+public class EncryptKeySearchDesc implements Writable {
+    @SerializedName(value = "encryptKeyName")
+    private EncryptKeyName encryptKeyName;
+
+    public EncryptKeySearchDesc(EncryptKeyName encryptKeyName) {
+        this.encryptKeyName = encryptKeyName;
+    }
+
+    public EncryptKeyName getKeyEncryptKeyName() {
+        return encryptKeyName;
+    }
+
+    public boolean isIdentical(EncryptKey encryptKey) {
+        if (encryptKeyName.equals(encryptKey.getEncryptKeyName())) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(encryptKeyName.toString());
+        return stringBuilder.toString();
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        String json = GsonUtils.GSON.toJson(this);
+        Text.writeString(out, json);
+    }
+
+    public static EncryptKeySearchDesc read(DataInput input) throws IOException {
+        String json = Text.readString(input);
+        return GsonUtils.GSON.fromJson(json, EncryptKeySearchDesc.class);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java b/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java
index 17b3e3c..55d8713 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java
@@ -214,6 +214,8 @@ public final class FeMetaVersion {
     public static final int VERSION_100 = 100;
     // add errorRowsAfterResumed to distinguish totalErrorRows and currentErrorRows even if the job is paused.
     public static final int VERSION_101 = 101;
+    // add data encrypt
+    public static final int VERSION_102 = 102;
     // note: when increment meta version, should assign the latest version to VERSION_CURRENT
-    public static final int VERSION_CURRENT = VERSION_101;
+    public static final int VERSION_CURRENT = VERSION_102;
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
index fcc3070..4c73f05 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
@@ -28,6 +28,8 @@ import org.apache.doris.catalog.BrokerMgr;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Function;
 import org.apache.doris.catalog.FunctionSearchDesc;
+import org.apache.doris.catalog.EncryptKey;
+import org.apache.doris.catalog.EncryptKeySearchDesc;
 import org.apache.doris.catalog.Resource;
 import org.apache.doris.cluster.BaseParam;
 import org.apache.doris.cluster.Cluster;
@@ -484,6 +486,16 @@ public class JournalEntity implements Writable {
                 isRead = true;
                 break;
             }
+            case OperationType.OP_CREATE_ENCRYPTKEY: {
+                data = EncryptKey.read(in);
+                isRead = true;
+                break;
+            }
+            case OperationType.OP_DROP_ENCRYPTKEY: {
+                data = EncryptKeySearchDesc.read(in);
+                isRead = true;
+                break;
+            }
             case OperationType.OP_BACKEND_TABLETS_INFO: {
                 data = BackendTabletsInfo.read(in);
                 isRead = true;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
index 0bdbbbb..38ea66d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
@@ -29,8 +29,11 @@ import org.apache.doris.backup.RestoreJob;
 import org.apache.doris.catalog.BrokerMgr;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Database;
+import org.apache.doris.catalog.EncryptKeyHelper;
 import org.apache.doris.catalog.Function;
 import org.apache.doris.catalog.FunctionSearchDesc;
+import org.apache.doris.catalog.EncryptKey;
+import org.apache.doris.catalog.EncryptKeySearchDesc;
 import org.apache.doris.catalog.Resource;
 import org.apache.doris.cluster.BaseParam;
 import org.apache.doris.cluster.Cluster;
@@ -661,6 +664,16 @@ public class EditLog {
                     Catalog.getCurrentCatalog().replayDropFunction(function);
                     break;
                 }
+                case OperationType.OP_CREATE_ENCRYPTKEY: {
+                    final EncryptKey encryptKey = (EncryptKey) journal.getData();
+                    EncryptKeyHelper.replayCreateEncryptKey(encryptKey);
+                    break;
+                }
+                case OperationType.OP_DROP_ENCRYPTKEY: {
+                    EncryptKeySearchDesc encryptKeySearchDesc = (EncryptKeySearchDesc) journal.getData();
+                    EncryptKeyHelper.replayDropEncryptKey(encryptKeySearchDesc);
+                    break;
+                }
                 case OperationType.OP_BACKEND_TABLETS_INFO: {
                     BackendTabletsInfo backendTabletsInfo = (BackendTabletsInfo) journal.getData();
                     Catalog.getCurrentCatalog().replayBackendTabletsInfo(backendTabletsInfo);
@@ -1277,6 +1290,14 @@ public class EditLog {
         logEdit(OperationType.OP_DROP_FUNCTION, function);
     }
 
+    public void logAddEncryptKey(EncryptKey encryptKey) {
+        logEdit(OperationType.OP_CREATE_ENCRYPTKEY, encryptKey);
+    }
+
+    public void logDropEncryptKey(EncryptKeySearchDesc desc) {
+        logEdit(OperationType.OP_DROP_ENCRYPTKEY, desc);
+    }
+
     public void logBackendTabletsInfo(BackendTabletsInfo backendTabletsInfo) {
         logEdit(OperationType.OP_BACKEND_TABLETS_INFO, backendTabletsInfo);
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
index e32e287..48b8bf3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
@@ -176,6 +176,8 @@ public class OperationType {
     // small files 251~260
     public static final short OP_CREATE_SMALL_FILE = 251;
     public static final short OP_DROP_SMALL_FILE = 252;
+    public static final short OP_CREATE_ENCRYPTKEY = 253;
+    public static final short OP_DROP_ENCRYPTKEY = 254;
 
     // dynamic partition 261~265
     public static final short OP_DYNAMIC_PARTITION = 261;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
index 705299a..e3b082a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
@@ -38,6 +38,7 @@ import org.apache.doris.analysis.CreateClusterStmt;
 import org.apache.doris.analysis.CreateDbStmt;
 import org.apache.doris.analysis.CreateFileStmt;
 import org.apache.doris.analysis.CreateFunctionStmt;
+import org.apache.doris.analysis.CreateEncryptKeyStmt;
 import org.apache.doris.analysis.CreateMaterializedViewStmt;
 import org.apache.doris.analysis.CreateRepositoryStmt;
 import org.apache.doris.analysis.CreateResourceStmt;
@@ -53,6 +54,7 @@ import org.apache.doris.analysis.DropClusterStmt;
 import org.apache.doris.analysis.DropDbStmt;
 import org.apache.doris.analysis.DropFileStmt;
 import org.apache.doris.analysis.DropFunctionStmt;
+import org.apache.doris.analysis.DropEncryptKeyStmt;
 import org.apache.doris.analysis.DropMaterializedViewStmt;
 import org.apache.doris.analysis.DropRepositoryStmt;
 import org.apache.doris.analysis.DropResourceStmt;
@@ -77,6 +79,7 @@ import org.apache.doris.analysis.SyncStmt;
 import org.apache.doris.analysis.TruncateTableStmt;
 import org.apache.doris.analysis.UninstallPluginStmt;
 import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.EncryptKeyHelper;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
 import org.apache.doris.load.EtlJobType;
@@ -102,6 +105,10 @@ public class DdlExecutor {
             catalog.createFunction((CreateFunctionStmt) ddlStmt);
         } else if (ddlStmt instanceof DropFunctionStmt) {
             catalog.dropFunction((DropFunctionStmt) ddlStmt);
+        } else if (ddlStmt instanceof CreateEncryptKeyStmt) {
+            EncryptKeyHelper.createEncryptKey((CreateEncryptKeyStmt) ddlStmt);
+        } else if (ddlStmt instanceof DropEncryptKeyStmt) {
+            EncryptKeyHelper.dropEncryptKey((DropEncryptKeyStmt) ddlStmt);
         } else if (ddlStmt instanceof CreateTableStmt) {
             catalog.createTable((CreateTableStmt) ddlStmt);
         } else if (ddlStmt instanceof CreateTableLikeStmt) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
index e526b00..fecc3e1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
@@ -46,6 +46,7 @@ import org.apache.doris.analysis.ShowFrontendsStmt;
 import org.apache.doris.analysis.ShowFunctionsStmt;
 import org.apache.doris.analysis.ShowGrantsStmt;
 import org.apache.doris.analysis.ShowIndexStmt;
+import org.apache.doris.analysis.ShowEncryptKeysStmt;
 import org.apache.doris.analysis.ShowLoadStmt;
 import org.apache.doris.analysis.ShowLoadWarningsStmt;
 import org.apache.doris.analysis.ShowMigrationsStmt;
@@ -85,6 +86,7 @@ import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.DynamicPartitionProperty;
 import org.apache.doris.catalog.Function;
 import org.apache.doris.catalog.Index;
+import org.apache.doris.catalog.EncryptKey;
 import org.apache.doris.catalog.MaterializedIndex;
 import org.apache.doris.catalog.MaterializedIndex.IndexExtState;
 import org.apache.doris.catalog.MetadataViewer;
@@ -214,6 +216,8 @@ public class ShowExecutor {
             handleShowFunctions();
         } else if (stmt instanceof ShowCreateFunctionStmt) {
             handleShowCreateFunction();
+        } else if (stmt instanceof ShowEncryptKeysStmt) {
+            handleShowEncryptKeys();
         } else if (stmt instanceof ShowVariablesStmt) {
             handleShowVariables();
         } else if (stmt instanceof ShowColumnStmt) {
@@ -409,6 +413,46 @@ public class ShowExecutor {
         resultSet = new ShowResultSet(showCreateFunctionStmt.getMetaData(), resultRowSet);
     }
 
+    // Handle show encryptkeys
+    private void handleShowEncryptKeys() throws AnalysisException {
+        ShowEncryptKeysStmt showStmt = (ShowEncryptKeysStmt) stmt;
+        Database db = ctx.getCatalog().getDb(showStmt.getDbName());
+        if (db == null) {
+            ErrorReport.reportAnalysisException(ErrorCode.ERR_BAD_DB_ERROR, showStmt.getDbName());
+        }
+        List<EncryptKey> encryptKeys = db.getEncryptKeys();
+
+        List<List<Comparable>> rowSet = Lists.newArrayList();
+        for (EncryptKey encryptKey : encryptKeys) {
+            List<Comparable> row = encryptKey.getInfo();
+            // like predicate
+            if (showStmt.getWild() == null || showStmt.like(encryptKey.getEncryptKeyName().getKeyName())) {
+                rowSet.add(row);
+            }
+
+        }
+
+        // sort function rows by first column asc
+        ListComparator<List<Comparable>> comparator = null;
+        OrderByPair orderByPair = new OrderByPair(0, false);
+        comparator = new ListComparator<>(orderByPair);
+        Collections.sort(rowSet, comparator);
+        List<List<String>> resultRowSet = Lists.newArrayList();
+
+        Set<String> encryptKeyNameSet = new HashSet<>();
+        for (List<Comparable> row : rowSet) {
+            List<String> resultRow = Lists.newArrayList();
+            for (Comparable column : row) {
+                resultRow.add(column.toString());
+            }
+            resultRowSet.add(resultRow);
+            encryptKeyNameSet.add(resultRow.get(0));
+        }
+
+        ShowResultSetMetaData showMetaData = showStmt.getMetaData();
+        resultSet = new ShowResultSet(showMetaData, resultRowSet);
+    }
+
     private void handleShowProc() throws AnalysisException {
         ShowProcStmt showProcStmt = (ShowProcStmt) stmt;
         ShowResultSetMetaData metaData = showProcStmt.getMetaData();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/RewriteEncryptKeyRule.java b/fe/fe-core/src/main/java/org/apache/doris/rewrite/RewriteEncryptKeyRule.java
new file mode 100644
index 0000000..0583b26
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/RewriteEncryptKeyRule.java
@@ -0,0 +1,48 @@
+// 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.doris.rewrite;
+
+import org.apache.doris.analysis.Analyzer;
+import org.apache.doris.analysis.EncryptKeyRef;
+import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.LiteralExpr;
+import org.apache.doris.catalog.EncryptKey;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.common.AnalysisException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * rewrite EncryptKey to LiteralExpr
+ */
+public class RewriteEncryptKeyRule implements ExprRewriteRule {
+    private static final Logger LOG = LogManager.getLogger(FoldConstantsRule.class);
+
+    public static ExprRewriteRule INSTANCE = new RewriteEncryptKeyRule();
+
+    @Override
+    public Expr apply(Expr expr, Analyzer analyzer) throws AnalysisException {
+        if (expr instanceof EncryptKeyRef) {
+            // rewrite encryptKey to stringLiteral
+            EncryptKey encryptKey = ((EncryptKeyRef) expr).getEncryptKey();
+            LiteralExpr literalExpr = LiteralExpr.create(encryptKey.getKeyString(), Type.VARCHAR);
+            return literalExpr;
+        }
+        return expr;
+    }
+}
diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex b/fe/fe-core/src/main/jflex/sql_scanner.flex
index 53c6c70..2fede3b 100644
--- a/fe/fe-core/src/main/jflex/sql_scanner.flex
+++ b/fe/fe-core/src/main/jflex/sql_scanner.flex
@@ -171,6 +171,8 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("duplicate", new Integer(SqlParserSymbols.KW_DUPLICATE));
         keywordMap.put("else", new Integer(SqlParserSymbols.KW_ELSE));
         keywordMap.put("enable", new Integer(SqlParserSymbols.KW_ENABLE));
+        keywordMap.put("encryptkey", new Integer(SqlParserSymbols.KW_ENCRYPTKEY));
+        keywordMap.put("encryptkeys", new Integer(SqlParserSymbols.KW_ENCRYPTKEYS));
         keywordMap.put("end", new Integer(SqlParserSymbols.KW_END));
         keywordMap.put("engine", new Integer(SqlParserSymbols.KW_ENGINE));
         keywordMap.put("engines", new Integer(SqlParserSymbols.KW_ENGINES));
diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowEncryptKeysStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowEncryptKeysStmtTest.java
new file mode 100644
index 0000000..7051b58
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowEncryptKeysStmtTest.java
@@ -0,0 +1,79 @@
+// 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.doris.analysis;
+
+import mockit.Expectations;
+import mockit.Mocked;
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.FakeCatalog;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.MockedAuth;
+import org.apache.doris.mysql.privilege.PaloAuth;
+import org.apache.doris.qe.ConnectContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class ShowEncryptKeysStmtTest {
+    @Mocked
+    private Analyzer analyzer;
+    private Catalog catalog;
+
+    @Mocked
+    private PaloAuth auth;
+    @Mocked
+    private ConnectContext ctx;
+    private FakeCatalog fakeCatalog;
+
+    @Rule
+    public ExpectedException expectedEx = ExpectedException.none();
+
+    @Before
+    public void setUp() {
+        fakeCatalog = new FakeCatalog();
+        catalog = AccessTestUtil.fetchAdminCatalog();
+        MockedAuth.mockedAuth(auth);
+        MockedAuth.mockedConnectContext(ctx, "root", "192.188.3.1");
+        FakeCatalog.setCatalog(catalog);
+
+        new Expectations() {
+            {
+                analyzer.getDefaultDb();
+                minTimes = 0;
+                result = "testDb";
+
+                analyzer.getCatalog();
+                minTimes = 0;
+                result = catalog;
+
+                analyzer.getClusterName();
+                minTimes = 0;
+                result = "testCluster";
+            }
+        };
+    }
+
+    @Test
+    public void testNormal() throws UserException {
+        ShowEncryptKeysStmt stmt = new ShowEncryptKeysStmt(null, "%my%");
+        stmt.analyze(analyzer);
+        Assert.assertEquals("SHOW ENCRYPTKEYS FROM `testDb` LIKE `%my%`", stmt.toString());
+    }
+}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateEncryptKeyTest.java b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateEncryptKeyTest.java
new file mode 100644
index 0000000..cbd829e
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateEncryptKeyTest.java
@@ -0,0 +1,94 @@
+// 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.doris.catalog;
+
+import org.apache.doris.analysis.CreateDbStmt;
+import org.apache.doris.analysis.CreateEncryptKeyStmt;
+import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.FunctionCallExpr;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.jmockit.Deencapsulation;
+import org.apache.doris.planner.PlanFragment;
+import org.apache.doris.planner.Planner;
+import org.apache.doris.planner.UnionNode;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.QueryState;
+import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.utframe.UtFrameUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+import java.util.UUID;
+
+public class CreateEncryptKeyTest {
+    private static String runningDir = "fe/mocked/CreateEncryptKeyTest/" + UUID.randomUUID().toString() + "/";
+
+    @BeforeClass
+    public static void setup() throws Exception {
+        UtFrameUtils.createMinDorisCluster(runningDir);
+        FeConstants.runningUnitTest = true;
+    }
+
+    @AfterClass
+    public static void teardown() {
+        File file = new File("fe/mocked/CreateEncryptKeyTest/");
+        file.delete();
+    }
+
+    @Test
+    public void test() throws Exception {
+        ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+
+        // create database db1
+        String createDbStmtStr = "create database db1;";
+        CreateDbStmt createDbStmt = (CreateDbStmt) UtFrameUtils.parseAndAnalyzeStmt(createDbStmtStr, ctx);
+        Catalog.getCurrentCatalog().createDb(createDbStmt);
+        System.out.println(Catalog.getCurrentCatalog().getDbNames());
+
+        Database db = Catalog.getCurrentCatalog().getDb("default_cluster:db1");
+        Assert.assertNotNull(db);
+
+        String createFuncStr = "create encryptkey db1.my_key as \"beijing\";";
+
+        CreateEncryptKeyStmt createFunctionStmt = (CreateEncryptKeyStmt) UtFrameUtils.parseAndAnalyzeStmt(createFuncStr, ctx);
+        EncryptKeyHelper.createEncryptKey(createFunctionStmt);
+
+        List<EncryptKey> encryptKeys = db.getEncryptKeys();
+        Assert.assertEquals(1, encryptKeys.size());
+        Assert.assertEquals("beijing", encryptKeys.get(0).getKeyString());
+
+        String queryStr = "SELECT HEX(AES_ENCRYPT(\"Doris is Great\", key db1.my_key));";
+        ctx.getState().reset();
+        StmtExecutor stmtExecutor = new StmtExecutor(ctx, queryStr);
+        stmtExecutor.execute();
+        Assert.assertNotEquals(QueryState.MysqlStateType.ERR, ctx.getState().getStateType());
+        Planner planner = stmtExecutor.planner();
+        Assert.assertEquals(1, planner.getFragments().size());
+        PlanFragment fragment = planner.getFragments().get(0);
+        Assert.assertTrue(fragment.getPlanRoot() instanceof UnionNode);
+        UnionNode unionNode =  (UnionNode)fragment.getPlanRoot();
+        List<List<Expr>> constExprLists = Deencapsulation.getField(unionNode, "constExprLists_");
+        Assert.assertEquals(1, constExprLists.size());
+        Assert.assertEquals(1, constExprLists.get(0).size());
+        Assert.assertTrue(constExprLists.get(0).get(0) instanceof FunctionCallExpr);
+    }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org