1#!/bin/sh
2#ident $Id: certutil,v 1.2 2002/04/14 14:16:16 lukeh Exp $
3#
4# certutil -- manage trusted X.509 certificates
5# inspired by Netscape PKCS #11 toolkit
6# contributed by Jarkko Turkulainen <jt@wapit.com>
7#
8#
9# INTRODUCTION
10#
11#    certutil can be used with various OpenSSL routines and tools
12#    that utilize OpenSSL. Example:
13#
14#    $ openssl s_client -CApath certdir
15#
16#    where certdir is a directory created by certutil. Other well known
17#    programs that use the same format are stunnel, sendmail and pam_ldap
18#
19#
20#
21# HOWTO
22#
23# 1. Initialize certificate database
24#
25#    Simply by adding a new certificate. If the certificate directory
26#    doesn't exist, the script asks for creating a one. Example:
27#
28#    $ certutil -a -n "First Cert" -i cert.pem -d /home/jt/mycerts
29#    ./certutil: cannot access /home/jt/mycerts, create? [y/N] y
30#
31#
32# 2. Add new certificate
33#
34#    $ certutil -a -n "My Cert" -i cert.pem [-d certdir]
35#
36#    Note that nickname (-n) must exist. certdir is optional - if it's
37#    not given, $PWD is used. The directory must have a file named certs.dat.
38#    If that file doesn't exist, the script refuses to do anything. If your
39#    certs.dat file is corrupted, "rm -rf" the whole dir and start from
40#    the scratch. cert.pem is the actual certificate.
41#
42# 3. Delete certificate
43#
44#    $ certutil -r -n "My Cert" [-d certdir]
45#
46#    This command removes the certificate named "My Cert". certdir is
47#    optional, see 2.
48#
49# 4. List certificates
50#
51#    $ certutil -l [-d certdir]
52#
53#    And again, certdir is optional.
54#
55# 5. View certificate properties
56#
57#    $ certutil -v -n "My Cert" [-d certdir]
58#
59#
60
61
62# Print usage
63usage() {
64	cat << EOF
65
66Usage: $0 -l [-d dir]
67          -a -n name -i file [-d dir]
68          -r -n name [-d dir]
69          -v -n name [-d dir]
70
71       Commands:
72          -l   -- List certificates (requires a valid dir)
73          -a   -- Add certificate and create dir if necessary
74          -r   -- Remove certificate (requires a valid dir)
75          -v   -- View certificate (requires a valid dir)
76
77       Parameters:
78          dir  -- Certificate directory, or \$PWD if not given
79          name -- Nickname of the certificate
80          file -- Certificate file in PEM format
81
82EOF
83	exit 1
84}
85
86# Check path
87check_path() {
88
89	# check the directory
90	if [ ! -d $CDIR -a $ADD -eq 1 ]; then
91		echo -n "$0: cannot access $CDIR, create? [y/N] "
92		read LINE
93		case $LINE in
94			y|Y)
95				mkdir $CDIR
96				chmod 700 $CDIR
97				touch $CDIR/certs.dat
98				chmod 600 $CDIR/certs.dat
99				;;
100			*)
101				exit 1
102				;;
103		esac
104	fi
105
106	# check certs.dat
107	if [ ! -e $CDIR/certs.dat ]; then
108		echo "$0: please specify a valid cert directory"
109		exit 1
110	fi
111}
112
113# Add certificates
114add_cert() {
115	check_path
116	if [ ! -e $FILE ]; then
117		echo "$0: cannot find $FILE"
118		exit 1
119	fi
120	HASH=`openssl x509 -in $FILE -hash -noout 2>/dev/null`.0
121	if [ $? -ne 0 ]; then
122		echo "$0: unable to load certificate $FILE"
123		exit 1
124	fi
125
126	if grep "^$CNAME|" $CDIR/certs.dat 1>/dev/null 2>&1; then
127		echo "$0: nickname already in use"
128		exit 1
129	fi
130
131	if [ -e $CDIR/$HASH ]; then
132		echo "$0: certificate already in directory"
133		echo `openssl x509 -in $CDIR/$HASH -subject -noout`
134		exit 1
135	else
136		cp $FILE $CDIR/$HASH
137		chmod 600 $CDIR/$HASH
138		echo "$CNAME|$HASH" >> $CDIR/certs.dat
139		chmod 600 $CDIR/certs.dat
140	fi
141
142}
143
144# List certificates
145#
146# (this is too slow...)
147#
148list_cert() {
149	check_path
150	echo
151	echo "Certificates in directory $CDIR"
152	echo
153	printf "%-30s%s\n" nickname subject/issuer
154	echo "----------------------------------------------------------------------------"
155	cat $CDIR/certs.dat | while read LINE; do
156		NICK=`echo $LINE | cut -d "|" -f 1`
157		HASH=`echo $LINE | cut -d "|" -f 2`
158		SUBJECT=`openssl x509 -in $CDIR/$HASH -subject -noout`
159		ISSUER=`openssl x509 -in $CDIR/$HASH -issuer -noout`
160		printf "%-30s%s\n" "$NICK" "$SUBJECT"
161		printf "%-30s%s\n\n" "" "$ISSUER"
162
163	done
164}
165
166# Remove certificates
167remove_cert() {
168	check_path
169	(
170	cat $CDIR/certs.dat | while read LINE; do
171		NICK=`echo $LINE | cut -d "|" -f 1`
172		HASH=`echo $LINE | cut -d "|" -f 2`
173		if [ "$CNAME" = "$NICK" ]; then
174			rm $CDIR/$HASH
175		else
176			echo $LINE
177		fi
178	done
179	) > /tmp/$$
180	mv /tmp/$$ $CDIR/certs.dat
181	chmod 600 $CDIR/certs.dat
182}
183
184# View certificate
185view_cert() {
186	check_path
187	cat $CDIR/certs.dat | while read LINE; do
188		NICK=`echo $LINE | cut -d "|" -f 1`
189		HASH=`echo $LINE | cut -d "|" -f 2`
190		if [ "$CNAME" = "$NICK" ]; then
191			openssl x509 -in $CDIR/$HASH -text
192			return 1
193		fi
194	done
195}
196
197# Parse option string
198ADD=0
199REMOVE=0
200LIST=0
201VIEW=0
202while getopts "arlvd:n:i:" OPT; do
203	case $OPT in
204		a)
205			ADD=1
206			;;
207		r)
208			REMOVE=1
209			;;
210		l)
211			LIST=1
212			;;
213		v)
214			VIEW=1
215			;;
216		d)
217			CDIR=$OPTARG
218			;;
219		n)
220			CNAME=$OPTARG
221			;;
222		i)
223			FILE=$OPTARG
224			;;
225		*)
226			usage
227			;;
228	esac
229done
230
231# Default options
232CDIR=${CDIR:=.}
233
234# Check command line options
235if [ $ADD -eq 1 -a $REMOVE -eq 0 -a $LIST -eq 0 -a $VIEW -eq 0 ]; then
236	if [ -n "$CNAME" -a -n "$FILE" ]; then
237		add_cert
238	else
239		echo "$0: missing certificate name or file"
240		usage
241	fi
242elif [ $REMOVE -eq 1 -a $ADD -eq 0 -a $LIST -eq 0 -a $VIEW -eq 0 ]; then
243	if [ -n "$CNAME" ]; then
244		remove_cert
245	else
246		echo "$0: missing certificate name"
247		usage
248	fi
249elif [ $LIST -eq 1 -a $ADD -eq 0 -a $REMOVE -eq 0 -a $VIEW -eq 0 ]; then
250	list_cert
251elif [ $VIEW -eq 1 -a $ADD -eq 0 -a $REMOVE -eq 0 -a $LIST -eq 0 ]; then
252	if [ -n "$CNAME" ]; then
253		if view_cert; then
254			echo "$0: cert named \"$CNAME\" not found"
255			exit 1
256		fi
257	else
258		echo "$0: missing certificate name"
259		usage
260	fi
261else
262	usage
263fi
264