posts - 8, comments - 77, trackbacks - 0, articles - 11

本人目前在做一个关于“数字签名”的项目,在网上查资料,感觉这方面的都非常的少,一般都是讲RSA的算法,或者“数字签名”的原理,完全是浪费时间。我经过摸索,小有进展,初步实现功能。今天写出来,希望能给以后的做这个的朋友提供个方便。还有一件事,在我查资料的时候也遇见很多朋友在做这个项目,但是我感觉他们都在进入了几个误区。我先把这几个误区说说,以免大家以后走错方向。说得不对的地方希望大家能批评指正,毕竟本人能力有限(但是不要骂我) ^_^

误区一:大家对公钥私钥区分得太.......(我还找不到形容来形容了....),就太死板了吧。其实当你把公钥保密不公开的时候,公钥就是私钥了;当你把私钥公开的时候,私钥也是公钥了。没必要记得这么死的(个人意见)。
误区二:对RSA算法的原理理解不够,实际上所谓的公钥和私钥只是RSA算法(说穿了RSA就是个数学方程式)的参数(未知数),比如 X+Y+M=Z,X就可以说是私钥,Y就可以说是公钥,M就是需要加密的内容,Z就是加密后的密文,当然RSA中不可能只有X和Y两个未知数的,所以就经常有朋友问,到底X是私钥还是Y是私钥(嘻嘻... 我也问过)。其实这个就要取决于你用在时候,什么地方了。
误区三:对Security.Cryptography命名空间不熟悉。“数字签名”一般的做法是:A先计算出文件M的HASH码,再对HASH码进行加密(这个步骤就是签名),再把M(文件M不要加密,第三方可以查阅)和加密后的HASH码传送给B,B再用A的公钥来解密刚才得到的加密HASH码,如果能解密,那就说明这个文件是A发的,具有法律效应。再计算出得到的文件M的HASH码,再和刚才解密出来的HASH码比较(这个步骤叫验证签名),如果一致就说明文件M在传输过程中没有被修改。但是在C#中需要解密RSA,就必须提供公钥和私钥,当然这和我们的现实不符,因为A不可能把他的私钥给B。许多人就是在这里难住了。其实在C#的Security.Cryptography命名空间中有RSAPKCS1SignatureFormatter 和RSAPKCS1SignatureDeformatter 两个方法或者说是对象。前者用来对HASH进行加密(签名),后者用来验证签名,用这个验证就只需要A的公钥就行了。在C#中,签名就是这个两个专用的方法,不是用RSA普通的加密解密。

下面我就贴出我的代码,我的公钥和私钥就是从两个文件PublicKey.xml、PrivateKey.xml中读取出来,相当于是现实中的指定公钥和私钥;再验证时我为了方便直接验证的A计算出的HASH码,现实中应该是由B重新计算出文件M的HASH码(这个大家明白就行了^_^)。

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Security.Cryptography;
using System.Xml;

namespace StandSoftRSA
{
 /// <summary>
 /// Form1 的摘要说明。
 /// </summary>
 public class Form1 : System.Windows.Forms.Form
 {
  private System.Windows.Forms.RichTextBox richTextBox1;
  private System.Windows.Forms.Button button1;
  private System.Windows.Forms.Button button2;
  private System.Windows.Forms.RichTextBox richTextBox2;
  private System.Windows.Forms.Button button3;
  private RSACryptoServiceProvider RSA;
  private byte[] Hashbyte;
  private byte[] EncryptedData;
  private RSAParameters PrivateKey;
  private RSAParameters PublicKey;
  private XmlDocument objXml;
  private XmlNodeList objXmlNode;
  
  /// <summary>
  /// 必需的设计器变量。
  /// </summary>
  private System.ComponentModel.Container components = null;

  public Form1()
  {
   //
   // Windows 窗体设计器支持所必需的
   //
   InitializeComponent();

   //
   // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
   //
  }

