aboutsummaryrefslogtreecommitdiffstats
path: root/doc/ms3-ca.doc
blob: f8350aadc2de0e3a987a90f87cca484d67b7f609 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
Date: Mon, 9 Jun 97 08:00:33 +0200
From: Holger.Reif@PrakInf.TU-Ilmenau.DE (Holger Reif)
Subject: ms3-ca.doc
Organization: TU Ilmenau, Fak. IA, FG Telematik
Content-Length: 14575
Status: RO
X-Status: 

Loading client certs into MSIE 3.01
===================================

This document conatains all the information necessary to succesfully set up 
some scripts to issue client certs to Microsoft Internet Explorer. It 
includes the required knowledge about the model MSIE uses for client 
certification and includes complete sample scripts ready to play with. The 
scripts were tested against a modified ca program of SSLeay 0.6.6 and should 
work with the regular ca program that comes with version 0.8.0. I haven't 
tested against MSIE 4.0

You can use the information contained in this document in either way you 
want. However if you feel it saved you a lot of time I ask you to be as fair 
as to mention my name: Holger Reif <reif@prakinf.tu-ilmenau.de>.

1.) The model used by MSIE
--------------------------

The Internet Explorer doesn't come with a embedded engine for installing 
client certs like Netscape's Navigator. It rather uses the CryptoAPI (CAPI) 
defined by Microsoft. CAPI comes with WindowsNT 4.0 or is installed together 
with Internet Explorer since 3.01. The advantage of this approach is a higher 
flexibility because the certificates in the (per user) system open 
certificate store may be used by other applications as well. The drawback 
however is that you need to do a bit more work to get a client cert issued.

CAPI defines functions which will handle basic cryptographic work, eg. 
generating keys, encrypting some data, signing text or building a certificate 
request. The procedure is as follows: A CAPI function generates you a key 
pair and saves it into the certificate store. After that one builds a 
Distinguished Name. Together with that key pair another CAPI function forms a 
PKCS#10 request which you somehow need to submit to a CA. Finally the issued 
cert is given to a yet another CAPI function which saves it into the 
certificate store.

The certificate store with the user's keys and certs is in the registry. You 
will find it under HKEY_CURRENT_USER/Software/Microsoft/Cryptography/ (I 
leave it to you as a little exercise to figure out what all the entries mean 
;-). Note that the keys are protected only with the user's usual Windows 
login password.

2.) The practical usage
-----------------------

Unfortunatly since CAPI is a system API you can't access its functions from 
HTML code directly. For this purpose Microsoft provides a wrapper called 
certenr3.dll. This DLL accesses the CAPI functions and provides an interface 
usable from Visual Basic Script. One needs to install that library on the 
computer which wants to have client cert. The easiest way is to load it as an 
ActiveX control (certenr3.dll is properly authenticode signed by MS ;-). If 
you have ever enrolled e cert request at a CA you will have installed it.

At time of writing certenr3.dll is contained in 
http://www.microsoft.com/workshop/prog/security/csa/certenr3.exe. It comes 
with an README file which explains the available functions. It is labeled 
beta but every CA seems to use it anyway. The license.txt allows you the 
usage for your own purposes (as far as I understood) and a somehow limited 
distribution. 

The two functions of main interest are GenerateKeyPair and AcceptCredentials. 
For complete explanation of all possible parameters see the README file. Here 
are only minimal required parameters and their values.

