概述
GreatDB(或mysql)服务端支持对表内数据进行加密存储,称之为表空间(table space)加密。使用方法和基本原理参考:
基本原理
(本节内容摘自上述参考资料,并经过了排版和校对)
从5.7.11开始,mysql开始支持物理表空间的加密,它使用两层加密架构。包括:主密钥(master key) 和 表空间密钥(tablespace key)。
主密钥用于加密表空间密钥,加密后的表空间密钥存储在表空间文件的header中。表空间密钥用于加密数据。当用户想访问加密的表时,innoDB会先用 主密钥 对之前存储在表空间header中被加密的表空间密钥进行解密,得到明文的表空间密钥,再用表空间密钥解密数据信息。
表空间密钥是不会改变的,而主密钥可以通过命令随时改变。
代码结构
表空间加密功能由innodb和keyring插件共同实现。innodb负责使用表空间密钥加/解密表内数据,keyring负责管理主密钥。
Encryption类
innodb中表空间加密功能主要由Encryption类控制,其声明和实现在如下2个文件中:
storage/innobase/include/os0enc.h
storage/innobase/os/os0enc.cc
表空间密钥的生成
void Encryption::set_or_generate(Type type, byte *key, byte *iv,
Encryption_metadata &metadata) {
ut_ad(type != Encryption::NONE);
metadata.m_type = type;
metadata.m_key_len = Encryption::KEY_LEN;
if (key == nullptr && iv == nullptr) {
Encryption::random_value(metadata.m_key);
Encryption::random_value(metadata.m_iv);
} else if (key != nullptr && iv != nullptr) {
memcpy(metadata.m_key, key, Encryption::KEY_LEN);
memcpy(metadata.m_iv, iv, Encryption::KEY_LEN);
} else {
ut_error;
}
}
表空间密钥先在Encryption::set_or_generate生成,存入一个Encryption_metadata结构,再通过Encryption::set_key设置。
加密
对表空间密钥进行加密:
/** Fill the encryption information.
@param[in] encryption_metadata encryption metadata (key,iv)
@param[in] encrypt_key encrypt with master key
@param[out] encrypt_info encryption information
@return true if success. */
static bool fill_encryption_info(
const Encryption_metadata &encryption_metadata, bool encrypt_key,
byte *encrypt_info) noexcept;
对表内数据进行加密:
/** Encrypt the redo log block.
@param[in] type IORequest
@param[in,out] src_ptr log block which need to encrypt
@param[in,out] dst_ptr destination area
@return true if success. */
bool encrypt_log_block(const IORequest &type, byte *src_ptr,
byte *dst_ptr) noexcept;
/** Encrypt the redo log data contents.
@param[in] type IORequest
@param[in,out] src page data which need to encrypt
@param[in] src_len size of the source in bytes
@param[in,out] dst destination area
@param[in,out] dst_len size of the destination in bytes
@return buffer data, dst_len will have the length of the data */
byte *encrypt_log(const IORequest &type, byte *src, ulint src_len, byte *dst,
ulint *dst_len) noexcept;
/** Encrypt the page data contents. Page type can't be
FIL_PAGE_ENCRYPTED, FIL_PAGE_COMPRESSED_AND_ENCRYPTED,
FIL_PAGE_ENCRYPTED_RTREE.
@param[in] type IORequest
@param[in,out] src page data which need to encrypt
@param[in] src_len size of the source in bytes
@param[in,out] dst destination area
@param[in,out] dst_len size of the destination in bytes
@return buffer data, dst_len will have the length of the data */
[[nodiscard]] byte *encrypt(const IORequest &type, byte *src, ulint src_len,
byte *dst, ulint *dst_len) noexcept;
解密
解密表空间密钥:
/** Decoding the encryption info from the given array of bytes,
which are assumed not to be related to any particular tablespace.
@param[out] encryption_metadata decoded encryption metadata
@param[in] encryption_info encryption info to decode
@param[in] decrypt_key decrypt key using master key
@return true if success */
static bool decode_encryption_info(Encryption_metadata &encryption_metadata,
const byte *encryption_info,
bool decrypt_key) noexcept;
/** Decoding the encryption info from the given array of bytes,
which are assumed to be related to a given tablespace (unless
space_id == dict_sys_t::s_invalid_space_id). The given tablespace
is noted down in s_tablespaces_to_reencrypt if the encryption info
became successfully decrypted using the master key and the space_id
is not dict_sys_t::s_invalid_space_id. For such tablespaces the
encryption info is later re-encrypted using the rotated master key
in innobase_dict_recover().
@param[in] space_id Tablespace id
@param[in,out] e_key key, iv
@param[in] encryption_info encryption info to decode
@param[in] decrypt_key decrypt key using master key
@return true if success */
static bool decode_encryption_info(space_id_t space_id, Encryption_key &e_key,
const byte *encryption_info,
bool decrypt_key) noexcept;
解密表内数据:
/** Decrypt the log block.
@param[in] type IORequest
@param[in,out] src data read from disk, decrypted data
will be copied to this page
@param[in,out] dst scratch area to use for decryption
@return DB_SUCCESS or error code */
dberr_t decrypt_log_block(const IORequest &type, byte *src,
byte *dst) noexcept;
/** Decrypt the log data contents.
@param[in] type IORequest
@param[in,out] src data read from disk, decrypted data
will be copied to this page
@param[in] src_len source data length
@param[in,out] dst scratch area to use for decryption
@return DB_SUCCESS or error code */
dberr_t decrypt_log(const IORequest &type, byte *src, ulint src_len,
byte *dst) noexcept;
/** Decrypt the page data contents. Page type must be
FIL_PAGE_ENCRYPTED, FIL_PAGE_COMPRESSED_AND_ENCRYPTED,
FIL_PAGE_ENCRYPTED_RTREE, if not then the source contents are
left unchanged and DB_SUCCESS is returned.
@param[in] type IORequest
@param[in,out] src data read from disk, decrypt
data will be copied to this page
@param[in] src_len source data length
@param[in,out] dst scratch area to use for decrypt
@param[in] dst_len size of the scratch area in bytes
@return DB_SUCCESS or error code */
[[nodiscard]] dberr_t decrypt(const IORequest &type, byte *src, ulint src_len,
byte *dst, ulint dst_len) noexcept;
使用国密算法
GreatDB目前支持国密算法(SM4等),想要表空间加密使用国密算法,需要在cmake阶段加入“-DWITH_SSL_GM=ON”,代码无需改动。编译完成后,无法再通过配置文件或命令换回普通AES。
keyring
未完待续。。。。。。