论电子签章(Electronic Signature)在C#中的实现方法
作者:代恩
工作单位:武汉英思工程科技有限公司
日期:
2005
-
12
-
05
关键字:
电子签章(Electronic Signature)、数字签章(Digital Signature)、加密签名、公钥私钥、非对称加密、哈希加密、C#、MD5
摘要:
电子签章(Electronic Signature)泛指所有以电子形式存在,依附在电子文件并与其逻辑相关,可用以辨识电子文件签署者身份,保证文件的完整性,并表示签署者同意电子文件所陈述事项的内容。包括数字签章技术和逐渐普及的用于身份验证的生物识别技术如指纹、面纹、DNA技术等。本文从本人参与OA系统项目中数字签章技术的设计来讲述如何使用微软公司的编程平台Vs.Net中的C#语言来实现数字签章技术。
正文:
1
、绪论
经过10年的高速发展,互联网已经慢慢地渗透到我们的日常生活中,从各个方面不经意地改变着人类的生活。电子商务越来越接近理想中的样子了;电子政务也正在一步步地走向我们……这一系列新兴网络行业发展的同时也带来了许多法律问题,如网络通讯的安全与隐私保护问题、知识产权问题、电子支付问题、合同问题、交易认证问题等等。
为解决这些问题,国家在2004年8月28日通过《中华人民共和国电子签名法》,此法令从法律意义上肯定了电子签章的作用,并明确了电子签章的用途及范围,为电子签章的进一步发展提供了法律上的保证。
所谓电子签章就是用于电子文件之上,与传统的手写签名、盖章具有完全相同功能的技术。有了电子签章,任何信息都可以放心地通过网络以电子文件的形式传输,因此,电子签章问题是电子商务和电子政务建设中必须首先解决的核心问题。
2
、引言
传统的交易行为和政府间的公文传送,必须要用书面的文件来完成,为了保证文件是某个当事人或者机关签发的,并且文件没有被篡改,还必须要有签发人的手写签字或者公章。
网络环境中,文件的传递采取电子的方式,比如E
-
mail。这种方式最大的优点就是速度快,文件可以在数秒中到达远在万里之外的客户手中。在“时间就是金钱、效率就是生命”的生意场上,这种优势无疑成为广大厂商的首选。
电子文件并非只用于合同的签订和政府文件的传递,而是适用于所有信息的传递。可以理想化地认为,未来的信息传递,可以不借助于任何纸张。
但如何保证一项文件或者一条信息是某个人发出的?某个人发出文件或者信息后,如果发现对自己不利抵赖怎么办?如果文件或者信息在网络传输过程中被他人截取,并被篡改了怎么办?许多人都知道,我们平时发送和接收的电子邮件是不加密的,对于某些人比如网管员来说,看这些文件甚至比看没有封上的书面信件更容易。
3
、 实例
某信息公司要求研发一套办公自动化系统,其中就包括了对电子公文进行电子签章的功能,本人有幸参与并设计了电子签章部份。
目前最成熟的电子签章技术就是“数字签章(Digital Signature)”,它是以公钥及私钥的“非对称型”密码技术制作的电子签章。使用原理大致为:由计算机程序将密钥和需传送的文件浓缩成信息摘要予以运算,得出数字签章,将数字签章并同原交易信息传送给对方,后者可用公钥来验证该信息确实由前者传送、并可查验文件在传送过程是否遭他人篡改,并防止对方抵赖。由于数字签章技术采用的是单向不可逆运算方式,要想对其破解,以目前的计算机速度以及技术而言,几乎是不可能的。文件传输可选择明文或密文进行传输。因此,从某种意义上讲,使用电子文件和数字签章,甚至比使用经过签字盖章的书面文件安全得多。
设计之初,我就先提出了需要解决的几个问题:
如何保证电子文件在传输过程中不被篡改?
如何保证电子文件合法性及不可抵赖?
一般来说要保证电子文件在传输过程中不被篡改,一是对电子文件进行私钥加密。在传输方使用私钥对电子文件进行明文加密,接收方再用私钥对传输过来的密文进行解密,以此来达到电子文件不被篡改。但此方法需要传输方与接收方都拥有私钥而且出现问题后双方都可能指责对方将私钥解密,因此单纯使用此方法,一般都只用于安全性要求不高的应用,不值得推荐。二是传输方对电子文件创建哈希,将电子文件与哈希一同发给接收方,接收方对接受到的电子文件也创建哈希,然后对比哈希值,以此来达到电子文件不被篡改。这是本人所选用的方案。下面是根据本人电子签章设计所写的一个DEMO片段,以供参考。
以下是对电子文件创建哈希的具本实现(使用了MD5加密):
1
/**/
///
<summary>
2
///
得到指定电子文件的哈希
3
///
</summary>
4
///
<param name="filePath">
电子文件地址
</param>
5
///
<returns>
哈希值
</returns>
6
public
static
byte
[] GetFileHash(
string
filePath)
7
{
8
try
9
{
10
FileStream objFile
=
File.OpenRead(filePath);
11
HashAlgorithm MD5
=
HashAlgorithm.Create(
"
MD5
"
);
12
byte
[] Hashbyte
=
MD5.ComputeHash(objFile);
13
objFile.Close();
14
return
Hashbyte;
15
}
16
catch
17
{
18
return
null
;
19
}
20
}
要实现合法性及不可抵赖就必需使用公钥私钥非对称加密方式并结合哈希加密方式。
公钥及私钥是一对很有意思的组合,它们是成对使用的,一一对应的,使用私钥加密的数据,可以使用公钥来证明是否是由此公钥所对应的私钥所加密的,将电子文件的哈希用私钥加密,接收人就算出电子文件的哈希,然后用公钥来验证电子文件是否是传输者所确认的文件。
生成公钥及私钥:
1
/**/
///
<summary>
2
///
得到公钥与私钥
3
///
</summary>
4
///
<param name="ContainerName">
私钥容器名
</param>
5
///
<param name="privatekey">
真为得到私钥,假为得到公钥
</param>
6
///
<returns>
公钥或私钥
</returns>
7
public
static
string
GetKeyFromContainer(
string
ContainerName,
bool
privatekey)
8
{
9
CspParameters cp
=
new
CspParameters();
10
cp.KeyContainerName
=
ContainerName;
11
RSACryptoServiceProvider rsa
=
new
RSACryptoServiceProvider(cp);
12
return
rsa.ToXmlString(privatekey);
13
}
对电子文件的哈希进行数字签名:
1
/**/
///
<summary>
2
///
对哈希进行数字签名
3
///
</summary>
4
///
<param name="privateKey">
私钥
</param>
5
///
<param name="fileHash">
电子文件哈希
</param>
6
///
<returns></returns>
7
public
static
byte
[] EncryptHash(
string
privateKey,
byte
[] fileHash)
8
{
9
RSACryptoServiceProvider RSA
=
new
RSACryptoServiceProvider();
10
11
RSA.FromXmlString(privateKey);
12
13
RSAPKCS1SignatureFormatter RSAFormatter
=
new
RSAPKCS1SignatureFormatter(RSA);
14
15
RSAFormatter.SetHashAlgorithm(
"
MD5
"
);
16
17
return
RSAFormatter.CreateSignature(fileHash);
18
}
19
对数字签名用公钥进行验证
1
/**/
///
<summary>
2
///
对数字签名用公钥进行验证
3
///
</summary>
4
///
<param name="publicKey">
公钥
</param>
5
///
<param name="fileHash">
接收到的电子文件的哈希
</param>
6
///
<param name="electronicSignature">
数字签名
</param>
7
///
<returns>
数字签名有效为真,数字签名无效为假
</returns>
8
public
static
bool
DecryptHash(
string
publicKey,
byte
[] fileHash,
byte
[] electronicSignature)
9
{
10
RSACryptoServiceProvider RSA
=
new
RSACryptoServiceProvider();
11
12
RSA.FromXmlString(publicKey);
13
14
RSAPKCS1SignatureDeformatter RSADeformatter
=
new
RSAPKCS1SignatureDeformatter(RSA);
15
16
RSADeformatter.SetHashAlgorithm(
"
MD5
"
);
17
18
return
RSADeformatter.VerifySignature(fileHash,electronicSignature);
19
}
主体程序
1
static
void
Main()
2
{
3
//
生成电子文件
4
string
filePath
=
"
C:\\公文.txt
"
;
5
StreamWriter sw
=
File.CreateText(filePath);
6
sw.Write(
"
测试公文
"
);
7
sw.Close();
8
9
//
对电子文件进行哈希
10
byte
[] fileHash
=
GetFileHash(filePath);
11
//
取得公钥
12
string
publicKey
=
GetKeyFromContainer(
"
公文
"
,
false
);
13
//
取得私钥
14
string
privateKey
=
GetKeyFromContainer(
"
公文
"
,
true
);
15
16
Console.WriteLine(
"
发送方:A
"
);
17
Console.WriteLine(
"
电子文件地址:
"
);
18
Console.WriteLine(filePath);
19
20
Console.WriteLine(
"
哈希:
"
);
21
Console.WriteLine(ConvertBytesToString(fileHash));
22
23
Console.WriteLine(
"
使用私钥进行数字签名:
"
);
24
//
取得电子签名
25
byte
[] ElectronicSignature
=
EncryptHash(privateKey,fileHash);
26
Console.WriteLine(ConvertBytesToString(ElectronicSignature));
27
28
Console.WriteLine(
"
传送给接收方。
"
);
29
Console.WriteLine(
""
);
30
string
fileCopyPath
=
"
C:\\公文接收.txt
"
;
31
File.Copy(filePath,fileCopyPath,
true
);
32
33
Console.WriteLine(
"
是否篡改文件?(Y/N)
"
);
34
string
sRe
=
string
.Empty;
35
do
36
{
37
sRe
=
Console.ReadLine();
38
}
39
while
(sRe
!=
"
Y
"
&&
sRe
!=
"
N
"
);
40
41
byte
[] fileCopyHash;
42
if
(sRe
==
"
N
"
)
43
{
44
Console.WriteLine(
"
接收方收到电子文件。
"
);
45
46
Console.WriteLine(
"
接收方:B
"
);
47
Console.WriteLine(
"
接收文件地址:
"
);
48
Console.WriteLine(fileCopyPath);
49
50
fileCopyHash
=
GetFileHash(fileCopyPath);
51
Console.WriteLine(
"
哈希:
"
);
52
Console.WriteLine(ConvertBytesToString(fileCopyHash));
53
}
54
else
55
{
56
Console.WriteLine(
"
正在篡改文件。
"
);
57
sw
=
new
StreamWriter(fileCopyPath);
58
sw.WriteLine(
"
公文已被篡改。
"
);
59
sw.Close();
60
61
Console.WriteLine(
"
接收方收到电子文件。
"
);
62
63
Console.WriteLine(
"
接收方:
"
);
64
Console.WriteLine(
"
接收文件地址:
"
);
65
Console.WriteLine(fileCopyPath);
66
67
fileCopyHash
=
GetFileHash(fileCopyPath);
68
Console.WriteLine(
"
哈希:
"
);
69
Console.WriteLine(ConvertBytesToString(fileCopyHash));
70
}
71
72
Console.WriteLine(
"
公钥:
"
);
73
Console.WriteLine(publicKey);
74
75
//
使用公钥进行验证
76
Console.WriteLine(
"
使用公钥进行验证:
"
);
77
if
(DecryptHash(publicKey,fileCopyHash,ElectronicSignature))
78
{
79
Console.WriteLine(
"
通过验证,电子文件合法有效。
"
);
80
}
81
else
82
{
83
Console.WriteLine(
"
未通过验证,电子文件非法或被人篡改过。
"
);
84
}
85
86
Console.Read();
87
}
4
、 问题的解决
由主体程序我们可以得出这样的结论,当对电子文件进行数字签名时,此时电子文档是得到了签名者的认可的,也证明了此时电子文档的合法性与不可抵赖。如果此电子文档在传输过程中被篡改后,则数字签名失效,并可证明被篡改的电子文档与进行数字签名时被认可的电子文档不一致。这样就保护了双方利益。
而此实现也楔合了《中华人民共和国电子签名法》第五条第二款,“能够可靠地保证自最终形成时起,内容保持完整、未被更改。但是,在数据电文上增加背书以及数据交换、储存和显示过程中发生的形式变化不影响数据电文的完整性。”
5
、 结论及展望
当然,根据《中华人民共和国电子签名法》第十三条,本例只完成了其中第三第四二条款,其第一第二二个条款还需完善。以下是第十三条:
(一)电子签名制作数据用于电子签名时,属于电子签名人专有;
(二)签署时电子签名制作数据仅由电子签名人控制;
(三)签署后对电子签名的任何改动能够被发现;
(四)签署后对数据电文内容和形式的任何改动能够被发现。
参考文献:
。。。。。。
本来是不想使用论文这种形式来写这篇,但为了锻炼自己写论文的能力,还是斗胆用论文写出来了,如有何不确之处,望勉之。
完整代码如下:
using
System;
using
System.IO;
using
System.Security.Cryptography;
namespace
oozinoz1
{
/**/
///
<summary>
///
Class5 的摘要说明。
///
</summary>
public
class
Class5
{
static
void
Main()
{
//
生成电子文件
string
filePath
=
"
C:\\公文.txt
"
;
StreamWriter sw
=
File.CreateText(filePath);
sw.Write(
"
测试公文
"
);
sw.Close();
//
对电子文件进行哈希
byte
[] fileHash
=
GetFileHash(filePath);
//
取得公钥
string
publicKey
=
GetKeyFromContainer(
"
公文
"
,
false
);
//
取得私钥
string
privateKey
=
GetKeyFromContainer(
"
公文
"
,
true
);
Console.WriteLine(
"
发送方:A
"
);
Console.WriteLine(
"
电子文件地址:
"
);
Console.WriteLine(filePath);
Console.WriteLine(
"
哈希:
"
);
Console.WriteLine(ConvertBytesToString(fileHash));
Console.WriteLine(
"
使用私钥进行数字签名:
"
);
//
取得电子签名
byte
[] ElectronicSignature
=
EncryptHash(privateKey,fileHash);
Console.WriteLine(ConvertBytesToString(ElectronicSignature));
Console.WriteLine(
"
传送给接收方。
"
);
Console.WriteLine(
""
);
string
fileCopyPath
=
"
C:\\公文接收.txt
"
;
File.Copy(filePath,fileCopyPath,
true
);
Console.WriteLine(
"
是否篡改文件?(Y/N)
"
);
string
sRe
=
string
.Empty;
do
{
sRe
=
Console.ReadLine();
}
while
(sRe
!=
"
Y
"
&&
sRe
!=
"
N
"
);
byte
[] fileCopyHash;
if
(sRe
==
"
N
"
)
{
Console.WriteLine(
"
接收方收到电子文件。
"
);
Console.WriteLine(
"
接收方:B
"
);
Console.WriteLine(
"
接收文件地址:
"
);
Console.WriteLine(fileCopyPath);
fileCopyHash
=
GetFileHash(fileCopyPath);
Console.WriteLine(
"
哈希:
"
);
Console.WriteLine(ConvertBytesToString(fileCopyHash));
}
else
{
Console.WriteLine(
"
正在篡改文件。
"
);
sw
=
new
StreamWriter(fileCopyPath);
sw.WriteLine(
"
公文已被篡改。
"
);
sw.Close();
Console.WriteLine(
"
接收方收到电子文件。
"
);
Console.WriteLine(
"
接收方:
"
);
Console.WriteLine(
"
接收文件地址:
"
);
Console.WriteLine(fileCopyPath);
fileCopyHash
=
GetFileHash(fileCopyPath);
Console.WriteLine(
"
哈希:
"
);
Console.WriteLine(ConvertBytesToString(fileCopyHash));
}
Console.WriteLine(
"
公钥:
"
);
Console.WriteLine(publicKey);
//
使用公钥进行验证
Console.WriteLine(
"
使用公钥进行验证:
"
);
if
(DecryptHash(publicKey,fileCopyHash,ElectronicSignature))
{
Console.WriteLine(
"
通过验证,电子文件合法有效。
"
);
}
else
{
Console.WriteLine(
"
未通过验证,电子文件非法或被人篡改过。
"
);
}
Console.Read();
}
/**/
///
<summary>
///
将Byte[]转换成十六进制字符串
///
</summary>
///
<param name="bytes">
要转换的Byte[]
</param>
///
<returns>
十六进制字符串
</returns>
public
static
string
ConvertBytesToString(
byte
[] bytes)
{
string
bytestring
=
string
.Empty;
if
(bytes
!=
null
&&
bytes.Length
>
0
)
{
for
(
int
i
=
0
;i
<
bytes.Length;i
++
)
{
bytestring
+=
bytes[i].ToString(
"
X
"
)
+
"
"
;
}
}
return
bytestring;
}
/**/
///
<summary>
///
得到指定电子文件的哈希
///
</summary>
///
<param name="filePath">
电子文件地址
</param>
///
<returns>
哈希值
</returns>
public
static
byte
[] GetFileHash(
string
filePath)
{
try
{
FileStream objFile
=
File.OpenRead(filePath);
HashAlgorithm MD5
=
HashAlgorithm.Create(
"
MD5
"
);
byte
[] Hashbyte
=
MD5.ComputeHash(objFile);
objFile.Close();
return
Hashbyte;
}
catch
{
return
null
;
}
}
/**/
///
<summary>
///
得到公钥与私钥
///
</summary>
///
<param name="ContainerName">
私钥容器名
</param>
///
<param name="privatekey">
真为得到私钥,假为得到公钥
</param>
///
<returns>
公钥或私钥
</returns>
public
static
string
GetKeyFromContainer(
string
ContainerName,
bool
privatekey)
{
CspParameters cp
=
new
CspParameters();
cp.KeyContainerName
=
ContainerName;
RSACryptoServiceProvider rsa
=
new
RSACryptoServiceProvider(cp);
return
rsa.ToXmlString(privatekey);
}
/**/
///
<summary>
///
对哈希进行数字签名
///
</summary>
///
<param name="privateKey">
私钥
</param>
///
<param name="fileHash">
电子文件哈希
</param>
///
<returns></returns>
public
static
byte
[] EncryptHash(
string
privateKey,
byte
[] fileHash)
{
RSACryptoServiceProvider RSA
=
new
RSACryptoServiceProvider();
RSA.FromXmlString(privateKey);
RSAPKCS1SignatureFormatter RSAFormatter
=
new
RSAPKCS1SignatureFormatter(RSA);
RSAFormatter.SetHashAlgorithm(
"
MD5
"
);
return
RSAFormatter.CreateSignature(fileHash);
}
/**/
///
<summary>
///
对数字签名用公钥进行验证
///
</summary>
///
<param name="publicKey">
公钥
</param>
///
<param name="fileHash">
接收到的电子文件的哈希
</param>
///
<param name="electronicSignature">
数字签名
</param>
///
<returns>
数字签名有效为真,数字签名无效为假
</returns>
public
static
bool
DecryptHash(
string
publicKey,
byte
[] fileHash,
byte
[] electronicSignature)
{
RSACryptoServiceProvider RSA
=
new
RSACryptoServiceProvider();
RSA.FromXmlString(publicKey);
RSAPKCS1SignatureDeformatter RSADeformatter
=
new
RSAPKCS1SignatureDeformatter(RSA);
RSADeformatter.SetHashAlgorithm(
"
MD5
"
);
return
RSADeformatter.VerifySignature(fileHash,electronicSignature);
}
}
}