GenerateKeyPair(sessionID, FASLE, szName, 0, "ClientAuth", TRUE, FALSE, 1)
- sessionID is a (locally to that computer) unique string to correlate the 
generated key pair with a cert installed later.
- szName is the DN of the form "C=DE; S=Thueringen; L=Ilmenau; CN=Holger 
Reif; 1.2.840.113549.1.9.1=reif@prakinf.tu-ilmenau.de". Note that S is the 
abreviation for StateOrProvince. The recognized abreviation include CN, O, C, 
OU, G, I, L, S, T. If the abreviation is unknown (eg. for PKCS#9 email addr) 
you need to use the full object identifier. The starting point for searching 
them could be crypto/objects.h since all OIDs know to SSLeay are listed 
there.
- note: the possible ninth parameter which should give a default name to the 
certificate storage location doesn't seem to work. Changes to the constant 
values in the call above doesn't seem to make sense. You can't generate 
PKCS#10 extensions with that function.

The result of GenerateKeyPair is the base64 encoded PKCS#10 request. However 
it has a little strange format that SSLeay doesn't accept. (BTW I feel the 
decision of rejecting that format as standard conforming.) It looks like 
follows:
	1st line with 76 chars
	2nd line with 76 chars
	...
	(n-2)th line with 76 chars
	(n-1)th line contains a multiple of 4 chars less then 76 (possible 
empty)
	(n)th line has zero or 4 chars (then with 1 or 2 equal signs - the 
		original text's lenght wasn'T a multiple of 3) 
	The line separator has two chars: 0x0d 0x0a

AcceptCredentials(sessionID, credentials, 0, FALSE)
- sessionID needs to be the same as while generating the key pair
- credentials is the base64 encoded PKCS#7 object containing the cert. 

CRL's and CA certs are not required simply just the client cert. (It seems to 
me that both are not even checked somehow.) The only format of the base64 
encoded object I succesfully used was all characters in a very long string 
without line feeds or carriage returns. (Hey, it doesn't matter, only a 
computer reads it!)

The result should be S_OK. For error handling see the example that comes with 
certenr3.dll.

A note about ASN.1 character encodings. certenr3.dll seems to know only about 
2 of them: UniversalString and PrintableString. First it is definitely wrong 
for an email address which is IA5STRING (checked by ssleay's ca). Second 
unfortunately MSIE (at least until version 3.02) can't handle UniversalString 
correctly - they just blow up you cert store! Therefore ssleay's ca (starting 
from version 0.8.0) tries to convert the encodings automatically to IA5STRING 
or TeletexString. The beef is it will work only for the latin-1 (western) 
charset. Microsoft still has to do abit of homework...

3.) An example
--------------

At least you need two steps: generating the key & request and then installing 
the certificate. A real world CA would have some more steps involved, eg. 
accepting some license. Note that both scripts shown below are just 
experimental state without any warrenty!

First how to generate a request. Note that we can't use a static page because 
of the sessionID. I generate it from system time plus pid and hope it is 
unique enough. Your are free to feed it through md5 to get more impressive 
ID's ;-) Then the intended text is read in with sed which inserts the 
sessionID. 

-----BEGIN ms-enroll.cgi-----
#!/bin/sh
SESSION_ID=`date '+%y%m%d%H%M%S'`$$
echo Content-type: text/html
echo
sed s/template_for_sessId/$SESSION_ID/ <<EOF
<HTML><HEAD>
<TITLE>Certificate Enrollment Test Page</TITLE>
</HEAD><BODY>

<OBJECT
    classid="clsid:33BEC9E0-F78F-11cf-B782-00C04FD7BF43"
    codebase=certenr3.dll
    id=certHelper
    >
</OBJECT>

<CENTER>
<H2>enrollment for a personal cert</H2>
<BR><HR WIDTH=50%><BR><P>
<FORM NAME="MSIE_Enrollment" ACTION="ms-gencert.cgi" ENCTYPE=x-www-form-
encoded METHOD=POST>
<TABLE>
    <TR><TD>Country</TD><TD><INPUT NAME="Country" VALUE=""></TD></TR>
    <TR><TD>State</TD><TD><INPUT NAME="StateOrProvince" VALUE=""></TD></TR>
    <TR><TD>Location</TD><TD><INPUT NAME="Location" VALUE=""></TD></TR>
    <TR><TD>Organization</TD><TD><INPUT NAME="Organization" 
VALUE=""></TD></TR>
    <TR><TD>Organizational Unit</TD>
        <TD><INPUT NAME="OrganizationalUnit" VALUE=""></TD></TR>
    <TR><TD>Name</TD><TD><INPUT NAME="CommonName" VALUE=""></TD></TR>
    <TR><TD>eMail Address</TD>
        <TD><INPUT NAME="EmailAddress" VALUE=""></TD></TR>
    <TR><TD></TD>
        <TD><INPUT TYPE="BUTTON" NAME="submit" VALUE="Beantragen"></TD></TR>
