The object of what follows is to make a version of Apache that handles the HTTPS (HTTP over SSL) protocol. Currently this is only available in Unix versions, and given the many concerns that exist over the security of Win32, there seems little point in trying to implement SSL in the Win32 version of Apache.
The first step is to get hold of the appropriate version of Apache; see Chapter 1, "Getting Started", and the Apache-SSL home page at http://www.apache-ssl.org/ for current information. Download the source code, or copy it from the demonstration CD-ROM, and expand the files in some suitable directory. An src subdirectory will appear. So far, so good.
The next, and easiest step of all, is to decide whether you are in the United States and Canada or the rest of the world. Then follow these guidelines:
You have two choices. You can get a commercial SSL-enabled web server, or you can do what the rest of the world does (see below), noting only that you need to get a license to use RSA's patents if you want to make money out of your SSL-enabled Apache (see www.rsa.com).
If your deliberations lead you to believe that you live in the rest of the world, proceed as described in the following sections.
The first thing to do is to get SSLeay. SSLeay is a a freely available library, written by the Australian Eric Young, which does pretty much everything cryptological that the most secretive heart could desire. We went to ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/ (which seems to belong to the psychology department of the University of Queensland, Australia, and why should we quibble?), downloaded SSLeay-0_9_0b_tar.gz since it looked the freshest, and put it into /usr/local/etc/SSL. We uncompressed it with:
% gzip -d SSLeay-0_9_0b_tar.gz % tar xvf SSLeay-0_9_0b_tar
producing a surprising amount of stuff in a subdirectory SSLeay-0.9.0b. Go there. First, read INSTALL, which describes a configuration process not unlike that for Apache, but somewhat rougher. Things will go more smoothly if you have already liberated Perl and it is in /usr/local/bin. The script will put SSL in /usr/local/bin; if you don't like this, you can change its home. You are told to run ./Configure system type but, slightly alarmingly, INSTALL doesn't tell you what the possible system types are. However, we remember that if anything goes wrong, we can just go back to the top directory, run tar again to start over, and boldly type:
% ./Configure
A list of systems appears, among which is FreeBSD and, we hope, yours. We ran ./Configure again:
% ./Configure FreeBSD
This sets up a number of system variables and reports them to the screen. As long as there is not an obvious error, we don't really care what it says. INSTALL then tells us to tidy up the place, make SSL, make the test certificate, and test the result by using these four commands:
% make clean % make % make rehash % make test
Again, a lot of prattle outputs to the screen that is probably really interesting if you are Eric Young, and less fascinating otherwise. The output ends with a printout of your signed certificate, newcert.pem.
And then we perform the final step recommended in INSTALL :
% make install
It turned out that ssleay hadn't been installed in /usr/local/bin as promised, but was in /usr/local/ssl/bin. This may have been fixed by the time you do all this, but if not, add the new directory to your path. Just how you do this depends on the shell you are running, so we won't confuse you with advice that may be inappropriate. See your administrator in case of difficulty.
It is important that if you have already made Apache you should delete the whole directory with:
% rm -R apache directory
Reexpand the original Apache .tar file to create a complete directory (see Section 1.8, "Making Apache Under Unix", in Chapter 1, "Getting Started" ) and download the Apache-SSL patch file from Oxford University: ftp://ftp.ox.ac.uk/pub/crypto/SSL/ or one of the mirror sites. It is important that the file you download is as new as you can get and matches the Apache version you have just expanded. The reason you should reexpand Apache is that Apache-SSL has to patch the source of Apache, so it must be "as-new."[69] In our case we got apache_1_3_1+ssl_1_22_tar.gz, copied it into the ... /apache/apache_1.3.1 subdirectory (not the .../src subdirectory, as in the previous edition), and expanded it with:
[69]To answer a FAQ: No, Apache-SSL cannot be a pure module; the Apache API is not powerful enough to permit that.
% gzip -d apache_1_3_1+ssl_1_22_tar.gz % tar xvf apache_1_3_1+ssl_1_22_tar
You find a number of *.SSL files. The immediately interesting one is README.SSL, written by one of the authors of this book (BL), which you should, of course, read.
The next step is to do as instructed in README.SSL :
% ./FixPatch
You will be asked if you want the patch applied, to which you reply y. A good deal of chat ensues on the screen, but as long as it does not stop with an error, all is well.[70]
[70]Note that some operating systems (notably Solaris) come with an exceedingly out-of-date version of patch, which doesn't work properly with Apache-SSL's patch files. The current version of patch at the time of writing is 2.5.
patch is a Unix utility. If you get the message:
Looks like a new style context diff File to patch:
and not much else, you may have an out-of-date version of patch. You can get the version number by typing:
% patch -version
If you have a version earlier than 2.1, you need to upgrade. If you have 2.5 and you still have problems, you may find that:
% patch -pl < SSLpatch
will work.
A useful site, which has FAQs about Apache-SSL, is www.apache-ssl.org.
You then have to rebuild Apache. Since you have replaced all the files, including the original Configuration, you may want to copy the version you saved in the top directory (see Section 1.8.4, "Configuration Settings and Rules", in Chapter 1, "Getting Started") back down. Check that this line in this file has been correctly altered:
SSL_BASE=<current location of SSL>
This should be the directory where SSLeay has unpacked itself -- in our case /usr/local/etc/SSL/SSLeay-0.9.0b.
Run ./Configure to remake the Makefile, and then make to compile the code. The end result, if all has gone well, is an executable: httpsd. Copy it into /usr/local/bin next to httpd.
We now need a test certificate. .../apache_1.3.1/src/Makefile has the necessary commands in the section headed "certificate":
certificate: $(SSL_APP_DIR)/ssleay req -config ../SSLconf/conf/ssleay.cnf \ -new -x509 -nodes -out ../SSLconf/conf/httpsd.pem \ -keyout ../SSLconf/conf/httpsd.pem; \ ln -sf ../SSLconf/conf/httpsd.pem ../SSLconf/conf/`$(SSL_APP_DIR)/ssleay \ x509 -noout -hash < ../SSLconf/conf/httpsd.pem`.0
Now type:
% make certificate
A number of questions appear about who and where you are:
/usr/local/etc/SSL/SSLeay-0.9.0b/apps/ssleay req -config ../SSLconf/conf/
ssleay.cnf -new -x509 -nodes -out ../SSLconf/conf/httpsd.pem -keyout ../
SSLconf/conf/httpsd.pem; ln -sf ../SSLconf/conf/httpsd.pem ../SSLconf/conf/
`/usr/local/etc/SSL/SSLeay-0.9.0b/apps/ssleay x509 -noout -hash < ../SSLconf/conf/httpsd.pem`.0
Generating a 1024 bit RSA private key
...........+++++
...........+++++
writing new private key to '../SSLconf/conf/httpsd.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank.
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [GB]:US
State or Province Name (full name) [Some-State]:Nevada
Locality Name (eg, city) []:Hopeful City
Organization Name (eg, company; recommended) []:Butterthlies Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (eg, ssl.domain.tld; required!!!) []:www.butterthlies.com
Email Address []:[email protected]
Your inputs are shown in bold type in the usual way. The only one that really matters is "Common Name," which must be the fully qualified domain name (FQDN) of your server. This has to be correct because your client's Netscapes (and presumably other security-conscious browsers) will check to see that this address is the same as that being accessed. The result is the file ... /conf/httpsd.pem (yours should not be identical to this, of course):
-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDBpDjpJQxvcPRdhNOflTOCyQp1Dhg0kBruGAHiwxYYHdlM/z6k pi8EJFvvkoYdesTVzM+6iABQbk9fzvnG5apxy8aB+byoKZ575ce2Rg43i3KNTXY+ RXUzy/5HIiL0JtX/oCESGKt5W/xd8G/xoKR5Qe0P+1hgjASF2p97NUhtOQIDAQAB AoGALIh4DiZXFcoEaP2DLdBCaHGT1hfHuU7q4pbi2CPFkQZMU0jgPz140psKCa7I 6T6yxfi0TVG5wMWdu4r+Jp/q8ppQ94MUB5oOKSb/Kv2vsZ+T0ZCBnpzt1eia9ypX ELTZhngFGkuq7mHNGlMyviIcq6Qct+gxd9omPsd53W0th4ECQQDmyHpqrrtaVlw8 aGXbTzlXp14Bq5RG9Ro1eibhXId3sHkIKFKDAUEjzkMGzUm7Y7DLbCOD/hdFV6V+ pjwCvNgDAkEA1szPPD4eB/tuqCTZ+2nxcR6YqpUkT9FPBAV9Gwe7Svbct0yu/nny bpv2fcurWJGI23UIpWScyBEBR/z34El3EwJBALdw8YVtIHT9IlHN9fCt93mKCrov JSyF1PBfCRqnTvK/bmUij/ub+qg4YqS8dvghlL0NVumrBdpTgbO69QaEDvsCQDVe P6MNH/MFwnGeblZr9SQQ4QeI9LOsIoCySGod2qf+e8pDEDuD2vsmXvDUWKcxyZoV Eufc/qMqrnHPZVrhhecCQCsP6nb5Aku2dbhX+TdYQZZDoRE2mkykjWdK+B22C2/4 C5VTb4CUF7d6ukDVMT2d0/SiAVHBEI2dR8Vw0G7hJPY= -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIICvTCCAiYCAQAwDQYJKoZIhvcNAQEEBQAwgaYxCzAJBgNVBAYTAlVTMQ8wDQYD VQQIEwZOZXZhZGExFTATBgNVBAcTDEhvcGVmdWwgQ2l0eTEZMBcGA1UEChMQQnV0 dGVydGhsaWVzIEluYzEOMAwGA1UECxMFU2FsZXMxHTAbBgNVBAMTFHd3dy5idXR0 ZXJ0aGxpZXMuY29tMSUwIwYJKoZIhvcNAQkBFhZzYWxlc0BidXR0ZXJ0aGxpZXMu Y29tMB4XDTk4MDgyNjExNDUwNFoXDTk4MDkyNTExNDUwNFowgaYxCzAJBgNVBAYT AlVTMQ8wDQYDVQQIEwZOZXZhZGExFTATBgNVBAcTDEhvcGVmdWwgQ2l0eTEZMBcG A1UEChMQQnV0dGVydGhsaWVzIEluYzEOMAwGA1UECxMFU2FsZXMxHTAbBgNVBAMT FHd3dy5idXR0ZXJ0aGxpZXMuY29tMSUwIwYJKoZIhvcNAQkBFhZzYWxlc0BidXR0 ZXJ0aGxpZXMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBpDjpJQxv cPRdhNOflTOCyQp1Dhg0kBruGAHiwxYYHdlM/z6kpi8EJFvvkoYdesTVzM+6iABQ bk9fzvnG5apxy8aB+byoKZ575ce2Rg43i3KNTXY+RXUzy/5HIiL0JtX/oCESGKt5 W/xd8G/xoKR5Qe0P+1hgjASF2p97NUhtOQIDAQABMA0GCSqGSIb3DQEBBAUAA4GB AIrQjOfQTeOHXBS+zcXy9OWpgcfyxI5GQBg6VWlRlhthEtYDSdyNq9hrAT/TGUwd Jm/whjGLtD7wPx6c0mR/xsoWWoEVa2hIQJhDlwmnXk1F3M55ZA3Cfg0/qb8smeTx 7kM1LoxQjZL0bg61Av3WG/TtuGqYshpE09eu77ANLngp -----END CERTIFICATE-----
This is, in fact, rather an atypical certificate, because it combines our private key with the certificate, whereas you would probably want to apply more stringent security to the private key than to the certificate. Also, it is signed by ourselves, making it a root certification authority certificate; this is just a convenience for test purposes. In the real world, root CAs are likely to be somewhat more impressive organizations than little old us.
This certificate also is without a passphrase, which httpsd would otherwise ask for at startup. We think a passphrase is a bad idea because it prevents automatic server restarts, but if you want to make yourself a certificate that incorporates one, edit Makefile (remembering to reedit if you run Configuration again), find the "certificate:" section, remove the -nodes flag and proceed as before. Or, follow this procedure, which will also be useful when we ask Thawte for a demo certificate. Go to wherever you need the results -- .../site.ssl/conf would be good. Type:
% ssleay req -new -outform PEM> new3.cert.csr ... writing new private key to 'privkey.pem' enter PEM pass phrase:
Type in your passphrase and then answer the questions as before. This generates a Certificate Signing Request (CSR) with your passphrase encrypted into it. You will need this if you want to get a server certificate, together with the key file privkey.pem.
However, if you then decide you don't want a passphrase after all, you can remove it with:
% ssleay -in privkey.pem -out new3.cert.key
Either way, you then convert the request into a signed certificate:
% ssleay c509 -in new3cert.csr -out new3.cert.cert -req -signkey privkey.pem
You now have a secure version of Apache, httpsd; a site to use it on, site.ssl; a certificate, new3.cert.cert; and a signed key, privkey.pem.
SSL uses a session key to secure each connection. When the connection starts, certificates are checked and a new session key is agreed between the client and server (note that because of the joys of public key encryption, this new key is only known to the client and server). This is a time-consuming process, so Apache-SSL and the client can conspire to improve the situation by reusing session keys. Unfortunately, since Apache uses a multiprocess execution model, there's no guarantee that the next connection from the client will use the same instance of the server. In fact, it is rather unlikely. Thus, it is necessary to store session information in a cache that is accessible to all the instances of Apache-SSL. This is the function of the gcache program. It is controlled by the SSLCacheServerPath, SSLCacheServerPort, and SSLSessionCacheTimeout directives described later in this chapter.
You now have to think about the Config files for the site. A sample Config file will be found at .../apache_1.3.1/SSLconf/conf. After we edit it to fit our site, the Config file is as follows:
# This is an example configuration file for Apache-SSL. # Copyright (C) 1995,6,7 Ben Laurie # By popular demand, this file now illustrates the way to create two # websites, one secured (on port 8888), the other not (on port 8887). # You may need one of these. User webuser Group webgroup LogLevel debug # SSL servers MUST be standalone, currently. ServerType standalone # The default port for SSL is 443... but we use 8888 here so we don't have # to be root. Port 8887 Listen 8887 Listen 8888 # My test document root DocumentRoot /usr/www/site.ssl/htdocs <Directory /usr/www/site.ssl/htdocs/manual> SSLRequireSSL # This directive protects a directory by forbidding access except when SSL is # in use. Very handy for defending against configuration errors that expose # stuff that should be protected. </Directory> # Watch what's going on. TransferLog logs/transfer_log # Note that all SSL options can apply to virtual hosts. # Disable SSL. Useful in combination with virtual hosts. Note that # SSLEnable is now also supported. SSLDisable # Set the path for the global cache server executable. # If this facility gives you trouble, you can disable it by setting # CACHE_SESSIONS to FALSE in apache_ssl.c SSLCacheServerPath /usr/local/etc/apache/apache_1.3.1/src/modules/ssl/gcache # Set the global cache server port number or path. If it is a path, a Unix # domain socket is used. If a number, a TCP socket. SSLCacheServerPort logs/gcache_port # The number should either refer to a path consisting of a directory that # exists and a file that doesn't, or an unused TCP/IP port. # Set the session cache timeout, in seconds (set to 15 for testing, use a # higher value in real life). SSLSessionCacheTimeout 15 # Set the CA certificate verification path (must be PEM encoded). # (in addition to getenv("SSL_CERT_DIR"), I think). # (Not used in this example) #SSLCACertificatePath /usr/local/etc/apache/apache_1.3.1/SSLconf/conf # Set the CA certificate verification file (must be PEM encoded). # (in addition to getenv("SSL_CERT_FILE"), I think). SSLCACertificateFile /usr/www/site.ssl/conf/thawte.cert # Point SSLCertificateFile at a PEM-encoded certificate. # If the certificate is encrypted, then you will be prompted for a # passphrase. Note that a kill -1 will prompt again. # A test certificate can be generated with "make certificate". # If the key is not combined with the certificate, use this directive to # point at the key file. If this starts with a '/' it specifies an absolute # path; otherwise, it is relative to the default certificate area. That is, # it means "<default>/private/<keyfile>". #SSLCertificateKeyFile /some/place/with/your.key # Set SSLVerifyClient to: # 0 if no certicate is required. # 1 if the client may present a valid certificate. # 2 if the client must present a valid certificate. # 3 if the client may present a valid certificate but it is not required to # have a valid CA. SSLVerifyClient 0 # How deeply to verify before deciding they don't have a valid certificate. SSLVerifyDepth 10 # Translate the client X509 into a Basic authorization. This means that the # standard Auth/DBMAuth methods can be used for access control. The username # is the "one-line" version of the client's X509 certificate. Note that no # password is obtained from the user. Every entry in the user file needs this # password: xxj31ZMTZzkVA. See the code for further explanation. SSLFakeBasicAuth # List the ciphers that the client is permitted to negotiate. See the source # for a definitive list. For example: #SSLRequiredCiphers RC4-MD5:RC4-SHA:IDEA-CBC-MD5:DES-CBC3-SHA # These two can be used per-directory to require or ban ciphers. Note that # (at least in the current version) Apache-SSL will not attempt to # renegotiate if a cipher is banned (or not required). #SSLRequireCipher #SSLBanCipher # Custom logging CustomLog logs/ssl_log "%t %{version}c %{cipher}c %{clientcert}c" <VirtualHost www.butterthlies.com:8888> SSLEnable </VirtualHost> ScriptAlias /scripts/usr/www/cgi-bin
We have changed the user and group to webuser and webgroup in line with practice throughout the book. The default port for SSL is 443, but here we get a replay of port-based virtual hosting (see Chapter 3, "Toward a Real Web Site") so that it is easy to contrast the behavior of Apache with (port 8888) and without (port 8887) SSL.
Remember to edit go so it invokes httpsd (the secure version); otherwise, Apache will rather puzzlingly object to all the nice new SSL directives. Run ./go in the usual way. Apache starts up and produces a message:
Reading certificate and key for server www.butterthlies.com:8888
This message shows that the right sort of thing is happening. If you had opted for a passphrase, Apache would halt for you to type it in, and the message would remind you which passphrase to use. However, in this case there isn't one, so Apache starts up.[71] On the client side, log on to:
[71]Later versions of Apache may not show this message if a passphrase is not required.
https://www.butterthlies.com:8888
remembering the "s" in https. It's rather bizarre that the client is expected to know in advance that it is going to meet an SSL server and has to log on securely, but that's the way the Web is. However, in practice you would usually log on to an unsecured site with http and then choose or be steered to a link that would set you up automatically for a secure transaction. If you forget the "s", various things can happen:
You are mystifyingly told that the page contains no data.
Your browser hangs.
.../site.ssl/logs/error_log contains the following line:
SSL_Accept failed error:140760EB:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol
If you pass these perils, you find that Netscape's product liability team has been at work, and you are taken through a rigmarole of legal safeguards and "are you absolutely sure?" queries before you are finally permitted to view the secure page.
We were running with SSLVerifyClient 0, so Apache made no inquiry concerning our credibility as a client. Change it to 2, to force the client to present a valid certificate. Netscape now says:
No User Certificate The site 'www.butterthlies.com' has requested client authentication, but you do not have a Personal Certificate to authenticate yourself. The site may choose not to give you access without one.
Oh, the shame of it. The simple way to fix this smirch is to get a beta certificate from one of the following companies:
Log on to one of these sites, and follow the instructions.
In the interests of European unity we chose BelSign NV/SA first and tried to download their Class 1 Demo Certificate, lasting 30 days. BelSign's own certificate had expired and the process failed -- in our experience, this is quite usual when dealing with "secure" sites and is an indicator that secure e-business is not yet a reality.
Ho hum, try IKS GmbH. They take things more seriously and try to explain the whole complicated business in slightly fractured Germlish, but don't seem to offer a free demo certificate, so that was no good.
The attempt to contact Uptime timed out.
Certisign lives in Brazil and is lavishly documented in commercial Portuguese -- interesting in a way, but it didn't seem to offer a demo certificate either.
Finally we fell back on Thawte, who do offer a demo certificate; however, they use it to test their procedures -- and your understanding -- to the limit. You need to paste your CSR new2.cert.csr (see Section 13.6.5, "Make a Test Certificate", earlier in this chapter) into their form and then choose one of a number of options. In our case, we thought we needed the "PEM format" because the certificates we generated seemed to be PEMs. But no. We got the following error:
Can only generate PEM output from PEM input.
Thawte has an Apache-SSL help page, which tells us that what Apache and SSL call "PEM" files are actually not. What we should have asked for was a base 64 encoded X.509 certificate -- invoked by the radio button on Thawte's form labeled "the most basic format." This time Thawte did its thing and presented a page with the certificate on it:
-----BEGIN CERTIFICATE----- MIICXTCCAcYCAw9CQDANBgkqhkiG9w0BAQQFADBkMRowGAYDVQQKExFUaGF3dGUg Q29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZp c2lvbjEcMBoGA1UEAxMTVGVzdCBTZXJ2ZXIgQ0EgUm9vdDAeFw05ODA4MjgwOTM2 MzFaFw05ODA5MjgwOTM2MzFaMIGHMQswCQYDVQQGEwJHQjEPMA0GA1UECBMGRG9y c2V0MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxHTAbBgNVBAMT FHd3dy5idXR0ZXJ0aGxpZXMuY29tMSUwIwYJKoZIhvcNAQkBFhZwZXRlckBhYmJv dHNidXJ5LmNvLnVrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDT1KRNwOwT kCHkYqpJmXj10U9pH4YZ7Koccwe87rAdDJ8NM5WTNa9VR4BEBWzFd34bGt6GPn1P qBpZ8fBMgT7x5XQH1wXK32Itf7NZJJvFO0XBuA4i9C8VMVEUefTRFL8mZSFCmO3N A1EnXvwjpF85c37pNDyYipAU9iUa+nrKEQIDAQABMA0GCSqGSIb3DQEBBAUAA4GB AJeufu9DTQw8l941pnzW8UmTqGATmFxf01IwrN88bWS+I1YzhZZ0ZQQSs8IKVQPG to38aaeSMeE7TauGdqs5+xv0QY8WrzrY4rbGliiW/H3kfMukOiRbiJAyXJepXhRJ ezE1n2v9E16dlF6T6LI0IXSzwJ2JsCTtD/IDkSgg9Tqo -----END CERTIFICATE-----
We copied this as thawte.cert to .../site.ssl/conf. This triggered changes in the Config file:
SSLCACertificateFile /usr/www/site.ssl/conf/thawte.cert SSLCertificateKeyFile /usr/www/site.ssl/conf/privkey.pem
Finally, we had to change the way we ran Apache to cope with the new demand for a passphrase. The file go became:
% httpsd -d /usr/www/site.ssl ; sleep 10000
When we ran it, we got the following message:
Reading certificate and key for server www.butterthlies.com:8888 Enter PEM pass phrase:
You type in your passphrase and then hit CTRL-C or Delete, depending on the flavor of Unix, to kill sleep.
When we finally logged on to https://www.butterthlies.com:8888 from the client, we got the following encouraging message:
Certificate Is Expired www.butterthlies.com is a site that uses encryption to protect transmitted information. However the digital Certificate that identifies this site is not yet valid. This may be because the certificate was installed too soon by the site administrator, or because the date on your computer is wrong. The certificate is valid beginning Fri Aug 28, 1998. Your computer's date is set to Fri Aug 28, 1998. If this date is incorrect, then you should reset the date on your computer. You may continue or cancel this connection.
This message suggested, in a perverse way, that we were doing something right. Finally, because we had changed SSLVerifyClient to 2, the exchange correctly expired in a complaint that the client didn't have a certificate.
If you kill Apache in the time-honored way, make sure that gcache disappears too. The version of SSL (1.21) that we used to test all this left gcache hanging and it had to be killed before Apache-SSL would restart properly. The symptom was a message in error_log:
[<date>] gcache started bind: address already in use
followed by irrelevant complaints about the private key file. If this happens with later versions, please report it as a bug.
Copyright © 2001 O'Reilly & Associates. All rights reserved.