  /// <summary>
  /// 清理所有正在使用的资源。
  /// </summary>
  protected override void Dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null)
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
  }

  #region Windows 窗体设计器生成的代码
  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  /// 此方法的内容。
  /// </summary>
  private void InitializeComponent()
  {
   this.richTextBox1 = new System.Windows.Forms.RichTextBox();
   this.button1 = new System.Windows.Forms.Button();
   this.button2 = new System.Windows.Forms.Button();
   this.richTextBox2 = new System.Windows.Forms.RichTextBox();
   this.button3 = new System.Windows.Forms.Button();
   this.SuspendLayout();
   //
   // richTextBox1
   //
   this.richTextBox1.BackColor = System.Drawing.Color.Black;
   this.richTextBox1.ForeColor = System.Drawing.Color.LawnGreen;
   this.richTextBox1.Location = new System.Drawing.Point(8, 8);
   this.richTextBox1.Name = "richTextBox1";
   this.richTextBox1.Size = new System.Drawing.Size(384, 80);
   this.richTextBox1.TabIndex = 0;
   this.richTextBox1.Text = "";
   //
   // button1
   //
   this.button1.Location = new System.Drawing.Point(168, 104);
   this.button1.Name = "button1";
   this.button1.Size = new System.Drawing.Size(56, 24);
   this.button1.TabIndex = 1;
   this.button1.Text = "哈稀码";
   this.button1.Click += new System.EventHandler(this.button1_Click);
   //
   // button2
   //
   this.button2.Location = new System.Drawing.Point(8, 184);
   this.button2.Name = "button2";
   this.button2.Size = new System.Drawing.Size(384, 24);
   this.button2.TabIndex = 3;
   this.button2.Text = "加密";
   this.button2.Click += new System.EventHandler(this.button2_Click);
   //
   // richTextBox2
   //
   this.richTextBox2.BackColor = System.Drawing.Color.Black;
   this.richTextBox2.ForeColor = System.Drawing.Color.LawnGreen;
   this.richTextBox2.Location = new System.Drawing.Point(8, 216);
   this.richTextBox2.Name = "richTextBox2";
   this.richTextBox2.Size = new System.Drawing.Size(384, 80);
   this.richTextBox2.TabIndex = 4;
   this.richTextBox2.Text = "";
   //
   // button3
   //
   this.button3.Location = new System.Drawing.Point(8, 304);
   this.button3.Name = "button3";
   this.button3.Size = new System.Drawing.Size(384, 24);
   this.button3.TabIndex = 5;
   this.button3.Text = "解密";
   this.button3.Click += new System.EventHandler(this.button3_Click);
   //
   // Form1
   //
   this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
   this.ClientSize = new System.Drawing.Size(400, 421);
   this.Controls.Add(this.button3);
   this.Controls.Add(this.richTextBox2);
   this.Controls.Add(this.button2);
   this.Controls.Add(this.button1);
   this.Controls.Add(this.richTextBox1);
   this.Name = "Form1";
   this.Text = "RSA&MD5";
   this.ResumeLayout(false);

  }
  #endregion

  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main()
  {
   Application.Run(new Form1());
  }

  //获得文件的哈稀码
  private void GetFileHash()
  {
   FileStream objFile = File.OpenRead("Hash.doc");
   HashAlgorithm MD5 = HashAlgorithm.Create("MD5");
   Hashbyte = MD5.ComputeHash(objFile);
   objFile.Close();
   foreach(byte b in Hashbyte)
   {
    richTextBox1.AppendText(b.ToString()+" ");
   }
  }
    
  private void button1_Click(object sender, System.EventArgs e)
  {
   GetFileHash();
  }

  //设置公钥和私钥对(都包含)
  public void SetPrivateKey()
  {
   PrivateKey = new RSAParameters();
   objXml = new XmlDocument();
   objXml.Load("PublicKey.xml");
   //指定RSA参数Exponent的值(公钥)
   objXmlNode = objXml.SelectNodes("//Exponent");
   PrivateKey.Exponent = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());   
   //指定RSA参数Modulus的值(公钥)
   objXmlNode = objXml.SelectNodes("//Modulus");
   PrivateKey.Modulus = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
   objXml = new XmlDocument();
   objXml.Load("PrivateKey.xml");
   //指定RSA参数D的值(私钥)
   objXmlNode = objXml.SelectNodes("//D");
   PrivateKey.D = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
   //指定RSA参数DP的值(私钥)
   objXmlNode = objXml.SelectNodes("//DP");
   PrivateKey.DP = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
   //指定RSA参数DQ的值(私钥)
   objXmlNode = objXml.SelectNodes("//DQ");
   PrivateKey.DQ = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
   //指定RSA参数InverseQ的值(私钥)
   objXmlNode = objXml.SelectNodes("//InverseQ");
   PrivateKey.InverseQ = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
   //指定RSA参数P的值(私钥)
   objXmlNode = objXml.SelectNodes("//P");
   PrivateKey.P = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
   //指定RSA参数Q的值(私钥)
   objXmlNode = objXml.SelectNodes("//Q");
   PrivateKey.Q = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());   
  }

  //执行加密
  private void button2_Click(object sender, System.EventArgs e)
  {
   RSA = new RSACryptoServiceProvider();
   //设置私钥
   SetPrivateKey();
   //导入加密私钥
   RSA.ImportParameters(PrivateKey);
   RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
   //设置签名的算法为MD5
   RSAFormatter.SetHashAlgorithm("MD5");
   //执行签名
   EncryptedData = RSAFormatter.CreateSignature(Hashbyte);
   string strCode = "";
   //现实加密结果
   for(int i = 0; i < EncryptedData.Length; i++)
   {
    strCode += " " + EncryptedData[i].ToString();
   }
   richTextBox2.AppendText(strCode);
  }

  //设置公钥
  public void SetPublicKey()
  {
   PublicKey = new RSAParameters();
   objXml = new XmlDocument();
   objXml.Load("PublicKey.xml");
   //指定私钥Exponent的值
   objXmlNode = objXml.SelectNodes("//Exponent");
   PublicKey.Exponent = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());   
   //指定私钥Modulus的值
   objXmlNode = objXml.SelectNodes("//Modulus");
   PublicKey.Modulus = Convert.FromBase64String(objXmlNode[0].InnerXml.ToString());
  }
  

  //用公钥解密
  private void button3_Click(object sender, System.EventArgs e)
  {
   RSA = new RSACryptoServiceProvider();
   //设置公钥
   SetPublicKey();
   RSA.ImportParameters(PublicKey);
   RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);
   //指定解密的时候HASH算法为MD5
   RSADeformatter.SetHashAlgorithm("MD5");
   if(RSADeformatter.VerifySignature(Hashbyte,EncryptedData))
   {
    MessageBox.Show("身份已经确认!","确认身份",MessageBoxButtons.OK,MessageBoxIcon.Information);
   }
   else
   {
    MessageBox.Show("确认身份失败","确认身份",MessageBoxButtons.OK,MessageBoxIcon.Error);
   }
  }
 }
}
-----------------------------------------------------------------
在现实中解密方B的公钥是从CA服务器(证书服务器)发放的证书中得到的。欲知这里怎么操作,请听下回分解 ^_^  吃饭去拉~~!!!
2004年9月4日,钟书林,深圳

