|
1 |
| -#!/usr/bin/env bash |
2 |
| -#(@)generate-CA.sh - Create CA key-pair and server key-pair signed by CA |
| 1 | +#!/bin/bash |
3 | 2 |
|
4 |
| -# Copyright (c) 2013-2016 Jan-Piet Mens <jpmens()gmail.com> |
5 |
| -# All rights reserved. |
6 |
| -# |
7 |
| -# Redistribution and use in source and binary forms, with or without |
8 |
| -# modification, are permitted provided that the following conditions are met: |
9 |
| -# |
10 |
| -# 1. Redistributions of source code must retain the above copyright notice, |
11 |
| -# this list of conditions and the following disclaimer. |
12 |
| -# 2. Redistributions in binary form must reproduce the above copyright |
13 |
| -# notice, this list of conditions and the following disclaimer in the |
14 |
| -# documentation and/or other materials provided with the distribution. |
15 |
| -# 3. Neither the name of mosquitto nor the names of its |
16 |
| -# contributors may be used to endorse or promote products derived from |
17 |
| -# this software without specific prior written permission. |
18 |
| -# |
19 |
| -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
20 |
| -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 |
| -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
| -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
23 |
| -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 |
| -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 |
| -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 |
| -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 |
| -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 |
| -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 |
| -# POSSIBILITY OF SUCH DAMAGE. |
| 3 | +[ -n "$DEST" ] || DEST="." |
30 | 4 |
|
31 |
| -# |
32 |
| -# Usage: |
33 |
| -# ./generate-CA.sh creates ca.crt and server.{key,crt} |
34 |
| -# ./generate-CA.sh hostname creates hostname.{key,crt} |
35 |
| -# ./generate-CA.sh client email creates email.{key,crt} |
36 |
| -# |
37 |
| -# Set the following optional environment variables before invocation |
38 |
| -# to add the specified IP addresses and/or hostnames to the subjAltName list |
39 |
| -# These contain white-space-separated values |
40 |
| -# |
41 |
| -# IPLIST="172.13.14.15 192.168.1.1" |
42 |
| -# HOSTLIST="a.example.com b.example.com" |
| 5 | +mkdir -p "$DEST" |
43 | 6 |
|
44 |
| -set -e |
| 7 | +pushd "$DEST" || exit |
45 | 8 |
|
46 |
| -export LANG=C |
| 9 | +# CA certificate (FQDN must be different from server/client) |
| 10 | +openssl genrsa -out ca.key 2048 |
| 11 | +openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt |
47 | 12 |
|
48 |
| -kind=server |
| 13 | +# server certificate (for multiple domains, change subjectAltName to: DNS:example.com,DNS:www.example.com) |
| 14 | +openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=localhost" -out server.csr |
| 15 | +openssl x509 -sha256 -req -extfile <(printf "subjectAltName=DNS:localhost") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt |
49 | 16 |
|
50 |
| -if [ $# -ne 2 ]; then |
51 |
| - kind=server |
52 |
| - host=$(hostname -f) |
53 |
| - if [ -n "$1" ]; then |
54 |
| - host="$1" |
55 |
| - fi |
56 |
| -else |
57 |
| - kind=client |
58 |
| - CLIENT="$2" |
59 |
| -fi |
| 17 | +# client certificate (the p12/pem format may be useful for some clients) |
| 18 | +openssl req -newkey rsa:2048 -nodes -keyout client.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=client" -out client.csr |
| 19 | +openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt |
| 20 | +openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12 |
| 21 | +openssl pkcs12 -in client.p12 -out client.pem -clcerts |
60 | 22 |
|
61 |
| -[ -z "$USER" ] && USER=root |
62 |
| - |
63 |
| -DIR=${TARGET:='.'} |
64 |
| -# A space-separated list of alternate hostnames (subjAltName) |
65 |
| -# may be empty "" |
66 |
| -ALTHOSTNAMES=${HOSTLIST} |
67 |
| -ALTADDRESSES=${IPLIST} |
68 |
| -CA_ORG= '/O=OwnTracks.org/OU=generate-CA/[email protected]' |
69 |
| -CA_DN="/CN=An MQTT broker${CA_ORG}" |
70 |
| -CACERT=${DIR}/ca |
71 |
| -SERVER="${DIR}/${host}" |
72 |
| -SERVER_DN="/CN=${host}$CA_ORG" |
73 |
| -keybits=2048 |
74 |
| -openssl=$(which openssl) |
75 |
| -MOSQUITTOUSER=${MOSQUITTOUSER:=$USER} |
76 |
| - |
77 |
| -# Signature Algorithm. To find out which are supported by your |
78 |
| -# version of OpenSSL, run `openssl dgst -help` and set your |
79 |
| -# signature algorithm here. For example: |
80 |
| -# |
81 |
| -# defaultmd="-sha256" |
82 |
| -# |
83 |
| -defaultmd="-sha512" |
84 |
| - |
85 |
| -function maxdays() { |
86 |
| - nowyear=$(date +%Y) |
87 |
| - years=$(expr 2032 - $nowyear) |
88 |
| - days=$(expr $years '*' 365) |
89 |
| - |
90 |
| - echo $days |
91 |
| -} |
92 |
| - |
93 |
| -function getipaddresses() { |
94 |
| - /sbin/ifconfig | |
95 |
| - grep -v tunnel | |
96 |
| - sed -En '/inet6? /p' | |
97 |
| - sed -Ee 's/inet6? (addr:)?//' | |
98 |
| - awk '{print $1;}' | |
99 |
| - sed -e 's/[%/].*//' | |
100 |
| - egrep -v '(::1|127\.0\.0\.1)' # omit loopback to add it later |
101 |
| -} |
102 |
| - |
103 |
| - |
104 |
| -function addresslist() { |
105 |
| - |
106 |
| - ALIST="" |
107 |
| - for a in $(getipaddresses); do |
108 |
| - ALIST="${ALIST}IP:$a," |
109 |
| - done |
110 |
| - ALIST="${ALIST}IP:127.0.0.1,IP:::1," |
111 |
| - |
112 |
| - for ip in $(echo ${ALTADDRESSES}); do |
113 |
| - ALIST="${ALIST}IP:${ip}," |
114 |
| - done |
115 |
| - for h in $(echo ${ALTHOSTNAMES}); do |
116 |
| - ALIST="${ALIST}DNS:$h," |
117 |
| - done |
118 |
| - ALIST="${ALIST}DNS:localhost" |
119 |
| - echo $ALIST |
120 |
| - |
121 |
| -} |
122 |
| - |
123 |
| -days=$(maxdays) |
124 |
| - |
125 |
| -if [ -n "$CAKILLFILES" ]; then |
126 |
| - rm -f $CACERT.??? $SERVER.??? $CACERT.srl |
127 |
| -fi |
128 |
| - |
129 |
| -if [ ! -f $CACERT.crt ]; then |
130 |
| - |
131 |
| - # ____ _ |
132 |
| - # / ___| / \ |
133 |
| - # | | / _ \ |
134 |
| - # | |___ / ___ \ |
135 |
| - # \____/_/ \_\ |
136 |
| - # |
137 |
| - |
138 |
| - # Create un-encrypted (!) key |
139 |
| - $openssl req -newkey rsa:${keybits} -x509 -nodes $defaultmd -days $days -extensions v3_ca -keyout $CACERT.key -out $CACERT.crt -subj "${CA_DN}" |
140 |
| - echo "Created CA certificate in $CACERT.crt" |
141 |
| - $openssl x509 -in $CACERT.crt -nameopt multiline -subject -noout |
142 |
| - |
143 |
| - chmod 400 $CACERT.key |
144 |
| - chmod 444 $CACERT.crt |
145 |
| - chown $MOSQUITTOUSER $CACERT.* |
146 |
| - echo "Warning: the CA key is not encrypted; store it safely!" |
147 |
| -fi |
148 |
| - |
149 |
| - |
150 |
| -if [ $kind == 'server' ]; then |
151 |
| - |
152 |
| - # ____ |
153 |
| - # / ___| ___ _ ____ _____ _ __ |
154 |
| - # \___ \ / _ \ '__\ \ / / _ \ '__| |
155 |
| - # ___) | __/ | \ V / __/ | |
156 |
| - # |____/ \___|_| \_/ \___|_| |
157 |
| - # |
158 |
| - |
159 |
| - if [ ! -f $SERVER.key ]; then |
160 |
| - echo "--- Creating server key and signing request" |
161 |
| - $openssl genrsa -out $SERVER.key $keybits |
162 |
| - $openssl req -new $defaultmd \ |
163 |
| - -out $SERVER.csr \ |
164 |
| - -key $SERVER.key \ |
165 |
| - -subj "${SERVER_DN}" |
166 |
| - chmod 400 $SERVER.key |
167 |
| - chown $MOSQUITTOUSER $SERVER.key |
168 |
| - fi |
169 |
| - |
170 |
| - if [ -f $SERVER.csr -a ! -f $SERVER.crt ]; then |
171 |
| - |
172 |
| - # There's no way to pass subjAltName on the CLI so |
173 |
| - # create a cnf file and use that. |
174 |
| - |
175 |
| - CNF=`mktemp /tmp/cacnf.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; } |
176 |
| - sed -e 's/^.*%%% //' > $CNF <<\!ENDconfig |
177 |
| - %%% [ JPMextensions ] |
178 |
| - %%% basicConstraints = critical,CA:false |
179 |
| - %%% nsCertType = server |
180 |
| - %%% keyUsage = nonRepudiation, digitalSignature, keyEncipherment |
181 |
| - %%% nsComment = "Broker Certificate" |
182 |
| - %%% subjectKeyIdentifier = hash |
183 |
| - %%% authorityKeyIdentifier = keyid,issuer:always |
184 |
| - %%% subjectAltName = $ENV::SUBJALTNAME |
185 |
| - %%% # issuerAltName = issuer:copy |
186 |
| - %%% ## nsCaRevocationUrl = http://mqttitude.org/carev/ |
187 |
| - %%% ## nsRevocationUrl = http://mqttitude.org/carev/ |
188 |
| - %%% certificatePolicies = ia5org,@polsection |
189 |
| - %%% |
190 |
| - %%% [polsection] |
191 |
| - %%% policyIdentifier = 1.3.5.8 |
192 |
| - %%% CPS.1 = "http://localhost" |
193 |
| - %%% userNotice.1 = @notice |
194 |
| - %%% |
195 |
| - %%% [notice] |
196 |
| - %%% explicitText = "This CA is for a local MQTT broker installation only" |
197 |
| - %%% organization = "OwnTracks" |
198 |
| - %%% noticeNumbers = 1 |
199 |
| -
|
200 |
| -!ENDconfig |
201 |
| - |
202 |
| - SUBJALTNAME="$(addresslist)" |
203 |
| - export SUBJALTNAME # Use environment. Because I can. ;-) |
204 |
| - |
205 |
| - echo "--- Creating and signing server certificate" |
206 |
| - $openssl x509 -req $defaultmd \ |
207 |
| - -in $SERVER.csr \ |
208 |
| - -CA $CACERT.crt \ |
209 |
| - -CAkey $CACERT.key \ |
210 |
| - -CAcreateserial \ |
211 |
| - -CAserial "${DIR}/ca.srl" \ |
212 |
| - -out $SERVER.crt \ |
213 |
| - -days $days \ |
214 |
| - -extfile ${CNF} \ |
215 |
| - -extensions JPMextensions |
216 |
| - |
217 |
| - rm -f $CNF |
218 |
| - chmod 444 $SERVER.crt |
219 |
| - chown $MOSQUITTOUSER $SERVER.crt |
220 |
| - fi |
221 |
| -else |
222 |
| - # ____ _ _ _ |
223 |
| - # / ___| (_) ___ _ __ | |_ |
224 |
| - # | | | | |/ _ \ '_ \| __| |
225 |
| - # | |___| | | __/ | | | |_ |
226 |
| - # \____|_|_|\___|_| |_|\__| |
227 |
| - # |
228 |
| - |
229 |
| - if [ ! -f $CLIENT.key ]; then |
230 |
| - echo "--- Creating client key and signing request" |
231 |
| - $openssl genrsa -out $CLIENT.key $keybits |
232 |
| - |
233 |
| - CNF=`mktemp /tmp/cacnf-req.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; } |
234 |
| - # Mosquitto's use_identity_as_username takes the CN attribute |
235 |
| - # so we're populating that with the client's name |
236 |
| - sed -e 's/^.*%%% //' > $CNF <<!ENDClientconfigREQ |
237 |
| - %%% [ req ] |
238 |
| - %%% distinguished_name = req_distinguished_name |
239 |
| - %%% prompt = no |
240 |
| - %%% output_password = secret |
241 |
| - %%% |
242 |
| - %%% [ req_distinguished_name ] |
243 |
| - %%% # O = OwnTracks |
244 |
| - %%% # OU = MQTT |
245 |
| - %%% # CN = Suzie Smith |
246 |
| - %%% CN = $CLIENT |
247 |
| - %%% # emailAddress = $CLIENT |
248 |
| -!ENDClientconfigREQ |
249 |
| - |
250 |
| - $openssl req -new $defaultmd \ |
251 |
| - -out $CLIENT.csr \ |
252 |
| - -key $CLIENT.key \ |
253 |
| - -config $CNF |
254 |
| - chmod 400 $CLIENT.key |
255 |
| - fi |
256 |
| - |
257 |
| - if [ -f $CLIENT.csr -a ! -f $CLIENT.crt ]; then |
258 |
| - |
259 |
| - CNF=`mktemp /tmp/cacnf-cli.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; } |
260 |
| - sed -e 's/^.*%%% //' > $CNF <<\!ENDClientconfig |
261 |
| - %%% [ JPMclientextensions ] |
262 |
| - %%% basicConstraints = critical,CA:false |
263 |
| - %%% subjectAltName = email:copy |
264 |
| - %%% nsCertType = client,email |
265 |
| - %%% extendedKeyUsage = clientAuth,emailProtection |
266 |
| - %%% keyUsage = digitalSignature, keyEncipherment, keyAgreement |
267 |
| - %%% nsComment = "Client Broker Certificate" |
268 |
| - %%% subjectKeyIdentifier = hash |
269 |
| - %%% authorityKeyIdentifier = keyid,issuer:always |
270 |
| -
|
271 |
| -!ENDClientconfig |
272 |
| - |
273 |
| - SUBJALTNAME="$(addresslist)" |
274 |
| - export SUBJALTNAME # Use environment. Because I can. ;-) |
275 |
| - |
276 |
| - echo "--- Creating and signing client certificate" |
277 |
| - $openssl x509 -req $defaultmd \ |
278 |
| - -in $CLIENT.csr \ |
279 |
| - -CA $CACERT.crt \ |
280 |
| - -CAkey $CACERT.key \ |
281 |
| - -CAcreateserial \ |
282 |
| - -CAserial "${DIR}/ca.srl" \ |
283 |
| - -out $CLIENT.crt \ |
284 |
| - -days $days \ |
285 |
| - -extfile ${CNF} \ |
286 |
| - -extensions JPMclientextensions |
287 |
| - |
288 |
| - rm -f $CNF |
289 |
| - chmod 444 $CLIENT.crt |
290 |
| - fi |
291 |
| -fi |
| 23 | +popd || exit |
0 commit comments