Content-Type: multipart/related; start=; boundary=----------RT9YN0xo9jGjoDDlaSRtUV Content-Location: http://www.madboa.com/geek/openssl/ Subject: =?utf-8?Q?OpenSSL=20Command-Line=20HOWTO?= MIME-Version: 1.0 ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=default.htm Content-Type: text/html; name=default.htm Content-Id: Content-Location: http://www.madboa.com/geek/openssl/ Content-Transfer-Encoding: 8bit OpenSSL Command-Line HOWTO

OpenSSL Command-Line HOWTO

Paul Heinlein

Initial publication: June 13, 2004
Most recent revision: August 8, 2007

The openssl application that ships with the OpenSSL libraries can perform a wide range of crypto operations. This HOWTO provides some cookbook-style recipes for using it.


Table of Contents

Introduction
How do I find out what OpenSSL version I’m running?
How do I get a list of the available commands?
How do I get a list of available ciphers?
Benchmarking
How do I benchmark my system’s performance?
How do I benchmark remote connections?
Certificates
How do I generate a self-signed certificate?
How do I generate a certificate request for VeriSign?
How do I test a new certificate?
How do I retrieve a remote certificate?
How do I extract information from a certificate?
How do I export or import a PKCS#12 certificate?
Certificate Verification
How do I verify a certificate?
What certificate authorities does OpenSSL recognize?
How do I get OpenSSL to recognize/verify a certificate?
Command-line clients and servers
How do I connect to a secure SMTP server?
How do I connect to a secure [whatever] server?
How do I set up an SSL server from the command line?
Digests
How do I create an MD5 or SHA1 digest of a file?
How do I sign a digest?
How do I verify a signed digest?
How do I create an Apache digest password entry?
What other kinds of digests are available?
Encryption/Decryption
How do I base64-encode something?
How do I simply encrypt a file?
Errors
How do I interpret SSL error messages?
Keys
How do I generate an RSA key?
How do I generate a public RSA key?
How do I generate a DSA key?
How do I create an elliptic curve key?
How do I remove a passphrase from a key?
Password hashes
How do I generate a crypt-style password hash?
How do I generate a shadow-style password hash?
Prime numbers
How do I test whether a number is prime?
How do I generate a set of prime numbers?
Random data
How do I generate random data?
S/MIME
How do I verify a signed S/MIME message?
How do I encrypt a S/MIME message?
How do I sign a S/MIME message?
For further reading
Comments welcome

Introduction

The openssl command-line binary that ships with the OpenSSL libraries can perform a wide range of cryptographic operations. It can come in handy in scripts or for accomplishing one-time command-line tasks.

Documentation for using the openssl application is somewhat scattered, however, so this article aims to provide some practical examples of its use. I assume that you’ve already got a functional OpenSSL installation and that the openssl binary is in your shell’s PATH.

Just to be clear, this article is strictly practical; it does not concern cryptographic theory and concepts. If you don’t know what an MD5 sum is, this article won’t enlighten you one bit—but if all you need to know is how to use openssl to generate a file sum, you’re in luck.

The nature of this article is that I’ll be adding new examples incrementally. Check back at a later date if I haven’t gotten to the information you need.

How do I find out what OpenSSL version I’m running?

Use the version option.

$ openssl version
OpenSSL 0.9.8b 04 May 2006

You can get much more information with the version -a option.

$ openssl version -a
OpenSSL 0.9.8b 04 May 2006
built on: Fri Sep 29 18:45:58 UTC 2006
platform: debian-i386-i686/cmov
options:  bn(64,32) md2(int) rc4(idx,int) des(ptr,risc1,16,long) blowfish(idx) 
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT
-DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DTERMIO -O3 -march=i686
-Wa,--noexecstack -g -Wall -DOPENSSL_BN_ASM_PART_WORDS -DOPENSSL_IA32_SSE2
-DSHA1_ASM -DMD5_ASM -DRMD160_ASM -DAES_ASM
OPENSSLDIR: "/usr/lib/ssl"

How do I get a list of the available commands?

There are three built-in options for getting lists of available commands, but none of them provide what I consider useful output. The best thing to do is provide an invalid command (help or -h will do nicely) to get a readable answer.

$ openssl help
openssl:Error: 'help' is an invalid command.

Standard commands
asn1parse      ca             ciphers        crl            crl2pkcs7      
dgst           dh             dhparam        dsa            dsaparam       
ec             ecparam        enc            engine         errstr         
gendh          gendsa         genrsa         nseq           ocsp           
passwd         pkcs12         pkcs7          pkcs8          prime          
rand           req            rsa            rsautl         s_client       
s_server       s_time         sess_id        smime          speed          
spkac          verify         version        x509           

