Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MacOS support, configs per dir, makefile fixes #10

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 8 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
NAME=ca-scripts
NAME=tiny-ca
VERSION=0.9.0

DIRS=bin lib tpl
INSTALL_DIRS=`find $(DIRS) -type d 2>/dev/null`
INSTALL_FILES=`find $(DIRS) -type f 2>/dev/null`
DOC_FILES=*.conf *.md doc/*.pod
SCRIPTS=ca-create-cert ca-init ca-list-certs ca-renew-cert ca-revoke-cert
SCRIPTS=tiny-ca

PKG_DIR=ca-scripts
PKG_DIR=tiny-ca
PKG_NAME=$(NAME)_$(VERSION)
PKG=$(PKG_DIR)/$(PKG_NAME).tar.gz
SIG=$(PKG_DIR)/$(PKG_NAME).asc

PREFIX?=/opt/$(NAME)
DOC_DIR=$(PREFIX)/doc
#DOC_DIR=$(PREFIX)/share/doc/$(PKG_NAME)
PREFIX?=/usr/local/$(NAME)

pkg:
mkdir -p $(PKG_DIR)
Expand Down Expand Up @@ -42,21 +40,18 @@ tag:

release: $(PKG) $(SIG) tag

install:
install:
for dir in $(INSTALL_DIRS); do mkdir -p $(PREFIX)/$$dir; done
for file in $(INSTALL_FILES); do cp $$file $(PREFIX)/$$file; done
mkdir -p $(DOC_DIR)
cp -r $(DOC_FILES) $(DOC_DIR)/

symlinks:
for link in $(SCRIPTS); do ln -s $(PREFIX)/bin/$$link /usr/local/bin/$$link
for link in $(SCRIPTS); do ln -s $(PREFIX)/bin/$$link /usr/local/bin/$$link; done

uninstall:
for file in $(INSTALL_FILES); do rm -f $(PREFIX)/$$file; done
rm -rf $(DOC_DIR)
for file in $(INSTALL_FILES); do rm -f $(PREFIX)/$$file; done

rmsymlinks:
for link in $(SCRIPTS); do rm -f /usr/local/bin/$$link
for link in $(SCRIPTS); do rm -f /usr/local/bin/$$link; done


.PHONY: build sign clean test tag release install uninstall all
Expand Down
159 changes: 23 additions & 136 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,142 +1,29 @@
ca-scripts
tiny-ca
==========

A set of **bash**(1) scripts for working with SSL Certificate Authorities.
tiny-ca is forked from awesome [ca-scripts](https://github.com/fluffle/ca-scripts). It's pretty much the same scripts, but with a few tweaks/changes that doesn't make sense to merge into main repo.

These scripts are designed to provide a configurable wrapping layer around
[**openssl**(1)](http://www.openssl.org/), similar to CA.pl. They're potentially
a little heavyweight if you just need a single self-signed certificate to secure
an HTTPs webserver, but they may come in handy if you want to:
The main differences from ca-scripts are

* Generate multiple service certificates signed by a single authority
* Provide signed client certificates to end users for authentication purposes
* Provide client certificates for S/MIME encrypted e-mail or code signing
* Easily set extensions such as x509v3 subjectAltName in your certificates

Portability
-----------

Currently, these scripts are limited to systems with a recent install of
**openssl**(1), GNU **date**(1) -- sorry BSD folks; patches welcome -- and
version 3 or greater of **bash**(1).
* MacOS support
* UTF-8 is default encoding.
* Per directory configuration (in addition to /etc/tiny-ca.conf and $HOME/.tiny-ca.cong)
* Openssl config templates are now copied to CA home to allow their customization on per CA basis.

Installation
------------

There aren't any tarballs as yet. Nor will the `make` command below work until
I've written a Makefile. It's coming, sometime. Sorry ;-)

$ git clone git://github.com/fluffle/ca-scripts
$ cd ca-scripts
$ make; sudo make install

This will by default install to `/usr/local`, either `export PREFIX=/path` or
`make PREFIX=/path; sudo make PREFIX=/path install` to change this to an
alternative location.

Creating a Certificate Authority
--------------------------------

Before running **ca-init**(1), a configuration file for the CA scripts must be
created. This configuration file sets up some templating variables that will
be present in certificates created for this CA, such as the domain, CA name,
and the root directory which will be populated with the generated certificates.
An example configuration file is provided with the scripts, and the comments
should be self-explanatory.

By default the CA scripts will read `/etc/ca-scripts.conf`. This is fine for
creating a single CA serving a single domain with no intermediary certificates,
but for a more complex setup a directory of configuration files will probably
be needed. Some settings are required, namely the **CA\_HOME**, **CA\_DOMAIN**,
and **CA\_DN\_\*** variables, while others can be inferred from these or have
sensible defaults set. See **ca-scripts.conf**(5) for more detail on these.

Once the configuration has been created the initial CA setup can be performed
with **ca-init(1)**, but please note that the path set in **CA\_HOME** must exist
and be writeable before it will run correctly. It is recommended (but not in an
way required) to create an unprivileged "ssl" user to run all the scripts as, so
the permissions are correctly set. A number of subdirectories will be set
up underneath this root, and an openssl configuration file, certificate and
private key will be generated. This key can be 3DES encrypted for security.

Optionally, it is possible to split the initial setup process so that the
directory structure and openssl configuration generation can be done in a
seperate step to the generation of the CA certificates, so that the config can
be manually edited. To fully understand it's contents you're unfortunately
going to need to read the following:

* [**ca**(1ssl)](http://www.openssl.org/docs/apps/ca.html)
* [**req**(1ssl)](http://www.openssl.org/docs/apps/req.html)
* [**x509**(1ssl)](http://www.openssl.org/docs/apps/x509.html)
* [**config**(5ssl)](http://www.openssl.org/docs/apps/config.html)
* [**x509v3\_config**(5ssl)](http://www.openssl.org/docs/apps/x509v3_config.html)

Particularly important are the x509v3 extensions present in the certificate,
which are defined in the "ca\_x509\_extensions" section of the config file.

Creating a certificate
----------------------

The **ca-create-cert**(1) script can generate three "types" of certificate:
*server* certificates for securing a service with SSL/TLS; *client* certificates
for authenticating a client to these services; and *user* certificates for
authentication, S/MIME e-mail signing or encryption, and code signing. There
are minor but important differences in the key usage extensions present in
these different certificate types, details can be found in the documentation
for **ca-create-cert**(1). In each case, a Common Name must be provided to give
a unique name for the certificate.

**ca-create-cert**(1) takes a number of options to customise the generated
certificate. The **--type** option defaults to creating *server* certs. It is
likely that the **--alt-name** option (which sets X.509v3 subjectAltName DNS
records for other hostnames for the server) will be useful; it may also be used
when creating *client* certs. Both the server hostname and any alternative
names will be fully-qualified to **CA\_DOMAIN** if they do not contain any dots
unless the **--no-qualify** option is used. If unqualified names are passed in
they are preserved as alternative DNS names in the certificate. The private key
may be encrypted with 3DES using the **--encrypt** option, and the certificate,
key, and CA certificate can be bundled together into a PKCS#12 format
certificate archive by passing **--pkcs12**. By default certificates are valid
for 365 days from signing, but this may be changed with the **--days** option.

The certificate's DN can be completely changed from the defaults provided by
**ca-scripts.conf**(5), but be wary as by default the generated openssl config
file requires that the country (C) and organisation (O) fields match those of
the CA certificate. A comment may also be set that will show up in user browsers
when they click on their padlock icons to examine the certificate's properties.
As with the CA setup, the steps to generate the certificate can be split up so
that configurations that are created from templates can be edited beforehand.

Renewing a certificate
----------------------

Certificates are renewed using **ca-renew-cert**(1). This script currently
does some painful certificate manipulation that is not strictly necessary in
most cases, and may in fact decrease SSL security slightly. This is done because
the normal renewal process re-generates the certificate signing request and
thus creates a new public/private keypair. If the certificates are used for
S/MIME encryption or code signing, this renders all the encrypted e-mail
unreadable and requires you to re-sign the code with your new private key.

To avoid this, **ca-renew-cert**(1) re-signs the old certificate request with
a new expiry date using the extensions generated when the old certificate was
signed. In the future it is possible (even likely) that this renewal method
will only be used on *user* type certificates, and the *server* and *client*
types will be renewed normally. If the current renewal method doesn't provide
sufficient security, the current certificate should be revoked and a new one
generated that is valid for the correct period of time using the **--days**
option to **ca-create-cert**(1).

As with the certificate creation script a Common Name can be passed to
identify the certificate to renew; alternatively the path to a previously
created certificate can be given. Internally these will be both be resolved to
the correct information required for certificate renewal.

Revoking a certificate
----------------------

To revoke a certificate and re-generate the CA certficate revocation list in
both PEM and DER encodings, invoke **ca-revoke-cert**(1), again providing a
Common Name or the path to the certificate to be revoked. Along with
**ca-init**(1) this script can optionally generate a basic HTML template to
serve the CA certificate and CRL with verifiable MD5 and SHA1 checksums.
==========
```
git clone http://github.com/sovcharenko/tiny-ca
cd tiny-ca
[sudo] make install
[sudo] make symlinks
```

MacOS note
==========
tiny-ca requires the following brew packages installed
```
brew install coreutils
brew install gnu-getopt
brew install gnu-sed
```
7 changes: 4 additions & 3 deletions bin/ca-create-cert
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash
set -e

source $(dirname $(readlink -f $0))/../lib/ca-functions

ALT_NAMES=()
Expand All @@ -15,7 +16,7 @@ MAKE_P12=0
usage() {
cat <<__EOT__
Usage:
$PROGNAME [options] <common name>
tiny-ca new [options] <common name>

Options:
-h, --help Print this helpful message!
Expand All @@ -33,7 +34,7 @@ Options:
-x, --cnf-only Only generate templates, do not create CSR or sign CRT
--country Certificate DN -- C
--state Certificate DN -- ST
--loc Certificate DN -- L
--location Certificate DN -- L
--org Certificate DN -- O
--ounit Certificate DN -- OU
--email Certificate DN -- E
Expand All @@ -45,7 +46,7 @@ __EOT__
short="hcf:t:d:b:n:i:pqrsx"
long="help,encrypt,config:,type:,days:,bits:,alt-name:,ip:"
long="$long,pkcs12,no-qualify,req-only,sign-only,cnf-only"
long="$long,country:,state:,loc:,org:,ounit:,email:,comment:"
long="$long,country:,state:,location:,org:,ounit:,email:,comment:"
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts";
Expand Down
18 changes: 9 additions & 9 deletions bin/ca-init
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash
set -e

source $(dirname $(readlink -f $0))/../lib/ca-functions

# XXX: Add an interactive mode to this script to obviate the need for a
Expand All @@ -15,7 +16,7 @@ source $(dirname $(readlink -f $0))/../lib/ca-functions

usage() {
cat <<__EOT__
Usage: $PROGNAME [options]
Usage: tiny-ca init [options]

Options:
-h, --help Print this helpful message!
Expand Down Expand Up @@ -71,23 +72,22 @@ fi

if [ 1 -ne "$CRT_ONLY" ]; then
# create the directory structure that'll be populated by the scripts
mkdir -p $CA_HOME/{cnf,crl,crt,csr,db,idx,key,p12}
mkdir -p $CA_HOME/{cnf,crl,crt,csr,db,idx,key,p12}
echo "01" > $CA_HOME/db/crlnumber
touch $CA_HOME/db/index.txt
touch $CA_HOME/db/.rand

# generate an openssl configuration for this CA
ca_template ca-config "$CA_HOME/cnf/$CA_NAME.ca.cnf"
ca_init_templates
fi
if [ 1 -ne "$CNF_ONLY" ]; then
if [ ! -f "$CA_HOME/cnf/$CA_NAME.ca.cnf" ]; then
# looks like someone's running ca-init with -s without using -x first
error "Could not find CA config. Please run ca-init -x before using ca-init -s."
fi
# generate an openssl configuration for this CA
ca_template ca-config "$CA_HOME/cnf/$CA_NAME.ca.cnf"

# generate a self-signed cert that is valid for 10 years, with
# ... the private key in $CA_HOME/key/$CA_NAME.ca.key
# ... the certificate in $CA_HOME/crt/$CA_NAME.ca.crt
# ... using the config in $CA_HOME/cnf/$CA_NAME.ca.cnf
# ... using the config in $CA_HOME/cnf/$CA_NAME.ca.cnf

openssl req -new $CRYPTKEY -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
-keyout "$CA_HOME/key/$CA_NAME.ca.key" \
-out "$CA_HOME/csr/$CA_NAME.ca.csr"
Expand Down
3 changes: 2 additions & 1 deletion bin/ca-list-certs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/bin/bash
set -e

source $(dirname $(readlink -f $0))/../lib/ca-functions

usage() {
cat <<__EOT__
Usage: $PROGNAME [options] <common name>|<path to certificate>
Usage: tiny-ca list [options] <common name>|<path to certificate>

Options:
-h, --help Print this helpful message!
Expand Down
3 changes: 2 additions & 1 deletion bin/ca-renew-cert
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/bin/bash
set -e

source $(dirname $(readlink -f $0))/../lib/ca-functions

usage() {
cat <<__EOT__
Usage: $PROGNAME [options] <common name>|<path to certificate>
Usage: tiny-ca renew [options] <common name>|<path to certificate>

Options:
-h, --help Print this helpful message!
Expand Down
3 changes: 2 additions & 1 deletion bin/ca-revoke-cert
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/bin/bash
set -e

source $(dirname $(readlink -f $0))/../lib/ca-functions

usage() {
cat <<__EOT__
Usage: $PROGNAME [options] <common name>|<path to certificate>
Usage: tiny-ca revoke [options] <common name>|<path to certificate>

Options:
-h, --help Print this helpful message!
Expand Down
38 changes: 38 additions & 0 deletions bin/tiny-ca
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash
# set -e

#Add GNU tools to path for MacOS
if [ "Darwin" = `uname -s` ]; then
export PATH="/usr/local/opt/gnu-getopt/bin:$PATH"
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
fi

scriptsDir=$(dirname $(readlink -f $0))
script=$1

shift

usage() {
cat <<__EOT__
Usage: tiny-ca subcommand [options] <common name>|<path to certificate>

Subcommands:
init
new
list
renew
revoke

__EOT__
}

case "$script" in
"") usage; exit 1;;
init) $scriptsDir/ca-init $@;;
new) $scriptsDir/ca-create-cert $@;;
list) $scriptsDir/ca-list-certs $@;;
renew) $scriptsDir/ca-renew-cert $@;;
revoke) $scriptsDir/ca-revoke-cert $@;;
*) echo "Unknown subcomand '$1'"; usage; exit 1;;
esac
Loading