1 Date: Mon, 9 Jun 97 08:00:33 +0200
2 From: Holger.Reif@PrakInf.TU-Ilmenau.DE (Holger Reif)
4 Organization: TU Ilmenau, Fak. IA, FG Telematik
9 Loading client certs into MSIE 3.01
10 ===================================
12 This document conatains all the information necessary to succesfully set up
13 some scripts to issue client certs to Microsoft Internet Explorer. It
14 includes the required knowledge about the model MSIE uses for client
15 certification and includes complete sample scripts ready to play with. The
16 scripts were tested against a modified ca program of SSLeay 0.6.6 and should
17 work with the regular ca program that comes with version 0.8.0. I haven't
18 tested against MSIE 4.0
20 You can use the information contained in this document in either way you
21 want. However if you feel it saved you a lot of time I ask you to be as fair
22 as to mention my name: Holger Reif <reif@prakinf.tu-ilmenau.de>.
24 1.) The model used by MSIE
25 --------------------------
27 The Internet Explorer doesn't come with a embedded engine for installing
28 client certs like Netscape's Navigator. It rather uses the CryptoAPI (CAPI)
29 defined by Microsoft. CAPI comes with WindowsNT 4.0 or is installed together
30 with Internet Explorer since 3.01. The advantage of this approach is a higher
31 flexibility because the certificates in the (per user) system open
32 certificate store may be used by other applications as well. The drawback
33 however is that you need to do a bit more work to get a client cert issued.
35 CAPI defines functions which will handle basic cryptographic work, eg.
36 generating keys, encrypting some data, signing text or building a certificate
37 request. The procedure is as follows: A CAPI function generates you a key
38 pair and saves it into the certificate store. After that one builds a
39 Distinguished Name. Together with that key pair another CAPI function forms a
40 PKCS#10 request which you somehow need to submit to a CA. Finally the issued
41 cert is given to a yet another CAPI function which saves it into the
44 The certificate store with the user's keys and certs is in the registry. You
45 will find it under HKEY_CURRENT_USER/Software/Microsoft/Cryptography/ (I
46 leave it to you as a little exercise to figure out what all the entries mean
47 ;-). Note that the keys are protected only with the user's usual Windows
50 2.) The practical usage
51 -----------------------
53 Unfortunatly since CAPI is a system API you can't access its functions from
54 HTML code directly. For this purpose Microsoft provides a wrapper called
55 certenr3.dll. This DLL accesses the CAPI functions and provides an interface
56 usable from Visual Basic Script. One needs to install that library on the
57 computer which wants to have client cert. The easiest way is to load it as an
58 ActiveX control (certenr3.dll is properly authenticode signed by MS ;-). If
59 you have ever enrolled e cert request at a CA you will have installed it.
61 At time of writing certenr3.dll is contained in
62 http://www.microsoft.com/workshop/prog/security/csa/certenr3.exe. It comes
63 with an README file which explains the available functions. It is labeled
64 beta but every CA seems to use it anyway. The license.txt allows you the
65 usage for your own purposes (as far as I understood) and a somehow limited
68 The two functions of main interest are GenerateKeyPair and AcceptCredentials.
69 For complete explanation of all possible parameters see the README file. Here
70 are only minimal required parameters and their values.
72 GenerateKeyPair(sessionID, FASLE, szName, 0, "ClientAuth", TRUE, FALSE, 1)
73 - sessionID is a (locally to that computer) unique string to correlate the
74 generated key pair with a cert installed later.
75 - szName is the DN of the form "C=DE; S=Thueringen; L=Ilmenau; CN=Holger
76 Reif; 1.2.840.113549.1.9.1=reif@prakinf.tu-ilmenau.de". Note that S is the
77 abreviation for StateOrProvince. The recognized abreviation include CN, O, C,
78 OU, G, I, L, S, T. If the abreviation is unknown (eg. for PKCS#9 email addr)
79 you need to use the full object identifier. The starting point for searching
80 them could be crypto/objects.h since all OIDs know to SSLeay are listed
82 - note: the possible ninth parameter which should give a default name to the
83 certificate storage location doesn't seem to work. Changes to the constant
84 values in the call above doesn't seem to make sense. You can't generate
85 PKCS#10 extensions with that function.
87 The result of GenerateKeyPair is the base64 encoded PKCS#10 request. However
88 it has a little strange format that SSLeay doesn't accept. (BTW I feel the
89 decision of rejecting that format as standard conforming.) It looks like
91 1st line with 76 chars
92 2nd line with 76 chars
94 (n-2)th line with 76 chars
95 (n-1)th line contains a multiple of 4 chars less then 76 (possible
97 (n)th line has zero or 4 chars (then with 1 or 2 equal signs - the
98 original text's lenght wasn'T a multiple of 3)
99 The line separator has two chars: 0x0d 0x0a
101 AcceptCredentials(sessionID, credentials, 0, FALSE)
102 - sessionID needs to be the same as while generating the key pair
103 - credentials is the base64 encoded PKCS#7 object containing the cert.
105 CRL's and CA certs are not required simply just the client cert. (It seems to
106 me that both are not even checked somehow.) The only format of the base64
107 encoded object I succesfully used was all characters in a very long string
108 without line feeds or carriage returns. (Hey, it doesn't matter, only a
111 The result should be S_OK. For error handling see the example that comes with
114 A note about ASN.1 character encodings. certenr3.dll seems to know only about
115 2 of them: UniversalString and PrintableString. First it is definitely wrong
116 for an email address which is IA5STRING (checked by ssleay's ca). Second
117 unfortunately MSIE (at least until version 3.02) can't handle UniversalString
118 correctly - they just blow up you cert store! Therefore ssleay's ca (starting
119 from version 0.8.0) tries to convert the encodings automatically to IA5STRING
120 or TeletexString. The beef is it will work only for the latin-1 (western)
121 charset. Microsoft still has to do abit of homework...
126 At least you need two steps: generating the key & request and then installing
127 the certificate. A real world CA would have some more steps involved, eg.
128 accepting some license. Note that both scripts shown below are just
129 experimental state without any warrenty!
131 First how to generate a request. Note that we can't use a static page because
132 of the sessionID. I generate it from system time plus pid and hope it is
133 unique enough. Your are free to feed it through md5 to get more impressive
134 ID's ;-) Then the intended text is read in with sed which inserts the
137 -----BEGIN ms-enroll.cgi-----
139 SESSION_ID=`date '+%y%m%d%H%M%S'`$$
140 echo Content-type: text/html
142 sed s/template_for_sessId/$SESSION_ID/ <<EOF
144 <TITLE>Certificate Enrollment Test Page</TITLE>
148 classid="clsid:33BEC9E0-F78F-11cf-B782-00C04FD7BF43"
149 codebase=certenr3.dll
155 <H2>enrollment for a personal cert</H2>
156 <BR><HR WIDTH=50%><BR><P>
157 <FORM NAME="MSIE_Enrollment" ACTION="ms-gencert.cgi" ENCTYPE=x-www-form-
160 <TR><TD>Country</TD><TD><INPUT NAME="Country" VALUE=""></TD></TR>
161 <TR><TD>State</TD><TD><INPUT NAME="StateOrProvince" VALUE=""></TD></TR>
162 <TR><TD>Location</TD><TD><INPUT NAME="Location" VALUE=""></TD></TR>
163 <TR><TD>Organization</TD><TD><INPUT NAME="Organization"
165 <TR><TD>Organizational Unit</TD>
166 <TD><INPUT NAME="OrganizationalUnit" VALUE=""></TD></TR>
167 <TR><TD>Name</TD><TD><INPUT NAME="CommonName" VALUE=""></TD></TR>
168 <TR><TD>eMail Address</TD>
169 <TD><INPUT NAME="EmailAddress" VALUE=""></TD></TR>
171 <TD><INPUT TYPE="BUTTON" NAME="submit" VALUE="Beantragen"></TD></TR>
173 <INPUT TYPE="hidden" NAME="SessionId" VALUE="template_for_sessId">
174 <INPUT TYPE="hidden" NAME="Request" VALUE="">
176 <BR><HR WIDTH=50%><BR><P>
179 <SCRIPT LANGUAGE=VBS>
184 Set TheForm = Document.MSIE_Enrollment
185 sessionId = TheForm.SessionId.value
187 C = TheForm.Country.value
188 SP = TheForm.StateOrProvince.value
189 L = TheForm.Location.value
190 O = TheForm.Organization.value
191 OU = TheForm.OrganizationalUnit.value
192 CN = TheForm.CommonName.value
193 Email = TheForm.EmailAddress.value
194 szPurpose = "ClientAuth"
195 doAcceptanceUINow = FALSE
201 Call Add_RDN("S", SP)
204 Call Add_RDN("OU", OU)
205 Call Add_RDN("CN", CN)
206 Call Add_RDN("1.2.840.113549.1.9.1", Email)
212 sz10 = certHelper.GenerateKeyPair(sessionId, _
213 FALSE, DN, 0, ClientAuth, FASLE, TRUE, 1)_
214 theError = Err.Number
216 if (sz10 = Empty OR theError <> 0) Then
217 sz = "The error '" & Hex(theError) & "' occurred." & chr(13) & _
218 chr(10) & "Your credentials could not be generated."
219 result = MsgBox(sz, 0, "Credentials Enrollment")
222 TheForm.Request.value = sz10
227 Sub Add_RDN(sn, value)
228 if (value <> "") then
232 DN = DN & sn & "=" & value
239 -----END ms-enroll.cgi-----
241 Second, how to extract the request and feed the certificate back? We need to
242 "normalize" the base64 encoding of the PKCS#10 format which means
243 regenerating the lines and wrapping with BEGIN and END line. This is done by
244 gawk. The request is taken by ca the normal way. Then the cert needs to be
245 packed into a PKCS#7 structure (note: the use of a CRL is necessary for
246 crl2pkcs7 as of version 0.6.6. Starting with 0.8.0 it it might probably be
247 ommited). Finally we need to format the PKCS#7 object and generate the HTML
248 text. I use two templates to have a clearer script.
250 1st note: postit2 is slightly modified from a program I found at ncsa's ftp
251 site. Grab it from http://www.easterngraphics.com/certs/IX9704/postit2.c. You
252 need utils.c from there too.
254 2nd note: I'm note quite sure wether the gawk script really handles all
255 possible inputs for the request right! Today I don't use this construction
258 3d note: the cert must be of version 3! This could be done with the nsComment
259 line in ssleay.cnf...
261 ------BEGIN ms-gencert.cgi-----
263 FILE="/tmp/"`date '+%y%m%d%H%M%S'-`$$
266 HOME=`pwd`; export HOME # as ssleay.cnf insists on having such an env var
267 cd /usr/local/ssl #where demoCA (as named in ssleay.conf) is located
269 postit2 -s " " -i 0x0d > "$FILE".inp # process the FORM vars
271 SESSION_ID=`gawk '$1 == "SessionId" { print $2; exit }' "$FILE".inp`
276 print "-----BEGIN CERTIFICATE REQUEST-----"; \
281 if (length($2) == 72) print($2); \
286 if (req_seen == 1) { \
287 if (length($1) >= 72) print($1); \
288 else if (length(lastline) < 72) { \
290 print (lastline,$1); \
296 print "-----END CERTIFICATE REQUEST-----"; \
297 }' > "$FILE".pem < "$FILE".inp
299 ssleay ca -batch -in "$FILE".pem -key passwd -out "$FILE".out
300 ssleay crl2pkcs7 -certfile "$FILE".out -out "$FILE".pkcs7 -in demoCA/crl.pem
302 sed s/template_for_sessId/$SESSION_ID/ <ms-enroll2a.html >"$FILE".cert
303 /usr/local/bin/gawk \
306 dq = sprintf("%c",34); \
308 $0 ~ "PKCS7" { next; } \
310 print dq$0dq" & _"; \
311 }' <"$FILE".pkcs7 >> "$FILE".cert
312 cat ms-enroll2b.html >>"$FILE".cert
314 echo Content-type: text/html
315 echo Content-length: `wc -c "$FILE".cert`
319 -----END ms-gencert.cgi-----
321 ----BEGIN ms-enroll2a.html----
322 <HTML><HEAD><TITLE>Certificate Acceptance Test Page</TITLE></HEAD><BODY>
325 classid="clsid:33BEC9E0-F78F-11cf-B782-00C04FD7BF43"
326 codebase=certenr3.dll
332 <H2>Your personal certificate</H2>
333 <BR><HR WIDTH=50%><BR><P>
335 <P><INPUT TYPE=BUTTON VALUE="Nimm mich!" NAME="InstallCert">
337 <BR><HR WIDTH=50%><BR>
339 <SCRIPT LANGUAGE=VBS>
340 Sub InstallCert_OnClick
342 sessionId = "template_for_sessId"
344 ----END ms-enroll2a.html----
346 ----BEGIN ms-enroll2b.html----
349 result = certHelper.AcceptCredentials(sessionId, credentials, 0,
351 if (IsEmpty(result)) Then
352 sz = "The error '" & Err.Number & "' occurred." & chr(13) &
353 chr(10) & "This Digital ID could not be registered."
354 msgOut = MsgBox(sz, 0, "Credentials Registration Error")
355 navigate "error.html"
357 sz = "Digital ID successfully registered."
358 msgOut = MsgBox(sz, 0, "Credentials Registration")
359 navigate "success.html"
366 ----END ms-enroll2b.html----
368 4.) What do do with the cert?
369 -----------------------------
371 The cert is visible (without restarting MSIE) under the following menu:
372 View->Options->Security->Personal certs. You can examine it's contents at
375 To use it for client authentication you need to use SSL3.0 (fortunately
376 SSLeay supports it with 0.8.0). Furthermore MSIE is told to only supports a
377 kind of automatic selection of certs (I personally wasn't able to test it
378 myself). But there is a requirement that the issuer of the server cert and
379 the issuer of the client cert needs to be the same (according to a developer
380 from MS). Which means: you need may more then one cert to talk to all
383 I'm sure we will get a bit more experience after ApacheSSL is available for
387 I hope you enjoyed reading and that in future questions on this topic will
388 rarely appear on ssl-users@moncom.com ;-)
390 Ilmenau, 9th of June 1997
391 Holger Reif <reif@prakinf.tu-ilmenau.de>
393 read you later - Holger Reif
394 ---------------------------------------- Signaturprojekt Deutsche Einheit
395 TU Ilmenau - Informatik - Telematik (Verdamp lang her)
396 Holger.Reif@PrakInf.TU-Ilmenau.DE Alt wie ein Baum werden, um ueber
397 http://Remus.PrakInf.TU-Ilmenau.DE/Reif/ alle 7 Bruecken gehen zu koennen