在基于ejbca搭建CA时候,需要实现如下功能:
1)、在客户端实现对页面关键数据(例如订单金额等)采用签名、数字信封等方式进行加密
2)、能够较好支持USB KEY集成
3)、用户申请数字证书导入浏览器后,能够通过Web页面对浏览器证书进行重新申请(renewal)、删除、显示、查询、验证等功能
单独依靠XEnroll.dll或CertEnroll.dll控件已经无法满足以上要求。微软的CAPICOM组件封装了Windows CryptAPI的各种操作,可以在Windows环境下各种语言中使用。而且CAPICOM中的大多数接口都是“脚本安全”的,可以直接在网页脚本中安全使用CAPICOM接口所提供的功能。
需求1)、2)的实现思路:在前台利用CAPICOM组件读取浏览器或USB盘中的用户证书,对页面表单的关键数据进行SHA1签名。将签名后的密文与页面表单中的明文提交到服务器端。服务器从用户请求密文解密得到用户证书及页面关键数据的摘要(利用Bouncycastle、Apache Commons Codec),验证证书的合法性及有效性。然后对提交的页面明文计算SHA1,把得到的结果与从密文是解出摘要进行对比,从而实现数据完整性的校验。
需求3)的实现思路:关键是要实现证书的删除、查询、验证功能,结合IE中自动安装用户数字证书 、IE中自动安装根数字证书 ,可以很容易实现相关功能。
CAPICOM中常用的类主要包括:Store、Certificates、Certificate
基本操作步骤为:创建Store对象->打开Store对象->查找需要操作的证书集合(Certificates)->对单个证书(Certificate)进行操作
1、一个简单例子:
<OBJECT id="capicom" codeBase="http://download.microsoft.com/download/E/1/8/E18ED994-8005-4377-A7D7-0A8E13025B94/capicom.cab#version=2,0,0,3" classid="clsid:A996E48C-D3DC-4244-89F7-AFA33EC60679" VIEWASTEXT> </OBJECT> <script language="javascript"> var CAPICOM_CURRENT_USER_STORE = 2 var CAPICOM_MY_STORE = "My" var CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME = 1 var CAPICOM_STORE_OPEN_READ_WRITE=1 var myStore = new ActiveXObject("CAPICOM.Store"); myStore.Open(CAPICOM_CURRENT_USER_STORE,CAPICOM_MY_STORE,CAPICOM_STORE_OPEN_READ_WRITE); var myStoreCerts = myStore.Certificates; var info=""; for(i = 1; i<= myStoreCerts.Count; i++) { info+= " Subject Name : "+myStoreCerts.Item(i).SubjectName +"<br/>"; } document.write(info); </script>
2、一个更复杂的例子:
2.1)、capicomtest.html
<html> <head> <title>CAPICOM使用DEMO</title> <meta http-equiv="no-cache"> </head> <body onLoad="listCert()"> <OBJECT id="capicom" codeBase="http://download.microsoft.com/download/E/1/8/E18ED994-8005-4377-A7D7-0A8E13025B94/capicom.cab#version=2,0,0,3" classid="clsid:A996E48C-D3DC-4244-89F7-AFA33EC60679" VIEWASTEXT> </OBJECT> <script language="javascript" src="capicom.js"></script> <form name="frmStore" method="post" action=""> 1.读取用户证书 <br> <br> 证书类型: <select id="storeName" size="1" name="storeName"> <option value="my" selected>Personal</option> <option value="root">Root</option> <option value="AddressBook">Address Book</option> <option value="ca">CA</option> </select> <input type="button" value="获取证书列表" onclick="listCert()"> </p> <p>选择一个证书:<br> <select id="allCerts" size="10" name="allCerts" > </select> <br> <input id="delcert" type="button" name="delcert" value="删除选定证书" onclick="deleteCert()" /> <input id="verifydate" type="button" name="verifydate" value="验证选定证书有效期" onclick="alert(verifyCertValidDate())" /> </form> </body> </html>
2.2)、capicom.js
var CAPICOM_CURRENT_USER_STORE = 2 var CAPICOM_MY_STORE = "My" var CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME = 1 var CAPICOM_STORE_OPEN_READ_WRITE=1 var myStore = new ActiveXObject("CAPICOM.Store"); myStore.Open(CAPICOM_CURRENT_USER_STORE,CAPICOM_MY_STORE,CAPICOM_STORE_OPEN_READ_WRITE); function deleteCert() { var myStore = new ActiveXObject("CAPICOM.Store"); try{ var storeName = frmStore.storeName.options(frmStore.storeName.selectedIndex).value; myStore.Open(CAPICOM_CURRENT_USER_STORE,storeName,CAPICOM_STORE_OPEN_READ_WRITE); var index = frmStore.allCerts.options.selectedIndex; var cert = frmStore.allCerts.options[index].value; subjectName=getCertCN(cert); var myStoreCerts = myStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, subjectName, true); for(i = 1; i<= myStoreCerts.Count; i++) { if(myStoreCerts.Item(i).HasPrivateKey()){ //要删除用户证书,首先要删除私钥,才能够调用Certificates.Remove方法删除证书 myStoreCerts.Item(i).PrivateKey.Delete(); } myStore.Remove(myStoreCerts.Item(i)); } alert("删除证书成功"); myStoreCerts=null; myStore.Close() ; myStore=null; }catch(e){ alert("删除证书失败,错误码为:"+e.number); } window.location.reload(); } function verifyCertValidDate(){ var myStore = new ActiveXObject("CAPICOM.Store"); try{ var storeName = frmStore.storeName.options(frmStore.storeName.selectedIndex).value; myStore.Open(CAPICOM_CURRENT_USER_STORE,storeName,CAPICOM_STORE_OPEN_READ_WRITE); } catch (e) { alert("打开证书库失败"); return ; } var index = frmStore.allCerts.options.selectedIndex; var cert = frmStore.allCerts.options[index].value; subjectName=getCertCN(cert); var myStoreCerts = myStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, subjectName, true); var result=""; for(i = 1; i<= myStoreCerts.Count; i++) { var validToDate=new Date(myStoreCerts.Item(i).ValidToDate+""); var currentDate=new Date(); var diff=(validToDate-currentDate)/(3600*24*1000); if(diff >31){ result="证书"+myStoreCerts.Item(i).subjectName+" 有效期为:"+formatDate(validToDate); } else if(diff<=31 && diff >0){ result="证书"+myStoreCerts.Item(i).subjectName+" "+diff+"后即将过期,请更新证书"; alert(result); return result; } else{ result="证书"+myStoreCerts.Item(i).subjectName+" 已经过期,请更新证书"; alert(result); return result; } } if(result=="") return "没有有效期"; return result; } function listCert() { var myStore = new ActiveXObject("CAPICOM.Store"); try { var storeName = frmStore.storeName.options(frmStore.storeName.selectedIndex).value; myStore.Open(CAPICOM_CURRENT_USER_STORE, storeName, CAPICOM_STORE_OPEN_READ_WRITE); } catch (e) { alert("打开证书库失败"); return false; } var count = frmStore.allCerts.options.length; for (i = 1; i <= count; i++) { frmStore.allCerts.options.remove(count-i); } while (frmStore.allCerts.options.length) frmStore.allCerts.options[0] = null; //只列出DN中有yeeach.com的证书 var myStoreCerts = myStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, "yeeach.com", true); for(i = 1; i<= myStoreCerts.Count; i++) { var certInfo = new Option("cert: " + myStoreCerts.Item(i).subjectName ); certInfo.value=myStoreCerts.Item(i).subjectName ; frmStore.allCerts.options.add(certInfo, i); } } function getCertCN(dn){ //对于CAPICOM_CA_STORE、CAPICOM_OTHER_STORE、CAPICOM_ROOT_STORE不适用,需要调整 i=dn.indexOf('CN='); if(i==-1){ return "没有CN"; }else{ cn=dn.substr(i+3); return cn; } } function formatDate(inputDate){ if(null == inputDate){ return ""; } //var year = 1900+date.getYear(); var year = inputDate.getYear(); var month = inputDate.getMonth()+1; if(month < 10) month = "0"+month; var day = inputDate.getDate(); if(day < 10) day = "0"+day; var hour = inputDate.getHours(); if(hour < 10) hour = "0"+hour; var minute = inputDate.getMinutes(); if(minute < 10) minute = "0"+minute; var second = inputDate.getSeconds(); if(second < 10) second = "0"+second; return year+"-"+month+"-"+day; }
3、参考文档:
How to use Java produce Signature by USBKey under CryptoAPI/CSP
How to create a digital signing solution with only JavaScript
How to obtain signer’s details from a JavaScript signed data
Automatic sign of a text in with a web script using CAPICOM with an ActiveX
转载请注明:出家如初,成佛有余 » 使用CAPICOM实现证书管理