Feedback

#1楼   回复  引用    

2004-09-04 20:01 by zhengshb
不错,期待下文。。。

#2楼   回复  引用    

2004-09-06 09:47 by mo
呵。。本人好像更迷糊了!
我现在用c#开发了一个web系统,我想在我的用户认证时先确实它是否有客户端认证证书,不知道怎样实现啊?大哥给点思路。。。

#3楼   回复  引用    

2004-09-09 08:49 by apple
小钟,有个地方有点疑惑,在误区三中,你提到"再把M(文件M不要加密,第三方可以查阅)和加密后的HASH码传送给B",我要问得是为什么要把原文和加密文同时传给B?难道只是单纯为了验证吗?如果中途被截获,岂不是会被其他人看到?

#4楼   回复  引用    

2004-09-09 18:19 by lyzhong
呵呵.........
  聪明,既然是电子签章,就只是为了验证身份用的,所以文件没必要加密的。当然如果需要加密也可以加啊,但是那就不叫电子签章了
   ^_^

#5楼   回复  引用    

2004-11-09 11:45 by cnming
针对这个问题我不大理解
您看完之后能否给我写封Mail?
cnming@netease.com

误区一:大家对公钥私钥区分得太.......(我还找不到形容来形容了....),就太死板了吧。其实当你把公钥保密不公开的时候,公钥就是私钥了;当你把私钥公开的时候,私钥也是公钥了。没必要记得这么死的(个人意见)。



