在基于ejbca搭建数字证书时候,需要实现用户自助申请数字证书(Certificate signing request)并自动安装到IE浏览器中的功能(Certificate enrollment)。
相关的资料极其缺少,好在ejbca中有相关的例子可以参考,尽管不是很完整。整理一下研究的大致成果。
1、基本思路:
1)、Certificate signing request(CSR)
证书签发请求(CSR),也叫做证书请求,是从请求者浏览器发送到证书中心来申请一个数字身份证书的一条信息,在公共密钥基础架构系统中。在创建一个 CSR 之前,这个请求者首先产生一个密钥对,为这个私有密钥保密。CSR 包括鉴别请求者一条的信息,和由这个请求者选择的公共密钥。相应的私有密钥不包含在 CSR 中,但是被用于数位签名整个请求。
在XP、Windows 2003的IE上,通过XEnroll.dll控件的createPKCS10方法来生成CSR(Certificate signing request)。
在Vista,Windows 2008,Windows 7 的IE上,需要使用CertEnroll.dll的X509Enrollment.CX509CertificateRequestPkcs10方法来生成CSR(Certificate signing request)。
2)、用户数字证书的自动安装
要实现用户数字证书在IE浏览器中自动安装,首先要客户端浏览器提交CSR到证书中心服务器,证书中心服务器端根据CSR对用户私钥和公钥进行签名并将签名后的证书返回给客户端。
在XP、Windows 2003的IE上,通过XEnroll.dll控件的acceptPKCS7方法实现证书自动安装到客户端浏览器,大致步骤如下:
<object id=”XEnroll” classid=”clsid:127698e4-e730-4e5c-a2b1-21490a70c8a1″ codebase=”xenroll.dll”></object>
XEnroll.acceptPKCS7
在Vista,Windows 2008,Windows 7 的IE上,需要使用CertEnroll.dll控件的InstallResponse方法来实现自动安装到客户端浏览器,大致过程如下:
<object id=”CertEnroll” classid=”clsid:884e2049-217d-11da-b2a4-000e7bbb2b09″ codebase=”CertEnroll.dll”></object>
var objEnroll = CertEnroll.CreateObject(“X509Enrollment.CX509Enrollment”)
Call objEnroll.Initialize(1)
objEnroll.InstallResponse
此处安装用户数字证书时候并没有自动安装根证书,根证书自动安装的实现方式可以参考:IE中自动安装根数字证书
3)、服务器端的处理逻辑
在ejbca中src\java\org\ejbca\ui\web\pub\DemoCertReqServlet.java、src\publicweb\publicweb\templates\certInstTemplate.jsp可以作为例子来理解服务器端对CSR请求处理及服务器响应客户端实现证书自动安装的实现机制。
以上思路其实应用于openssl的方案也可以。
2、测试页面
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<TITLE>VBScript Certificate Enrollment Control Request 例子(使用XEnroll)
</TITLE>
<OBJECT classid="clsid:127698E4-E730-4E5C-A2b1-21490A70C8A1"
codebase="xenroll.dll"
id=XEnroll >
</OBJECT>
<form name="form1" id="form1" action="http://192.168.1.16/ejbca/democertreq" method="post" onsubmit="cert()">
<center>Certificate Enrollment Control Request 例子<br/><br/>
这里只演示使用XEnroll.dll(XP、Windows 2003的IE)来生成CSR的例子<br/>
在Vista,Windows 2008,Windows 7 的IE上需要使用CertEnroll.dll,与此类似<br/>
<!--ejbca 中设定的Certificate Profile-->
<input name="certificateprofile" value="liang" type="hidden">
<!--ejbca 中设定的End Entity Profile-->
<input name="entityprofile" value="liang" type="hidden"><br>
<!-- XEnroll.createPKCS10产生的CSR值 -->
<input name="pkcs10req" id="pkcs10req" type="hidden"><br>
<br>
<!-- DemoCertReqServlet需要user参数 -->
<input name="user" value="C=CN,O=yeeach.com,OU=yeeach.com,CN=liang" type="hidden">
<table>
<tr>
<td align="right">用户DN之Canonical Name(CN):</td><td><input name="canonical_name" value="liang" type="text"></td>
</tr>
<tr>
<td align="right">用户DN之Organization(O):</td><td><input name="organization" value="yeeach.com" type="text"></td>
</tr>
<tr>
<td align="right">用户DN之Organization Unit(C):</td><td><input name="organization_unit" value="R&D" type="text"></td>
</tr>
<tr>
<td align="right">用户DN之County(C):</td><td><input name="country" value="CN" type="text"></td>
</tr>
<tr>
<td align="right">用户密码:</td><td><input name="password" value="liang" type="password"></td>
</tr>
<tr>
<td align="right">邮箱:</td><td><input name="email" type="text" value="chuanliang@gmail.com"></td>
</tr>
<tr><td></td><td></td></tr>
<tr><td></td><td></td></tr>
<tr><td align="center" colspan="2"><input value="申请证书" name="submit" type="submit" ></td></tr>
</table>
<input name="includeemail" value="true" type="hidden">
</form>
<SCRIPT language="VBScript">
Sub cert
<!--
' Declare the distinguished name variable.
Dim strDN
' Declare the request variable.
Dim strReq
' Enable error handling.
On Error Resume Next
' Declare consts used by CertRequest object.
const CR_IN_BASE64 = &H1
const CR_IN_PKCS10 = &H100
' Build the DN.
strDN = "CN="+document.getElementById("canonical_name").value _
& ",OU="+document.getElementById("organization_unit").value _
& ",O="+document.getElementById("organization").value _
& ",C="+document.getElementById("country").value
' Attempt to use the control, in this case, to create a PKCS #10.
MsgBox("Creating PKCS #10 " & strDN)
document.getElementById("user").value=strDN
strReq = XEnroll.createPKCS10(strDN," ")
' If above line failed, Err.Number will not be 0.
if ( Err.Number <> 0 ) then
MsgBox("Error in call to createPKCS10 " & Err.Number)
err.clear
else
'MsgBox("Submitting request " & strReq)
' If the preceding line failed, Err.Number will not be 0.
if ( Err.Number <> 0 ) then
MsgBox("Error in Request Submit " & Err.Number)
err.clear
return false
else
document.getElementById("pkcs10req").value=_
"-----BEGIN NEW CERTIFICATE REQUEST-----" + _
CHR(13) + _
strReq + _
"-----END NEW CERTIFICATE REQUEST-----"
end if
end if
Exit Sub
End Sub
-->
</SCRIPT>
</body>
</html>
3、certInstTemplate.jsp
<!-- Header -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>EJBCA Certification Authority</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<script type="text/javascript" src="scripts/functions.js"></script>
<script type="text/vbscript" src="scripts/functions.vbs"></script>
</head>
<body>
<div class="main">
<div class="content">
<!-- Header -->
<object classid="$CLASSID" id="g_objClassFactory"></object>
<!-- Updated w CertEnroll for Vista
Class ID: {884e2049-217d-11da-b2a4-000e7bbb2b09}
-->
<!-- New updated enrollment activeX-control 2002-09-02 (Q323172)
New Xenroll.dll information:
Class ID: {127698e4-e730-4e5c-a2b1-21490a70c8a1}
sXEnrollVersion="5,131,3659,0"
New Scrdenrl.dll information:
Class ID: {c2bbea20-1f2b-492f-8a06-b1c5ffeace3b}
sScrdEnrlVersion="5,131,3642,0"
-->
<!-- Old Xenroll.dll information:
Class ID: {43F8F289-7A20-11D0-8F06-00C04FC295E1}
Old Scrdenrl.dll information:
Class ID: {80CB7887-20DE-11D2-8D5C-00C04FC29D45}
-->
<script language="VBScript" type="text/vbscript">
cert = "MIICdgYJKoZIhvcNAQcCoIICZzCCAmMCAQExADALBgkqhkiG9w0BBwGgggJLMIIC" & _
' This function can be moved to functions.vbs when the header is parsed as jsp
Sub installcertvista
Dim objEnroll
Set objEnroll = g_objClassFactory.CreateObject("X509Enrollment.CX509Enrollment")
Call objEnroll.Initialize(1) 'EnrollmentContext UserContext
err.clear
On Error Resume Next
Call objEnroll.InstallResponse(0, cert, 6, "") 'AllowNone, , XCN_CRYPT_STRING_BASE64_ANY, pw
If err.number = -2146762487 Then ' 0x800b0109 Not trusted root
r = Msgbox("Could not complete the request since, the CAs' certificates were not properly installed.", , "Certificate Management")
ElseIf err.number <> 0 Then
r = Msgbox("The certificate could not be installed", , "Certificate Management")
Else
r = Msgbox("A new certificate has been installed", , "Certificate Management")
End If
End Sub
Sub installcert
Err.Clear
On Error Resume Next
g_objClassFactory.acceptPKCS7(cert)
If Err.Number <> 0 Then
r = Msgbox("The certificate could not be installed in this web browser", , "Certificate Management")
Else
r = Msgbox ("A new certificate has been installed", , "Certificate Management")
End if
End Sub
If InStr(navigator.userAgent, "Windows NT 6") <> 0 Then
installcertvista
Else
installcert
End If
</script>
<h1 class="title">Internet Explorer Certificate enrollment.</h1>
<p>If the installation was completed without any errors, your certificate has
been installed in your web browser and you may now start using your certificate.<br />
You can look at your certificate with "<tt>Tools->Internet
Options->Content->Certificates</tt>".</p>
<!-- Footer -->
</div>
</div>
</body>
</html>
<!-- Footer -->
转自:http://www.yeeach.com/?p=949
======================================================================
通过这几天的摸索,得出了一个基本的证书模式:
1)证书的申请、提取必须用同一个客户端(即使同一个电脑不同的客户端发出的申请,不能互相交错提取);
2)审批通过的字符串,提取,只能提取一次,再次提取就会报错。
以上两点,保证了“证书谁申请谁提取”;“签发过的是一次性的——只能生成一个证书”不能重复利用