DM 系统中内置了常用的 DES,AES,RC4,SHA 等类型的加密和散列算法供用户使用,以此来保护数据的安全性。然而在有些特殊的环境下,这些算法可能不能满足用户的需求,用户可能希望使用自己特殊的或更高强度的加密和散列算法(例如:国密算法)。DM 的加密引擎功能则可以满足这样的需求。
用户只需要按照 DM 提供的加密引擎 C 语言编程接口,封装自己的加密和散列算法,并编译成第三方加密动态库,即可以在 DM 系统中使用自己的加密和散列算法。
注意用户使用自行封装的加密和散列算法前需确保该算法适用于当前应用场景。例如,用户使用自行封装的加密算法进行存储加密时,需要确保该算法适用于存储加密。
第三方加密动态库需要提供如下的对外接口,如下表所示。
接口名称 | 功能说明 |
---|---|
cipher_get_count | 获取实现的算法个数 |
cipher_get_info | 获取算法的基本信息,当实现了 cipher_get_info_ex 时,cipher_get_info 自动失效 |
cipher_get_info_ex | 获取算法的基本信息 |
cipher_get_para | 获取当前加密算法的当前参数的值 |
cipher_encrypt_init | 加密初始化 |
cipher_get_cipher_text_size | 计算给定长度的明文,加密后得到的密文所占的字节数。 |
cipher_encrypt | 加密函数 |
cipher_cleanup | 回收密码算法在处理过程中申请的系统资源 |
cipher_decrypt_init | 解密初始化 |
cipher_decrypt | 解密函数 |
cipher_hash_init | 散列过程的初始化工作 |
cipher_hash_update | 计算数据的散列值 |
cipher_hash_final | 返回散列值的实际长度 |
cipher_hash_mac_init | HMAC 算法散列过程的初始化工作 |
cipher_hash_mac_update | HMAC 算法计算数据的散列值 |
cipher_hash_mac_final | HMAC 算法返回散列值的实际长度 |
crypto_login | 登录设备 |
crypto_logout | 退出设备 |
crypto_read_cert | 读取用户证书 |
cipher_gen_random | 产生随机数 |
cipher_asym_sign | 用户私钥对数据进行签名 |
cipher_asym_verify | 用户公钥对数据进行验签 |
crypto_get_name | 获取加密引擎名字 |
crypto_get_type | 获取加密引擎类型。0:第三方软加密;1:第三方硬件加密;2:ukey。 |
crypto_encrypt | 进行服务器端硬件加密 |
crypto_decrypt | 进行服务器端硬件解密。与 crypto_encrypt 对应 |
cipher_get_key_id | 生成加密 key 的标识符 key_id |
cipher_free_key_id | 释放加密 key 的标识符 key_id |
8.1 编程接口介绍
8.1.1 算法信息相关接口
- cipher_get_count
ulint
cipher_get_count(
);
功能说明:
获取实现的算法个数。
返回值:
返回实现的算法个数。
- cipher_get_info
dm_bool
cipher_get_info(
ulint seqno,
ulint* cipher_id,
byte** cipher_name,
byte* type,
ulint* blk_size,
ulint* kh_size
);
参数说明:
seqno:输入参数,算法的序号,从 1 开始,小于等于算法的个数。
cipher_id:输出参数,算法的 ID,第三方加密算法的 ID 必须大于等于 5000。
cipher_name:输出参数,算法名。
type:输出参数,算法类型。算法类型取值见下表:
算法类型取值 | 释义 |
---|---|
0 或 CYT_TYPE_UNKNOWN | 未知类型 |
1 或 CYT_TYPE_SYM_BLOCK_ENCRYPT | 分组加密算法 |
2 或 CYT_TYPE_SYM_STREAM_ENCRYPT | 流加密算法 |
3 或 CYT_TYPE_ASYM_ENCRYPT | 非对称加密算法 |
4 或 CYT_TYPE_HASH | 散列算法 |
5 或 CYT_TYPE_HASH_MAC | HMAC 算法(HASH_MAC 算法) |
blk_size:输出参数,块大小。
kh_size:输出参数,密钥长度或 hash 长度。
功能说明:
获取算法的基本信息,有 cipher_get_info_ex 存在时,cipher_get_info 失效。
返回值:
TRUE/FALSE:获取成功/获取失败。
- cipher_get_info_ex
dm_bool
cipher_get_info_ex(
ulint seqno,
ulint* cipher_id,
byte** cipher_name,
byte* type,
ulint* blk_size,
ulint* kh_size,
byte* work_mode
);
参数说明:
seqno:输入参数,算法的序号,从 1 开始,小于等于算法的个数。
cipher_id:输出参数,算法的 ID,第三方加密算法的 ID 必须大于等于 5000。
cipher_name:输出参数,算法名。
type:输出参数,算法类型,具体见表 8.2。
blk_size:输出参数,块大小。
kh_size:输出参数,密钥长度或 hash 长度。
work_mode:输出参数,工作模式。work_mode 取值见下表:
模式名取值 | 释义 |
---|---|
0 或 WORK_MODE_ECB | ECB(Electronic CodeBook),电子密码本模式 |
2 或 WORK_MODE_CBC | CBC(Cipher-Block Chaining),密码块连接模式 |
4 或 WORK_MODE_CFB | CFB(Cipher FeedBack),密文反馈模式 |
8 或 WORK_MODE_OFB | OFB(Output FeedBack),输出反馈模式 |
16 或 WORK_MODE_CBC_NOPAD | NOPAD(NO PADDING),非填充模式 |
32 或 WORK_MODE_ECB_NOPAD | NOPAD(NO PADDING),非填充模式 |
64 或 WORK_MODE_EXTKEY | WORK_MODE_EXTKEY(EXTERNAL KEY),外部密钥模式,专用算法模式 |
67 或 WORK_MODE_KID | WORK_MODE_KID(KEY ID),密钥 ID 模式, 专用算法模式。备份还原不支持使用 WORK_MODE_KID 的加密算法,该种加密算法也不支持作为 external_cipher_name 来进行数据库初始化 |
WORK_MODE_KID 配置文件为 dmkid.ini。dmkid.ini 是使用 WORK_MODE_KID 加密算法的必要文件,文件位于 DM 安装目录 $DM_HOME/bin 目录下。dmkid.ini 详细参数如下表所示:
参数名 | 缺省值 | 说明 |
---|---|---|
InstanceURL | http://127.0.0.1:8214/kmip | 加密服务器 URL |
InstanceFormat | 1 | 格式:none = 1, json = 1, xml = 2 |
InstanceUser | kmipAdmin | 用户名 |
InstancePwd | 88888888 | 密码 |
InstanceCa | ca 证书 | 如果使用 https, 则在这里指定 ca 证书 |
RETRY_TIMES | 5 | 出现错误时,重试次数 |
RETRY_SLEEP | 1000 | 每次重试等待时间(ms) |
ENC_DEBUG | 0 | 是否生成日志 |
ENC_LOG | enc.log | 日志文件完整路径 |
CONFIG_LOG_FILE | 无 | 加密服务器生成日志完整路径 |
功能说明:
获取算法的基本信息,有 cipher_get_info_ex 存在时,cipher_get_info 失效。
返回值:
TRUE/FALSE:获取成功/获取失败。
4. cipher_get_para
dmbool cipher_get_para(
ulint cipher_id,
ulint para_id,
void* value
);
参数说明:
cipher_id:输入参数,算法的 ID,第三方加密算法的 ID 必须大于等于 5000。
para_id:输入参数,参数 ID,目前仅支持两个参数,0 表示 WORK_MODE,1 表示 EXTEND_SIZE。
value:输出参数,参数值。类型将根据 para_id 变化:para_id 为 0 时,value 为 unsigned char*;para_id 为 1 时,value 为 unsigned int*。用户须保证不会写越界。
功能说明:
获取当前加密算法的当前参数的值,目前支持获取 WORK_MODE 和 EXTEND_SIZE。WORK_MODE 表示加密算法工作模式;EXTEND_SIZE 表示加密算法最大扩展长度,即密文相对于明文一次加密最大扩展的长度,通常可以认为是一次加密(密文-明文)的最大值。
返回值:
TRUE/FALSE:获取成功/获取失败。
注意当获取失败时,将使用参数的默认值。WORK_MODE默认值为0;EXTEND_SIZE 默认值为通过cipher_get_info获得的kh_size。
当同时使用cipher_get_info_ex和cipher_get_para对work_mode进行正确赋值时,最终结果为cipher_get_para所设置的值。
8.1.2 加密过程相关接口
- cipher_encrypt_init
dm_bool
cipher_encrypt_init(
ulint inner_id,
byte* key,
ulint key_size,
void** encrypt_para
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
key:输入参数,加密密钥。
key_size:输入参数,密钥的字节数。
encrypt_para:输出参数,加密过程的全局参数。该参数为加密过程全程使用的参数,由用户负责分配内存空间,其中可以携带 key、key_size、用户自定义信息等。该参数需要在整个加密过程中保持有效,后续加密相关接口中的 encrypt_para 都是由该接口生成。
功能说明:
加密初始化。
返回值
TRUE/FALSE:初始化成功/初始化失败。
- cipher_get_cipher_text_size
lint
cipher_get_cipher_text_size(
ulint inner_id,
void* encrypt_para,
lint plain_text_size
);
参数说明:
inner_id:输入参数,密码算法在加密引擎中的内部编号。
encrypt_para:输入参数,cipher_encrypt_init 的输出参数。encrypt_para 是 cipher_encrypt_init 时产生的输出参数,如何利用其中信息由用户决定。
plain_text_size:输入参数,待加密的明文所占的字节数。
功能说明
计算给定长度的明文加密后得到的密文所占的字节数。DM 照返回值分配密文缓冲区。
返回值
与明文对应的密文所占的字节数。
- cipher_encrypt
lint
cipher_encrypt(
ulint inner_id,
void* encrypt_para,
byte* plain_text,
ulint plain_text_size,
byte* cipher_text,
ulint cipher_text_buf_size
);
参数说明:
inner_id:输入参数,密码算法在加密引擎中的内部编号。
encrypt_para:输入参数,密码算法的全局变量。encrypt_para 是 cipher_encrypt_init 时产生的输出参数,如何利用其中信息由用户决定。
plain_text:输入参数,明文。
plain_text_size:输入参数,明文所占的字节数。
cipher_text:输出参数,密文。
cipher_text_buf_size:输入参数,密文缓冲区的字节数。
功能说明
进行服务端硬件加密。通过设置初始化参数 EXTERNAL_CRYPTO_NAME 使用, 专用于服务器 server key 的硬件加密。由第三方动态加密库指定加密的算法和密钥 key,可以使用第三方设备(硬件加密卡、ukey)中的密钥 key。为了安全起见,设备中的密钥 key 可以不加载到内存中,加密的全程都在第三方设备内完成。
返回值
加密后密文的长度。
- cipher_cleanup
void
cipher_cleanup(
ulint inner_id,
void* encrypt_para
);
参数说明:
inner_id:输入参数,密码算法在加密引擎中的内部编号。
encrypt_para:输入参数,密码算法的全局参数。
功能说明
回收密码算法在处理过程中申请的系统资源。一般情况下,用户需要在这里释放在 cipher_encrypt_init 或 cipher_decrypt_init 中创建的 encrypt_para/decrypt_para。
返回值
无,假定该算法总是成功。
建议在使用一个密码算法进行加密之前应首先对该密码算法进行初始化处理,DM数据库管理系统根据所指定的密码算法准备明文,并为密文分配密文缓冲区。然后调用加密函数进行加密,完成加密动作后回收加密过程中申请的系统资源。
8.1.3 解密过程相关接口
- cipher_decrypt_init
dm_bool
cipher_decrypt_init(
ulint inner_id,
byte* key,
ulint key_size,
void** decrypt_para
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
key:输入参数,加密密钥。
key_size:输入参数,密钥的字节数。
decrypt_para:输出参数,解密过程的全局参数,由用户分配内存。
功能说明
解密初始化。用法与加密的 cipher_encrypt_init 类似。
返回值
TRUE/FALSE:初始化成功/初始化失败。
- cipher_decrypt
lint
cipher_decrypt(
ulint inner_id,
void* decrypt_para,
byte* cipher_text,
ulint cipher_text_size,
byte* plain_text,
ulint plain_text_buf_size
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
decrypt_para:输入参数,cipher_decrypt_init 的输出参数。该参数来自于 cipher_decrypt_init 的输出参数,如何利用其中数据由用户决定。
cipher_text:输入参数,密文。
cipher_text_size:输入参数,密文所占的字节数。
plain_text:输出参数,明文。
plain_text_buf_size:输入参数,明文缓冲区的长度。
功能说明
解密。用户需要保证根据提供的参数不会出现写越界等问题,明文长度不能超过所提供的空间。
返回值
明文的实际长度。
建议在使用一个密码算法进行解密之前应首先对该密码算法进行初始化处理,然后调用解密函数进行解密,完成解密动作后回收解密过程中申请的系统资源。
8.1.4 散列过程相关接口
- cipher_hash_init
dm_bool
cipher_hash_init(
ulint inner_id,
void** hash_para
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
hash_para:输出参数,散列过程的全局变量,由用户分配内存。
功能说明
散列过程初始化。
返回值
TRUE/FALSE:初始化成功/初始化失败。
- cipher_hash_update
void
cipher_hash_update(
ulint inner_id,
void* hash_para,
byte* msg,
ulint msg_size
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
hash_para:输入参数,散列过程中的全局参数。
msg:输入参数,待散列的数据。
msg_size:输入参数,数据长度。
功能说明
计算 msg 的散列值。
返回值
无。
- cipher_hash_final
lint
cipher_hash_final(
ulint inner_id,
void* hash_para,
byte* digest,
ulint digest_buf_size
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
hash_para:输入参数,散列过程中的全局参数。
digest:输出参数,散列值。
digest_buf_size:输入参数,散列缓冲区的大小。
功能说明
将整个散列过程得到的散列值存放到 digest 中。
返回值
散列值的实际长度。
- cipher_hash_mac_init
dm_bool
cipher_hash_mac_init(
ulint inner_id,
byte* key,
ulint key_len,
void** cipher_para
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
key:输入参数,密钥。
key_len:输入参数,密钥长度。
cipher_para:输出参数,散列过程的全局变量,由用户分配内存。
功能说明
HMAC 算法散列过程的初始化工作。
返回值
TRUE/FALSE:初始化成功/初始化失败。
- cipher_hash_mac_update
dm_bool
cipher_hash_mac_update(
ulint inner_id,
byte* msg,
ulint msg_len,
void* cipher_para
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
msg:输入参数,待散列的数据。
msg_len:输入参数,数据长度。
cipher_para:输入参数,散列过程中的全局参数。
功能说明
HMAC 算法计算 msg 的散列值。
返回值
TRUE/FALSE:计算成功/计算失败。
- cipher_hash_mac_final
lint
cipher_hash_mac_final(
ulint inner_id,
void* cipher_para,
byte* digest,
ulint digest_buf_size
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
cipher_para:输入参数,散列过程中的全局参数。
digest:输出参数,散列值。
digest_buf_size:输入参数,散列缓冲区的大小。
功能说明
HMAC 算法将整个散列过程得到的散列值存放到 digest 中。
返回值
散列值的实际长度。
8.1.5 其他可选相关接口
- cipher_asym_sign
dm_bool
cipher_asym_sign(
ulint inner_id,
byte* prikey,
ulint prikey_size,
byte* data,
ulint data_size,
byte* signdata,
ulint* signdata_buf_size
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
prikey:输入参数,私钥。
prikey_size:输入参数,私钥长度。
data:输入参数,需要进行签名的数据。
data_size:输入参数,数据的长度。
signdata:输出参数,数据的签名值。
signdata_buf_size:输出参数,签名值长度。
功能说明
用户私钥对数据进行签名。
返回值
TRUE/FALSE:签名成功/签名失败。
- cipher_asym_verify
dm_bool
cipher_asym_verify(
ulint inner_id,
byte* pubkey,
ulint pubkey_size,
byte* data,
ulint data_size,
byte* signdata,
ulint signdata_size
);
参数说明:
inner_id:输入参数,调用的密码算法在加密引擎中的内部编号。
pubkey:输入参数,公钥。
pubkey_size:输入参数,公钥长度。
data:输入参数,需要进行签名的数据。
data_size:输入参数,数据的长度。
signdata:输出参数,数据的签名值。
signdata_buf_size:输出参数,签名值长度。
功能说明
用户公钥对数据进行验签。
返回值
TRUE/FALSE:验签成功/验签失败。
- crypto_login
dm_bool
crypto_login(
void** cipher_para,
byte* pin,
ulint pin_len
);
参数说明:
cipher_para:输出参数,登录过程的全局参数,由用户负责内存分配。
pin:输入参数,设备口令。
pin_len:输入参数,口令长度。
功能说明
登录设备。
返回值
TRUE/FALSE:登录成功/登录失败。
- crypto_logout
dm_bool
crypto_logout(
void* cipher_para
);
参数说明:
cipher_para:输出参数,登录过程时使用的全局参数,此时需要释放。
功能说明
退出设备。
返回值
TRUE/FALSE:退出成功/退出失败。
- crypto_read_cert
dm_bool
crypto_read_cert(
void* cipher_para,
byte* cert,
ulint* cert_len
);
参数说明:
cipher_para:输出参数,登录过程时使用的全局参数。
cert:输出参数,用户证书信息。
cert_len:输出参数,用户证书长度。
功能说明
读取用户证书。
返回值
TRUE/FALSE:读取成功/读取失败。
- cipher_gen_random
dm_bool
cipher_gen_random(
byte* random,
ulint random_length
);
参数说明:
random:输出参数,生成的随机数。
random_length:输入参数,需要产生随机数的长度。
功能说明
产生随机数。
返回值
TRUE/FALSE:随机数生成成功/随机数生成失败。
- cipher_get_name
dm_bool
crypto_get_name(
byte** crypto_name,
ulint* len
);
参数说明:
crypto_name:输出参数,加密引擎的名字。
len:输出参数,加密引擎的名字的长度。
功能说明
获取加密引擎名字。
返回值
TRUE/FALSE:获取成功/获取失败。
- cipher_get_type
dm_bool
crypto_get_type(
ulint* crypto_type
);
参数说明:
crypto_type:输出参数,加密引擎的类型。
功能说明
获取加密引擎类型。
返回值
TRUE/FALSE:获取成功/获取失败。
- crypto_encrypt
lint
crypto_encrypt(
byte* plain_text,
ulint plain_text_size,
byte* cipher_text,
ulint cipher_text_buf_size
);
参数说明:
plain_text:输入参数,明文。
plain_text_size:输入参数,明文所占的字节数。
cipher_text:输出参数,密文。
cipher_text_buf_size:输入参数,密文缓冲区的字节数。
功能说明
加密。由第三方动态加密库指定加密的算法和密钥 key,可以使用第三方设备(硬件加密卡、ukey)中的密钥 key。为了安全起见,设备中的密钥 key 可以不加载到内存中,加密的全程都在第三方设备内完成。
返回值
加密后密文的长度。
- crypto_decrypt
lint
crypto_decrypt(
byte* cipher_text,
ulint cipher_text_size,
byte* plain_text,
ulint plain_text_buf_size
);
参数说明:
cipher_text:输入参数,密文。
cipher_text_buf_size:输入参数,密文所占的字节数。
plain_text:输出参数,明文。
plain_text_size:输入参数,明文缓冲区的字节数。
功能说明
进行服务端硬件解密。与 crypto_encrypt 对应。
返回值
明文的实际长度。
- cipher_get_key_id
lint
cipher_get_key_id(
ulint cipher_id,
byte* key_id,
ulint key_id_size,
ulint* key_size
);
参数说明:
cipher_id:输入参数,算法的 ID,第三方加密算法的 ID 必须大于等于 5000。
key_id:输出参数,key_id。
key_id_size:输入参数,key_id 预设的空间大小,防止出现空间越界问题。
key_size:输出参数,key_id 实际大小。
功能说明
生成加密 key 的标识符 key_id,需要对应加密算法工作模式为 WORK_MODE_KID。
返回值
是否生成成功。
- cipher_free_key_id
lint
cipher_free_key_id(
ulint cipher_id,
byte* key_id,
ulint key_id_size
);
参数说明:
cipher_id:输入参数,算法的 ID,第三方加密算法的 ID 必须大于等于 5000。
key_id:输入参数,key_id。
key_id_size:输入参数,key_id 预设的空间大小,防止出现空间越界问题。
功能说明
释放加密 key 的标识符 key_id,需要对应加密算法工作模式为 WORK_MODE_KID。
返回值
是否释放成功。
8.2 接口库文件使用说明
想要使用自己的加密算法的用户需要用 C 语言编写实现 8.1 节中介绍的接口(8.1.5 节中的接口可选),并编译生成第三方动态库文件,如:external_crypto.dll(LINUX 操作系统下为 libexternal_crypto.so)。
DM 支持同时加载多个第三方动态库文件。但这些 DLL 库文件必须放在 DM 数据库服务器所在 BIN 目录的 external_crypto_libs 子目录下。external_crypto_libs 目录需用户自己创建。如果是 DM 控制台工具,需将这些 DLL 库文件放在 CONSOLE 工具所在 TOOL 目录的 external_crypto_libs 子目录下。
为了保证加密算法有效性,所有第三方库文件中的加密方法名及 ID 不同。DM 提供一个动态视图 V$EXTERNAL_CIPHERS,用户可查看所有的第三方加密算法的 ID、名称、所在的库文件以及是否有效标记。
V$EXTERNAL_CIPHERS 的结构如下表所示。
序号 | 列 | 数据类型 | 说明 |
---|---|---|---|
1 | ID | INTEGER | 算法 ID |
2 | NAME | VARCHAR(128) | 算法名 |
3 | LIB | VARCHAR(300) | 算法所在的 lib 库文件名 |
4 | VALID | CHAR | 算法是否有效。‘Y’:是;‘N’:否 |
8.3 ukey 使用说明
8.3.1 使用介绍
DM 支持使用 UKEY 进行身份鉴别和加密。UKEY 用到的 UKEY 标准库(Linux 环境 libe2acsp11.so、Windows 环境 e2acsp11.dll)请用户自行从 UKEY 厂家获取,并放入达梦安装目录 bin 中。
ukey 认证操作说明如下:
- 编写动态库,分别作用于客户端和服务器。
使用 UKEY 进行身份鉴别时,应按照 8.1 节的介绍编写两个动态库,分别作用于客户端和服务器。
服务器端动态库需要实现:
- crypto_get_name 接口,用于设置加密引擎名, 服务器与客户端一致;
- crypto_get_type 接口,返回 1;
- cipher_asym_verify 接口,用于验签;
- cipher_gen_random 接口,用于生成随机数。
客户端动态库需要实现:
- crypto_get_name 接口,用于设置加密引擎名,服务器与客户端一致;
- crypto_get_type 接口,返回 2,表明为移动设备 ukey;
- crypto_read_cert 接口,用于从 ukey 中读取客户端证书(DER 编码),并发送给服务器;
- cipher_asym_sign 接口,用于签名;
- crypto_login 接口,用于登录 ukey;
- crypto_logout 接口,用于退出登录。
编写动态库时应注意:
- 客户端和服务器端的动态库 crypto_get_name()的返回值必须相同;
- 客户端动态库的名字*.dll/*.so 中的*必须与 crypto_get_name()返回值相同,否则 JDBC 驱动无法完成 UKEY 鉴别,DPI 接口无此要求;
- 客户端动态库的 crypto_get_type()应返回 2,表示移动设备 UKEY;服务器端动态库的 crypto_get_type()返回 1,表示软件或硬件加密卡。
编写动态库完成后, 分别将服务端动态库和客户端动态库放置于服务端和客户端的 external_crypto_libs 目录下。
- 生成认证所需证书或使用版本中自带的 SSL 证书文件。
所需文件为 ca-cert.pem 和 client.crt(DER 编码)。
ca-cert.pem 文件放置于服务器端DM_HOME/bin/server_ukey目录下, 若该目录下没有ca-cert.pem, 服务器将读取DM_HOME/bin/server_ssl 下的 ca_cert.pem 文件。
client.crt 文件需要存储在 ukey 中, 用户可以自行导入, 仅需保证通过客户端动态库接口 crypto_read_cert 可以获取 DER 编码的客户端证书即可。
- 尝试连接
完成以上操作之后,即可尝试连接。
以 disql 为例,假设 SYSDBA 用户的密码为 DMdba_123,加密引擎名为 test, ukey 登录 pin 为 password。
./disql "SYSDBA/DMdba_123@LOCALHOST#{UKEY_NAME=test,UKEY_PIN=password}"
8.3.2 示例
以渔翁 ukey、证书使用 SYSDBA 的通信加密证书为例。
我们已经提供了对应的服务端和客户端动态库, 并均已放置在对应目录。
ukey 导入交互式工具是 fm_gen,其专为渔翁 ukey 使用,在 $DM_HOME/bin/thirdparty/fisherman 目录下。目前仅有导入 DER 编码的客户端证书功能, 无参数。其可以初始化 ukey 文件系统, 创建目录及文件, 将客户端证书写入文件,如下所示:
open device succeed
input password:
12345678
login succeed
write cert(1)/exit(0):
1
Init File System ? 1/0
1
succeed
Create dir ? 1/0
1
succeed
Create File ? 1/0
1
succeed
Write File ? 1/0
1
input cert file path
/home/dm/bin/client_ssl/SYSDBA/client.crt
使用 fm_gen 将 SYSDBA 目录下的 client.crt 导入 ukey 中,若没有对应文件, 可以将 client-cert.pem 转换为 DER 编码。
最后启动服务器,通过 disql 进行连接。
假设 SYSDBA 用户的密码为 DMdba_123,加密引擎名为 dmukey_fm,默认 pin 为 12345678
./disql "SYSDBA/DMdba_123@LOCALHOST#{UKEY_NAME=dmukey_fm,UKEY_PIN=12345678}"
8.4 DM 提供的第三方加密和散列算法
DM 提供了第三方加密和第三方散列算法供用户使用。第三方算法来源分为两类:一 OPENSSL 软件库提供 ;二 渔翁硬件加密卡提供。
8.4.1 OPENSSL 软件库提供
OPENSSL 软件库提供了 OPENSSL_SM4 系列、OPENSSL_SM3 算法。OPENSSL_SM4 系列为分组加密算法,与 DM 内置的分组加密算法用途一致,可以用于通信加密、存储加密等场景。OPENSSL_SM3 为散列算法,与 DM 内置的散列算法用途一致,可以用于完整性校验。
OPENSSL 动态库提供了调用 OPENSSL 算法的接口。OPENSSL 动态库文件为位于 bin/external_crypto_libs 文件夹下的 openssl1.1.1_crypto.dll(Windows 操作系统)或 libopenssl_crypto.so(Linux 操作系统),此动态库依赖于 bin 文件夹下的 libeay32.dll(Windows 操作系统)或 libcrypto.so(Linux 操作系统)。
DM 提供的 OPENSSL_SM3,OPENSSL_SM4 系列算法是由 OPENSSL 软件库提供,DM 进行封装的,方便用户直接使用。因此,如果用户不想使用 DM 提供的 OPENSSL 算法,也可以直接使用 OPENSSL 软件库提供的算法。但用户须使用版本号大于 1.1.1 的 OPENSSL,因为只有大于 1.1.1 的版本才包含 OPENSSL_SM3,OPENSSL_SM4 算法。其中,OPENSSL_SM4_CFB_V1、OPENSSL_SM4_OFB_V1 算法分别是 OPENSSL_SM4_CFB、OPENSSL_SM4_OFB 算法的新版本,与老版本不兼容,因此当用户需要使用上述算法时,建议优先选择新版本。
算法名称 | 算法类型 | 分组长度(单位:字节) | 密钥长度(单位:字节) |
---|---|---|---|
OPENSSL_SM4_ECB | 分组加密算法 | 16 | 16 |
OPENSSL_SM4_CBC | 分组加密算法 | 16 | 16 |
OPENSSL_SM4_CFB | 分组加密算法 | 16 | 16 |
OPENSSL_SM4_OFB | 分组加密算法 | 16 | 16 |
OPENSSL_SM3 | 散列算法 | - | - |
OPENSSL_SM4_ECB_NOPAD | 分组加密算法 | 16 | 16 |
OPENSSL_SM4_CBC_NOPAD | 分组加密算法 | 16 | 16 |
OPENSSL_SM4_CFB_V1 | 分组加密算法 | 16 | 16 |
OPENSSL_SM4_OFB_V1 | 分组加密算法 | 16 | 16 |
OPENSSL_SM3、OPENSSL_SM4 系列算法可以通过动态视图 VCIPHERS查看。因为通过external_crypto_libs文件夹下的文件进行支持, 因此也可以通过查询动态视图Vexternal_ciphers 进行查询。
select * from V$external_ciphers;
行号 ID NAME LIB VALID
---------- ----------- --------------------- ----------------------- -----
1 5201 OPENSSL_SM4_ECB openssl1.1.1_crypto.dll Y
2 5202 OPENSSL_SM4_CBC openssl1.1.1_crypto.dll Y
3 5203 OPENSSL_SM4_CFB openssl1.1.1_crypto.dll Y
4 5204 OPENSSL_SM4_OFB openssl1.1.1_crypto.dll Y
5 5207 OPENSSL_SM3 openssl1.1.1_crypto.dll Y
6 5205 OPENSSL_SM4_ECB_NOPAD openssl1.1.1_crypto.dll Y
7 5206 OPENSSL_SM4_CBC_NOPAD openssl1.1.1_crypto.dll Y
8 5208 OPENSSL_SM4_CFB_V1 openssl1.1.1_crypto.dll Y
9 5209 OPENSSL_SM4_OFB_V1 openssl1.1.1_crypto.dll Y
8.4.2 渔翁硬件加密卡提供
渔翁硬件加密卡提供了 FM_SM1、FM_SM2、FM_SM3、FM_SM4、FM_SM6 算法。只有使用了渔翁加密卡的场景中才适用本节。FM_SM1 系列、 FM_SM4 系列、FM_SM6 系列为分组加密算法,与 DM 内置的分组加密算法用途一致,可以用于通信加密、存储加密等场景。FM_SM3、FM_SM2 为散列算法,与 DM 内置的散列算法用途一致,可以用于完整性校验。FM_SM3_HMAC 为 HMAC 算法,基于哈希的消息认证码算法,可以用于完整性校验, 使用方式与散列算法类似。
渔翁动态库提供了调用渔翁算法的接口。渔翁动态库文件为位于 bin/external_crypto_libs 文件夹下的 libenc_dll.so(linux)或 card_enc.dll(windows),此动态库依赖于 bin 文件夹下的 libfmapiv100.so(linux)或 fmapiv100.dll(windows)。
算法名称 | 算法类型 | 分组长度(单位:字节) | 密钥长度(单位:字节) |
---|---|---|---|
FM_SM1_ECB | 分组加密算法 | 16 | 16 |
FM_SM1_CBC | 分组加密算法 | 16 | 16 |
FM_SM4_ECB | 分组加密算法 | 16 | 16 |
FM_SM4_CBC | 分组加密算法 | 16 | 16 |
FM_SM6_ECB | 分组加密算法 | 16 | 16 |
FM_SM6_CBC | 分组加密算法 | 16 | 16 |
FM_SM1_ECB_NOPAD | 分组加密算法 | 16 | 16 |
FM_SM1_CBC_NOPAD | 分组加密算法 | 16 | 16 |
FM_SM4_ECB_NOPAD | 分组加密算法 | 16 | 16 |
FM_SM4_CBC_NOPAD | 分组加密算法 | 16 | 16 |
FM_SM6_ECB_NOPAD | 分组加密算法 | 16 | 16 |
FM_SM6_CBC_NOPAD | 分组加密算法 | 16 | 16 |
FM_SM3 | 散列算法 | - | - |
FM_SM2 | 散列算法 | - | - |
FM_SM3_HMAC | HMAC 算法 | - | - |
渔翁硬件加密卡算法可以通过动态视图 VCIPHERS或Vexternal_ciphers 查看。示例使用了渔翁硬件加密卡 5.0 版本。
SQL> select * from v$external_ciphers;
行号 ID NAME LIB VALID
---------- ----------- ---------------- ------------- -------------
1 5001 FM_SM1_ECB libenc_dll.so Y
2 5002 FM_SM1_CBC libenc_dll.so Y
3 5003 FM_SM4_ECB libenc_dll.so Y
4 5004 FM_SM4_CBC libenc_dll.so Y
5 5005 FM_SM6_ECB libenc_dll.so Y
6 5006 FM_SM6_CBC libenc_dll.so Y
7 5007 FM_SM3 libenc_dll.so Y
8 5008 FM_SM2 libenc_dll.so Y
9 5009 FM_SM1_ECB_NOPAD libenc_dll.so Y
10 5010 FM_SM1_CBC_NOPAD libenc_dll.so Y
11 5011 FM_SM4_ECB_NOPAD libenc_dll.so Y
12 5012 FM_SM4_CBC_NOPAD libenc_dll.so Y
13 5013 FM_SM6_ECB_NOPAD libenc_dll.so Y
14 5014 FM_SM6_CBC_NOPAD libenc_dll.so Y
15 5015 FM_SM3_HMAC libenc_dll.so Y
8.5 编程实例
8.5.1 加密引擎接口
本节用一个实例来说明 DM 加密引擎接口的编程方法。
例子中用户自定义加密算法为实现一个简单的异或加密算法和一个 HASH 算法。异或加密算法包括单字节的流加密(xor1、xor2)和 4 字节的块加密方式(xor3),HASH 算法为 MD5(名称为 new_md5)。
第一步,生成动态库。
使用 Microsoft Visual Studio 2008 创建新项目 mycrypto,将 xor.h 和 xor.c、hash.h 和 hash.c、crypto_engine.h 和 my_crypto.c 生成动态库 mycrypto.dll。其中 crypto_engine.h 被放在 DM 安装目录的 include 文件夹中。
涉及到的文件如下:
- xor.h
说明:异或加密算法函数的声明。
//xor.h :declare the xor cipher
#ifndef CRYPTO_ENGINE_H
typedef unsigned char byte;
#endif
int
xor_encrypt(
byte key,
byte* plain_text,
int plain_text_len,
byte* cipher_text,
int cipher_text_len);
int
xor_decrypt(
byte key,
byte* cipher_text,
int cipher_text_len,
byte* plain_text,
int plain_text_len);
int
xor_data_block_encrypt(
int key,
byte* in_text,
int in_text_len,
byte* out_text,
int out_text_len);
int
xor_data_block_decrypt(
int key,
byte* in_text,
int in_text_len,
byte* out_text,
int out_text_len);
- xor.c
说明:异或加密算法的实现。
// xor.c : implement the xor cipher.
#include <stdio.h>
#include <stdlib.h>
#include "xor.h"
int
xor_data(
byte key,
byte* in_text,
int in_text_len,
byte* out_text,
int out_text_len)
{
int i = 0;
for (i = 0; i < in_text_len; i++)
{
out_text[i] = in_text[i] ^ key;
}
return in_text_len;
}
int
xor_data_block_encrypt(
int key,
byte* in_text,
int in_text_len,
byte* out_text,
int out_text_len)
{
int i = 0;
int in_buf = 0;
int out_buf = 0;
int out_len = 0;
int pos = 0;
int block_len = sizeof(int);
byte* p = NULL;
byte* t = NULL;
int n = 0;
int m = 0;
int fill_num = 0;
n = in_text_len / block_len + 1;
m = in_text_len % block_len;
out_len = block_len * n;
p = (byte*)malloc(out_len);
memset(p, 0, out_len);
memcpy(p, in_text, in_text_len);
fill_num = out_len - in_text_len;
memset(p + in_text_len, fill_num, out_len - in_text_len);
for (i = 0; i < out_len; i++)
{
p[i] = p[i] ^ key;
}
memcpy(out_text, p, out_len);
free(p);
return out_len;
}
int
xor_data_block_decrypt(
int key,
byte* in_text,
int in_text_len,
byte* out_text,
int out_text_len)
{
int i = 0;
int in_buf = 0;
int out_buf = 0;
int out_len = 0;
int pos = 0;
int block_len = sizeof(int);
byte* p = NULL;
byte* t = NULL;
int n = 0;
int m = 0;
int fill_num = 0;
n = in_text_len / block_len;
m = in_text_len % block_len;
if (m != 0)
{
return -1;
}
p = (byte*)malloc(in_text_len);
memcpy(p, in_text, in_text_len);
for (i = 0; i < in_text_len; i++)
{
p[i] = p[i] ^ key;
}
fill_num = p[in_text_len - 1];
memcpy(out_text, p, in_text_len - fill_num);
free(p);
return in_text_len - fill_num;
}
int
xor_encrypt(
byte key,
byte* plain_text,
int plain_text_len,
byte* cipher_text,
int cipher_text_len)
{
return xor_data(key, plain_text, plain_text_len, cipher_text, cipher_text_len);
}
int
xor_decrypt(
byte key,
byte* cipher_text,
int cipher_text_len,
byte* plain_text,
int plain_text_len)
{
return xor_data(key, cipher_text, cipher_text_len, plain_text, plain_text_len);
}
- hash.h
说明:hash 算法函数的声明。
// hash.h
typedef struct {
unsigned int state[4];
unsigned int count[2];
unsigned char buffer[64];
} MD5Context;
void MD5_Init(MD5Context * context);
void MD5_Update(MD5Context * context, unsigned char * buf, int len);
void MD5_Final(MD5Context * context, unsigned char digest[16]);
- hash.c
说明:hash 算法实现。
#include <string.h>
#include "hash.h"
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
static unsigned char PADDING[64] =
{0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
#define FF(a, b, c, d, x, s, ac) \
{ \
(a) += F((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) \
{ \
(a) += G((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) \
{ \
(a) += H((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) \
{ \
(a) += I((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
static void MD5_Encode(unsigned char * output, unsigned int * input, int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < (unsigned int)len; i++, j += 4)
{
output[j] = (unsigned char) (input[i] & 0xff);
output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
}
}
static void MD5_Decode(unsigned int * output, unsigned char * input, int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < (unsigned int)len; i++, j += 4)
{
output[i] = ((unsigned int) input[j]) |
(((unsigned int) input[j + 1]) << 8) |
(((unsigned int) input[j + 2]) << 16) |
(((unsigned int) input[j + 3]) << 24);
}
}
static void MD5_Transform(unsigned int state[4], unsigned char block[64])
{
unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
MD5_Decode(x, block, 64);
/、 Round 1
FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
// Round 2
GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
// Round 3
HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
// Round 4
II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
memset((char *) x, 0, sizeof(x));
}
void MD5_Init(MD5Context * context)
{
context->count[0] = context->count[1] = 0;
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
void MD5_Update(MD5Context * context, unsigned char * buf, int len)
{
unsigned int i, index, partLen;
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
if ((context->count[0] += ((unsigned int) len << 3)) < ((unsigned int) len << 3))
context->count[1]++;
context->count[1] += ((unsigned int) len >> 29);
partLen = 64 - index;
if ((unsigned int)len >= partLen)
{
memcpy((char *) &context->buffer[index], (char *) buf, partLen);
MD5_Transform(context->state, context->buffer);
for (i = partLen; i + 63 < (unsigned int)len; i += 64)
MD5_Transform(context->state, &buf[i]);
index = 0;
}
else
{
i = 0;
}
memcpy((char *) &context->buffer[index], (char *) &buf[i], len - i);
}
void MD5_Final(MD5Context * context, unsigned char digest[16])
{
unsigned char bits[8];
unsigned int index, padLen;
MD5_Encode(bits, context->count, 8);
index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD5_Update(context, PADDING, padLen);
MD5_Update(context, bits, 8);
MD5_Encode(digest, context->state, 16);
memset((char *) context, 0, sizeof(*context));
}
- my_crypto.c
说明:封装供 DM 加载的函数接口。
//my_crypto.c
#include <stdlib.h>
#include "crypto_engine.h"
#include "xor.h"
#include "hash.h"
#define MIN_EXTERNAL_CIPHER_ID 5000
#define CYT_TYPE_UNKNOWN 0 //类型, 未知算法
#defineCYT_TYPE_SYM_BLOCK_ENCRYPT 1 //类型, 分组加密算法
#define CYT_TYPE_SYM_STREAM_ENCRYPT 2 //类型, 流加密算法
#define CYT_TYPE_ASYM_ENCRYPT 3 //类型, 非对称加密算法, 保留
#define CYT_TYPE_HASH 4 //类型, 散列算法
#define KEY1 ((byte)(0XAA))
#define KEY2 ((byte)(0x55))
#define KEY3 ((int)(0xCCCCCCCC))
DllExport
ulint
cipher_get_count(
)
{
return 4;
}
DllExport
dm_bool
cipher_get_info(
ulint seqno,
ulint* cipher_id,
byte** cipher_name,
byte* type,
ulint* blk_size,
ulint* kh_size
)
{
switch (seqno)
{
case 1:
*cipher_id = MIN_EXTERNAL_CIPHER_ID;
*cipher_name = "xor1";
*type = CYT_TYPE_SYM_STREAM_ENCRYPT;
*blk_size = 0;
*kh_size = sizeof(KEY1);
break;
case 2:
*cipher_id = MIN_EXTERNAL_CIPHER_ID + 1;
*cipher_name = "xor2";
*type = CYT_TYPE_SYM_STREAM_ENCRYPT;
*blk_size = 0;
*kh_size = sizeof(KEY1);
break;
case 3:
*cipher_id = MIN_EXTERNAL_CIPHER_ID + 2;
*cipher_name = "xor3";
*type = CYT_TYPE_SYM_BLOCK_ENCRYPT;
*blk_size = 4;
*kh_size = sizeof(KEY1);
break;
case 4:
*cipher_id = MIN_EXTERNAL_CIPHER_ID + 3;
*cipher_name = "new_md5";
*type = CYT_TYPE_HASH;
*blk_size = 0;
*kh_size = 16;
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
dm_bool
cipher_get_para(
ulint cipher_id,
ulint para_id,
void* value
){
switch (cipher_id)
{
case IN_EXTERNAL_CIPHER_ID:
switch (para_id)
{
case CYT_PARA_WORKMODE:
*(byte*)value = 0;
break;
case CYT_PARA_EXTENDSIZE:
*(int*)value = 0;
break;
default:
return DM_FALSE;
}
break;
case IN_EXTERNAL_CIPHER_ID+1:
switch (para_id)
{
case CYT_PARA_WORKMODE:
*(byte*)value = 0;
break;
case CYT_PARA_EXTENDSIZE:
*(int*)value = 0;
break;
default:
return DM_FALSE;
}
break;
case IN_EXTERNAL_CIPHER_ID+2:
switch (para_id)
{
case CYT_PARA_WORKMODE:
*(byte*)value = 0;
break;
case CYT_PARA_EXTENDSIZE:
*(int*)value = 4;
break;
default:
return DM_FALSE;
}
break;
case IN_EXTERNAL_CIPHER_ID+3:
switch (para_id)
{
case CYT_PARA_WORKMODE:
*(byte*)value = 0;
break;
case CYT_PARA_EXTENDSIZE:
*(int*)value = 0;
break;
default:
return DM_FALSE;
}
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
dm_bool
cipher_encrypt_init(
ulint inner_id,
byte* key,
ulint key_size,
void** encrypt_para
)
{
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID:
break;
case MIN_EXTERNAL_CIPHER_ID + 1:
break;
case MIN_EXTERNAL_CIPHER_ID + 2:
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
lint
cipher_get_cipher_text_size(
ulint inner_id,
void* cipher_para,
lint plain_text_size
)
{
lint len = 0;
lint block_len = sizeof(int);
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID:
len = plain_text_size;
break;
case MIN_EXTERNAL_CIPHER_ID + 1:
len = plain_text_size;
break;
case MIN_EXTERNAL_CIPHER_ID + 2:
len = plain_text_size + block_len;
break;
default:
return -1;
}
return len;
}
DllExport
lint
cipher_encrypt(
ulint inner_id,
void* cipher_para,
byte* plain_text,
ulint plain_text_size,
byte* cipher_text,
ulint cipher_text_buf_size
)
{
int len = plain_text_size;
if (plain_text_size > cipher_text_buf_size)
{
return -1;
}
switch(inner_id)
{
case MIN_EXTERNAL_CIPHER_ID:
len = xor_encrypt(KEY1, plain_text, plain_text_size, cipher_text,cipher_text_buf_size);
break;
case MIN_EXTERNAL_CIPHER_ID + 1:
len = xor_encrypt(KEY2, plain_text, plain_text_size, cipher_text, cipher_text_buf_size);
break;
case MIN_EXTERNAL_CIPHER_ID + 2:
len = xor_data_block_encrypt(KEY3, plain_text, plain_text_size, cipher_text, cipher_text_buf_size);
break;
default:
return -1;
}
return len;
}
DllExport
dm_bool
cipher_decrypt_init(
ulint inner_id,
byte* key,
ulint key_size,
void** decrypt_para
)
{
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID:
break;
case MIN_EXTERNAL_CIPHER_ID + 1:
break;
case MIN_EXTERNAL_CIPHER_ID + 2:
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
lint
cipher_decrypt(
ulint inner_id,
void* decrypt_para,
byte* cipher_text,
ulint cipher_text_size,
byte* plain_text,
ulint plain_text_buf_size
)
{
dm_bool bRet = DM_FALSE;
int len = cipher_text_size;
if (cipher_text_size > plain_text_buf_size)
{
return -1;
}
switch(inner_id)
{
case MIN_EXTERNAL_CIPHER_ID:
len = xor_decrypt(KEY1, cipher_text, cipher_text_size, plain_text, plain_text_buf_size);
break;
case MIN_EXTERNAL_CIPHER_ID + 1:
len = xor_decrypt(KEY2, cipher_text, cipher_text_size, plain_text, plain_text_buf_size);
break;
case MIN_EXTERNAL_CIPHER_ID + 2:
len = xor_data_block_decrypt(KEY3, cipher_text, cipher_text_size, plain_text, plain_text_buf_size);
break;
default:
return -1;
}
return len;
}
DllExport
dm_bool
cipher_hash_init(
ulint inner_id,
void** hash_para
)
{
MD5Context* md5_context = NULL;
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID + 3:
*hash_para = malloc(sizeof(MD5Context));
md5_context = (MD5Context*)(*hash_para);
MD5_Init(md5_context);
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
void
cipher_hash_update(
ulint inner_id,
void* hash_para,
byte* msg,
ulint msg_size
)
{
MD5Context* md5_context = NULL;
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID + 3:
md5_context = (MD5Context*)(hash_para);
MD5_Update(md5_context, msg, msg_size);
break;
default:
return;
}
return;
}
DllExport
lint
cipher_hash_final(
ulint inner_id,
void* hash_para,
byte* digest,
ulint digest_buf_size
)
{
lint len = 0;
MD5Context* md5_context = NULL;
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID + 3:
md5_context = (MD5Context*)(hash_para);
MD5_Final(md5_context, digest);
len = 16; // MD5后为一个bit的摘要
free(md5_context);
break;
default:
return -1;
}
return len;
}
DllExport
void
cipher_cleanup(
ulint inner_id,
void* cipher_para
)
{
switch (inner_id)
{
case MIN_EXTERNAL_CIPHER_ID:
break;
case MIN_EXTERNAL_CIPHER_ID + 1:
break;
case MIN_EXTERNAL_CIPHER_ID + 2:
break;
case MIN_EXTERNAL_CIPHER_ID + 3:
break;
default:
return;
}
return;
}
DllExport
dm_bool
cipher_asym_sign(
ulint inner_id,
byte* prikey, //私钥签名
ulint prikey_size,
byte* data,
ulint data_size,
byte* signdata,
ulint* signdata_buf_size
)
{
return DM_FALSE;
}
DllExport
dm_bool
cipher_asym_verify(
ulint inner_id,
byte* pubkey, //公钥验签
ulint pubkey_size,
byte* data,
ulint data_size,
byte* signdata,
ulint signdata_size
)
{
return DM_FALSE;
}
DllExport
dm_bool
crypto_login(
void** cipher_para,
byte* pin,
ulint pin_len
)
{
return DM_FALSE;
}
DllExport
dm_bool
crypto_logout(
void* cipher_para
)
{
return DM_FALSE;
}
DllExport
dm_bool
crypto_read_cert(
void* cipher_para,
byte* cert,
ulint * cert_len
)
{
return DM_FALSE;
}
DllExport
dm_bool
cipher_gen_random(
byte* random,
ulint random_length
)
{
return DM_FALSE;
}
DllExport
dm_bool
crypto_get_name(
byte** crypto_name,
ulint* len
)
{
*crypto_name = "mycrypto";
*len = 8;
return DM_TRUE;
}
DllExport
dm_bool
crypto_get_type(
ulint* crypto_type
)
{
*crypto_type = 0;
return DM_TRUE;
}
- Crypto_engine.h
说明:库接口说明文件。
#ifndef CRYPTO_ENGINE_H
#define CRYPTO_ENGINE_H
#ifndef dm_h
typedef int lint;
typedef short sint;
typedef signed char tint;
typedef unsigned int ulint;
typedef unsigned short usint;
typedef unsigned char byte;
#ifdef _WIN64
#define dm_int3264 __int64
#define dm_uint3264 unsigned __int64
#else
#define dm_int3264 long
#define dm_uint3264 unsigned long
#endif
#endif //#ifndef dm_h
#ifdef WIN32
#define DllImport __declspec( dllimport )
#define DllExport __declspec( dllexport )
#else
#define DllImport
#define DllExport
#endif
#define MIN_EXTERNAL_CIPHER_ID 5000
#define CYT_TYPE_UNKNOWN 0 //type, unknown arithmetic
#define CYT_TYPE_SYM_BLOCK_ENCRYPT 1 //type, symmetry encrypt arithmetic
#define CYT_TYPE_SYM_STREAM_ENCRYPT 2 //type, flow encrypt arithmetic
#define CYT_TYPE_ASYM_ENCRYPT 3 //type, unsymmetrical encrypt arithmetic, reserved
#define CYT_TYPE_HASH 4 //type, hash arithmetic
#ifdef __cplusplus
extern "C" {
#endif
#ifdef WIN32
#define dm_bool long
#else
#define dm_bool unsigned int
#endif
#define DM_FALSE 0
#define DM_TRUE 1
#define CYT_PARA_WORKMODE 0
#define CYT_PARA_EXTENDSIZE 1
DllExport
ulint
cipher_get_count(
);
DllExport
dm_bool
cipher_get_info(
ulint seqno,
ulint* cipher_id,
byte** cipher_name,
byte* type,
ulint* blk_size,
ulint* kh_size
);
DllExport
dm_bool
cipher_encrypt_init(
ulint inner_id,
byte* key,
ulint key_size,
void** encrypt_para
);
DllExport
lint
cipher_get_cipher_text_size(
ulint inner_id,
void* cipher_para,
lint plain_text_size
);
DllExport
lint
cipher_encrypt(
ulint inner_id,
void* encrypt_para,
byte* plain_text,
ulint plain_text_size,
byte* cipher_text,
ulint cipher_text_buf_size
);
DllExport
dm_bool
cipher_decrypt_init(
ulint inner_id,
byte* key,
ulint key_size,
void** decrypt_para
);
DllExport
lint
cipher_decrypt(
ulint inner_id,
void* decrypt_para,
byte* cipher_text,
ulint cipher_text_size,
byte* plain_text,
ulint plain_text_buf_size
);
DllExport
dm_bool
cipher_hash_init(
ulint inner_id,
void** hash_para
);
DllExport
void
cipher_hash_update(
ulint inner_id,
void* hash_para,
byte* msg,
ulint msg_size
);
DllExport
lint
cipher_hash_final(
ulint inner_id,
void* hash_para,
byte* digest,
ulint digest_buf_size
);
DllExport
void
cipher_cleanup(
ulint inner_id,
void* hash_para
);
#ifdef __cplusplus
}
#endif
#endif
第二步,在安装目录 bin 下创建一个 external_crypto_libs 文件夹。将第一步中生成的动态库 mycrypto.dll 放入该文件夹中。
第三步,重启服务器。
第四步,至此,可以使用自定义的算法了。也可以通过 V$EXTERNAL_CIPHERS 查看到。
SQL> select * from V$EXTERNAL_CIPHERS;
行号 ID NAME LIB VALID
---------- ----------- ------- --------- -----
1 5000 xor1 mycrypto.dll Y
2 5001 xor2 mycrypto.dll Y
3 5002 xor3 mycrypto.dll Y
4 5003 new_md5 mycrypto.dll Y
8.5.2 密码机引擎接口
密码机引擎和常规加密引擎的主要区别在于增加了 cipher_get_key_id 和 cipher_free_key_id 的使用,分别用于生成 key_id 和销毁 key_id。
密码机引擎的主要特点是密钥由密码机生成、销毁。因此当数据库需要生成密钥时, 将会调用加密引擎中的 cipher_get_key_id 接口来获取一个新密钥,后续的相关加解密均使用该密钥。当不再需要秘钥,例如和密钥相关的表被删除时,数据库会调用 cipher_free_key_id 接口进行密钥的销毁。
本例实现了可供密码机使用的,通过加密引擎来管理秘钥的两个加密算法 XASJ_SYMM 和 XASJ_ASYMM。 引擎向服务器提供密钥标识符。加解密时服务器提供该标识符, 引擎根据标识符决定使用哪一个密钥。
第一步,生成动态库。
使用 Microsoft Visual Studio 2008 创建新项目 cryptoengine,将头文件 crypto_engine.h 和源文件 crypto_engine.c 生成动态库 cryptoengine.dll。
在“配置属性—常规”中:输出目录设为 D:\xx\tt\;目标文件扩展名选择动态库(.dll);配置类型选择动态库(.dll);字符集选择使用多字节字符集。
如果是 Windows 系统,还需要选择“配置属性—C/C++—预处理器—预处理定义”中增加 WIN32。
涉及到的文件如下:
- 头文件 crypto_engine.h
crypto_engine.h 为系统自带文件,包含了引擎的接口定义和宏定义。请用户前往 DM 安装目录 include 文件夹中查找。
- 源文件 crypto_engine.c
实现加密算法 XASJ_SYMM 和 XASJ_ASYMM。
// crypto_engine.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "crypto_engine.h"
#ifdef WIN32
#include "windows.h"
#include "Wincrypt.h"
#else
#include <pthread.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#endif
#define DM_NULL 0
#define CRYPTO_NAME "DMSDB"
#define CIPHER_COUNT 1
#define RET_OK 0
#define ISEC_ERR_NONE 0X00000000
#define ISEC_ERR_FAIL 0X0A000001
#define CYT_NAME_XASJ_SYMM "XASJ_SYMM"
#define CYT_NAME_XASJ_ASYMM "XASJ_ASYMM"
#define EXTERNAL_CIPHER_ID 5100
//symm
#define CYT_ID_XASJ_SYMM (EXTERNAL_CIPHER_ID + 1)
//asymm
#define CYT_ID_XASJ_ASYMM (EXTERNAL_CIPHER_ID + 2)
#define WDIR_CHAR '\\'
#define WDIR_STR "\\"
#define UDIR_CHAR '/'
#define UDIR_STR "/"
#ifdef WIN32
#define DIR_CHAR WDIR_CHAR
#define DIR_STR WDIR_STR
#define DIR_STR_LEN 1
#else
#define DIR_CHAR UDIR_CHAR
#define DIR_STR UDIR_STR
#define DIR_STR_LEN 1
#endif
typedef char pathname_t[256];
typedef unsigned int ulint;
#define END 0 /* \0 */
#define QUOTATION_MARK 39 /* ' */
#define CYT_TYPE_UNKNOWN 0
#define CYT_TYPE_SYM_BLOCK 1
#define CYT_TYPE_SYM_STREAM 2
#define CYT_TYPE_ASYM_ENCRYPT 3
#define TOKEN_TYPE_ENCRYPT_CARD 1
#define TOKEN_TYPE_USB_KEY 2
#define ENC_LOG_FILE "enc.log"
#define CONFIG_LOG_FILE "/dev/stderr"
#define ENC_EXTENT_SIZE 0 //0 - plain_size == cipher_size.
#define ENC_EXTENT_SIZE_SYMM 0
#define ENC_EXTENT_SIZE_ASYMM 0
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
#ifdef WIN32
typedef struct __stat64 dmstat_t;
#define dm_stat(path, st) _stat64(path, st)
#else
typedef struct stat dmstat_t;
#define dm_stat(path, st) stat(path, st)
#endif
char* global_msg_buff;
int global_msg_size;
char str_time[100] = "";
char global_log_str[1024] = "";
int global_enc_debug = 0;
int global_sleep = 1000;
int global_retry_time = 5;
char global_enc_log[512] = {0};
char global_log_file[512] = {0};
DllExport
ulint
cipher_get_count()
{
return CIPHER_COUNT;
}
DllExport
dm_bool
cipher_get_info(
ulint seqno,
ulint* cipher_id,
byte** cipher_name,
byte* type,
ulint* blk_size,
ulint* kh_size
)
{
if (seqno > CIPHER_COUNT)
{
return DM_FALSE;
}
*cipher_id = seqno + EXTERNAL_CIPHER_ID;
switch(*cipher_id)
{
case CYT_ID_XASJ_SYMM:
*cipher_name = CYT_NAME_XASJ_SYMM;
*type = CYT_TYPE_SYM_BLOCK;
*blk_size = 16;
*kh_size = 0;
break;
case CYT_ID_XASJ_ASYMM:
*cipher_name = CYT_NAME_XASJ_ASYMM;
*type = CYT_TYPE_ASYM_ENCRYPT;
*blk_size = 16;
*kh_size = 0;
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
dm_bool
cipher_encrypt_init(
ulint cipher_id,
byte* key,
ulint key_size,
void** cipher_para
)
{
byte* para;
para = (byte*)malloc(key_size);
memcpy(para, key, key_size);
*cipher_para = para;
return DM_TRUE;
}
DllExport
lint
cipher_get_cipher_text_size(
ulint cipher_id,
void* cipher_para,
lint plain_text_size
)
{
lint len = 0;
switch (cipher_id)
{
case CYT_ID_XASJ_SYMM:
len = plain_text_size;
break;
case CYT_ID_XASJ_ASYMM:
len = plain_text_size;
break;
default:
return -1;
}
return len;
}
DllExport
lint
cipher_encrypt(
ulint cipher_id,
void* cipher_para,
byte* plain_text,
ulint plain_text_size,
byte* cipher_text,
ulint cipher_text_buf_size
)
{
ulint rv = 0;
ulint cipher_len;
char key[1024]="";
ulint key_len = 0;
int i;
if (plain_text_size == 0)
return 0;
switch(cipher_id)
{
case CYT_ID_XASJ_SYMM:
for (i = 0; i < plain_text_size; ++i)
cipher_text[i] = plain_text[i] + ((byte*)cipher_para)[0];
break;
case CYT_ID_XASJ_ASYMM:
for (i = 0; i < plain_text_size; ++i)
cipher_text[i] = plain_text[i] + ((byte*)cipher_para)[0];
break;
default:
return -1;
}
if (rv != 0)
{
return -1;
}
cipher_len = plain_text_size;
return cipher_len;
}
DllExport
dm_bool
cipher_decrypt_init(
ulint cipher_id,
byte* key,
ulint key_size,
void** cipher_para
)
{
return cipher_encrypt_init(cipher_id, key, key_size, cipher_para);
}
DllExport
lint
cipher_decrypt(
ulint cipher_id,
void* cipher_para,
byte* cipher_text,
ulint cipher_text_size,
byte* plain_text,
ulint plain_text_buf_size
)
{
ulint rv = 0;
ulint cipher_len;
char key[1024]="";
ulint key_len = 0;
int i;
switch(cipher_id)
{
case CYT_ID_XASJ_SYMM:
for (i = 0; i < cipher_text_size; ++i)
plain_text[i] = cipher_text[i] - ((byte*)cipher_para)[0];
break;
case CYT_ID_XASJ_ASYMM:
for (i = 0; i < cipher_text_size; ++i)
plain_text[i] = cipher_text[i] - ((byte*)cipher_para)[0];
break;
default:
return -1;
}
if (rv != 0)
{
return -1;
}
return cipher_text_size;
}
DllExport
dm_bool
crypto_login(
void** cipher_para,
byte* pin,
ulint pin_len
)
{
return DM_FALSE;
}
DllExport
dm_bool
crypto_logout(
void* cipher_para
)
{
return DM_FALSE;
}
DllExport
dm_bool
cipher_asym_gen_keypair(
byte* pubkey,
ulint* pubkey_size,
byte* prikey,
ulint* prikey_size
)
{
return DM_FALSE;
}
DllExport
void
cipher_cleanup(
ulint cipher_id,
void* cipher_para
)
{
ulint rc = 0;
return;
}
DllExport
dm_bool
crypto_get_name(
byte** crypto_name,
ulint* len
)
{
*crypto_name = CRYPTO_NAME;
*len = (ulint)strlen(CRYPTO_NAME);
return DM_TRUE;
}
DllExport
dm_bool
crypto_get_type(
ulint* crypto_type
)
{
*crypto_type = 1;
return DM_TRUE;
}
DllExport
lint
crypto_encrypt(
byte* plain_text,
ulint plain_text_size,
byte* cipher_text,
ulint cipher_text_buf_size
)
{
return -1;
}
DllExport
lint
crypto_decrypt(
byte* cipher_text,
ulint cipher_text_size,
byte* plain_text,
ulint plain_text_buf_size
)
{
return -1;
}
DllExport
dm_bool
cipher_get_para(
ulint cipher_id,
ulint para_id,
void* value
)
{
switch (para_id)
{
case CYT_PARA_WORKMODE:
*(byte*)value=WORK_MODE_KID;
break;
case CYT_PARA_EXTENDSIZE:
if(cipher_id == CYT_ID_XASJ_SYMM)
*(int*)value = 0;
else
*(int*)value = 0;
break;
default:
return DM_FALSE;
}
return DM_TRUE;
}
DllExport
dm_bool
cipher_hash_init(
ulint cipher_id,
void** cipher_para
)
{
return DM_FALSE;
}
DllExport
void
cipher_hash_update(
ulint cipher_id,
void* hash_para,
byte* msg,
ulint msg_size
)
{
return ;
}
DllExport
lint
cipher_hash_final(
ulint cipher_id,
void* hash_para,
byte* digest,
ulint digest_buf_size
)
{
return -1;
}
DllExport
dm_bool
cipher_get_key_id(
ulint cipher_id,
byte* key_id,
ulint key_id_size,
ulint* key_size
)
{
*key_size = 1;
key_id[0] = 1;
return DM_TRUE;
}
DllExport
dm_bool
cipher_free_key_id(
ulint cipher_id,
byte *key_id,
ulint key_id_size)
{
return DM_TRUE;
}
第二步,在安装目录 bin 下创建一个 external_crypto_libs 文件夹。将第一步中生成的动态库 D:\xx\tt\cryptoengine.dll 放入该文件夹中。
第三步,重启服务器。
第四步,至此,可以使用自定义加密引擎中的算法了。也可以通过 V$EXTERNAL_CIPHERS 查看到。
SQL> select * from V$EXTERNAL_CIPHERS;
行号 ID NAME LIB VALID
---------- ----------- --------------------- -------------------------- -----
1 5101 XASJ_SYMM cryptoengine.dll Y
2 5102 XASJ_ASYMM cryptoengine.dll Y