</TABLE>
	<INPUT TYPE="hidden" NAME="SessionId" VALUE="template_for_sessId">
	<INPUT TYPE="hidden" NAME="Request" VALUE="">
</FORM>
<BR><HR WIDTH=50%><BR><P>
</CENTER>

<SCRIPT LANGUAGE=VBS>
    Dim DN

    Sub Submit_OnClick
	Dim TheForm
	Set TheForm = Document.MSIE_Enrollment
	sessionId	= TheForm.SessionId.value
	reqHardware     = FALSE
	C		= TheForm.Country.value
	SP		= TheForm.StateOrProvince.value
	L		= TheForm.Location.value
	O		= TheForm.Organization.value
	OU		= TheForm.OrganizationalUnit.value
	CN              = TheForm.CommonName.value
	Email		= TheForm.EmailAddress.value
        szPurpose       = "ClientAuth"
        doAcceptanceUINow   = FALSE
        doOnline        = TRUE

	DN = ""

	Call Add_RDN("C", C)
	Call Add_RDN("S", SP)
	Call Add_RDN("L", L)
	Call Add_RDN("O", O)
	Call Add_RDN("OU", OU)
	Call Add_RDN("CN", CN)
	Call Add_RDN("1.2.840.113549.1.9.1", Email)
		      ' rsadsi
				     ' pkcs
				       ' pkcs9
					 ' eMailAddress
        On Error Resume Next
        sz10 = certHelper.GenerateKeyPair(sessionId, _
                FALSE, DN, 0, ClientAuth, FASLE, TRUE, 1)_
        theError = Err.Number
        On Error Goto 0
        if (sz10 = Empty OR theError <> 0) Then
            sz = "The error '" & Hex(theError) & "' occurred." & chr(13) & _
                chr(10) & "Your credentials could not be generated."
            result = MsgBox(sz, 0, "Credentials Enrollment")
            Exit Sub
	else 
	    TheForm.Request.value = sz10
	    TheForm.Submit
        end if
    End Sub

    Sub Add_RDN(sn, value)
	if (value <> "") then
	    if (DN <> "") then
		DN = DN & "; "
	    end if
	    DN = DN & sn & "=" & value
	end if
    End Sub
</SCRIPT>
</BODY>
</HTML>
EOF
-----END ms-enroll.cgi-----

Second, how to extract the request and feed the certificate back? We need to 
"normalize" the base64 encoding of the PKCS#10 format which means 
regenerating the lines and wrapping with BEGIN and END line. This is done by 
gawk. The request is taken by ca the normal way. Then the cert needs to be 
packed into a PKCS#7 structure (note: the use of a CRL is necessary for 
crl2pkcs7 as of version 0.6.6. Starting with 0.8.0 it it might probably be 
ommited). Finally we need to format the PKCS#7 object and generate the HTML 
text. I use two templates to have a clearer script.

1st note: postit2 is slightly modified from a program I found at ncsa's ftp 
site. Grab it from http://www.easterngraphics.com/certs/IX9704/postit2.c. You 
need utils.c from there too.

2nd note: I'm note quite sure wether the gawk script really handles all 
possible inputs for the request right! Today I don't use this construction 
anymore myself.

3d note: the cert must be of version 3! This could be done with the nsComment 
line in ssleay.cnf...

------BEGIN ms-gencert.cgi-----
#!/bin/sh
FILE="/tmp/"`date '+%y%m%d%H%M%S'-`$$
rm -f "$FILE".*

HOME=`pwd`; export HOME  # as ssleay.cnf insists on having such an env var
cd /usr/local/ssl #where demoCA (as named in ssleay.conf) is located

postit2 -s " " -i 0x0d > "$FILE".inp  # process the FORM vars

SESSION_ID=`gawk '$1 == "SessionId" { print $2; exit }' "$FILE".inp`

