diff --git a/src/common/legacy_config_opts.h b/src/common/legacy_config_opts.h
index 10c4d7d206b625b2a2af95ac2e4de52b3d5ed80c..c2e9d9a83b91f9c9ba1e43a83055a728566f1988 100644
--- a/src/common/legacy_config_opts.h
+++ b/src/common/legacy_config_opts.h
@@ -1241,6 +1241,7 @@ OPTION(rgw_max_attr_size, OPT_SIZE)
 OPTION(rgw_max_attrs_num_in_req, OPT_U64)
 
 OPTION(rgw_max_chunk_size, OPT_INT)
+OPTION(rgw_crypt_chunk_size, OPT_INT)
 OPTION(rgw_put_obj_min_window_size, OPT_INT)
 OPTION(rgw_put_obj_max_window_size, OPT_INT)
 OPTION(rgw_max_put_size, OPT_U64)
@@ -1527,6 +1528,7 @@ OPTION(rgw_crypt_kmip_s3_key_template, OPT_STR) // sse-s3; kmip key names
 
 OPTION(rgw_crypt_s3_kms_encryption_keys, OPT_STR) // extra keys that may be used for aws:kms
                                                       // defined as map "key1=YmluCmJvb3N0CmJvb3N0LQ== key2=b3V0CnNyYwpUZXN0aW5nCg=="
+OPTION(rgw_crypt_s3_kms_algorithm, OPT_STR)  // SSE-KMS encryption algorithm, support aes and sm4
 OPTION(rgw_crypt_suppress_logs, OPT_BOOL)   // suppress logs that might print customer key
 OPTION(rgw_list_bucket_min_readahead, OPT_INT) // minimum number of entries to read from rados for bucket listing
 
diff --git a/src/common/options.cc b/src/common/options.cc
index cef26003c208cf9cef8120599e98487e198699f9..dcc22b0ff4755ad80adb11e368cb3b311825f77f 100644
--- a/src/common/options.cc
+++ b/src/common/options.cc
@@ -5772,6 +5772,11 @@ std::vector<Option> get_rgw_options() {
         "need to be atomic, and anything larger than this would require more than a single "
         "operation."),
 
+    Option("rgw_crypt_chunk_size", Option::TYPE_SIZE, Option::LEVEL_ADVANCED)
+    .set_default(4096)
+    .set_min(4096)
+    .set_description("Set RGW crypt chunk size"),
+
     Option("rgw_put_obj_min_window_size", Option::TYPE_SIZE, Option::LEVEL_ADVANCED)
     .set_default(16_M)
     .set_description("The minimum RADOS write window size (in bytes).")
@@ -7139,6 +7144,11 @@ std::vector<Option> get_rgw_options() {
     .set_default("")
     .set_description(""),
 
+    Option("rgw_crypt_s3_kms_algorithm", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("aes")
+    .set_enum_allowed({"aes", "sm4"})
+    .set_description("rgw s3 SSE-KMS algorithm, aes or sm4, Default is aes alg."),
+
     Option("rgw_crypt_vault_auth", Option::TYPE_STR, Option::LEVEL_ADVANCED)
     .set_default("token")
     .set_enum_allowed({"token", "agent"})
diff --git a/src/rgw/rgw_crypt.cc b/src/rgw/rgw_crypt.cc
index 9f7e692564218e62d524dece6f1e6521683ca8cf..db6098f7a4667c3a6536f5625f362320b1b3dcf5 100644
--- a/src/rgw/rgw_crypt.cc
+++ b/src/rgw/rgw_crypt.cc
@@ -309,7 +309,6 @@ CryptoAccelRef get_crypto_accel(CephContext *cct)
 }
 
 
-template <std::size_t KeySizeV, std::size_t IvSizeV>
 static inline
 bool evp_sym_transform(CephContext* const cct,
                        const EVP_CIPHER* const type,
@@ -318,6 +317,8 @@ bool evp_sym_transform(CephContext* const cct,
                        const size_t size,
                        const unsigned char* const iv,
                        const unsigned char* const key,
+                       size_t IvSizeV,
+                       size_t KeySizeV,
                        const bool encrypt)
 {
   using pctx_t = \
@@ -335,11 +336,11 @@ bool evp_sym_transform(CephContext* const cct,
   }
 
   // we want to support ciphers that don't use IV at all like AES-256-ECB
-  if constexpr (static_cast<bool>(IvSizeV)) {
-    ceph_assert(EVP_CIPHER_CTX_iv_length(pctx.get()) == IvSizeV);
-    ceph_assert(EVP_CIPHER_CTX_block_size(pctx.get()) == IvSizeV);
+  if (static_cast<bool>(IvSizeV)) {
+    ceph_assert(EVP_CIPHER_CTX_iv_length(pctx.get()) == (int)IvSizeV);
+    ceph_assert(EVP_CIPHER_CTX_block_size(pctx.get()) == (int)IvSizeV);
   }
-  ceph_assert(EVP_CIPHER_CTX_key_length(pctx.get()) == KeySizeV);
+  ceph_assert(EVP_CIPHER_CTX_key_length(pctx.get()) == (int)KeySizeV);
 
   if (1 != EVP_CipherInit_ex(pctx.get(), nullptr, nullptr, key, iv, encrypt)) {
     ldout(cct, 5) << "EVP: failed to 2nd initialization stage" << dendl;
@@ -398,81 +399,99 @@ bool evp_sym_transform(CephContext* const cct,
  * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern
  *    obtained by CBC ENCRYPTION of {0} with IV derived from offset
  */
-class AES_256_CBC : public BlockCrypt {
-public:
-  static const size_t AES_256_KEYSIZE = 256 / 8;
-  static const size_t AES_256_IVSIZE = 128 / 8;
-  static const size_t CHUNK_SIZE = 4096;
+const size_t IVSIZE = 128 / 8;
+const size_t KEYSIZE = 256 / 8;
+const uint8_t IV[IVSIZE] = 
+    { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; // no change, version compatibility
+class CBC_MODE : public BlockCrypt {
 private:
-  static const uint8_t IV[AES_256_IVSIZE];
   CephContext* cct;
-  uint8_t key[AES_256_KEYSIZE];
+  uint8_t* iv;
+  uint8_t* key;
 public:
-  explicit AES_256_CBC(CephContext* cct): cct(cct) {
+  const EVP_CIPHER* type;
+  const size_t keysize;
+  const size_t ivsize;
+  const size_t chunksize;
+
+  explicit CBC_MODE(CephContext* cct, const EVP_CIPHER* _type, const size_t _keysize,
+                    const size_t _ivsize, const size_t _chunksize):
+          cct(cct),
+          type(_type),
+          keysize(_keysize),
+          ivsize(_ivsize),
+          chunksize(_chunksize)
+  {
+    iv = new uint8_t[ivsize]();
+    key = new uint8_t[keysize];
+    if (ivsize == IVSIZE) {
+      memcpy(iv , IV, IVSIZE);
+    }
   }
-  ~AES_256_CBC() {
-    ::ceph::crypto::zeroize_for_security(key, AES_256_KEYSIZE);
+  ~CBC_MODE() {
+    delete []iv;
+    delete []key;
   }
-  bool set_key(const uint8_t* _key, size_t key_size) {
-    if (key_size != AES_256_KEYSIZE) {
+  bool set_key(const uint8_t* _key, size_t _keysize) {
+    if (_keysize != keysize) {
       return false;
     }
-    memcpy(key, _key, AES_256_KEYSIZE);
+    memcpy(key, _key, keysize);
     return true;
   }
   size_t get_block_size() {
-    return CHUNK_SIZE;
+    return chunksize;
   }
 
   bool cbc_transform(unsigned char* out,
                      const unsigned char* in,
                      const size_t size,
-                     const unsigned char (&iv)[AES_256_IVSIZE],
-                     const unsigned char (&key)[AES_256_KEYSIZE],
+                     const unsigned char* const _iv,
+                     const unsigned char* const _key,
                      bool encrypt)
   {
-    return evp_sym_transform<AES_256_KEYSIZE, AES_256_IVSIZE>(
-      cct, EVP_aes_256_cbc(), out, in, size, iv, key, encrypt);
+    return evp_sym_transform(cct, type, out, in,
+        size, _iv, _key, ivsize, keysize, encrypt);
   }
 
   bool cbc_transform(unsigned char* out,
                      const unsigned char* in,
                      size_t size,
                      off_t stream_offset,
-                     const unsigned char (&key)[AES_256_KEYSIZE],
+                     const unsigned char* _key,
                      bool encrypt)
   {
     static std::atomic<bool> failed_to_get_crypto(false);
     CryptoAccelRef crypto_accel;
-    if (! failed_to_get_crypto.load())
-    {
+    if (! failed_to_get_crypto.load()) {
       crypto_accel = get_crypto_accel(cct);
       if (!crypto_accel)
         failed_to_get_crypto = true;
     }
     bool result = true;
-    unsigned char iv[AES_256_IVSIZE];
-    for (size_t offset = 0; result && (offset < size); offset += CHUNK_SIZE) {
-      size_t process_size = offset + CHUNK_SIZE <= size ? CHUNK_SIZE : size - offset;
+    unsigned char iv[IVSIZE];
+    unsigned char keybuff[KEYSIZE];
+    memcpy(keybuff, _key, keysize);
+    
+    for (size_t offset = 0; result && (offset < size); offset += chunksize) {
+      size_t process_size = offset + chunksize <= size ? chunksize : size - offset;
       prepare_iv(iv, stream_offset + offset);
-      if (crypto_accel != nullptr) {
+      if (EVP_CIPHER_nid(type) != NID_sm4_cbc && crypto_accel != nullptr) {
         if (encrypt) {
           result = crypto_accel->cbc_encrypt(out + offset, in + offset,
-                                             process_size, iv, key);
+                                             process_size, iv, keybuff);
         } else {
           result = crypto_accel->cbc_decrypt(out + offset, in + offset,
-                                             process_size, iv, key);
+                                             process_size, iv, keybuff);
         }
       } else {
-        result = cbc_transform(
-            out + offset, in + offset, process_size,
-            iv, key, encrypt);
+        result = cbc_transform(out + offset, in + offset,
+                               process_size, iv, keybuff, encrypt);
       }
     }
     return result;
   }
 
-
   bool encrypt(bufferlist& input,
                off_t in_ofs,
                size_t size,
@@ -480,10 +499,10 @@ public:
                off_t stream_offset)
   {
     bool result = false;
-    size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE;
+    size_t aligned_size = size / ivsize * ivsize;
     size_t unaligned_rest_size = size - aligned_size;
     output.clear();
-    buffer::ptr buf(aligned_size + AES_256_IVSIZE);
+    buffer::ptr buf(aligned_size + ivsize);
     unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str());
     const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(input.c_str());
 
@@ -494,31 +513,30 @@ public:
                            stream_offset, key, true);
     if (result && (unaligned_rest_size > 0)) {
       /* remainder to encrypt */
-      if (aligned_size % CHUNK_SIZE > 0) {
+      if (aligned_size % chunksize > 0) {
         /* use last chunk for unaligned part */
-        unsigned char iv[AES_256_IVSIZE] = {0};
+        unsigned char iv[IVSIZE] = {0};
         result = cbc_transform(buf_raw + aligned_size,
-                               buf_raw + aligned_size - AES_256_IVSIZE,
-                               AES_256_IVSIZE,
+                               buf_raw + aligned_size - ivsize,
+                               ivsize,
                                iv, key, true);
       } else {
-        /* 0 full blocks in current chunk, use IV as base for unaligned part */
-        unsigned char iv[AES_256_IVSIZE] = {0};
-        unsigned char data[AES_256_IVSIZE];
-        prepare_iv(data, stream_offset + aligned_size);
+        unsigned char iv[IVSIZE] = {0};
+        std::unique_ptr<unsigned char[]> data(new unsigned char[ivsize]);
+        prepare_iv(&data[0], stream_offset + aligned_size);
         result = cbc_transform(buf_raw + aligned_size,
-                               data,
-                               AES_256_IVSIZE,
+                               &data[0],
+                               ivsize,
                                iv, key, true);
       }
       if (result) {
-        for(size_t i = aligned_size; i < size; i++) {
+        for (size_t i = aligned_size; i < size; i++) {
           *(buf_raw + i) ^= *(input_raw + in_ofs + i);
         }
       }
     }
     if (result) {
-      ldout(cct, 25) << "Encrypted " << size << " bytes"<< dendl;
+      ldout(cct, 25) << "Encrypted " << size << " bytes" << dendl;
       buf.set_length(size);
       output.append(buf);
     } else {
@@ -527,7 +545,6 @@ public:
     return result;
   }
 
-
   bool decrypt(bufferlist& input,
                off_t in_ofs,
                size_t size,
@@ -535,45 +552,44 @@ public:
                off_t stream_offset)
   {
     bool result = false;
-    size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE;
+    size_t aligned_size = size / ivsize * ivsize;
     size_t unaligned_rest_size = size - aligned_size;
     output.clear();
-    buffer::ptr buf(aligned_size + AES_256_IVSIZE);
+    buffer::ptr buf(aligned_size + ivsize);
     unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str());
-    unsigned char* input_raw = reinterpret_cast<unsigned char*>(input.c_str());
+    const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(input.c_str());
 
-    /* decrypt main bulk of data */
+    /* decrypt mian bulk of data */
     result = cbc_transform(buf_raw,
                            input_raw + in_ofs,
                            aligned_size,
                            stream_offset, key, false);
     if (result && unaligned_rest_size > 0) {
       /* remainder to decrypt */
-      if (aligned_size % CHUNK_SIZE > 0) {
-        /*use last chunk for unaligned part*/
-        unsigned char iv[AES_256_IVSIZE] = {0};
+      if (aligned_size % chunksize > 0) {
+        unsigned char iv[IVSIZE] = {0};
         result = cbc_transform(buf_raw + aligned_size,
-                               input_raw + in_ofs + aligned_size - AES_256_IVSIZE,
-                               AES_256_IVSIZE,
+                               input_raw + in_ofs + aligned_size - ivsize,
+                               ivsize,
                                iv, key, true);
       } else {
         /* 0 full blocks in current chunk, use IV as base for unaligned part */
-        unsigned char iv[AES_256_IVSIZE] = {0};
-        unsigned char data[AES_256_IVSIZE];
-        prepare_iv(data, stream_offset + aligned_size);
+        unsigned char iv[IVSIZE] = {0};
+        std::unique_ptr<unsigned char[]> data(new unsigned char[ivsize]);
+        prepare_iv(&data[0], stream_offset + aligned_size);
         result = cbc_transform(buf_raw + aligned_size,
-                               data,
-                               AES_256_IVSIZE,
+                               &data[0],
+                               ivsize,
                                iv, key, true);
       }
       if (result) {
-        for(size_t i = aligned_size; i < size; i++) {
+        for (size_t i = aligned_size; i < size; i++) {
           *(buf_raw + i) ^= *(input_raw + in_ofs + i);
         }
       }
     }
     if (result) {
-      ldout(cct, 25) << "Decrypted " << size << " bytes"<< dendl;
+      ldout(cct, 25) << "Decrypted " << size << " bytes" << dendl;
       buf.set_length(size);
       output.append(buf);
     } else {
@@ -582,15 +598,14 @@ public:
     return result;
   }
 
-
-  void prepare_iv(unsigned char (&iv)[AES_256_IVSIZE], off_t offset) {
-    off_t index = offset / AES_256_IVSIZE;
-    off_t i = AES_256_IVSIZE - 1;
+  void prepare_iv(unsigned char* _iv, off_t offset) {
+    off_t index = offset / ivsize;
+    off_t i = ivsize - 1;
     unsigned int val;
     unsigned int carry = 0;
-    while (i>=0) {
-      val = (index & 0xff) + IV[i] + carry;
-      iv[i] = val;
+    while (i >= 0) {
+      val = (index & 0xff) + iv[i] + carry;
+      _iv[i] = val;
       carry = val >> 8;
       index = index >> 8;
       i--;
@@ -598,18 +613,21 @@ public:
   }
 };
 
-
 std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len)
 {
-  auto cbc = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(cct));
+  auto cbc = std::unique_ptr<CBC_MODE>(new CBC_MODE(cct, EVP_aes_256_cbc(),
+               AES_256_KEYSIZE, AES_256_IVSIZE, cct->_conf->rgw_crypt_chunk_size));
   cbc->set_key(key, AES_256_KEYSIZE);
   return cbc;
 }
 
-
-const uint8_t AES_256_CBC::IV[AES_256_CBC::AES_256_IVSIZE] =
-    { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' };
-
+std::unique_ptr<BlockCrypt> SM4_CBC_create(CephContext* cct, const uint8_t* key, size_t len)
+{
+  auto cbc = std::unique_ptr<CBC_MODE>(new CBC_MODE(cct, EVP_sm4_cbc(),
+               SM4_KEYSIZE, SM4_IVSIZE, cct->_conf->rgw_crypt_chunk_size));
+  cbc->set_key(key, SM4_KEYSIZE);
+  return cbc;
+}
 
 bool AES_256_ECB_encrypt(CephContext* cct,
                          const uint8_t* key,
@@ -619,9 +637,8 @@ bool AES_256_ECB_encrypt(CephContext* cct,
                          size_t data_size)
 {
   if (key_size == AES_256_KEYSIZE) {
-    return evp_sym_transform<AES_256_KEYSIZE, 0 /* no IV in ECB */>(
-      cct, EVP_aes_256_ecb(),  data_out, data_in, data_size,
-      nullptr /* no IV in ECB */, key, true /* encrypt */);
+    return evp_sym_transform(cct, EVP_aes_256_ecb(), data_out, data_in, data_size,
+      nullptr /* no IV in ECB */, key, 0, AES_256_KEYSIZE, true /* encrypt */);
   } else {
     ldout(cct, 5) << "Key size must be 256 bits long" << dendl;
     return false;
@@ -835,10 +852,10 @@ int RGWPutObj_BlockEncrypt::process(bufferlist&& data, uint64_t logical_offset)
 }
 
 
-std::string create_random_key_selector(CephContext * const cct) {
-  char random[AES_256_KEYSIZE];
-  cct->random()->get_bytes(&random[0], sizeof(random));
-  return std::string(random, sizeof(random));
+std::string create_random_key_selector(CephContext * const cct, const size_t keysize) {
+  std::unique_ptr<char[]> random(new char[keysize]);
+  cct->random()->get_bytes(&random[0], keysize);
+  return std::string(&random[0], keysize);
 }
 
 typedef enum {
@@ -908,11 +925,11 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
     std::string_view req_sse_ca =
         get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM);
     if (! req_sse_ca.empty()) {
-      if (req_sse_ca != "AES256") {
+      if (req_sse_ca != "AES256" && req_sse_ca != "SM4") {
         ldpp_dout(s, 5) << "ERROR: Invalid value for header "
                          << "x-amz-server-side-encryption-customer-algorithm"
                          << dendl;
-        s->err.message = "The requested encryption algorithm is not valid, must be AES256.";
+        s->err.message = "The requested encryption algorithm is not valid, must be AES256 or SM4.";
         return -ERR_INVALID_ENCRYPTION_ALGORITHM;
       }
       if (s->cct->_conf->rgw_crypt_require_ssl &&
@@ -934,11 +951,22 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
         return -EINVAL;
       }
 
-      if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) {
-        ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl;
-        s->err.message = "Requests specifying Server Side Encryption with Customer "
-                         "provided keys must provide an appropriate secret key.";
-        return -EINVAL;
+      if (req_sse_ca == "AES256") {
+        if (key_bin.size() != AES_256_KEYSIZE) {
+          ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl;
+          s->err.message = "Requests specifying Server Side Encryption with Customer "
+                           "provided keys must provide an appropriate secret key.";
+          return -EINVAL;
+        }
+      }
+
+      if (req_sse_ca == "SM4") {
+        if (key_bin.size() != SM4_KEYSIZE) {
+          ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl;
+          s->err.message = "Requests specifying Server Side Encryption with Customer "
+                           "provided keys must provide an appropriate secret key.";
+          return -EINVAL;
+        }
       }
 
       std::string_view keymd5 =
@@ -974,16 +1002,28 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
         return -EINVAL;
       }
 
-      set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256");
+      if (req_sse_ca == "AES256") {
+        set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256");
+      } else if (req_sse_ca == "SM4") {
+        set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-SM4");
+      }
       set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin);
 
       if (block_crypt) {
-        auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
-        aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE);
-        *block_crypt = std::move(aes);
+        if (req_sse_ca == "AES256") {
+          auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(),
+                       AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+          aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE);
+          *block_crypt = std::move(aes);
+        } else if (req_sse_ca == "SM4") {
+          auto sm4 = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(),
+                       SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+          sm4->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), SM4_KEYSIZE);
+          *block_crypt = std::move(sm4);
+        }
       }
 
-      crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
+      crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = req_sse_ca;
       crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = std::string(keymd5);
       return 0;
     } else {
@@ -1036,8 +1076,15 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
 	  return -ERR_INVALID_ACCESS_KEY;
 	}
 	/* try to retrieve actual key */
-	std::string key_selector = create_random_key_selector(s->cct);
-	set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS");
+        std::string s3_kms_algorithm = s->cct->_conf->rgw_crypt_s3_kms_algorithm;
+        std::string key_selector;
+        if (s3_kms_algorithm == "aes") {
+          key_selector = create_random_key_selector(s->cct, AES_256_KEYSIZE);
+          set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS-AES");
+        } else if (s3_kms_algorithm == "sm4") {
+          key_selector = create_random_key_selector(s->cct, SM4_KEYSIZE);
+          set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS-SM4");
+	}
 	set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id);
 	set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector);
 	set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, cooked_context);
@@ -1048,18 +1095,38 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
 	  s->err.message = "Failed to retrieve the actual key, kms-keyid: " + std::string(key_id);
 	  return res;
 	}
-	if (actual_key.size() != AES_256_KEYSIZE) {
-	  ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" <<
-            key_id << " is not 256 bit size" << dendl;
-	  s->err.message = "KMS provided an invalid key for the given kms-keyid.";
-	  return -ERR_INVALID_ACCESS_KEY;
-	}
+        if (s3_kms_algorithm == "aes") {
+          if (actual_key.size() != AES_256_KEYSIZE) {
+            ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" <<
+              key_id << " is not 256 bit size" << dendl;
+            s->err.message = "KMS provided an invalid key for the given kms-keyid.";
+            return -ERR_INVALID_ACCESS_KEY;
+          }
+        }
 
-	if (block_crypt) {
-	  auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
-	  aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
-	  *block_crypt = std::move(aes);
-	}
+        if (s3_kms_algorithm == "sm4") {
+          if (actual_key.size() != SM4_KEYSIZE) {
+            ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" <<
+              key_id << " is not 128 bit size" << dendl;
+            s->err.message = "KMS provided an invalid key for the given kms-keyid.";
+            return -ERR_INVALID_ACCESS_KEY;
+          }
+        }
+
+        if (block_crypt) {
+          if (s3_kms_algorithm == "aes") {
+            auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(),
+                         AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+            aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
+            *block_crypt = std::move(aes);
+          }
+          if (s3_kms_algorithm == "sm4") {
+            auto sm4 = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(),
+                         SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+            sm4->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), SM4_KEYSIZE);
+            *block_crypt = std::move(sm4);
+          }
+        }
         ::ceph::crypto::zeroize_for_security(actual_key.data(), actual_key.length());
 
 	crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms";
@@ -1111,7 +1178,7 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
       }
 
       set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO");
-      std::string key_selector = create_random_key_selector(s->cct);
+      std::string key_selector = create_random_key_selector(s->cct, AES_256_KEYSIZE);
       set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector);
 
       uint8_t actual_key[AES_256_KEYSIZE];
@@ -1123,7 +1190,8 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
         return -EIO;
       }
       if (block_crypt) {
-        auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
+        auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(),
+	        AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
         aes->set_key(reinterpret_cast<const uint8_t*>(actual_key), AES_256_KEYSIZE);
         *block_crypt = std::move(aes);
       }
@@ -1184,7 +1252,7 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
       return -EINVAL;
     }
 
-    if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) {
+    if (key_bin.size() != AES_256_KEYSIZE) {
       ldpp_dout(s, 5) << "ERROR: Invalid encryption key size" << dendl;
       s->err.message = "Requests specifying Server Side Encryption with Customer "
                        "provided keys must provide an appropriate secret key.";
@@ -1223,8 +1291,9 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
       s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided.";
       return -EINVAL;
     }
-    auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
-    aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_CBC::AES_256_KEYSIZE);
+    auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(),
+	       AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+    aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE);
     if (block_crypt) *block_crypt = std::move(aes);
 
     crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
@@ -1232,7 +1301,90 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
     return 0;
   }
 
-  if (stored_mode == "SSE-KMS") {
+  if (stored_mode == "SSE-C-SM4") {
+    if (s->cct->_conf->rgw_crypt_require_ssl &&
+        !rgw_transport_is_secure(s->cct, *s->info.env)) {
+      ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
+      return -ERR_INVALID_REQUEST;
+    }
+    const char *req_cust_alg =
+        s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL);
+
+    if (nullptr == req_cust_alg)  {
+      ldpp_dout(s, 5) << "ERROR: Request for SSE-C encrypted object missing "
+                       << "x-amz-server-side-encryption-customer-algorithm"
+                       << dendl;
+      s->err.message = "Requests specifying Server Side Encryption with Customer "
+                       "provided keys must provide a valid encryption algorithm.";
+      return -EINVAL;
+    } else if (strcmp(req_cust_alg, "SM4") != 0) {
+      ldpp_dout(s, 5) << "ERROR: The requested encryption algorithm is not valid, must be SM4." << dendl;
+      s->err.message = "The requested encryption algorithm is not valid, must be SM4.";
+      return -ERR_INVALID_ENCRYPTION_ALGORITHM;
+    }
+
+    std::string key_bin;
+    try {
+      key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", ""));
+    } catch (...) {
+      ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key "
+                       << "which contains character that is not base64 encoded."
+                       << dendl;
+      s->err.message = "Requests specifying Server Side Encryption with Customer "
+                       "provided keys must provide an appropriate secret key.";
+      return -EINVAL;
+    }
+
+    if (key_bin.size() != SM4_KEYSIZE) {
+      ldpp_dout(s, 5) << "ERROR: Invalid encryption key size" << dendl;
+      s->err.message = "Requests specifying Server Side Encryption with Customer "
+                       "provided keys must provide an appropriate secret key.";
+      return -EINVAL;
+    }
+
+    std::string keymd5 =
+        s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "");
+    std::string keymd5_bin;
+    try {
+      keymd5_bin = from_base64(keymd5);
+    } catch (...) {
+      ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 "
+                       << "which contains character that is not base64 encoded."
+                       << dendl;
+      s->err.message = "Requests specifying Server Side Encryption with Customer "
+                       "provided keys must provide an appropriate secret key md5.";
+      return -EINVAL;
+    }
+
+
+    if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) {
+      ldpp_dout(s, 5) << "ERROR: Invalid key md5 size " << dendl;
+      s->err.message = "Requests specifying Server Side Encryption with Customer "
+                       "provided keys must provide an appropriate secret key md5.";
+      return -EINVAL;
+    }
+
+    MD5 key_hash;
+    uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
+    key_hash.Update(reinterpret_cast<const unsigned char*>(key_bin.c_str()), key_bin.size());
+    key_hash.Final(key_hash_res);
+
+    if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) ||
+        (get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) {
+      s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided.";
+      return -EINVAL;
+    }
+    auto sm4 = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(),
+               SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+    sm4->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), SM4_KEYSIZE);
+    if (block_crypt) *block_crypt = std::move(sm4);
+
+    crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "SM4";
+    crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5;
+    return 0;
+  }
+
+  if (stored_mode == "SSE-KMS-AES") {
     if (s->cct->_conf->rgw_crypt_require_ssl &&
         !rgw_transport_is_secure(s->cct, *s->info.env)) {
       ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
@@ -1254,7 +1406,8 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
       return -ERR_INVALID_ACCESS_KEY;
     }
 
-    auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
+    auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(),
+            AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
     aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
     actual_key.replace(0, actual_key.length(), actual_key.length(), '\000');
     if (block_crypt) *block_crypt = std::move(aes);
@@ -1264,6 +1417,39 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
     return 0;
   }
 
