出家如初,成佛有余

使用CAPICOM实现证书管理

Posted in Uncategorized by chuanliang on 2010/02/15

    在基于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中常用的类主要包括:StoreCertificatesCertificate 

  基本操作步骤为:创建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、参考文档:

CAPICOM Reference

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

数字证书在WEB应用中登录

 

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: