通过代码来申请CA证书

时间:2024-03-02 13:38:35

    最近一个项目中有关于PKI体系的搭建的内容,由于以前没有做过这部分,所以一开始一头雾水。慢慢摸索有了一丁点成果,拿出来和大家分享。说的不对的地方望能指正。

    PKI体系目前成了一个企业中信息化安全方面的关键点,是信息化安全的支柱。我所在的项目是以微软技术为基础的项目,CA的证书服务器是windows 2003 server自带的证书组件。
    
    废话就不说太多了啊,说一下在CA搭建好以后如何申请和导出证书的事情啊。


    一、区分企业CA和独立CA
    企业CA和独立CA的目标不一样,企业CA主要是用在以域为基础局域网内,适合搭建各种企业级别的CA平台,而独立CA主要用于专门的CA颁发机构,具有权威性。两种不同类型的CA决定了他们的安装和使用都有些差别。安装的时候系统会让你选择企业CA还是独立CA。
    企业CA在安装的时候默认会安装一些证书模板,以方便CA的管理。而独立CA则没有模板。企业CA可以申请多种类型的证书,比如域身份验证,客户端身份验证,web服务器身份验证,智能卡登陆,无线身份验证等等。而独立CA就比较少了。


    那么如何区分你是安装了企业CA还是独立CA呢(如果服务器不是你装的):
    1、是否有模板
    2、通过web方式申请证书的时候是否提示你填写公司,部门,地区,省,国家等信息


    独立CA是可以转化成企业CA的,微软有一篇文章说明如何操作 Windows Server 2003 PKI 操作指南


    二、申请数字证书的三种方式(主要针对企业CA)
    1、通过web的方式来申请
     安装完CA后,系统会自动在IIS的默认站点下创建一个虚拟目录叫Certsrv。这个虚拟目录用来申请证书的申请方式很简单的。
这个目录下面有好多个asp和inc文件,里面的代码就显示了系统如何通过CA的组件编程接口来创建证书申请的,在里面可以看到主要用到了哪些组件以及那些方法。
这很关键,因为通过web的方式申请,效率太低了,我们需要通过自己的程序来申请证书。

    2、通过命令行的方式来申请
    CA自带了多个非常有用的证书工具,主要有CertReg.exe ,CertUtil.exe。这两个工具的功能都非常的强大,拥有丰富的参数,可以在命令行模式下查看帮助。

    使用CertReg.exe可以以命令行的方式添加一个数字证书申请,这种方式需要创建一个inf文件,再通过这个inf文件生成一个req文件,inf文件中定义了申请信息。
inf文件的制作可以在命令行模式下使用certreq -new -? 看到一些帮助信息,重点查看[NewRequest]部门

    通过下面的命令生成
    req文件 certreq -new <文件名>.inf <文件名>.req

    通过下面的命令提交req申请文件,并同时导出cer证书文件:
    certreq -submit <文件名>.req <文件名>.cer

    对于CertUtil.exe工具,我们可以用它来查询CA的很多信息。
    使用CertUtil -view RequestID号 来查询证书的信息
    CertUtil -view 不加参数可以查询所有的证书的信息
    小技巧:由于证书的信息太多,在dos模式下无法看全,因此使用dos下的输出重定向符将证书信息全部输出到一个文本文件中,非常适合查阅了。如
    certUtil -view > c:\view.txt


    3、通过程序调用对象模型来申请
    通过程序调用对象模型生成证书是最有用的功能了,因为很多时候我们要提供非常友好的界面给用户申请证书,而不是像IIS那个CertSrv虚拟目录那样子的。另外还可以批量生成证书,然后把证书保存到智能卡当中。对象模型主要是用COM对象,不知道.Net框架里面的对象是否能做到生成证书。 下面是部分代码,可作参考。

    引用COM对象CERTCLIENTLib,XENROLLLib

    常量定义
const int AT_KEYEXCHANGE = 1 ;
const int CRYPT_EXPORTABLE = 1 ;
const int CR_IN_ENCODEANY = 0xff ;
const int CR_IN_FORMATANY = 0 ;
const int XECR_CMC = 3 ;

public string CreateClientCertificate(string a_sName, string a_sEmail, string a_sCompany, string a_sDepartment, string a_sCity, string a_sState, string a_sCountry, string a_sCA)
{
 CERTCLIENTLib.ICertRequest CertRequest = new CERTCLIENTLib.CCertRequestClass();
 XENROLLLib.ICEnroll4 CertEnroll = new XENROLLLib.CEnroll2Class();
 try
 {
  string RequestStr ;
  int Disposition, ID ;
  string dNName = (((((("CN=" + a_sName) + ",O=" + a_sCompany) + ",OU=" + a_sDepartment) + ",L=" + a_sCity) + ",S=" + a_sState) + ",C=" + a_sCountry) + ",E=" + a_sEmail;
    
  CertEnroll.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0" ;
  CertEnroll.KeySpec = AT_KEYEXCHANGE ;
  CertEnroll.GenKeyFlags = CRYPT_EXPORTABLE ;
  CertEnroll.addCertTypeToRequest("User"); ///证书模板信息
  RequestStr = CertEnroll.createRequest( XECR_CMC, dNName , "1.3.6.1.5.5.7.3.2" ) ; 
  Disposition = CertRequest.Submit(CR_IN_ENCODEANY | CR_IN_FORMATANY, RequestStr, "", a_sCA) ;

  ID = CertRequest.GetRequestId();

  return ID.ToString() ;
    
 }

 catch(Exception e)
 {
  return "";
 }
}

在独立ca模式下的申请数字证书的代码,主要是不要这一句:CertEnroll.addCertTypeToRequest("User");  其他一样。


 /// <summary>
  /// 导出数字证书
  /// </summary>
  /// <param name="_RequestID"></param>
  public void ExportCertification(string _UserName ,string _RequestID)
  {
   CERTCLIENTLib.ICertRequest2 CertRequest = new CERTCLIENTLib.CCertRequestClass();

   XENROLLLib.ICEnroll4 CertEnroll = new XENROLLLib.CEnroll2Class();
   int Disposition ;
   string ErrText = "" ;


   Disposition = CertRequest.GetIssuedCertificate(CAServer,int.Parse(_RequestID) ,"") ;

   if(Disposition == CR_DISP_ISSUED )
   {
    string Cert ;
    string sCert  ;

    try
    {
     Cert = CertRequest.GetFullResponseProperty(FR_PROP_FULLRESPONSE, 0, PROPTYPE_BINARY, CR_OUT_BASE64 ) as string ;

     sCert = CertEnroll.getCertFromResponse(Cert)  ;

     
     CertEnroll.createFilePFX("password", CertificationFileName); ///创建pfx文件(包含密钥)

    }
    catch(Exception ex)
    {
     return ;
    }
   }
   
  }

    4、企业CA和独立CA面对设置申请人信息时的不同反应
    申请证书后,用户的注册信息在企业CA管理单元的颁发的证书那里默认情况是看不到的,因为那里显示的列中主要有申请人姓名,证书的有效时间信息,以及颁发者的信息,包括国家,公司,省、市等。而我们通过程序自动申请的证书,它的申请人姓名都是一样的,比如我在CA服务器用程序申请的所有证书的申请人姓名都是 域名\Administrator,如此一来区分某个证书是谁的就非常困难了(可以通过CertUtil -view RequestID ,但很麻烦) 。我摸索了一阵子后,发现我们在申请的时候填的那些信息其实是在申请人信息部分,默认情况下,这些列是没有显示的。因此我们可以在“查看”菜单的添加/删除列中将那些列显示出来并调整显示顺序,可以将申请公用名放到前面,这样就可以方面的区分该证书是为谁申请的了。

    以上是匆匆忙忙整理的一些文字,感觉有些乱。呵呵,还请多多包含。