+  if (stored_mode == "SSE-KMS-SM4") {
+    if (s->cct->_conf->rgw_crypt_require_ssl &&
+        !rgw_transport_is_secure(s->cct, *s->info.env)) {
+      ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
+      return -ERR_INVALID_REQUEST;
+    }
+    /* try to retrieve actual key */
+    std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID);
+    std::string actual_key;
+    res = reconstitute_actual_key_from_kms(s->cct, attrs, actual_key);
+    if (res != 0) {
+      ldpp_dout(s, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl;
+      s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id;
+      return res;
+    }
+    if (actual_key.size() != SM4_KEYSIZE) {
+      ldpp_dout(s, 0) << "ERROR: key obtained from key_id:" <<
+          key_id << " is not 128 bit size" << dendl;
+      s->err.message = "KMS provided an invalid key for the given kms-keyid.";
+      return -ERR_INVALID_ACCESS_KEY;
+    }
+
+    auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_sm4_cbc(),
+            SM4_KEYSIZE, SM4_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
+    aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), SM4_KEYSIZE);
+    actual_key.replace(0, actual_key.length(), actual_key.length(), '\000');
+    if (block_crypt) *block_crypt = std::move(aes);
+
+    crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms";
+    crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id;
+    return 0;
+  }
+
   if (stored_mode == "RGW-AUTO") {
     std::string master_encryption_key;
     try {
@@ -1281,7 +1467,7 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
       return -EIO;
     }
     std::string attr_key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL);
-    if (attr_key_selector.size() != AES_256_CBC::AES_256_KEYSIZE) {
+    if (attr_key_selector.size() != AES_256_KEYSIZE) {
       ldpp_dout(s, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl;
       return -EIO;
     }
@@ -1294,7 +1480,8 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
       ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key));
       return -EIO;
     }
-    auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
+    auto aes = std::unique_ptr<CBC_MODE>(new CBC_MODE(s->cct, EVP_aes_256_cbc(),
+            AES_256_KEYSIZE, AES_256_IVSIZE, s->cct->_conf->rgw_crypt_chunk_size));
     aes->set_key(actual_key, AES_256_KEYSIZE);
     ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key));
     if (block_crypt) *block_crypt = std::move(aes);
diff --git a/src/rgw/rgw_crypt.h b/src/rgw/rgw_crypt.h
index ff221549d6fde83a0f05aca08d4ffcf53020d29c..94e12ff4a1433dd520231c6393607e13258a9820 100644
--- a/src/rgw/rgw_crypt.h
+++ b/src/rgw/rgw_crypt.h
@@ -80,6 +80,9 @@ public:
 };
 
 static const size_t AES_256_KEYSIZE = 256 / 8;
+static const size_t AES_256_IVSIZE  = 128 / 8;
+static const size_t SM4_KEYSIZE     = 128 / 8;
+static const size_t SM4_IVSIZE      = 128 / 8;
 bool AES_256_ECB_encrypt(CephContext* cct,
                          const uint8_t* key,
                          size_t key_size,
diff --git a/src/rgw/rgw_kms.cc b/src/rgw/rgw_kms.cc
index dcdcf875115dbe5f042084cd8b631df2de045954..42aa3d492dbced55bf233311c3276fa8d1ef1e63 100644
--- a/src/rgw/rgw_kms.cc
+++ b/src/rgw/rgw_kms.cc
@@ -765,16 +765,30 @@ static int get_actual_key_from_conf(CephContext *cct,
   }
 
   if (master_key.length() == AES_256_KEYSIZE) {
-    uint8_t _actual_key[AES_256_KEYSIZE];
-    if (AES_256_ECB_encrypt(cct,
-        reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE,
-        reinterpret_cast<const uint8_t*>(key_selector.data()),
-        _actual_key, AES_256_KEYSIZE)) {
-      actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE);
-    } else {
-      res = -EIO;
+    std::string s3_kms_algorithm = cct->_conf->rgw_crypt_s3_kms_algorithm;
+    if (s3_kms_algorithm == "aes") {
+      uint8_t _actual_key[AES_256_KEYSIZE];
+      if (AES_256_ECB_encrypt(cct,
+          reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE,
+          reinterpret_cast<const uint8_t*>(key_selector.data()),
+          _actual_key, AES_256_KEYSIZE)) {
+        actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE);
+      } else {
+        res = -EIO;
+      }
+      ::ceph::crypto::zeroize_for_security(_actual_key, sizeof(_actual_key));
+    } else if (s3_kms_algorithm == "sm4") {
+      uint8_t _actual_key[SM4_KEYSIZE];
+      if (AES_256_ECB_encrypt(cct,
+          reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE,
+          reinterpret_cast<const uint8_t*>(key_selector.data()),
+          _actual_key, SM4_KEYSIZE)) {
+        actual_key = std::string((char*)&_actual_key[0], SM4_KEYSIZE);
+      } else {
+        res = -EIO;
+      }
+      ::ceph::crypto::zeroize_for_security(_actual_key, sizeof(_actual_key));
     }
-    ::ceph::crypto::zeroize_for_security(_actual_key, sizeof(_actual_key));
   } else {
     ldout(cct, 20) << "Wrong size for key=" << key_id << dendl;
     res = -EIO;
@@ -811,9 +825,11 @@ static int request_key_from_barbican(CephContext *cct,
     return -EACCES;
   }
 
+  std::string s3_kms_algorithm = cct->_conf->rgw_crypt_s3_kms_algorithm;
   if (secret_req.get_http_status() >=200 &&
       secret_req.get_http_status() < 300 &&
-      secret_bl.length() == AES_256_KEYSIZE) {
+      ((s3_kms_algorithm == "aes" && secret_bl.length() == AES_256_KEYSIZE) ||
+       (s3_kms_algorithm == "sm4" && secret_bl.length() == SM4_KEYSIZE))) {
     actual_key.assign(secret_bl.c_str(), secret_bl.length());
     secret_bl.zero();
     } else {
diff --git a/src/test/rgw/test_rgw_crypto.cc b/src/test/rgw/test_rgw_crypto.cc
index b0dbabfd4e22ad8e87f47dc6b627efea936e099d..4d42f568cd04ded5c414c2243f5723cfba2b2fdf 100644
--- a/src/test/rgw/test_rgw_crypto.cc
+++ b/src/test/rgw/test_rgw_crypto.cc
@@ -25,6 +25,7 @@ using namespace std;
 
 
 std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len);
+std::unique_ptr<BlockCrypt> SM4_CBC_create(CephContext* cct, const uint8_t* key, size_t len);
 
 
 class ut_get_sink : public RGWGetObj_Filter {
@@ -144,6 +145,56 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity)
   }
 }
 
+TEST(TestRGWCrypto, verify_SM4_CBC_identity)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  buffer::ptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+  {
+    //make some random key
+    uint8_t key[16];
+    for(size_t i=0;i<sizeof(key);i++)
+      key[i]=i*step;
+
+    auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16));
+    ASSERT_NE(sm4.get(), nullptr);
+
+    size_t block_size = sm4->get_block_size();
+    ASSERT_NE(block_size, 0u);
+
+    for (size_t r = 97; r < 123 ; r++)
+    {
+      off_t begin = (r*r*r*r*r % test_range);
+      begin = begin - begin % block_size;
+      off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
+      if (r % 3)
+        end = end - end % block_size;
+      off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+      offset = offset - offset % block_size;
+
+      ASSERT_EQ(begin % block_size, 0u);
+      ASSERT_LE(end, test_range);
+      ASSERT_EQ(offset % block_size, 0u);
+
+      bufferlist encrypted;
+      ASSERT_TRUE(sm4->encrypt(input, begin, end - begin, encrypted, offset));
+      bufferlist decrypted;
+      ASSERT_TRUE(sm4->decrypt(encrypted, 0, end - begin, decrypted, offset));
+
+      ASSERT_EQ(decrypted.length(), end - begin);
+      ASSERT_EQ(std::string_view(input.c_str() + begin, end - begin),
+                std::string_view(decrypted.c_str(), end - begin) );
+    }
+  }
+}
 
 TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2)
 {
@@ -192,6 +243,52 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2)
   }
 }
 
+TEST(TestRGWCrypto, verify_SM4_CBC_identity_2)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  buffer::ptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+  {
+    //make some random key
+    uint8_t key[16];
+    for(size_t i=0;i<sizeof(key);i++)
+      key[i]=i*step;
+
+    auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16));
+    ASSERT_NE(sm4.get(), nullptr);
+
+    size_t block_size = sm4->get_block_size();
+    ASSERT_NE(block_size, 0u);
+
+    for (off_t end = 1; end < 6096 ; end+=3)
+    {
+      off_t begin = 0;
+      off_t offset = end*end*end*end*end % (1000*1000*1000);
+      offset = offset - offset % block_size;
+
+      ASSERT_EQ(begin % block_size, 0u);
+      ASSERT_LE(end, test_range);
+      ASSERT_EQ(offset % block_size, 0u);
+
+      bufferlist encrypted;
+      ASSERT_TRUE(sm4->encrypt(input, begin, end, encrypted, offset));
+      bufferlist decrypted;
+      ASSERT_TRUE(sm4->decrypt(encrypted, 0, end, decrypted, offset));
+
+      ASSERT_EQ(decrypted.length(), end);
+      ASSERT_EQ(std::string_view(input.c_str(), end),
+                std::string_view(decrypted.c_str(), end) );
+    }
+  }
+}
 
 TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3)
 {
@@ -269,6 +366,81 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3)
   }
 }
 
+TEST(TestRGWCrypto, verify_SM4_CBC_identity_3)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  buffer::ptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+  {
+    //make some random key
+    uint8_t key[16];
+    for(size_t i=0;i<sizeof(key);i++)
+      key[i]=i*step;
+
+    auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16));
+    ASSERT_NE(sm4.get(), nullptr);
+
+    size_t block_size = sm4->get_block_size();
+    ASSERT_NE(block_size, 0u);
+    size_t rr = 111;
+    for (size_t r = 97; r < 123 ; r++)
+    {
+      off_t begin = 0;
+      off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
+      //sometimes make aligned
+      if (r % 3)
+        end = end - end % block_size;
+      off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+      offset = offset - offset % block_size;
+
+      ASSERT_EQ(begin % block_size, 0u);
+      ASSERT_LE(end, test_range);
+      ASSERT_EQ(offset % block_size, 0u);
+
+      bufferlist encrypted1;
+      bufferlist encrypted2;
+
+      off_t pos = begin;
+      off_t chunk;
+      while (pos < end) {
+        chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
+        chunk = chunk - chunk % block_size;
+        if (pos + chunk > end)
+          chunk = end - pos;
+        bufferlist tmp;
+        ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos));
+        encrypted1.append(tmp);
+        pos += chunk;
+        rr++;
+      }
+
+      pos = begin;
+      while (pos < end) {
+        chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
+        chunk = chunk - chunk % block_size;
+        if (pos + chunk > end)
+          chunk = end - pos;
+        bufferlist tmp;
+        ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos));
+        encrypted2.append(tmp);
+        pos += chunk;
+        rr++;
+      }
+      ASSERT_EQ(encrypted1.length(), end);
+      ASSERT_EQ(encrypted2.length(), end);
+      ASSERT_EQ(std::string_view(encrypted1.c_str(), end),
+                std::string_view(encrypted2.c_str(), end) );
+    }
+  }
+}
 
 TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15)
 {
@@ -318,6 +490,53 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15)
   }
 }
 
+TEST(TestRGWCrypto, verify_SM4_CBC_size_0_15)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  buffer::ptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+  {
+    //make some random key
+    uint8_t key[16];
+    for(size_t i=0;i<sizeof(key);i++)
+      key[i]=i*step;
+
+    auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16));
+    ASSERT_NE(sm4.get(), nullptr);
+
+    size_t block_size = sm4->get_block_size();
+    ASSERT_NE(block_size, 0u);
+    for (size_t r = 97; r < 123 ; r++)
+    {
+      off_t begin = 0;
+      off_t end = begin + r*r*r*r*r*r*r % (16);
+
+      off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+      offset = offset - offset % block_size;
+
+      ASSERT_EQ(begin % block_size, 0u);
+      ASSERT_LE(end, test_range);
+      ASSERT_EQ(offset % block_size, 0u);
+
+      bufferlist encrypted;
+      bufferlist decrypted;
+      ASSERT_TRUE(sm4->encrypt(input, 0, end, encrypted, offset));
+      ASSERT_TRUE(sm4->encrypt(encrypted, 0, end, decrypted, offset));
+      ASSERT_EQ(encrypted.length(), end);
+      ASSERT_EQ(decrypted.length(), end);
+      ASSERT_EQ(std::string_view(input.c_str(), end),
+                std::string_view(decrypted.c_str(), end) );
+    }
+  }
+}
 
 TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block)
 {
@@ -394,8 +613,82 @@ TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block)
   }
 }
 
+TEST(TestRGWCrypto, verify_SM4_CBC_identity_last_block)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  buffer::ptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+  {
+    //make some random key
+    uint8_t key[16];
+    for(size_t i=0;i<sizeof(key);i++)
+      key[i]=i*step;
 
-TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges)
+    auto sm4(SM4_CBC_create(g_ceph_context, &key[0], 16));
+    ASSERT_NE(sm4.get(), nullptr);
+
+    size_t block_size = sm4->get_block_size();
+    ASSERT_NE(block_size, 0u);
+    size_t rr = 111;
+    for (size_t r = 97; r < 123 ; r++)
+    {
+      off_t begin = 0;
+      off_t end = r*r*r*r*r*r*r % (test_range - 16);
+      end = end - end % block_size;
+      end = end + (r+3)*(r+5)*(r+7) % 16;
+
+      off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+      offset = offset - offset % block_size;
+
+      ASSERT_EQ(begin % block_size, 0u);
+      ASSERT_LE(end, test_range);
+      ASSERT_EQ(offset % block_size, 0u);
+
+      bufferlist encrypted1;
+      bufferlist encrypted2;
+
+      off_t pos = begin;
+      off_t chunk;
+      while (pos < end) {
+        chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
+        chunk = chunk - chunk % block_size;
+        if (pos + chunk > end)
+          chunk = end - pos;
+        bufferlist tmp;
+        ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos));
+        encrypted1.append(tmp);
+        pos += chunk;
+        rr++;
+      }
+      pos = begin;
+      while (pos < end) {
+        chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
+        chunk = chunk - chunk % block_size;
+        if (pos + chunk > end)
+          chunk = end - pos;
+        bufferlist tmp;
+        ASSERT_TRUE(sm4->encrypt(input, pos, chunk, tmp, offset + pos));
+        encrypted2.append(tmp);
+        pos += chunk;
+        rr++;
+      }
+      ASSERT_EQ(encrypted1.length(), end);
+      ASSERT_EQ(encrypted2.length(), end);
+      ASSERT_EQ(std::string_view(encrypted1.c_str(), end),
+                std::string_view(encrypted2.c_str(), end) );
+    }
+  }
+}
+
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges_AES)
 {
   //create some input for encryption
   const off_t test_range = 1024*1024;
@@ -440,8 +733,52 @@ TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges)
   }
 }
 
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges_SM4)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  bufferptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  uint8_t key[16];
+  for(size_t i=0;i<sizeof(key);i++)
+    key[i] = i;
+
+  auto cbc = SM4_CBC_create(g_ceph_context, &key[0], 16);
+  ASSERT_NE(cbc.get(), nullptr);
+  bufferlist encrypted;
+  ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
+
 
-TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks)
+  for (off_t r = 93; r < 150; r++ )
+  {
+    ut_get_sink get_sink;
+    auto cbc = SM4_CBC_create(g_ceph_context, &key[0], 16);
+    ASSERT_NE(cbc.get(), nullptr);
+    RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) );
+
+    //random ranges
+    off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
+    off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
+
+    off_t f_begin = begin;
+    off_t f_end = end;
+    decrypt.fixup_range(f_begin, f_end);
+    decrypt.handle_data(encrypted, f_begin, f_end - f_begin + 1);
+    decrypt.flush();
+    const std::string& decrypted = get_sink.get_sink();
+    size_t expected_len = end - begin + 1;
+    ASSERT_EQ(decrypted.length(), expected_len);
+    ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len));
+  }
+}
+
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks_AES)
 {
   //create some input for encryption
   const off_t test_range = 1024*1024;
@@ -496,6 +833,60 @@ TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks)
   }
 }
 
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks_SM4)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  bufferptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  uint8_t key[16];
+  for(size_t i=0;i<sizeof(key);i++)
+    key[i] = i;
+
+  auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16);
+  ASSERT_NE(cbc.get(), nullptr);
+  bufferlist encrypted;
+  ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
+
+  for (off_t r = 93; r < 150; r++ )
+  {
+    ut_get_sink get_sink;
+    auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16);
+    ASSERT_NE(cbc.get(), nullptr);
+    RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) );
+
+    //random
+    off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
+    off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
+
+    off_t f_begin = begin;
+    off_t f_end = end;
+    decrypt.fixup_range(f_begin, f_end);
+    off_t pos = f_begin;
+    do
+    {
+      off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
+      size = (pos + 1117) * (pos + 2229) % size + 1;
+      if (pos + size > f_end + 1)
+        size = f_end + 1 - pos;
+
+      decrypt.handle_data(encrypted, pos, size);
+      pos = pos + size;
+    } while (pos < f_end + 1);
+    decrypt.flush();
+
+    const std::string& decrypted = get_sink.get_sink();
+    size_t expected_len = end - begin + 1;
+    ASSERT_EQ(decrypted.length(), expected_len);
+    ASSERT_EQ(decrypted, std::string_view(input.c_str()+begin, expected_len));
+  }
+}
 
 using range_t = std::pair<off_t, off_t>;
 
@@ -688,7 +1079,7 @@ TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup_invalid_ranges)
 
 }
 
-TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks)
+TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks_AES)
 {
   //create some input for encryption
   const off_t test_range = 1024*1024;
@@ -745,8 +1136,64 @@ TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks)
   }
 }
 
+TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks_SM4)
+{
+  //create some input for encryption
+  const off_t test_range = 1024*1024;
+  bufferptr buf(test_range);
+  char* p = buf.c_str();
+  for(size_t i = 0; i < buf.length(); i++)
+    p[i] = i + i*i + (i >> 2);
+
+  bufferlist input;
+  input.append(buf);
+
+  uint8_t key[16];
+  for(size_t i=0;i<sizeof(key);i++)
+    key[i] = i;
+
+  for (off_t r = 93; r < 150; r++ )
+  {
+    ut_put_sink put_sink;
+    auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16);
+    ASSERT_NE(cbc.get(), nullptr);
+    RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink,
+                                   std::move(cbc) );
+
+    off_t test_size = (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - 1) + 1;
+    off_t pos = 0;
+    do
+    {
+      off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
+      size = (pos + 1117) * (pos + 2229) % size + 1;
+      if (pos + size > test_size)
+        size = test_size - pos;
+
+      bufferlist bl;
+      bl.append(input.c_str()+pos, size);
+      encrypt.process(std::move(bl), pos);
+
+      pos = pos + size;
+    } while (pos < test_size);
+    encrypt.process({}, pos);
+
+    ASSERT_EQ(put_sink.get_sink().length(), static_cast<size_t>(test_size));
+
+    cbc = AES_256_CBC_create(g_ceph_context, &key[0], 16);
+    ASSERT_NE(cbc.get(), nullptr);
+
+    bufferlist encrypted;
+    bufferlist decrypted;
+    encrypted.append(put_sink.get_sink());
+    ASSERT_TRUE(cbc->decrypt(encrypted, 0, test_size, decrypted, 0));
+
+    ASSERT_EQ(decrypted.length(), test_size);
+    ASSERT_EQ(std::string_view(decrypted.c_str(), test_size),
+              std::string_view(input.c_str(), test_size));
+  }
+}
 
-TEST(TestRGWCrypto, verify_Encrypt_Decrypt)
+TEST(TestRGWCrypto, verify_Encrypt_Decrypt_AES)
 {
   uint8_t key[32];
   for(size_t i=0;i<sizeof(key);i++)
@@ -795,6 +1242,54 @@ TEST(TestRGWCrypto, verify_Encrypt_Decrypt)
   while (test_size < 20000);
 }
 
+TEST(TestRGWCrypto, verify_Encrypt_Decrypt_SM4)
+{
+  uint8_t key[16];
+  for(size_t i=0;i<sizeof(key);i++)
+    key[i]=i;
+
+  size_t fi_a = 0;
+  size_t fi_b = 1;
+  size_t test_size;
+  do
+  {
+    //fibonacci
+    size_t tmp = fi_b;
+    fi_b = fi_a + fi_b;
+    fi_a = tmp;
+
+    test_size = fi_b;
+
+    uint8_t* test_in = new uint8_t[test_size];
+    //fill with something
+    memset(test_in, test_size & 0xff, test_size);
+
+    ut_put_sink put_sink;
+    RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink,
+                                   AES_256_CBC_create(g_ceph_context, &key[0], 16) );
+    bufferlist bl;
+    bl.append((char*)test_in, test_size);
+    encrypt.process(std::move(bl), 0);
+    encrypt.process({}, test_size);
+    ASSERT_EQ(put_sink.get_sink().length(), test_size);
+
+    bl.append(put_sink.get_sink().data(), put_sink.get_sink().length());
+    ASSERT_EQ(bl.length(), test_size);
+
+    ut_get_sink get_sink;
+    RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink,
+                                   AES_256_CBC_create(g_ceph_context, &key[0], 16) );
+
+    off_t bl_ofs = 0;
+    off_t bl_end = test_size - 1;
+    decrypt.fixup_range(bl_ofs, bl_end);
+    decrypt.handle_data(bl, 0, bl.length());
+    decrypt.flush();
+    ASSERT_EQ(get_sink.get_sink().length(), test_size);
+    ASSERT_EQ(get_sink.get_sink(), std::string_view((char*)test_in,test_size));
+  }
+  while (test_size < 20000);
+}
 
 int main(int argc, char **argv) {
   vector<const char*> args;