Message Digest commands (see the `dgst' command for more details)
md2            md4            md5            rmd160         sha            
sha1           

Cipher commands (see the `enc' command for more details)
aes-128-cbc    aes-128-ecb    aes-192-cbc    aes-192-ecb    aes-256-cbc    
aes-256-ecb    base64         bf             bf-cbc         bf-cfb         
bf-ecb         bf-ofb         cast           cast-cbc       cast5-cbc      
cast5-cfb      cast5-ecb      cast5-ofb      des            des-cbc        
des-cfb        des-ecb        des-ede        des-ede-cbc    des-ede-cfb    
des-ede-ofb    des-ede3       des-ede3-cbc   des-ede3-cfb   des-ede3-ofb   
des-ofb        des3           desx           rc2            rc2-40-cbc     
rc2-64-cbc     rc2-cbc        rc2-cfb        rc2-ecb        rc2-ofb        
rc4            rc4-40

What the shell calls “Standard commands” are the main top-level options.

You can use the same trick with any of the subcommands.

$ openssl dgst -h
unknown option '-h'
options are
-c              to output the digest with separating colons
-d              to output debug info
-hex            output as hex dump
-binary         output in binary form
-sign   file    sign digest using private key in file
-verify file    verify a signature using public key in file
-prverify file  verify a signature using private key in file
-keyform arg    key file format (PEM or ENGINE)
-signature file signature to verify
-binary         output in binary form
-engine e       use engine e, possibly a hardware device.
-md5 to use the md5 message digest algorithm (default)
-md4 to use the md4 message digest algorithm
-md2 to use the md2 message digest algorithm
-sha1 to use the sha1 message digest algorithm
-sha to use the sha message digest algorithm
-sha256 to use the sha256 message digest algorithm
-sha512 to use the sha512 message digest algorithm
-mdc2 to use the mdc2 message digest algorithm
-ripemd160 to use the ripemd160 message digest algorithm

In more boring fashion, you can consult the OpenSSL man pages.

How do I get a list of available ciphers?

Use the ciphers option. The ciphers(1) man page is quite helpful.

# list all available ciphers
openssl ciphers -v

# list only TLSv1 ciphers
openssl ciphers -v -tls1

# list only high encryption ciphers (keys larger than 128 bits)
openssl ciphers -v 'HIGH'

# list only high encryption ciphers using the AES algorithm
openssl ciphers -v 'AES+HIGH'

Benchmarking

How do I benchmark my system’s performance?

The OpenSSL developers have built a benchmarking suite directly into the openssl binary. It’s accessible via the speed option. It tests how many operations it can perform in a given time, rather than how long it takes to perform a given number of operations. This strikes me a quite sane, because the benchmarks don’t take significantly longer to run on a slow system than on a fast one.

To run a catchall benchmark, run it without any further options.

openssl speed

There are two sets of results. The first reports how many bytes per second can be processed for each algorithm, the second the times needed for sign/verify cycles. Here are the results on an 2.16GHz Intel Core 2.

The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
md2               1736.10k     3726.08k     5165.04k     5692.28k     5917.35k
mdc2                 0.00         0.00         0.00         0.00         0.00 
md4              18799.87k    65848.23k   187776.43k   352258.73k   474622.63k
md5              16807.01k    58256.45k   160439.13k   287183.53k   375220.91k
hmac(md5)        23601.24k    74405.08k   189993.05k   309777.75k   379431.59k
sha1             16774.59k    55500.39k   142628.69k   233247.74k   288382.98k
rmd160           13854.71k    40271.23k    87613.95k   124333.06k   141781.67k
rc4             227935.60k   253366.06k   261236.94k   259858.09k   194928.50k
des cbc          48478.10k    49616.16k    49765.21k    50106.71k    50034.01k
des ede3         18387.39k    18631.02k    18699.26k    18738.18k    18718.72k
idea cbc             0.00         0.00         0.00         0.00         0.00 
rc2 cbc          19247.24k    19838.12k    19904.51k    19925.33k    19834.98k
rc5-32/12 cbc        0.00         0.00         0.00         0.00         0.00 
blowfish cbc     79577.50k    83067.03k    84676.78k    84850.01k    85063.00k
cast cbc         45362.14k    48343.34k    49007.36k    49202.52k    49225.73k
aes-128 cbc      58751.94k    94443.86k   111424.09k   116704.26k   117997.57k
aes-192 cbc      53451.79k    82076.22k    94609.83k    98496.85k    99150.51k
aes-256 cbc      49225.21k    72779.84k    82266.88k    85054.81k    85762.05k
sha256            9359.24k    22510.83k    40963.75k    51710.29k    56014.17k
sha512            7026.78k    28121.32k    54330.79k    86190.76k   104270.51k
                  sign    verify    sign/s verify/s
rsa  512 bits 0.000522s 0.000042s   1915.8  23969.9
rsa 1024 bits 0.002321s 0.000109s    430.8   9191.1
rsa 2048 bits 0.012883s 0.000329s     77.6   3039.6
rsa 4096 bits 0.079055s 0.001074s     12.6    931.3
                  sign    verify    sign/s verify/s
dsa  512 bits 0.000380s 0.000472s   2629.3   2117.9
dsa 1024 bits 0.001031s 0.001240s    969.6    806.2
dsa 2048 bits 0.003175s 0.003744s    314.9    267.1

You can run any of the algorithm-specific subtests directly.

# test rsa speeds
openssl speed rsa

# do the same test on a two-way SMP system
openssl speed rsa -multi 2

How do I benchmark remote connections?

The s_time option lets you test connection performance. The most simple invocation will run for 30 seconds, use any cipher, and use SSL handshaking to determine number of connections per second, using both new and reused sessions:

openssl s_time -connect remote.host:443

Beyond that most simple invocation, s_time gives you a wide variety of testing options.

# retrieve remote test.html page using only new sessions
openssl s_time -connect remote.host:443 -www /test.html -new

# similar, using only SSL v3 and high encryption (see
# ciphers(1) man page for cipher strings)
openssl s_time \
  -connect remote.host:443 -www /test.html -new \
  -ssl3 -cipher HIGH

# compare relative performance of various ciphers in
# 10-second tests
IFS=":"
for c in $(openssl ciphers -ssl3 RSA); do
  echo $c
  openssl s_time -connect remote.host:443 \
    -www / -new -time 10 -cipher $c 2>&1 | \
    grep bytes
  echo
done

If you don’t have an SSL-enabled web server available for your use, you can emulate one using the s_server option.

# on one host, set up the server (using default port 4433)
openssl s_server -cert mycert.pem -www

# on second host (or even the same one), run s_time
openssl s_time -connect myhost:4433 -www / -new -ssl3

Certificates

How do I generate a self-signed certificate?

You’ll first need to decide whether or not you want to encrypt your key. Doing so means that the key is protected by a passphrase.

On the plus side, adding a passphrase to a key makes it more secure, so the key is less likely to be useful to someone who steals it. The downside, however, is that you’ll have to either store the passphrase in a file or type it manually every time you want to start your web or ldap server.

It violates my normally paranoid nature to say it, but I prefer unencrypted keys, so I don’t have to manually type a passphrase each time a secure daemon is started. (It’s not terribly difficult to decrypt your key if you later tire of typing a passphrase.)

This example will produce a file called mycert.pem which will contain both the private key and the public certificate based on it. The certificate will be valid for 365 days, and the key (thanks to the -nodes option) is unencrypted.

openssl req \
  -x509 -nodes -days 365 \
  -newkey rsa:1024 -keyout mycert.pem -out mycert.pem

Using this command-line invocation, you’ll have to answer a lot of questions: Country Name, State, City, and so on. The tricky question is “Common Name.” You’ll want to answer with the hostname or CNAME by which people will address the server. This is very important. If your web server’s real hostname is mybox.mydomain.com but people will be using www.mydomain.com to address the box, then use the latter name to answer the “Common Name” question.

Once you’re comfortable with the answers you provide to those questions, you can script the whole thing by adding the -subj option. I’ve included some information about location into the example that follows, but the only thing you really need to include for the certificate to be useful is the hostname (CN).

openssl req \
  -x509 -nodes -days 365 \
  -subj '/C=US/ST=Oregon/L=Portland/CN=www.madboa.com' \
  -newkey rsa:1024 -keyout mycert.pem -out mycert.pem

How do I generate a certificate request for VeriSign?

Applying for a certificate signed by a recognized certificate authority like VeriSign is a complex bureaucratic process. You’ve got to perform all the requisite paperwork before creating a certificate request.

As in the recipe for creating a self-signed certificate, you’ll have to decide whether or not you want a passphrase on your private key. The recipe below assumes you don’t. You’ll end up with two files: a new private key called mykey.pem and a certificate request called myreq.pem.

openssl req \
  -new -newkey rsa:1024 -nodes \
  -keyout mykey.pem -out myreq.pem

If you’ve already got a key and would like to use it for generating the request, the syntax is a bit simpler.

openssl req -new -key mykey.pem -out myreq.pem

Similarly, you can also provide subject information on the command line.

openssl req \
  -new -newkey rsa:1024 -nodes \
  -subj '/CN=www.mydom.com/O=My Dom, Inc./C=US/ST=Oregon/L=Portland' \
  -keyout mykey.pem -out myreq.pem

When dealing with an institution like VeriSign, you need to take special care to make sure that the information you provide during the creation of the certificate request is exactly correct. I know from personal experience that even a difference as trivial as substituting “and” for “&” in the Organization Name will stall the process.

If you’d like, you can double check the signature and information provided in the certificate request.

# verify signature
openssl req -in myreq.pem -noout -verify -key mykey.pem

# check info
openssl req -in myreq.pem -noout -text

Save the key file in a secure location. You’ll need it in order to use the certificate VeriSign sends you. The certificate request will typically be pasted into VeriSign’s online application form.

How do I test a new certificate?

The s_server option provides a simple but effective testing method. The example below assumes you’ve combined your key and certificate into one file called mycert.pem.

First, launch the test server on the machine on which the certificate will be used. By default, the server will listen on port 4433; you can alter that using the -accept option.

openssl s_server -cert mycert.pem -www

If the server launches without complaint, then chances are good that the certificate is ready for production use.

You can also point your web browser at the test server, e.g., https://yourserver:4433/. Don’t forget to specify the “https” protocol; plain-old “http” won’t work. You should see a page listing the various ciphers available and some statistics about your connection. Most modern browsers allow you to examine the certificate as well.

How do I retrieve a remote certificate?

If you combine openssl and sed, you can retrieve remote certificates via a shell one-liner or a simple script.

#!/bin/sh
#
# usage: retrieve-cert.sh remote.host.name [port]
#
REMHOST=$1
REMPORT=${2:-443}

echo |\
openssl s_client -connect ${REMHOST}:${REMPORT} 2>&1 |\
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'

How do I extract information from a certificate?

An SSL certificate contains a wide range of information: issuer, valid dates, subject, and some hardcore crypto stuff. The x509 subcommand is the entry point for retrieving this information. The examples below all assume that the certificate you want to examine is stored in a file named cert.pem.

Using the -text option will give you the full breadth of information.

openssl x509 -text -in cert.pem

Other options will provide more targeted sets of data.

# who issued the cert?
openssl x509 -noout -in cert.pem -issuer

# to whom was it issued?
openssl x509 -noout -in cert.pem -subject

# for what dates is it valid?
openssl x509 -noout -in cert.pem -dates

# the above, all at once
openssl x509 -noout -in cert.pem -issuer -subject -dates

# what is its hash value?
openssl x509 -noout -in cert.pem -hash

# what is its MD5 fingerprint?
openssl x509 -noout -in cert.pem -fingerprint

How do I export or import a PKCS#12 certificate?

PKCS#12 files can be imported and exported by a number of applications, including Microsoft IIS. They are often associated with the file extension .pfx.

To create a PKCS#12 certificate, you’ll need a private key and a certificate. During the conversion process, you’ll be given an opportunity to put an “Export Password” (which can be empty, if you choose) on the certificate.

# create a file containing key and self-signed certificate
openssl req \
  -x509 -nodes -days 365 \
  -newkey rsa:1024 -keyout mycert.pem -out mycert.pem

# export mycert.pem as PKCS#12 file, mycert.pfx
openssl pkcs12 -export \
  -out mycert.pfx -in mycert.pem \
  -name "My Certificate"

If someone sends you a PKCS#12 and any passwords needed to work with it, you can export it into standard PEM format.

# export certificate and passphrase-less key
openssl pkcs12 -in mycert.pfx -out mycert.pem -nodes

# same as above, but you’ll be prompted for a passphrase for
# the private key
openssl pkcs12 -in mycert.pfx -out mycert.pem

Certificate Verification

Applications linked against the OpenSSL libraries can verify certificates signed by a recognized certificate authority (CA).

How do I verify a certificate?

Use the verify option to verify certificates.

openssl verify cert.pem

If your local OpenSSL installation recognizes the certificate or its signing authority and everything else (dates, signing chain, etc.) checks out, you’ll get a simple OK message.

$ openssl verify remote.site.pem
remote.site.pem: OK

If anything is amiss, you’ll see some error messages with short descriptions of the problem, e.g.,

  • error 10 at 0 depth lookup:certificate has expired. Certificates are typically issued for a limited period of time—usually just one year—and openssl will complain if a certificate has expired.

  • error 18 at 0 depth lookup:self signed certificate. Unless you make an exception, OpenSSL won’t verify a self-signed certificate.

What certificate authorities does OpenSSL recognize?

When OpenSSL was built for your system, it was configured with a “Directory for OpenSSL files.” (That’s the --openssldir option passed to the configure script, for you hands-on types.) This is the directory that typically holds information about certificate authorities your system trusts.

The default location for this directory is /usr/local/ssl, but most vendors put it elsewhere, e.g., /usr/share/ssl (Red Hat/Fedora), /etc/ssl (Gentoo), /usr/lib/ssl (Debian), or /System/Library/OpenSSL (Macintosh OS X).

Use the version option to identify which directory (labeled OPENSSLDIR) your installation uses.

openssl version -d

Within that directory and a subdirectory called certs, you’re likely to find one or more of three different kinds of files.

  1. A large file called cert.pem, an omnibus collection of many certificates from recognized certificate authorities like VeriSign and Thawte.

  2. Some small files in the certs subdirectory named with a .pem file extension, each of which contains a certificate from a single CA.

  3. Some symlinks in the certs subdirectory with obscure filenames like 052eae11.0. There is typically one of these links for each .pem file.

    The first part of obscure filename is actually a hash value based on the certificate within the .pem file to which it points. The file extension is just an iterator, since it’s theoretically possible that multiple certificates can generate identical hashes.

    On my Gentoo system, for example, there’s a symlink named f73e89fd.0 that points to a file named vsignss.pem. Sure enough, the certificate in that file generates a hash the equates to the name of the symlink:

    $ openssl x509 -noout -hash -in vsignss.pem
    f73e89fd
    

When an application encounters a remote certificate, it will typically check to see if the cert can be found in cert.pem or, if not, in a file named after the certificate’s hash value. If found, the certificate is considered verified.

It’s interesting to note that some applications, like Sendmail, allow you to specify at runtime the location of the certificates you trust, while others, like Pine, do not.

How do I get OpenSSL to recognize/verify a certificate?

Put the file that contains the certificate you’d like to trust into the certs directory discussed above. Then create the hash-based symlink. Here’s a little script that’ll do just that.

#!/bin/sh
#
# usage: certlink.sh filename [filename ...]

for CERTFILE in $*; do
  # make sure file exists and is a valid cert
  test -f "$CERTFILE" || continue
  HASH=$(openssl x509 -noout -hash -in "$CERTFILE")
  test -n "$HASH" || continue

  # use lowest available iterator for symlink
  for ITER in 0 1 2 3 4 5 6 7 8 9; do
    test -f "${HASH}.${ITER}" && continue
    ln -s "$CERTFILE" "${HASH}.${ITER}"
    test -L "${HASH}.${ITER}" && break
  done
done

Command-line clients and servers

The s_client and s_server options provide a way to launch SSL-enabled command-line clients and servers. There are other examples of their use scattered around this document, but this section is dedicated solely to them.

In this section, I assume you are familiar with the specific protocols at issue: SMTP, HTTP, etc. Explaining them is out of the scope of this article.

How do I connect to a secure SMTP server?

You can test, or even use, an SSL-enabled SMTP server from the command line using the s_client option.

Secure SMTP servers offer secure connections on up to three ports: 25 (TLS), 465 (SSL), and 587 (TLS). Some time around the 0.9.7 release, the openssl binary was given the ability to use STARTTLS when talking to SMTP servers.

# port 25/TLS; use same syntax for port 587
openssl s_client -connect remote.host:25 -starttls smtp

# port 465/SSL
openssl s_client -connect remote.host:465

RFC821 suggests (although it falls short of explicitly specifying) the two characters "<CRLF>" as line-terminator. Most mail agents do not care about this and accept either "<LF>" or "<CRLF>" as line-terminators, but Qmail does not. If you want to comply to the letter with RFC821 and/or communicate with Qmail, use also the -crlf option:

openssl s_client -connect remote.host:25 -crlf -starttls smtp

How do I connect to a secure [whatever] server?

Connecting to a different type of SSL-enabled server is essentially the same operation as outlined above. As of the date of this writing, openssl only supports command-line TLS with SMTP servers, so you have to use straightforward SSL connections with any other protocol.

# https: HTTP over SSL
openssl s_client -connect remote.host:443

# ldaps: LDAP over SSL
openssl s_client -connect remote.host:636

# imaps: IMAP over SSL
openssl s_client -connect remote.host:993

# pop3s: POP-3 over SSL
openssl s_client -connect remote.host:995

How do I set up an SSL server from the command line?

The s_server option allows you to set up an SSL-enabled server from the command line, but it’s I wouldn’t recommend using it for anything other than testing or debugging. If you need a production-quality wrapper around an otherwise insecure server, check out Stunnel instead.

The s_server option works best when you have a certificate; it’s fairly limited without one.

# the -www option will sent back an HTML-formatted status page
# to any HTTP clients that request a page
openssl s_server -cert mycert.pem -www

# the -WWW option "emulates a simple web server. Pages will be
# resolved relative to the current directory." This example
# is listening on the https port, rather than the default
# port 4433
openssl s_server -accept 443 -cert mycert.pem -WWW

Digests

Generating digests with the dgst option is one of the more straightforward tasks you can accomplish with the openssl binary. Producing digests is done so often, as a matter of fact, that you can find special-use binaries for doing the same thing.

How do I create an MD5 or SHA1 digest of a file?

Digests are created using the dgst option.

# MD5 digest
openssl dgst -md5 filename

# SHA1 digest
openssl dgst -sha1 filename

The MD5 digests are identical to those created with the widely available md5sum command, though the output formats differ.

$ openssl dgst -md5 foo-2.23.tar.gz
MD5(foo-2.23.tar.gz)= 81eda7985e99d28acd6d286aa0e13e07
$ md5sum foo-2.23.tar.gz
81eda7985e99d28acd6d286aa0e13e07  foo-2.23.tar.gz

The same is true for SHA1 digests and the output of the sha1sum application.

$ openssl dgst -sha1 foo-2.23.tar.gz
SHA1(foo-2.23.tar.gz)= e4eabc78894e2c204d788521812497e021f45c08
$ sha1sum foo-2.23.tar.gz
e4eabc78894e2c204d788521812497e021f45c08  foo-2.23.tar.gz

How do I sign a digest?

If you want to ensure that the digest you create doesn’t get modified without your permission, you can sign it using your private key. The following example assumes that you want to sign the SHA1 sum of a file called foo-1.23.tar.gz.

# signed digest will be foo-1.23.tar.gz.sha1
openssl dgst -sha1 \
  -sign mykey.pem
  -out foo-1.23.tar.gz.sha1 \
  foo-1.23.tar.gz

How do I verify a signed digest?

To verify a signed digest you’ll need the file from which the digest was derived, the signed digest, and the signer’s public key.

# to verify foo-1.23.tar.gz using foo-1.23.tar.gz.sha1
# and pubkey.pem
openssl dgst -sha1 \
  -verify pubkey.pem \
  -signature foo-1.23.tar.gz.sha1 \
  foo-1.23.tar.gz

How do I create an Apache digest password entry?

Apache’s HTTP digest authentication feature requires a special password format. Apache ships with the htdigest utility, but it will only write to a file, not to standard output. When working with remote users, it’s sometimes nice for them to be able to generate a password hash on a machine they trust and then mail it for inclusion in your local password database.

The format of the password database is relatively simple: a colon-separated list of the username, authorization realm (specified by the Apache AuthName directive), and an MD5 digest of those two items and the password. Below is a script that duplicates the output of htdigest, except that the output is written to standard output. It takes advantage of the dgst option’s ability to read from standard input.

#!/bin/bash

echo "Create an Apache-friendly Digest Password Entry"
echo "-----------------------------------------------"

# get user input, disabling tty echoing for password
read -p "Enter username: " UNAME
read -p "Enter Apache AuthName: " AUTHNAME
read -s -p "Enter password: " PWORD; echo

printf "\n%s:%s:%s\n" \
  "$UNAME" \
  "$AUTHNAME" \
  $(printf "${UNAME}:${AUTHNAME}:${PWORD}" | openssl dgst -md5)

What other kinds of digests are available?

Use the built-in list-message-digest-commands option to get a list of the digest types available to your local OpenSSL installation.

openssl list-message-digest-commands

Encryption/Decryption

How do I base64-encode something?

Use the enc -base64 option.

# send encoded contents of file.txt to stdout
openssl enc -base64 -in file.txt

# same, but write contents to file.txt.enc
openssl enc -base64 -in file.txt -out file.txt.enc

It’s also possible to do a quick command-line encoding of a string value:

$ echo "encode me" | openssl enc -base64
ZW5jb2RlIG1lCg==

Note that echo will silently attach a newline character to your string. Consider using its -n option if you want to avoid that situation, which could be important if you’re trying to encode a password or authentication string.

$ echo -n "encode me" | openssl enc -base64
ZW5jb2RlIG1l

Use the -d (decode) option to reverse the process.

$ echo "ZW5jb2RlIG1lCg==" | openssl enc -base64 -d
encode me

How do I simply encrypt a file?

Simple file encryption is probably better done using a tool like GPG. Still, you may have occasion to want to encrypt a file without having to build or use a key/certificate structure. All you want to have to remember is a password. It can nearly be that simple—if you can also remember the cipher you employed for encryption.

To choose a cipher, consult the enc(1) man page. More simply (and perhaps more accurately), you can ask openssl for a list in one of two ways.

# see the list under the 'Cipher commands' heading
openssl -h

# or get a long list, one cipher per line
openssl list-cipher-commands

After you choose a cipher, you’ll also have to decide if you want to base64-encode the data. Doing so will mean the encrypted data can be, say, pasted into an email message. Otherwise, the output will be a binary file.

# encrypt file.txt to file.enc using 256-bit AES in CBC mode
openssl enc -aes-256-cbc -salt -in file.txt -out file.enc

# the same, only the output is base64 encoded for, e.g., e-mail
openssl enc -aes-256-cbc -a -salt -in file.txt -out file.enc

To decrypt file.enc you or the file’s recipient will need to remember the cipher and the passphrase.

# decrypt binary file.enc
openssl enc -d -aes-256-cbc -in file.enc

# decrypt base64-encoded version
openssl enc -d -aes-256-cbc -a -in file.enc

If you’d like to avoid typing a passphrase every time you encrypt or decrypt a file, the openssl(1) man page provides the details under the heading “PASS PHRASE ARGUMENTS.” The format of the password argument is fairly simple.

# provide password on command line
openssl enc -aes-256-cbc -salt -in file.txt \
  -out file.enc -pass pass:mySillyPassword

# provide password in a file
openssl enc -aes-256-cbc -salt -in file.txt \
  -out file.enc -pass file:/path/to/secret/password.txt

Errors

How do I interpret SSL error messages?

Poking through your system logs, you see some error messages that are evidently related to OpenSSL or crypto:

sshd[31784]: error: RSA_public_decrypt failed: error:0407006A:lib(4):func(112):reason(106)
sshd[770]: error: RSA_public_decrypt failed: error:0407006A:lib(4):func(112):reason(106)

The first step to figure out what’s going wrong is to use the errstr option to intrepret the error code. The code number is found between “error:” and “:lib”. In this case, it’s 0407006A.

$ openssl errstr 0407006A
error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01

If you’ve got a full OpenSSL installation, including all the development documentation, you can start your investigation there. In this example, the RSA_padding_add_PKCS1_type_1(3) man page will inform you that PKCS #1 involves block methods for signatures. After that, of course, you’d need to pore through your application’s source code to identify when it would expect be receiving those sorts of packets.

Keys

How do I generate an RSA key?

Use the genrsa option.

# default 512-bit key, sent to standard output
openssl genrsa

# 1024-bit key, saved to file named mykey.pem
openssl genrsa -out mykey.pem 1024

# same as above, but encrypted with a passphrase
openssl genrsa -des3 -out mykey.pem 1024

How do I generate a public RSA key?

Use the rsa option to produce a public version of your private RSA key.

openssl rsa -in mykey.pem -pubout

How do I generate a DSA key?

Building DSA keys requires a parameter file, and DSA verify operations are slower than their RSA counterparts, so they aren’t as widely used as RSA keys.

If you’re only going to build a single DSA key, you can do so in just one step using the dsaparam subcommand.

# key will be called dsakey.pem
openssl dsaparam -noout -out dsakey.pem -genkey 1024

If, on the other hand, you’ll be creating several DSA keys, you’ll probably want to build a shared parameter file before generating the keys. It can take a while to build the parameters, but once built, key generation is done quickly.

# create parameters in dsaparam.pem
openssl dsaparam -out dsaparam.pem 1024

# create first key
openssl gendsa -out key1.pem dsaparam.pem

# and second ...
openssl gendsa -out key2.pem dsaparam.pem

How do I create an elliptic curve key?

Routines for working with elliptic curve cryptography were added to OpenSSL in version 0.9.8. Generating an EC key involves the ecparam option.

openssl ecparam -out key.pem -name prime256v1 -genkey

# openssl can provide full list of EC parameter names suitable for
# passing to the -name option above:
openssl ecparam -list_curves

How do I remove a passphrase from a key?

Perhaps you’ve grown tired of typing your passphrase every time your secure daemon starts. You can decrypt your key, removing the passphrase requirement, using the rsa or dsa option, depending on the signature algorithm you chose when creating your private key.

If you created an RSA key and it is stored in a standalone file called key.pem, then here’s how to output a decrypted version of the same key to a file called newkey.pem.

# you'll be prompted for your passphrase one last time
openssl rsa -in key.pem -out newkey.pem

Often, you’ll have your private key and public certificate stored in the same file. If they are stored in a file called mycert.pem, you can construct a decrypted version called newcert.pem in two steps.

# you'll need to type your passphrase once more
openssl rsa -in mycert.pem -out newcert.pem
openssl x509 -in mycert.pem >>newcert.pem

Password hashes

Using the passwd option, you can generate password hashes that interoperate with traditional /etc/passwd files, newer-style /etc/shadow files, and Apache password files.

How do I generate a crypt-style password hash?

You can generate a new hash quite simply:

$ openssl passwd MySecret
8E4vqBR4UOYF.

If you know an existing password’s “salt,” you can duplicate the hash.

$ openssl passwd -salt 8E MySecret
8E4vqBR4UOYF.

How do I generate a shadow-style password hash?

Newer Unix systems use a more secure MD5-based hashing mechanism that uses an eight-character salt (as compared to the two-character salt in traditional crypt()-style hashes). Generating them is still straightforward using the -1 option:

$ openssl passwd -1 MySecret
$1$sXiKzkus$haDZ9JpVrRHBznY5OxB82.

The salt in this format consists of the eight characters between the second and third dollar signs, in this case sXiKzkus. So you can also duplicate a hash with a known salt and password.

$ openssl passwd -1 -salt sXiKzkus MySecret
$1$sXiKzkus$haDZ9JpVrRHBznY5OxB82.

Prime numbers

Current cryptographic techniques rely heavily on the generation and testing of prime numbers, so it’s no surprise that the OpenSSL libraries contain several routines dealing with primes. Beginning with version 0.9.7e (or so), the prime option was added to the openssl binary.

How do I test whether a number is prime?

Pass the number to the prime option. Note that the number returned by openssl will be in hex, not decimal, format.

$ openssl prime 119054759245460753
1A6F7AC39A53511 is not prime

You can also pass hex numbers directly.

$ openssl prime -hex 2f
2F is prime

How do I generate a set of prime numbers?

Pass a bunch of numbers to openssl and see what sticks. The seq utility is useful in this capacity.

# define start and ending points
AQUO=10000
ADQUEM=10100
for N in $(seq $AQUO $ADQUEM); do
  # use bc to convert hex to decimal
  openssl prime $N | awk '/is prime/ {print "ibase=16;"$1}' | bc
done

Random data

How do I generate random data?

Use the rand option to generate binary or base64-encoded data.

# write 128 random bytes of base64-encoded data to stdout
openssl rand -base64 128

# write 1024 bytes of binary random data to a file
openssl rand -out random-data.bin 1024

# seed openssl with semi-random bytes from browser cache
cd $(find ~/.mozilla/firefox -type d -name Cache)
openssl rand -rand $(find . -type f -printf '%f:') -base64 1024

On a Unix box with a /dev/urandom device and a copy of GNU head, you can achieve a similar effect, often with better entropy:

# get 32 bytes from /dev/urandom and base64 encode them
head -c 32 /dev/urandom | openssl enc -base64

S/MIME

S/MIME is a standard for sending and receiving secure MIME data, especially in e-mail messages. Automated S/MIME capabilities have been added to quite a few e-mail clients, though openssl can provide command-line S/MIME services using the smime option.

Note that the documentation in the smime(1) man page includes a number of good examples.

How do I verify a signed S/MIME message?

It’s pretty easy to verify a signed message. Use your mail client to save the signed message to a file. In this example, I assume that the file is named msg.txt.

openssl smime -verify -in msg.txt

If the sender’s certificate is signed by a certificate authority trusted by your OpenSSL infrastructure, you’ll see some mail headers, a copy of the message, and a concluding line that says Verification successful.

If the messages has been modified by an unauthorized party, the output will conclude with a failure message indicating that the digest and/or the signature doesn’t match what you received:

Verification failure
23016:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest
failure:pk7_doit.c:804:
23016:error:21075069:PKCS7 routines:PKCS7_verify:signature
failure:pk7_smime.c:265:

Likewise, if the sender’s certificate isn’t recognized by your OpenSSL infrastructure, you’ll get a similar error:

Verification failure
9544:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify
error:pk7_smime.c:222:Verify error:self signed certificate

Most e-mail clients send a copy of the public certificate in the signature attached to the message. From the command line, you can view the certificate data yourself. You’ll use the smime -pk7out option to pipe a copy of the PKCS#7 certificate back into the pkcs7 option. It’s oddly cumbersome but it works.

openssl smime -pk7out -in msg.txt | \
openssl pkcs7 -text -noout -print_certs

If you’d like to extract a copy of your correspondent’s certificate for long-term use, use just the first part of that pipe.

openssl smime -pk7out -in msg.txt -out her-cert.pem

At that point, you can either integrate it into your OpenSSL infrastructure or you can save it off somewhere for special use.

openssl smime -verify -in msg.txt -CAfile /path/to/her-cert.pem

How do I encrypt a S/MIME message?

Let’s say that someone sends you her public certificate and asks that you encrypt some message to her. You’ve saved her certificate as her-cert.pem. You’ve saved your reply as my-message.txt.

To get the default—though fairly weak—RC2-40 encryption, you just tell openssl where the message and the certificate are located.

openssl smime her-cert.pem -encrypt -in my-message.txt

If you’re pretty sure your remote correspondent has a robust SSL toolkit, you can specify a stronger encryption algorithm like triple DES:

openssl smime her-cert.pem -encrypt -des3 -in my-message.txt

By default, the encrypted message, including the mail headers, is sent to standard output. Use the -out option or your shell to redirect it to a file. Or, much trickier, pipe the output directly to sendmail.

openssl smime her-cert.pem \
  -encrypt \
  -des3 \
  -in my-message.txt \
  -from 'Your Fullname <you@youraddress.com>' \
  -to 'Her Fullname <her@heraddress.com>' \
  -subject 'My encrypted reply' |\
sendmail her@heraddress.com

How do I sign a S/MIME message?

If you don’t need to encrypt the entire message, but you do want to sign it so that your recipient can be assured of the message’s integrity, the recipe is similar to that for encryption. The main difference is that you need to have your own key and certificate, since you can’t sign anything with the recipient’s cert.

openssl smime \
  -sign \
  -signer /path/to/your-cert.pem \
  -in my-message.txt \
  -from 'Your Fullname <you@youraddress.com>' \
  -to 'Her Fullname <her@heraddress.com>' \
  -subject 'My signed reply' |\
sendmail her@heraddress.com

Comments welcome

Comments and suggestions about this document are appreciated and can be addressed to the author at .

This article is licensed under a Creative Commons License.

------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=leftfade.gif Content-Type: image/gif; name=leftfade.gif Content-Location: http://www.madboa.com/images/leftfade.gif Content-Transfer-Encoding: Base64 R0lGODlhZAAFAMYAAAAAAAICAgQEBAcHBwkJCQwMDA4ODhERERMTExYWFhgYGBsb Gx0dHSAgICIiIiUlJScnJyoqKiwsLC8vLzExMTMzMzY2Njg4ODs7Oz09PUBAQEJC QkVFRUdHR0pKSkxMTE9PT1FRUVRUVFZWVllZWVtbW15eXmBgYGJiYmVlZWdnZ2pq amxsbG9vb3FxcXR0dHZ2dnl5eXt7e35+foCAgIODg4WFhYiIiIqKio2NjY+Pj5KS kpSUlJaWlpmZmZubm56enqCgoKOjo6WlpaioqKqqqq2tra+vr7KysrS0tLe3t7m5 uby8vL6+vsHBwcPDw8XFxcjIyMrKys3Nzc/Pz9LS0tTU1NfX19nZ2dzc3N7e3uHh 4ePj4+bm5ujo6Ovr6+3t7fDw8PLy8vX19fX19fX19fX19fX19fX19fX19fX19fX1 9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX1 9fX19fX19fX19fX19SH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAsAAAAAGQABQAA B/6AAAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSor LC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpb XF1eX2BhYoKEhoiKjI6QkpSWmJqcnqCipKaoqqyusLK0trh08fIFTBgxY8iUMXMG TRo1a9i0cfMGThw5c4UOJVrU6FGkSZUuZdrU6VOoUaVOpVrV6lWsWbVu5drV61ew YcWOJVvW7Fm0adWuZdvW7Vu4ceUGZUzHkd3HdyLllayHEt/KfS79xQxIk+DNgzoV 9mwIFOLQiUYtJj2nUV3HdiEg4Y2cZ9Jeynws+b38J1NgzYI4Ee5c6NNh0IhEKR4l FwgAOw== ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=madboa.css Content-Type: text/css; name=madboa.css Content-Location: http://www.madboa.com/css/madboa.css Content-Transfer-Encoding: 8bit /* * CSS Stylesheet for madboa.com */ /* body */ body { background: url("/images/leftfade.gif") repeat-y; background-color: #f5f5f5; color: black; margin: 0; width: 700px; } /* main div's */ div.article, div.bibliography, div.chordpro { font-family: utopia, optima, "dejavu serif", "bitstream charter", serif; font-size: 11pt; margin: 0 0 0 110px; padding: 0; width: 590px; } div.bottommenu { background-color: white; border-bottom: 1px solid #b22222; border-top: 1px solid #b22222; margin: 5px 0px 5px 110px; width: 590px; } div.footer, div.license { font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-size: 8pt; margin: 5px 0px 5px 110px; width: 590px; } div.footer { padding-top: 5px; } div.license { text-align: center; } div.menu { color: #b22222; font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-size: 8pt; font-weight: bold; float: left; margin: 0; padding: 0; width: 100px; } div.pageheader { background-color: black; clear: both; color: white; } div.topspace { height: 60px; width: 700px; } /* tag-specific styles */ a { text-decoration: none; } a:link, a:visited { color: #000080; } a:hover { color: #b22222; } div.fivepix { height: 5px; margin: 0; padding: 0; } div.informaltable th { border-bottom: 1px solid #b22222; padding-right: 15px; } div.informaltable td { padding-right: 15px; } div.note { font-size: 10pt; } div.toc { font-size: 10pt; } h1, h2, h3, h4 { color: #b22222; font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; } h1.title { font-size: 16pt; } h2.title { font-size: 14pt; } h3.title { font-size: 12pt; } hr.pageinfo { border: 0; color: black; background-color: black; height: 1px; } img { border: 0; } p.arttitle { font-size: 10pt; margin-bottom: 0; margin-top: 10px; } p.updated { font-size: 10pt; } pre, tt, code { font-family: monaco, "dejavu sans mono", "luxi mono", monospace; font-size: 10pt; } pre.programlisting { border-top: 1px solid #666666; border-right: 2px solid #666666; border-bottom: 2px solid #666666; border-left: 1px solid #666666; background-color: #fddfdf; padding: 5px; } span.smallcaps { font-variant: small-caps; } span.symbol { font-family: monospace; } /* general styles */ .rightward { text-align: right; } .centered { text-align: center; } .w3cimg { width: 88px; height: 31px; } /* cascades */ div.artabstract p { font-size: 10pt; margin-bottom: 0; margin-top: 0; } p.arttitle a { font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-weight: bold; } div.article pre { font-size: 10pt; } div.article div.toc { border-bottom: 1px solid black; font-size: 10pt; } div.article div.titlepage { font-size: 10pt; } div.author .authorname { font-weight: bold; } div.bottommenu p { font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-size: 8pt; margin: 2px; text-align: center; text-transform: lowercase; } div.menu a { color: white; display: block; margin-left: 5px; } div.menu a:hover { background-color: #b22222; color: white; } div.menu a:visited { color: white; } div.pageheader .moniker { font-family: "times new roman", times, serif; font-size: 20pt; letter-spacing: 1ex; margin: 10px 0px 0px 10px; text-transform: lowercase; } div.titlepage .releaseinfo, div.titlepage .copyright, div.titlepage .pubdate, div.titlepage .author, div.titlepage .revision { margin-top: 0; margin-bottom: 0; } table.simplelist tr, table.simplelist td { margin-top: 0; margin-bottom: 0; } /* just for bibliographies */ div.biblioentry { border-top: 1px dotted black; } div.biblioentry div.abstract { border-top: 1px dotted black; } div.biblioentry div.reviewdate { margin-left: 1cm; font-size: 10pt; font-style: italic; } div.biblioentry span.subtitle { font-style: italic; } p.booklist { margin-top: 0; margin-bottom: 6pt; } /* just for chordpro */ div.chordpro td.chords { color: #b22222; font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-size: 9pt; text-align: left; } div.chordpro .lyrics { color: black; text-align: left; } div.chordpro p.lyrics { margin-bottom: 0; margin-top: 0; } div.chordpro p.comment, div.chordpro p.spacer { font-size: 10pt; font-style: italic; margin-bottom: 0; margin-top: 0; } div.chordpro div.chorus { margin-left: 1cm; } div.chordpro div.chorus .lyrics { font-style: italic; } td.t1, td.t2 { padding-right: 25px; } /* vim: set filetype=css : */ /* eof */ ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=print.css Content-Type: text/css; name=print.css Content-Location: http://www.madboa.com/css/print.css Content-Transfer-Encoding: 8bit /* * CSS print-friendly stylesheet for madboa.com */ /* body */ body { background-color: white; color: black; padding: 0; } /* main div's */ div.article, div.bibliography, div.chordpro { font-family: utopia, optima, "dejavu serif", "bitstream charter", serif; font-size: 11pt; } div.bottommenu, div.footer, div.menu, div.pageheader, div.topspace { display: none; } div.license { font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-size: 8pt; text-align: center; } /* tag-specific styles */ a { text-decoration: none; } a:link, a:visited, a:hover { color: black; } div.fivepix { height: 5px; margin: 0; padding: 0; } div.informaltable th { border-bottom: 1px solid black; padding-right: 15px; } div.informaltable td { padding-right: 15px; } div.toc { font-size: 10pt; } h1, h2, h3, h4 { font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; } h1.title { font-size: 16pt; } h2.title { font-size: 14pt; } h3.title { font-size: 12pt; } h3.author { font-size: 100%; color: black; } hr.pageinfo { border: 0; color: black; background-color: black; height: 1px; } img { border: 0; } p.arttitle { margin-bottom: 0; margin-top: 10px; } p.updated { font-size: 10pt; } pre, tt, code { font-family: monaco, "dejavu sans mono", "luxi mono", monospace; } pre.programlisting { border-top: 1px solid black; border-right: 2px solid black; border-bottom: 2px solid black; border-left: 1px solid black; background-color: white; padding: 5px; } span.symbol { font-family: monospace; } /* general styles */ .rightward { text-align: right; } .w3cimg { width: 88px; height: 31px; } /* cascades */ div.artabstract p { font-size: 10pt; margin-bottom: 0; margin-top: 0; } div.article pre { font-size: 10pt; } div.article div.toc { border-bottom: 1px solid black; font-size: 10pt; } div.article div.titlepage { font-size: 10pt; } div.pageheader .moniker { font-family: "times new roman", times, serif; font-size: 20pt; letter-spacing: 1ex; margin: 10px 0px 0px 10px; text-transform: lowercase; } div.titlepage .releaseinfo, div.titlepage .copyright, div.titlepage .pubdate, div.titlepage .author { margin-top: 0; margin-bottom: 0; } table.simplelist tr, table.simplelist td { margin-top: 0; margin-bottom: 0; } /* just for bibliographies */ div.biblioentry div.abstract { border-top: 1px solid black; } div.biblioentry div.reviewdate { margin-left: 1cm; font-size: 10pt; font-style: italic; } div.biblioentry span.subtitle { font-style: italic; } p.booklist { margin-top: 0; margin-bottom: 6pt; } /* just for chordpro */ div.chordpro td.chords { color: black; font-family: verdana, arial, helvetica, "dejavu sans", sans-serif; font-size: 10pt; text-align: left; } div.chordpro .lyrics { color: black; font-size: 12pt; text-align: left; } div.chordpro p.lyrics { margin-bottom: 0; margin-top: 0; } div.chordpro p.comment, div.chordpro p.spacer { font-size: 10pt; font-style: italic; margin-bottom: 0; margin-top: 0; } div.chordpro div.chorus { margin-left: 1cm; } div.chordpro div.chorus .lyrics { font-style: italic; } td.t1, td.t2 { padding-right: 25px; } /* vim: set filetype=css : */ /* eof */ ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=favicon.ico Content-Type: image/x-icon; name=favicon.ico Content-Location: http://www.madboa.com/favicon.ico Content-Transfer-Encoding: Base64 AAABAAEAEBAQAAAAAAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAwAAAAAAA AAAAAAAAEAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAwMDAAICA gAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAeDh3g4AAAAM7sAALuIAAi7 s//wu7uAAIu4//ewuAAAizD3/wBwAAAAD/f/AIAAAAcP9/8AcAAABwf/9wAAAAAA APf/BwAAAABwd3AAAAAAAAC7sHAAAAAAAAsAAAAAAAAAcHAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAADgD///wAP//4AB///AA///wAf//+AH///gB///4A////AP ///wH///+B////g////4P///+D////x///////// ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=madboa.js Content-Type: application/x-javascript; name=madboa.js Content-Location: http://www.madboa.com/js/madboa.js Content-Transfer-Encoding: Base64 LyogbWFkYm9hLmpzIC0tIGNvbW1vbiBqYXZhc2NyaXB0IGZ1bmN0aW9ucyBmb3Ig bWFkYm9hLmNvbSAqLwoKZnVuY3Rpb24gc2V0U3R5bGVzaGVldCh0aXRsZSkgewog IHZhciBpLCBhOwogIGZvciAoaSA9IDA7IChhID0gZG9jdW1lbnQuZ2V0RWxlbWVu dHNCeVRhZ05hbWUoImxpbmsiKVtpXSk7IGkrKykgewogICAgaWYgKGEuZ2V0QXR0 cmlidXRlKCJyZWwiKS5pbmRleE9mKCJzdHlsZXNoZWV0IikgIT0gLTEpICB7CiAg ICAgICBhLmRpc2FibGVkID0gKGEuZ2V0QXR0cmlidXRlKCJ0aXRsZSIpID09IHRp dGxlKSA/IGZhbHNlIDogdHJ1ZTsKICAgICB9CiAgIH0KfQoKLyogZW9mICovCg== ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=cc-by-nc-sa.png Content-Type: image/png; name=cc-by-nc-sa.png Content-Location: http://www.madboa.com/images/cc-by-nc-sa.png Content-Transfer-Encoding: Base64 iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAYAAABjyArgAAAACXBIWXMAAAsTAAAL EwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj33 3vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEs DIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIe EeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH /w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAn f+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJ V2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4 mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHg g/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl 7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/A V/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5 WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQ WHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAA RKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv 1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4 IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGy UT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPE bDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhM WE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPE NyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD 5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2h tlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0 dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHK CpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2ep O6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN 2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIp G6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3n U9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36 p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYP jGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLn m+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cR p7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0H DYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dn F2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofc n8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh 7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJ gUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5p DoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85 ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7 F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/R NtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9 MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo 1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5 sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWF fevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTP ZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJ zs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ +7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3v dy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtb Ylu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ7 52PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7 nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9 zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9D BY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfy l5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT 0Kf7kxmTk/8EA5jz/GMzLdsAAAAEZ0FNQQAAsY58+1GTAAAAIGNIUk0AAHolAACA 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 ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=valid-xhtml10.png Content-Type: image/png; name=valid-xhtml10.png Content-Location: http://www.madboa.com/images/valid-xhtml10.png Content-Transfer-Encoding: Base64 iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAAEjEcpEAAACiFBMVEUAAADe5+fO ezmtra3ejEKlhELvvWO9WlrehELOe3vepaWclHvetVLGc3PerVKcCAj3vVqUjHOU e1JjlL0xOUpjjL2UAAC91ueMrc7vrVKlvdbW3u+EpcbO3ufO1ucYWpSMKQi9SiF7 e3taWkoQEAiMczkQSoxaUkpzc3O1lEoICACEazEhGAgIAACEYzFra2utjELWcznG nEr/7+9jY2POazHOYzGta2NShLVrlL05OUqctdacCADGa2ucAADGpVqUtc61ORg5 OTmlUikYGAiUezl7YzEYEAiUczkxMTG9nEqtIRDe3t4AMXu9lEoQCACMazEAKXsp KSmljFrW1ta1jELOzs7n7/fGxsa9pVqEOSkpY5xznL29tZxahLXOpVr/99ZrY1L/ 79ZjUiljSikAOYTvxmMAMYScezmchFqUczGtlFp7c2utjFqUlJStxt73///39/9C e61CSkq9xsZznMbW5+9Cc62MjIxCQkrv9/fv7/fOzsbnlErWjIz/3mtCORhza1Ip IRBzWjH/1mtCMRhzY1L/zmvnvVpSQiHOpVJrUinntVr3zmOEc1L3xmNaWlq1nFo5 QkrGWim1lFoISpRSUlK1zt4hWpwASoz///////8xa6WUaykAQoxKe61KSkp7nMbW tWPe5+9jWlL39/f39/fWrWNCQkLera3nvWPv7+85MRjntWPetVp7c1IxKRCUlHtK ORh7a1IxIRCUjHtaSiHWrVIpIQhzWinvvVpaQiH/1mPWpVKMe1L/zmP/xmNrUiGE rc4YGBj/73PG1ucQWpT/53O9nFoQUpS1SiEQEBC9zt69vb05c6UISoxSUko5a6UI CAhSSkohUpS1tbXetWMAQoSUgD+kAAAA2HRSTlP/////////iP9sSf//dP////// //////////////////////////////////////////8M////////////ef////// //////////////////////////////////////////////////////////////// //////////////9d////////////////////////////////////AP////////// ////CP//RP////////////////////////////////////////////////////// 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 AJsAAAAASUVORK5CYII= ------------RT9YN0xo9jGjoDDlaSRtUV Content-Disposition: inline; filename=vcss.png Content-Type: image/png; name=vcss.png Content-Location: http://www.madboa.com/images/vcss.png Content-Transfer-Encoding: Base64 iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAABKVBMVEUAAAAjIx8M R51ZVUqAdlmdnZ3ejEWLDAuNjY1kiMG0n2d9fX19Ghfrp1FtbW3y39+3Ph6lIRNd XV2qJBFcVUhcVUhPT0/dsmpUfLr57+/u7u4/PDWZAACZAADOp1GdGxG+SyTgvnNd SySzk16+mkuxw+BOS0BOS0DOzs7MzMy4T09RRDwsJBG+vr73wV6fkG6eCQRFcLSu rq6/X1+ht9nXfz5sepHuwV59ZTHetFjQ2+wMCQQ2ZK5tWCsmWajsz8+Sq9NMPh4h VaY8MRj///////////////////////9MTEyOp9Lu8vhXU1A8PDyjOSTBz+YLRJ2r Ly8sLCwXTaKujEUcHByDn82dfz7/zGafDw+fDw+zRSlzlMcMDAyNcji1tbXf5vIc FgvATJOjAAAAY3RSTlP/8/////////////////8A//////P/////ov//8/////// ///////z///T//////////+i//////////////8w/////6IA/xAgMP////////// 8/////////8w0/////////+zehebAAACkUlEQVR42u2VfVPTQBDG19VqC6LY+lKr RIxFQaFSBPuSvhBPF8SIUZK2J5Yav/+HcO8uZdLqTCsU/nKnyWwvk1/unnt2D9Zm H+8/cMAaTRFy+ng69/yiwC/+gy8R3McGv5zHvGJEGAdR4eBgi1IbZwevIEZE24pF tBtzG1Q4AoD5zvw5pEDcJvIQV/TE3/l+H9GnNJwcdABS5wAbFQLMqI98/UReoAaO TlaJsp0zaHx7LwZvY0BUR2xpWTzqam0gzY8KGzG4MhBCNGucha4QbpETy+Yk/BP8 5nt734AjpQLTsE4ZFpf/dnkUCglXVNYB+OfUZJHvAqAoa45OeuPgm4+Xjtv7xm4N 7PMV4C61+Mrz3H2WImm3ATiWrAiwZRWcUA5Ej4dgIEMxDv6yxHHcNuAutnjv2HZ1 NeuycoVPh0mwC834zZC9Ao5dkZZKwLVGwT+WdLw0YOZ1saEkUDoT+QGWKZ0E2xpc rPakVW2KXwyUtYEtlEAj3GXD/fYwrryAdeiyGqidQSw1eqtJcA8cZq4zXqhPuCBY E1fKJjh/5X6MwRm9c2xf7WVdLf5oSdt64esVIwVAKC1HJ2oli8vj3L0YzC4zjkMa gt+arDAs6bApbL1RVlWIqrJbreqKZmh4y6VR7rAJeUYDVRj9VqRXkErpJ9lbEwtE 83KlIfeG4p52t7zWIMO1XcaGz54uUyet+hBM7BXXDS8Xc5+8Gmmbu1xwSoGIokA3 oTptQecQ4Iimm/Ew7jwbPfMi3TM91T9XVIGo+W9xC8oWpugVCXLuwXijjxJ3r/6P jX7nlFua8QmyM+TO/Gja2TTc2Z95C5uaewGH6cJi6bJO6Z+TY276eH3tbgy+/3ly 3Js+rj66osG/AV5htgaQ9SeRAAAAAElFTkSuQmCC ------------RT9YN0xo9jGjoDDlaSRtUV--