openssl/doc/ms3-ca.doc

399 lines
14 KiB
Plaintext
Raw Normal View History

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