首先对比一下我产生的这一对密钥
公钥
<RSAKeyValue><Modulus>xziM9FPjU3gQUWdZ8hGihiRdhJI1OXgmWeWfCAz3vXuGCcdvBP9SrJldy7W/QwlanBFqWQfceXyTJKpjqD0fpqHgyexDosDFbuN2lqI9sx4bxVHnb2rvLsGO9goWLx4z3hwBW8qWtPowjcOwrV7WRc/om9Uje537DHSi766gRCc=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
私钥
<RSAKeyValue><Modulus>xziM9FPjU3gQUWdZ8hGihiRdhJI1OXgmWeWfCAz3vXuGCcdvBP9SrJldy7W/QwlanBFqWQfceXyTJKpjqD0fpqHgyexDosDFbuN2lqI9sx4bxVHnb2rvLsGO9goWLx4z3hwBW8qWtPowjcOwrV7WRc/om9Uje537DHSi766gRCc=</Modulus><Exponent>AQAB</Exponent><P>/fbx41qC7x9H2OXELnY2hlq1dZ3lVk/fYVjBWJGz0N+xlw1n0R+Xi7aesOQwfJR2sDMf+6Ew2pJ7jVBsWcEO8w==</P><Q>yNFJ9cPPXCtGhtHhdDXwfHdEP9krjefmD1V4UXp3vMCQfVovb9FA3NWZqs06g2LXRKTQj4rZK4pszDTjpIUK/Q==</Q><DP>FipvgkBsHSLFypCDRuraZ2+TVuipE5d1D7IHbguJfjmH7n8lCIORXohZ4WaJNNZgZZCQwg2Xjfwq/HKAgnTvMQ==</DP><DQ>Q/RWg7zUEYtvXTXuheWu0F+Hj9eMXoPHnQ6+HUxE8nFzM4fuH3d+7Hu3eU3jbAxpbggbr0c7//jioiOPmN+QfQ==</DQ><InverseQ>illeV2T77J9Ix4DDsyFWgbYQvYV5u7VVq6abLPfRamVcA9JPawE/LUjYKubswquMmoDt+bf96h0H4xv/R9z/EQ==</InverseQ><D>UqisE1QbJi7S1nPDZiTbAj1kb/UaEFZB01rquTRoNj5iRvxwm+o0K1kXCcbHLNFcvxSaw4X3fJCrnLL11oBF+OjPL2PSP6f+69i+CNCBW1iXLdwi45WR1CiylnZisUfzdvHQYzOZlgL35FfaGXezm7NRH0D5YjBZAzIdAHzzbEE=</D></RSAKeyValue>

私钥里头包含着公钥,如果公开了私钥,不等于公钥也公开了?

#6楼   回复  引用    

2004-12-31 16:43 by feng
下回分解快一点啊,大哥。都等到过年了,急死了。。。。。。。。。

#7楼   回复  引用    

2005-02-27 18:59 by hsj
老大,能给个联系方法吗?有问题问你,对你来说很简单的问题,帮下新人,谢谢:)
hsj_hziee@163.com
qq:13176557

#8楼   回复  引用    

2005-03-14 13:27 by gino
本人菜鸟级别,不知道PublicKey.xml、PrivateKey.xml这两个文件放在哪里,我运行了程序,hash.doc没有,不知道Hash码如何生成的,能介绍下思路么,不胜感激

#9楼   回复  引用    

2005-03-20 17:27 by 钟书林
HASH.DOC只是被加密的对象文件,你自己随便建一个Word文件就行了,用来在实际中模拟加密的对象。PublicKey.xml、rivateKey.xml这两个文件是用来存放公钥和私钥的。

#10楼   回复  引用    

2005-03-23 20:04 by gino
为什么用公钥加密后hash.doc没有成密文?

#11楼   回复  引用    

2005-03-23 20:23 by gino
能给我发个生成公钥和私钥的XML文件的程序么,我生成的XML文件都是缺文件头的,本人初学,谢谢
dangyanwei111014@sina.com.cn

#12楼   回复  引用    

2007-08-29 10:24 by orichisonic[未注册用户]
我现在也在搞加密解密算法,公司有个项目是用C#写的服务器/客户端工具
现在要在csocketclient的networkstream流上加密数据,用了RSA的非对称密钥算法,但是如何反过来,由客户端发送公钥要服务器端加密成非对称私钥进行发送密文,我的 msn:orichisonic@hotmail.com,真心想向你请教

#13楼   回复  引用    

2008-06-06 15:59 by dassssa[未注册用户]
www.snprint.com

#14楼   回复  引用    

2008-09-04 11:12 by Elstage[未注册用户]
有没有在电子签章系统中不使用RSA算法,而是使用国密办认可的算法的?
交流:rain.xk@gmail.com



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 39613




相关文章:

相关链接: