白话数字签名3Web程序中的数字签名转帖.doc_第1页
白话数字签名3Web程序中的数字签名转帖.doc_第2页
白话数字签名3Web程序中的数字签名转帖.doc_第3页
白话数字签名3Web程序中的数字签名转帖.doc_第4页
白话数字签名3Web程序中的数字签名转帖.doc_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

白话数字签名 3 Web程序中的数字签名 转帖白话数字签名(3)-Web程序中的数字签名摘要阅读本文并探索-如何突破Web程序无状态性这个让人抓狂的障碍实现自动显示签名结果和批量签名功能。-如何将签名功能封装到一个实现了IHttpHandler接口的类库中,使Client端的代码尽可能的简单。-使用数字签名API函数需要注意的几个问题。本文介绍在Web程序中使用数字签名所遇到的特殊困难和解决方法,并给出一个超简单但相当实用的DEMO。DEMO程序的效果让我们先来看看实现之后的效果。让Client端代码尽可能的简单我们将数字签名操作的复杂性全部封装到一个命名空间为mylib.util.lnca的类库中,类库只暴露一个名为Signer的类。Signer的Client(本例中的Default.aspx)的职责只有-构造一个含有待签名的数据的Dictionary作为Signer的输入,然后调用Signer.do_sign()函数进行数字签名。-在页面上放置一个专门用于取得并显示签名结果的按钮,并将这个按钮的ClientID传递给Signer,这样Signer在完成签名后就可以自动触发这个按钮。在将程序发布给最终用户时,要把这个按钮的top属性设为-10000,这样最终用户就看不到这个按钮了。Default.aspx的设计视图的截图Default.aspx的源代码如下%Page Language=C#AutoEventWireup=trueCodeFile=Default.aspx.csInherits=_Default%!DOCTYPE html PUBLIC-/W3C/DTD XHTML 1.0 Transitional/ENhtmlxmlns=headrunat=servertitle无标题页/title/head body formid=form1runat=servertable tr td asp:ButtonID=do_sign_buttonrunat=serverText=签名OnClick=do_sign_button_Click/asp:LabelID=Label1runat=serverText=Label/asp:Label/td td asp:ButtonID=show_signed_data_buttonrunat=serverText=显示签名结果(自动)OnClick=show_signed_data_button_Click/td/tr tr tdstyle=border:solid 1px black;vertical-align:top;asp:GridViewID=sign_candidates_gridviewrunat=serverCaption=签名前的数据CellPadding=4ForeColor=#333333Font-Names=宋体Font-Size=10ptFooterStyleBackColor=#1C5E55Font-Bold=TrueForeColor=White/RowStyleBackColor=#E3EAEB/EditRowStyleBackColor=#7C6F57/SelectedRowStyleBackColor=#C5BBAFFont-Bold=TrueForeColor=#333333/PagerStyleBackColor=#666666ForeColor=WhiteHorizontalAlign=Center/HeaderStyleBackColor=#1C5E55Font-Bold=TrueForeColor=White/AlternatingRowStyleBackColor=White/asp:GridView/td tdstyle=border:solid 1px black;vertical-align:top;asp:GridViewID=signed_data_gridviewrunat=serverCaption=签名结果BackColor=LightGoldenrodYellowBorderColor=TanBorderWidth=1pxCellPadding=2ForeColor=BlackOnRowDataBound=signed_data_gridview_RowDataBoundFont-Names=宋体Font-Size=10ptFooterStyleBackColor=Tan/SelectedRowStyleBackColor=DarkSlateBlueForeColor=GhostWhite/PagerStyleBackColor=PaleGoldenrodForeColor=DarkSlateBlueHorizontalAlign=Center/HeaderStyleBackColor=TanFont-Bold=True/AlternatingRowStyleBackColor=PaleGoldenrod/asp:GridView/td/tr/table/form/body/html using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Collections.Generic;using mylib.util.lnca;public partialclass _Default:System.Web.UI.Pageprotectedvoid Page_Load(object sender,EventArgs e)protectedvoid do_sign_button_Click(object sender,EventArgs e)/这是待签名的数据,保存在一个Dictionary中。Key为数据的ID,Value为待签名的数据。Dictionary string,string sign_candidates=new Dictionary string,string();sign_candidates.Add(1,123.45);sign_candidates.Add(2,678.90);sign_candidates.Add(3,zhf);sign_candidates.Add(4,7788);sign_candidates.Add(5,1-2-3);sign_candidates.Add(6,cnblogs);sign_candidates_gridview.DataSource=sign_candidates;sign_candidates_gridview.DataBind();/调用Signer.do_sign()进行签名Signer.do_sign(Page,show_signed_data_button.ClientID,sign_candidates);protectedvoid show_signed_data_button_Click(object sender,EventArgs e)if(Signer.error_code=0)/签名成功signed_data_gridview.DataSource=Signer.signed_datas;signed_data_gridview.DataBind();else/签名失败Label1.Text=Signer.error_message;protectedvoid signed_data_gridview_RowDataBound(object sender,GridViewRowEventArgs e)/每个签名结果的长度都要将近2000个字符,会把GridView撑得很大,为了方便写Blog时截图,我/加了一个滚动条,实际作程序时是不需将签名数据显示给用户看的,也就用不着这段代码了。if(e.Row.RowType=DataControlRowType.DataRow)string content=e.Row.Cells1.Text;e.Row.Cells1.Text=div style=overflow:auto;width:300px;height:150px;+content+/div;由于Signer是一个HTTP处理程序,所以需要在Web.config中添加一行对Signer.ashx的注册:?xml version=1.0?configuration appSettings/connectionStrings/system.web httpHandlers addpath=Signer.ashxverb=*type=mylib.util.lnca.Signer,mylib.util.lncavalidate=false/httpHandlers/system.web/configuration有关HTTP处理程序的创建和应用,可以看实战HTTP处理程序(HTTP Handler)系列。由于我们把复杂性都放在了Signer.cs中,Signer.cs的代码有些长,我们会在后面讨论它的几个要点。1 using System;2 using System.Collections.Generic;3 using System.Text;4 using System.Web;5 using System.Web.UI;6 using System.Web.SessionState;7 using mylib.util.lnca.Properties;8 using System.Diagnostics;9 10 namespace mylib.util.lnca 1112 publicclass Signer:IHttpHandler,IRequiresSessionState 1314/summary 15/Opens anew window to do signing.16/Example 17/Dictionary string,string sign_candidates=new Dictionary string,string();18/sign_candidates.Add(1,123.45);19/sign_candidates.Add(2,678.90);20/sign_candidates.Add(3,zhf);21/sign_candidates.Add(4,7788);22/sign_candidates.Add(5,1-2-3);23/sign_candidates.Add(6,cnblogs);24/Signer.do_sign(Page,on_finished_signing_button.ClientID,sign_candidates);25/summary 26 publicstaticvoid do_sign(Page page,string on_signing_finished_button_client_id,27 Dictionary string,string sign_candidates)2829 Debug.Assert(sign_candidates!=null,sign_candidates should not be null);30 31 Signer.sign_candidates=sign_candidates;32 Signer.sign_candidates_enumerator=Signer.sign_candidates.GetEnumerator();33 Signer.on_signing_finished_button_client_id=on_signing_finished_button_client_id;34 35/initializes Signer.signed_datas 36 Signer.signed_datas=new Dictionary string,string();37 Signer.signing_counter=0;38 foreach(string keyin sign_candidates.Keys)3940 Signer.signed_datas.Add(key,);4142 43 error_code=0;44 error_message=;45 46 if(!page.ClientScript.IsStartupScriptRegistered(page.GetType(),OpenSignerWindow)4748 page.ClientScript.RegisterStartupScript(page.GetType(),OpenSignerWindow,49window.open(Signer.ashx,Signer_ashx,height=100,alwaysRaised=true,width=200,top=300,left=400,toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,status=no);,true);505152 53/summary 54/Stores error_code during signing.55/summary 56 publicstaticlong error_code 5758 getreturn(long)System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.error_code;59 setSystem.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.error_code=value;6061 62/summary 63/Stores error_message during signing.64/summary 65 publicstaticstring error_message 6667 getreturn System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.error_messageasstring;68 setSystem.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.error_message=value;6970 71/summary 72/Stores datas had be signed.73/summary 74 publicstatic Dictionary string,string signed_datas 7576 getreturn System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.signed_datasas Dictionary string,string;77 setSystem.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.signed_datas=value;7879 80/summary 81/The enumerator of sign_candidates 82/summary 83 privatestatic IEnumerator KeyValuePair string,string sign_candidates_enumerator 8485 getreturn System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.sign_candidates_enumeratoras IEnumerator KeyValuePair string,string;86 setSystem.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.sign_candidates_enumerator=value;8788 89/summary 90/Datas needs signing.91/summary 92 privatestatic Dictionary string,string sign_candidates 9394 get 9596 return System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.sign_candidatesas Dictionary string,string;9798 set 99100 System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.sign_candidates=value;101102103 104/Returns the current signing progress.105 privatestaticint signing_counter 106107 getreturn(int)System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.signing_counter;108 setSystem.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.signing_counter=value;109110 111/summary 112/Returns javascript code called doSignData()function at client.113/param name=sourceData想要进行签名的数据/param 114/param name=signAlgo签名算法,推荐使用SignAlgo.RSA_MD5/param 115/param name=isAddSignCert是否在结果中携带证书/param 116/param name=isAddSrcData是否在结果中携带原文/param 117/param name=innerOid数据类型,使用InnerOid.EMPUTY即可/param 118/param name=isAddTime是否添加签名时间/param 119/param name=pin用户口令,如果设为空字符串,则每次都会提示用户输入密码;如果在此参数中传入了正确的密码,则只要求用户输入密码一次./param 120/summary 121 privatestring sign_data(string source_data,string sign_algo,bool is_add_sign_cert,bool is_add_src_data,string inner_oid,long is_add_time,string pin)122123 returnstring.Format(doSignData(0,1,2,3,4,5,6);,124 source_data,125 sign_algo,126 is_add_sign_cert=true?1:0,127 is_add_src_data=true?1:0,128 inner_oid,129 is_add_time,130 pin);131132 133/summary 134/Alias of sign_data(source_data,SignAlgo.RSA_MD5,is_add_sign_cert,true,InnerOid.EMPUTY,0,string.Empty)135/summary 136 privatestring sign_data(string source_data,bool is_add_sign_cert)137138 return sign_data(source_data,SignAlgo.RSA_MD5,is_add_sign_cert,true,InnerOid.EMPUTY,0,string.Empty);139140 141/summary 142/Alias of sign_data(source_data,SignAlgo.RSA_MD5,true,true,InnerOid.EMPUTY,0,string.Empty);143/summary 144 privatestring sign_data(string source_data)145146 return sign_data(source_data,SignAlgo.RSA_MD5,true,true,InnerOid.EMPUTY,0,string.Empty);147148 149/summary 150/Returns signed data from HiddenFields in form.151/summary 152 privatestring get_signed_data(HttpRequest request)153154 if(get_error_code(request)!=0)155156 returnstring.Empty;157158 159 if(request.FormSigner_SubPage_SignedData_HiddenField=null)160161 returnstring.Empty;162163 164 return request.FormSigner_SubPage_SignedData_HiddenField.ToString();165166 167/summary 168/Returns error_code from the HiddenField in form.169/summary 170 privatelong get_error_code(HttpRequest request)171172 if(request.FormSigner_SubPage_ErrorCode_HiddenField=null)173174 return-1;175176 177 try 178179 returnlong.Parse(request.FormSigner_SubPage_ErrorCode_HiddenField.ToString();180181 catch(Exception)182183 return-1;184185186 187/summary 188/Returns error_message from the HiddenField in form.189/summary 190 privatestring get_error_message(HttpRequest request)191192 if(get_error_code(request)=-1)193194 return取得签名结果失败。;195196 197 if(request.FormSigner_SubPage_ErrorMessage_HiddenField=null)198199 returnstring.Empty;200201 202 return request.FormSigner_SubPage_ErrorMessage_HiddenField.ToString();203204 205/summary 206/Stores the client_id of button of father page wants to auto trigger.207/summary 208 privatestaticstring on_signing_finished_button_client_id 209210 getreturn System.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.on_signing_finished_button_client_idasstring;211 setSystem.Web.HttpContext.Current.Sessionmylib.util.lnca.Signer.on_signing_finished_button_client_id=value;212213 214 publicbool IsReusable 215216 getreturntrue;217218 219 publicvoid ProcessRequest(System.Web.HttpContext context)220221 context.Response.Clear();222 context.Response.ContentType=text/html;223 context.Response.Write(!DOCTYPE html PUBLIC-/W3C/DTD XHTML 1.0 Transitional/EN);224 context.Response.Write(html xmlns=+Environment.NewLine);225 context.Response.Write(title签名中/title);226 context.Response.Write(form name=form1method=postid=form1+Environment.NewLine);227 context.Response.Write(Settings.Default.controls+Environment.NewLine);228 context.Response.Write(Settings.Default.js+Environment.NewLine);229 230 if(sign_candidates_enumerator.Current.Key!=null)/not first reach here.231232 if(get_error_code(context.Request)=0)/signing success 233234 signed_datassign_candidates_enumerator.Current.Key=get_signed_data(context.Request);/gathers signed data.235236 else/signing failed.237238/sets error_code and error_message 239 error_code=get_error_code(context.Request);240 error_message=get_error_message(context.Request);241/terminates signing 242 context.Response.Write(script type=text/javascript+Environment.NewLine);243 context.Response.Write(244 string.Format(opener.document.getElementById_x(0).click();,on_signing_finished_button_client_id);245 context.Response.Write(window.close(););246 context.Response.Write(/script+Environment.NewLine);247248249 250 if(sign_candidates_enumerator.MoveNext()251252 signing_counter+;253 context.Response.Write(span正在签名+signing_counter.ToString()+/+sign_candidates.Count.ToString()+/span);254 context.Response.Write(script type=text/javascript+Environment.NewLine);255 context.Response.Write(sign_data(sign_candidates_enumerator.Current.Value)+Environment.NewLine);256 context.Response.Write(/script+Environment.NewLine);257258 else 259260 context.Response.Write(script type=text/javascript+Environment.NewLine);261 context.Response.Write(262 string.Format(opener.document.getElementById_x(0).click();,on_signing_finished_button_client_id);263 context.Response.Write(window.close(););264 context.Response.Write(/script+Environment.NewLine);265266 context.Response.Write(/form+Environment.NewLine);267 context.Response.Write(/html+Environment.NewLine);268269/class Signer 270/namespace mylib.util.lnca 271 Signer.cs的第227和228行的Settings.Default.controls和Settings.Default.js是需要发送给客户端浏览器用于回传签名结果的HiddenFields和执行签名操作的Javascript语句。我把它们放在了类库的配置文件里,它们的代码如下:1 objectid=subpage_Signer_SubPage_LNCAToolkitsclassid=clsid:6FBE853D-0DB0-4C62-B7DC-B49E9D447AF9style=width:0px;2 height:0px;codebase=3/object 4inputid=Signer_SubPage_ErrorCode_HiddenFieldname=Signer_SubPage_ErrorCode_HiddenFieldtype=hidden/5 inputid=Signer_SubPage_ErrorMessage_HiddenFieldname=Signer_SubPage_ErrorMessage_HiddenFieldtype=hidden/6 inputid=Signer_SubPage_SignedData_HiddenFieldname=Signer_SubPage_SignedData_HiddenFieldtype=hidden/1 script type=text/javascript2 function doSignData(sourceData,signAlgo,isAddSignCert,isAddSrcData,innerOid,isAddTime,pin)34 var caTool=document.getElementById_x(subpage_Signer_SubPage_LNCAToolkits);5 var ret=caTool.EnumClientCert();6 7if(ret=0)89 var signedData=caTool.SignDataEx(sourceData,signAlgo,isAddSignCert,isAddSignCert,innerOid,isAddTime,pin);10 11 document.getElementById_x(Signer_SubPage_ErrorCode_HiddenField).value=caTool.ErrorCode;12 document.getElementById_x(Signer_SubPage_ErrorMessage_HiddenField).value=caTool.ErrorMessage;13 document.getElementById_x(Signer_SubPage_SignedData_HiddenField).value=signedData;1415 else 1617 document.getElementById_x(Signer_SubPage_ErrorCode_HiddenField).value=caTool.ErrorCode;18 document.getElementById_x(Signer_SubPage_ErrorMessage_HiddenField).value=caTool.ErrorMessage;19 document.getElementById_x(Signer_SubPage_SignedData_HiddenField).value=;2021 22 document.forms0.submit();2324/script自动显示签名结果我们想要实现这样的交互效果:用户选定想要进行签名的数据后,只要按一个按钮就会自动弹出一个小窗体显示签名的进度;当签名结束后,可以自动显示签名结果,就像上面那个DEMO程序所展示的那样。如果我们开发的是WinForm的信息系统,实现这样的效果简直易如反掌。可是在Web程序中,我们却遇到了一点麻烦。自动显示签名结果的困难正如我们在第2篇所介绍的,为了防复制破解,我们是使用USB Key做数字签名。这个USB Key必须插在客户端的电脑上,我们在Server端无法直接控制它,只能通过在客户端浏览器上执行的javascript代码调用一个由辽宁CA认证中心开发的一个ActiveX控件操作USB Key进行签名,再将签名结果通过HiddenFields Post回Server端-不过这个Server端已经不是以前的Server端了,Web程序的这种无状态性没少让我们吃亏。换句话说,我们没办法像下面这样写(伪码):protected void do_sign_button_Click(object sender,EventArgs e)1 Dictionary string,string sign_candidates=prepare_sign_candidates();2 Dictionary string,string signed_datas=excute javascript at client browser to sign data,and return signed datas 3signed_data_gridview.DataSource=signed_datas;4 signed_data_gridview.DataBind();实现自动显示签名结果我们遇到的问题的实质是:准备签名数据(伪代码第1行)、显示签名结果(伪代码第3、4行)的操作在Server端进行;而使用USB Key进行签名的操作(伪代码第2行)必须在Client端的浏览器上执行,并且这两种操作是异步的!所以我们只能将显示签名结果的代码放到另一个函数中,在签名结束后以某种方法触发它。我们在Demo中所使用的方法是,将显示签名结果的代码放到显示签名结果(自动)按钮的Click事件中,在签名结束后,使用javascript:opener.document.getElementById_x(show_signed_data_button.ClientID).click();来触发这个按钮的Click事件。思考题我们使用一个伪隐藏的按钮可以简单地实现自动显示签名结果的效果,不过这种作法似乎有点土。你能否使用其它更高级的方法来实现同样的效果?实现批量签名我们需要让用户按一次按钮,就可以签名n条数据,可是数字签名API SignDataEx(sourcedata,.)一次只能签名一条数据。我们需要遍历每条待签名数据,调用SignDataEx()进行签名。我们有两种选择:1.在Server端进行遍历,每次传送一条数据给Client端进行签名。2.将n条待签名数据一次全部传给Client端,在Client端使用javascript的for循环遍历待签名数据并进行签名。我们在Demo程序中是使用了第1种方法。基于和自动显示签名结果一节所述的同样的困难,我们无法在Signer.cs的ProcessRequest()中这样写(伪码):public void ProcessRequest(System.Web.HttpContext context)foreach(string key in sign_candidates.Keys)string signed_data=excute javascript at client browser to sign data,and return signed data好在已经有大师发明了外部迭代器(external iterator),我们可以在第一次迭代之前,先创建一个待签名数据的一个外部迭代器,并把它保存在Session中。每次签名后,Client端PostBack回Server端,在Server端从Session中取出这个外部迭代器,调用sign_candidates_enumerator.MoveNext(),之后继续向Client端发送签名用的javascript语句,直至完成全部遍历,请参见Signer.cs的250268行。下面的时序图表示批量签名3条数据的过程。思考题我们的DEMO实现了第1种方法,你能否实现第2种方法?这两种实现方法各有什么优缺点?综合起来我们把批量签名与自动显示签名结果的功能都放在Signer.cs中,可以用下面这个经过简化的时序图来表示。附录数字签名API简介我们使用的是辽宁省数字认证中心发放的数字证书。他们还提供了两套数字签名API:一个是ActiveX控件;一个是COM组件。两套API都有完整、丰富的数字签名相关的函数,可以单独使用。如果是WinForm程序,直接使用COM组件即可。不过由于Web程序必须使用ActiveX控件,所以我们在作数字签名的时候使用ActiveX控件,在验证签名的时候使用COM组件。也许您手头的API和我们使用的API并不相同,不过您仍然可以下载这两套API的手册找找感觉。LNCAToolkits控件(通用版)程序员手册_v2.pdf-这个是ActiveX控件的手册LNCA-CryptoAPI-Com版程序员手册_v1.pdf-这个是COM组件

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论