想象一下,您需要设计一个在线系统,用于按用户存储文档,其中任何人(包括数据库管理员)都不能在文档所有者不注意的情况下更改这些文档的内容。
在raybetapp ,我们添加了一个名为加密udf-一个开源的替代MySQL企业加密它允许用户访问一些低级的OpenSSL加密原语直接从MySQL。这包括计算摘要(使用各种哈希函数),非对称密钥生成(RSA,DSA),用于非对称加密/解密RSA,以及计算/验证数码签署(RSA,DSA)以及用于工作的原语diffie - hellman(DH)密钥交换算法。
先决条件
相比之下MySQL企业加密为了使这些功能可用,Percona Server for MySQL的用户不需要手动注册雷竞技下载官网
|
1
|
创建函数
…
SONAME
…
|
对于每个单独的函数。他们所要做的就是召唤
|
1
|
安装组件
“文件:/ / component_encryption_udf”
|
模式定义
现在,让我们定义一个简单的数据模式,我们将在其中存储在线文档存储服务所需的所有信息。
它有两个表:用户和文档。
|
1
2
3.
4
5
6
7
8
|
创建
表格
用户(
id
INT
无符号
非空
AUTO_INCREMENT,
登录
VARCHAR(128)
非空,
相关联
VARCHAR(16)
非空,
public_key_pem
文本
非空,
主键(id),
独特的(登录)
);
|
在这里,
id-唯一的用户标识符(数字,供内部使用)。
登录-唯一用户标识符(公共使用的词汇)。
相关联-用户生成的非对称密钥类型RSA或DSA).
public_key_pem- public组件的非对称密钥由用户在PEM格式(“–––––开始公钥–––––”).
|
1
2
3.
4
5
6
7
8
9
10
11
|
创建
表格
文档(
id
INT
无符号
非空
AUTO_INCREMENT,
user_ref
INT
无符号
非空,
名字
VARCHAR(128)
非空,
内容
团
非空,
digest_type
VARCHAR(16)
非空,
签名
VARBINARY(2048)
非空,
主键(id),
外键
(user_ref)
参考文献
用户(id),
独特的
(user_ref名称)
);
|
在这里,
id-唯一的文档标识符(数字,内部)。
user_ref—拥有该文档的用户ID。
名字类的文档存储的名称user_ref用户。
内容-保存文档内容的二进制对象。
digest_type-用于计算数字签名的哈希函数的名称内容。
签名-通过签名摘要值计算的数字签名digest_type上的哈希函数内容)的私钥user_ref。
用户注册
因此,注册新用户的第一步是生成一个非对称密钥对。虽然在本例中我们将使用RSA算法,我们的系统足够灵活,允许我们使用DSA还有钥匙。
重要注意:对于我们的用例,私钥必须始终在客户端机器上生成,并且永远不要离开本地安全存储。仅允许通过网络传输公钥。
生成RSA私钥,用户可以标准运行openssl实用程序。
|
1
|
openssl
genrsa
-出
private_key
.pem
4096
|
所生成的4096位的内容RSA私钥入PEM格式将被写入private_key.pem文件。
接下来,我们需要从生成的密钥中提取公共组件。
|
1
|
openssl
rsa
-在
private_key
.pem
-pubout
-出
public_key
.pem
|
提取的4096位RSA公开密码匙PEM格式将被写入public_key.pem文件。
|
1
|
集
@public_key=
' < public_key_pem_content >”;
|
在这里,< public_key_pem_content >内容是什么public_key.pem文件(公钥在PEM使用“–––––开始公钥–––––”头)。
为了简化这篇博客文章(再次强调,永远不要在生产中的实际系统中使用这种方法),RSA/DSA密钥可以由Percona Server生成:雷竞技下载官网
|
1
2
3.
|
集
@算法
=
RSA的;
集
@private_key=create_asymmetric_priv_key (@算法,4096);
/*永远不要在生产中这样做*/
集
@public_key=create_asymmetric_pub_key (@算法,@private_key);
/*永远不要在生产中这样做*/
|
现在,当我们有一个公钥时,用户注册就很简单了。
|
1
|
插入
成
用户
值(默认的,
“爱丽丝”,
RSA的,@public_key);
|
在生产中,@public_key必须设置为内容的public_key.pem本地生成(使用openssl例如,实用程序),而不是在服务器上生成。
上传文件
当用户想要上传一个新文件到我们的在线文档存储时,第一步将是计算其数字签名。
例如,如果我们要上传一个名为secure_data.doc我们想用,比如说,SHA256作为一个哈希函数,在使用先前生成的4096位签名之前计算摘要RSA私钥,执行如下
|
1
|
openssl
dgst
-sha256
-标志
private_key
.pem
-出
secure_data
.binsig
secure_data
. doc
|
签名在二进制格式将被写入secure_data.binsig。
为了简化将此文件的内容复制到SQL语句,让我们还将此签名转换为十六进制格式。我们会用到xxd实用程序来执行此操作(请注意,在某些Linux发行版上,此实用程序是vim-common包)。
|
1
|
xxd
-p
-u
-c0
secure_data
.binsig
secure_data
.hexsig
|
签名在十六进制格式将被写入secure_data.hexsig文件。之后,用户应该调用upload_document ()存储过程。
|
1
2
|
调用
upload_document (“爱丽丝”,
“secure_data.doc”,
<file_content>,
“SHA256”,
UNHEX(<file_signature_hex>),@upload_status);
|
在这里,
爱丽丝—文档所有者的名称。
secure_data.doc-存储文档的名称。
< file_content >-本地的内容secure_data.doc作为二进制数据传递。
SHA256-用于计算文件摘要的哈希函数的名称。
< file_signature_hex >-文件签名十六进制的内容secure_data.hexsig文件)。
在服务器上upload_document ()存储的例程应该执行以下操作。
首先,它需要找到@user_ref,@key_type,和@public_key_pem在用户所提供业主的登入资料(爱丽丝).
|
1
2
3.
4
|
选择
id,相关联,public_key_pem
成
user_id,user_key_type,user_public_key_pem
从
用户
在哪里
登录=用户_;
|
其次,它需要计算消息摘要@digest使用提供的哈希函数名(SHA256)查阅所提供的档案资料(file_content).
|
1
|
集
消化=create_digest (digest_typefile_content);
|
然后,服务器代码需要验证用户提供的文件签名(file_signature)使用与文件所有者相关联的公钥(爱丽丝).
|
1
2
3.
|
集
verification_result=asymmetric_verify (
user_key_type,消化,file_signature,
user_public_key_pem,digest_type);
|
在那之后,只有verification_result等于1时,我们确认文档所有者的身份,并将一条新记录插入文档表格
|
1
2
|
插入
成
文档
值(默认的,@user_ref,
“secure_data.doc”,
file_content,
“SHA256”,file_signature);
|
以下是如何upload_document ()可能看起来像
|
1
2
3.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29
30.
31
32
33
34
35
36
37
38
39
40
41
42
|
创建
过程
upload_document (
用户_
VARCHAR(128),file_name
VARCHAR(128),
file_content
团,digest_type
VARCHAR(16),
file_signature
VARBINARY(2048),
出
状态
INT)
L_return:
开始
声明
成功
INT
默认的
0;
声明
error_login_not_found
INT
默认的
1;
声明
error_verification_failed
INT
默认的
2;
声明
user_id
INT
无符号
默认的
0;
声明
user_key_type
VARCHAR(16)
默认的
零;
声明
user_public_key_pem
文本
默认的
零;
声明
verification_result
INT
默认的
0;
声明
消化
VARBINARY(64)
默认的
零;
选择
id,相关联,public_key_pem
成
user_id,user_key_type,user_public_key_pem
从
用户
在哪里
登录=用户_;
如果
user_id=0
然后
集
状态
=error_login_not_found;
离开
l_return;
如果;
集
消化=create_digest (digest_typefile_content);
集
verification_result=asymmetric_verify (
user_key_type,消化,file_signature,
user_public_key_pem,digest_type)
如果
verification_result=0
然后
集
状态
=error_verification_failed;
离开
l_return;
如果;
插入
成
文档
值(
默认的,user_id,file_name,file_content,
digest_type,file_signature);
集
状态
=成功;
结束
|
下载文件并验证其完整性
为了从我们的在线文档存储中下载文件,第一步将是获得其内容和数字签名元数据。
|
1
2
|
调用
download_document (“爱丽丝”,
“secure_data.doc”,@downloaded_content,
@downloaded_digest_type,@downloaded_signature,@download_status);
|
在这里,
爱丽丝—文档所有者的名称。
secure_data.doc-我们要下载的文件的名称。
@downloaded_content-下载文件的内容将放在这个变量中。
@downloaded_digest_type用于计算该文件的文件摘要的哈希函数的名称将放在此变量中。
@downloaded_signature-下载文件的数字签名将放在这个变量中。
在服务器上,download_document ()存储的例程应该执行以下操作。
首先,它需要找到@user_ref,@key_type,和@public_key_pem在用户所提供业主的登入资料(爱丽丝).
|
1
2
3.
4
|
选择
id,相关联,public_key_pem
成
user_id,user_key_type,user_public_key_pem
从
用户
在哪里
登录=用户_;
|
其次,需要获取文件内容和数字签名元数据。
|
1
2
3.
4
|
选择
id,内容,digest_type,签名
成
file_id,file_content,file_digest_type,file_signature
从
文档
在哪里
user_ref=user_id
和
名字=file_name;
|
之后,我们计算的摘要file_content使用file_digest_type哈希函数。
|
1
|
集
消化=create_digest (file_digest_typefile_content);
|
最后,我们验证文档的完整性:
|
1
2
3.
|
集
verification_result=asymmetric_verify (
user_key_type,消化,file_signature,
user_public_key_pem,file_digest_type);
|
只有在verification_result等于1,我们是否确认文件的完整性并返回成功状态。
以下是如何download_document ()可能看起来像
|
1
2
3.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29
30.
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
创建
过程
download_document (
用户_
VARCHAR(128),file_name
VARCHAR(128)
出
file_content
团,
出
file_digest_type
VARCHAR(16),
出
file_signature
VARBINARY(2048),
出
状态
INT)
L_return:
开始
声明
成功
INT
默认的
0;
声明
error_login_not_found
INT
默认的
1;
声明
error_file_not_found
INT
默认的
2;
声明
error_verification_failed
INT
默认的
3;
声明
user_id
INT
无符号
默认的
0;
声明
user_key_type
VARCHAR(16)
默认的
零;
声明
user_public_key_pem
文本
默认的
零;
声明
verification_result
INT
默认的
0;
声明
file_id
INT
无符号
默认的
0;
声明
消化
VARBINARY(64)
默认的
零;
选择
id,相关联,public_key_pem
成
user_id,user_key_type,user_public_key_pem
从
用户
在哪里
登录=用户_;
如果
user_id=0
然后
集
状态
=error_login_not_found;
离开
l_return;
如果;
选择
id,内容,digest_type,签名
成
file_id,file_content,file_digest_type,file_signature
从
文档
在哪里
user_ref=user_id
和
名字=file_name;
如果
file_id=0
然后
集
状态
=error_file_not_found;
离开
l_return;
如果;
集
消化=create_digest (file_digest_typefile_content);
集
verification_result=asymmetric_verify (
user_key_type,消化,file_signature,user_public_key_pem,file_digest_type);
如果
verification_result=0
然后
集
状态
=error_verification_failed;
离开
l_return;
如果;
集
状态
=成功;
结束
|
虽然我们加入了数字签名验证码download_document ()例程,它不能保证最终用户(调用者)download_document ()例程)将获得未修改的文档。添加此代码只是作为在早期阶段检测完整性违反的附加步骤。真正的数字签名验证必须在客户端执行,而不是在Percona Server内部执行。雷竞技下载官网
基本上,在调用之后download_document (),我们需要保存的内容@downloaded_content输出变量到本地文件(例如,downloaded.doc).此外,内容的@downloaded_signature在十六进制格式(十六进制(@downloaded_signature))也必须保存到本地文件中(例如,downloaded.hexsig).
之后,我们可以转换签名十六进制格式为二进制的形式。
|
1
|
xxd
-r
-p
-c0
下载
.hexsig
下载
.binsig
|
在二进制形式将被写入downloaded.binsig文件。现在,我们要做的就是验证数字签名:
|
1
|
openssldgst-sha256验证public_key.pem签名downloaded.binsigdownloaded.doc
|
只有当我们看到想要的核实好了状态行,我们可以确定我们刚刚下载的文档没有被修改。
结论
首先,我想强调的是,一个真正的可用于生产的在线文档存储系统比我们刚才描述的要复杂得多。可能是因为它没有使用OpenSSLcommand-line utility to perform cryptographic operations on the client-side. Moreover, it takes into consideration a number of other security aspects that are outside the scope of this blog post.
尽管如此,我仍然希望这里显示的数字签名的示例有助于说服您,不对称密码学不是火箭科学,并且在加密udf组件raybetapp 确实可以简单明了。请查看完整的文档。