gawk \
	'BEGIN { \
		OFS = ""; \
		print "-----BEGIN CERTIFICATE REQUEST-----"; \
		req_seen=0 \
	} \
	$1 == "Request" { \
		req_seen=1; \
		if (length($2) == 72) print($2); \
		lastline=$2; \
		next; \
	} \
	{ \
		if (req_seen == 1) { \
			if (length($1) >= 72) print($1); \
			else if (length(lastline) < 72) { \
				req_seen=0; \
				print (lastline,$1); \
			} \
		lastline=$1; \
		} \
	} \
	END { \
		print "-----END CERTIFICATE REQUEST-----"; \
	}' > "$FILE".pem < "$FILE".inp 

ssleay ca -batch -in "$FILE".pem -key passwd -out "$FILE".out
ssleay crl2pkcs7 -certfile "$FILE".out -out "$FILE".pkcs7 -in demoCA/crl.pem

sed s/template_for_sessId/$SESSION_ID/ <ms-enroll2a.html >"$FILE".cert
/usr/local/bin/gawk \
	'BEGIN	{ \
		OFS = ""; \
		dq = sprintf("%c",34); \
	} \
	$0 ~ "PKCS7" { next; } \
	{ \
		print dq$0dq" & _"; \
	}' <"$FILE".pkcs7 >> "$FILE".cert
cat  ms-enroll2b.html >>"$FILE".cert

echo Content-type: text/html
echo Content-length: `wc -c "$FILE".cert`
echo
cat "$FILE".cert
rm -f "$FILE".*
-----END ms-gencert.cgi-----

----BEGIN ms-enroll2a.html----
<HTML><HEAD><TITLE>Certificate Acceptance Test Page</TITLE></HEAD><BODY>

<OBJECT
    classid="clsid:33BEC9E0-F78F-11cf-B782-00C04FD7BF43"
    codebase=certenr3.dll
    id=certHelper
    >
</OBJECT>

<CENTER>
<H2>Your personal certificate</H2>
<BR><HR WIDTH=50%><BR><P>
Press the button!
<P><INPUT TYPE=BUTTON VALUE="Nimm mich!" NAME="InstallCert">
</CENTER>
<BR><HR WIDTH=50%><BR>

<SCRIPT LANGUAGE=VBS>
    Sub InstallCert_OnClick

	sessionId	= "template_for_sessId"
credentials = "" & _
----END ms-enroll2a.html----

----BEGIN ms-enroll2b.html----
""
        On Error Resume Next
        result = certHelper.AcceptCredentials(sessionId, credentials, 0, 
FALSE)
        if (IsEmpty(result)) Then
           sz = "The error '" & Err.Number & "' occurred." & chr(13) & 
chr(10) & "This Digital ID could not be registered."
           msgOut = MsgBox(sz, 0, "Credentials Registration Error")
           navigate "error.html"
        else
           sz = "Digital ID successfully registered."
           msgOut = MsgBox(sz, 0, "Credentials Registration")
           navigate "success.html"
        end if
	Exit Sub
    End Sub
</SCRIPT>
</BODY>
</HTML>
----END ms-enroll2b.html----

4.) What do do with the cert?
-----------------------------

The cert is visible (without restarting MSIE) under the following menu:
View->Options->Security->Personal certs. You can examine it's contents at 
least partially.

To use it for client authentication you need to use SSL3.0 (fortunately 
SSLeay supports it with 0.8.0). Furthermore MSIE is told to only supports a 
kind of automatic selection of certs (I personally wasn't able to test it 
myself). But there is a requirement that the issuer of the server cert and 
the issuer of the client cert needs to be the same (according to a developer 
from MS). Which means: you need may more then one cert to talk to all 
servers...

I'm sure we will get a bit more experience after ApacheSSL is available for 
SSLeay 0.8.8.


I hope you enjoyed reading and that in future questions on this topic will 
rarely appear on ssl-users@moncom.com ;-)

Ilmenau, 9th of June 1997
Holger Reif <reif@prakinf.tu-ilmenau.de>
-- 
read you later  -  Holger Reif
----------------------------------------  Signaturprojekt Deutsche Einheit
TU Ilmenau - Informatik - Telematik                      (Verdamp lang her)
Holger.Reif@PrakInf.TU-Ilmenau.DE         Alt wie ein Baum werden, um ueber
http://Remus.PrakInf.TU-Ilmenau.DE/Reif/  alle 7 Bruecken gehen zu koennen