summaryrefslogtreecommitdiff
path: root/ca_maker_erg.sh
blob: db53278216ddc6beae31a5272f9ff7ca09e812ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#!/bin/bash
usage() {
   echo "Script name: $0";
   echo "";
   echo "Usage: $0 --key-type [ RSA | EC | EX25519 ]";
   echo "                      --curve [ P-256 | P-384 | P512 ]";
   echo "                      --rsa-bits [ 2048 | 4096 ]";
   echo "                      --days <n>";
   echo "";
   echo "Options: ";
   echo "      --key-type [ RSA | EC | EC25519 ]";
   echo "          Note: Pico W can use EC or RSA keys only, not EC25519,";
   echo "                and Tasmota can only use 2048 bit RSA keys.";
   echo "      --curve [ P-256 | P-384 | P512 ]";
   echo "      --rsa-bits [ 2048 | 4096 ]";
   echo "      --days [ <integer> ]";
   echo "      --dir [ <mosquitto directory> ]";
   echo "      --output-format [ PEM | DER ]";
   echo "      --server_only [ True | False ]";
   echo "      --subjectAltName <subjectAltName>";
   echo "       Example subjectAltName: DNS.1:example.com,DNS.2:192.168.1.167,IP.1:192.168.1.167'";
}

# This creates a very slimline CA and Server cert with a full SAN multihost server.
# The Mosquitto server requires keys and certs in PEM format, but it also exports
# the CA Cert in DER for use in clients such as the Pico W
TEMP="$(getopt -o h --long help,key-type:,curve:,rsa-bits:,ca_valid_for:,days:,dir:,output-format:,server_only:,subjectAltName: -- "$@")"

eval set -- "$TEMP"

if [ $# == 0 ] ; then
    usage;
    exit 1;
fi

mosquitto_dir="/home/erg/cert_test_1"
key_type='EC'
curve='P-256'
rsa_bits='2048'
# Choice to encrypt the CA Key or not? either BLANK or cypher (preferably aes-256-cbc)
encryption=''
# encryption='-aes-256-cbc'
output_format='der'
# Multiple DNS names and IP Addresses. Note mbedtls can't currently use data from IP fields,
# but can read an IP from a DNS field, to the same effect.
# Use as many unique names as you need, in the format DNS.1, DNS.2, DNS.3, IP.1, IP.2 etc.
# subjectAltName='DNS.1:server_name,DNS.2:server_name.example.com,DNS.3:192.168.1.1,IP.1:192.168.1.1'
subjectAltName='DNS.1:example.com,DNS.2:192.168.0.167,IP.1:192.168.0.167'
ca_valid_for=3650
certs_valid_for=365
server_only="True"

while true
do
  case "$1" in
    -h|--help)
        usage
        exit 0;
        ;;
        # Note: Pico W can use EC or RSA keys only, not EC25519, and Tasmota can only use 2048 bit RSA keys.
        # Default choice of Key Types: RSA || EC || ED25519
    --key-type) key_type="$2"; shift 2;;
        # Choice of NIST Curves for EC Keys: P-256 || P-384 || P-521
    --curve) curve="$2"; shift 2;;
        # Choice of Bits for RSA Keys: 2048 || 4096
    --rsa-bits) rsa_bits="$2"; shift 2;;
        # How many days is the CA Cert valid for:
    --ca_valid_for) ca_valid_for="$2"; shift;;
        # How many days is the Cert valid for:
    --days) certs_valid_for="$2"; shift 2;;
        # Mosquitto directory:
    --dir) mosquitto_dir="$2"; shift 2;;
        # Output format: PEM ||  DER
    --output-format) output_format="$2"; shift 2;;
        # Only generate server certificate and key, not the CA. Defaults to True.
    --server_only) server_only="$2";shift 2;;
    --subjectAltName) subjectAltName="$2";shift 2;;
    -- ) shift; break;;
    * ) break;;
  esac
done

echo "subjectAltName you specified: $subjectAltName"

if [ "$server_only" == "True" ] || [ "$server_only" == "true" ]|| [ "$server_only" == "Yes" ] || [ "$server_only" == 'yes' ]; then
    server_only='True'
else
    server_only='False'
fi

# Mosquitto subdirectories:
mosquitto_dir_ca="${mosquitto_dir}/certs/CA"
mosquitto_dir_server="${mosquitto_dir}/certs/server"
mosquitto_dir_csr="${mosquitto_dir}/certs/csr_files"
ssl_ca_crt="${mosquitto_dir_ca}/ca_crt.${output_format}"
ssl_ca_key="${mosquitto_dir_ca}/ca_key.${output_format}"

# NOTE: If you need to create one or more client Keys and Certs in either PEM or DER format,
# you can call the 'client_maker' script one or more times at the bottom of this script, so
# just scroll right to the end to add the details. You can call 'client_maker' independantly
# whenever you need more clients.

#############################################################
#                   Check passed option values sane         #
#############################################################
echo "Checking if CA dir exists: $mosquitto_dir_ca ..."
if [ ! -d $mosquitto_dir_ca ];then
    echo "Certificate authority folder does not exist, exiting"
    exit 1
fi

# Check CA key and cert exist:
echo "Checking CA key and cert exist: ..."
if [ ! -f ${ssl_ca_crt} ]; then # I: Double quote to prevent globbing and word splitting.
    echo "CA certificate does not exist, exiting!"
    exit 1
fi
if [ ! -f "${mosquitto_dir_ca}/ca_key.pem" ] && [ ! -f "${mosquitto_dir_ca}/ca_key.der" ]; then
    echo "CA key does not exist, exiting!"
    exit 1
fi
# Check CA certificate is still valid:
# Date format we get from openssl:
#  Nov 18 13:47:56 2034 GMT
#  %b %d  %H:%M:%S %Y %Z
#  compare dates from the CA cert and current one:
echo "Checking CA certificate valid ..."
cert_valid_until=$(openssl x509 -enddate -noout -in "${ssl_ca_crt}" | cut -d'=' -f2)
echo "CA certificate valid until: ${cert_valid_until}"
if [ ! $(($(date -d "$cert_valid_until" +"%s") > $(date +"%s"))) ];then
    echo "CA certificate expired, exiting!"
    exit 1
fi
# Check key type:
if [ "$key_type" != 'EC' ] && [ "$key_type" != 'RSA' ] && [ "$key_type" != 'EC25519' ]; then
    echo "Wrong key type specified: '$key_type'"
    echo "Valid keys: [ EC | RSA | EC25519 ]"
    usage
    exit 1;
fi

# Check curve:
if [ "$curve" != 'P-256' ] && [ "$curve" != 'P-384' ] && [ "$curve" != 'P-521' ]; then
    echo "Wrong NIST curve specified: '$curve'"
    echo "Curve must be one of: [ P-256 | P-384 | P-521 ]"
    usage
    exit 1;
fi

# Check RSA bits:
if [ "$rsa_bits" != '2048' ] && [ "$rsa_bits" != '4096' ]; then
    echo "Wrong RSA bits specified: '$rsa_bits'"
    echo "Must be one of: [ 2048 | 4096 ]"
    usage
    exit 1;
fi

# Check days parameter is a number:
if [ -n "$certs_valid_for" ] && [ "$certs_valid_for" -eq "$certs_valid_for" ] 2>/dev/null; then
  echo "Certificate will be valid for: '$certs_valid_for' days"
else
  echo "Not a number: $certs_valid_for"
  usage
  exit 1;
fi

# Check output format:
if [ ${output_format} == 'PEM' ] || [ ${output_format} == 'pem' ]; then # I: Double quote to prevent globbing and word splitting.
  output_format="pem"
elif [ $output_format == 'DER' ] || [ $output_format == 'der' ]; then # I: Double quote to prevent globbing and word splitting.
  output_format="der"
else echo "Incorrect certificate format type specified: '$output_format'";
    usage
    exit 1;
fi


############################################################
#   End of user defined variables
############################################################


# Set the algorithm
algorithm="-algorithm ${key_type}"

# Set the specific pkeyopt for the chosen algorithm (BLANK for ED25519)
if [ "${key_type}" == "EC" ]; then
  echo 'Creating EC Key ...'
  pkeyopt="-pkeyopt ec_paramgen_curve:${curve}"
elif [ "${key_type}" == "RSA" ]; then
  echo 'Creating RSA Key ...'
  pkeyopt="-pkeyopt rsa_keygen_bits:${rsa_bits}"
elif [ "${key_type}" == "ED25519" ]; then
  echo 'Creating ED25519 Key'
  pkeyopt=""
else
  echo 'Key Type not found!'
  exit 1
fi

############################################################
#   Backup existing certs and create dir structure
############################################################

# if certs dir already exists, rename it so we don't overwrite anything important
# but if it doesn't, then redirect the 'No such file or directory' error to null
time_stamp=$(date +"%Y-%m-%d_%H-%M")
if [ ! -d "${mosquitto_dir_ca}" ]; then
    mkdir -p "${mosquitto_dir_ca}-${time_stamp}" 2>/dev/null
else
    cp "${mosquitto_dir_ca}" "${mosquitto_dir_ca}-${time_stamp}" 2>/dev/null
fi

if [ ! -d "${mosquitto_dir_server}" ]; then
    mkdir -p "${mosquitto_dir_server}-${time_stamp}" 2>/dev/null
else
    cp "${mosquitto_dir_server}" "${mosquitto_dir_server}-$time_stamp" 2>/dev/null
fi

# create a sensible directory structure to store everything if not exists:
mkdir -p "${mosquitto_dir}"/certs/{DH,CA,server,clients,csr_files}


###########################################################
# dhparamfile Creation
###########################################################

# Output DH parameters for safe prime group ffdhe2048
if [ "$server_only" == "True" ]; then
openssl genpkey \
    -genparam \
    -algorithm DH \
    -pkeyopt group:ffdhe2048 \
    -out "$mosquitto_dir/certs/DH/dhp_ffdhe2048.pem"
fi

###########################################################
# CA Creation
###########################################################

if [ "${server_only}" == 'True' ]; then
openssl genpkey \
    $algorithm $pkeyopt \
    -outform pem \
    -out "$mosquitto_dir_ca/ca_key.pem" "$encryption"
fi


# Self sign the CA cert

if [ "${server_only}" == 'True' ]; then
openssl req \
    -x509 \
    -new \
    -key "$mosquitto_dir_ca/ca_key.pem" \
    -days $ca_valid_for\
    -subj '/CN=MQTT CA' \
    -outform pem \
    -out "$mosquitto_dir_ca/ca_crt.pem"
fi

###########################################################
# Server Creation
###########################################################

# Create the Key
openssl genpkey \
    $algorithm $pkeyopt \
    -outform pem \
    -out "$mosquitto_dir_server/server_key.pem"

# Create the certificate signing request (CSR)
openssl req \
    -new \
    -subj "/CN=MQTT Server" \
    -addext "subjectAltName = ${subjectAltName}" \
    -nodes \
    -key "$mosquitto_dir_server/server_key.pem" \
    -out "$mosquitto_dir_server/server_req.csr"

# Sign and authenticate it with the CA
# We use copy_extensions to include the subjectAltNames from the CSR in the Server Cert
echo "Sign and authenticate CSR with the CA ..."
openssl x509 \
    -req \
    -in "$mosquitto_dir_server/server_req.csr" \
    -copy_extensions copy \
    -CA "${mosquitto_dir_ca}/ca_crt.pem" \
    -CAkey "${mosquitto_dir_ca}/ca_key.pem" \
    -CAcreateserial \
    -days "${certs_valid_for}" \
    -outform pem \
    -out "$mosquitto_dir_server/server_crt.pem"


###########################################################
# Key and Cert Checking
###########################################################

# Examine the key to check that it looks OK
openssl pkey \
    -in "$mosquitto_dir_server/server_key.pem" \
    -text \
    -noout

# Examine the CSR
openssl req \
    -in "$mosquitto_dir_server/server_req.csr" \
    -noout \
    -text

# Validate the CA certificate
openssl x509 \
    -in "$mosquitto_dir_server/server_crt.pem" \
    -text \
    -noout

#clean up after the server cert creation
mv "$mosquitto_dir_server/server_req.csr" "$mosquitto_dir_csr"

###########################################################
# Key Export and Read Permissions fix
###########################################################

#We need to export a copy of our CA Certificate in DER format for micropython.
openssl x509 \
    -in "$mosquitto_dir_ca/ca_crt.pem" \
    -out "$mosquitto_dir_ca/ca_crt.der" \
    -outform "$output_format"

# and save a copy of it somewhere useful
cp \
    "$mosquitto_dir_ca/ca_crt.der" \
    "$mosquitto_dir/certs/clients"

# but pem based clients need the pem version of it too
cp \
   "$mosquitto_dir_ca/ca_crt.pem" \
   "$mosquitto_dir/certs/clients"

# We also need to give read access to server_key.pem so it can be used by Mosquitto
chmod 644 "$mosquitto_dir_server/server_key.pem"


# move the ca_maker script to certs/CA  and remove its execute permissions
# to reduce the probability of running it by accident again
# and overwriting everything in your certs directory
#mv $mosquitto_dir/ca_maker $mosquitto_dir/certs/CA/ca_maker
#chmod -x $mosquitto_dir/certs/CA/ca_maker

# auto run the client_maker pem/der username
# ./client_maker der pico
#client_maker pem user2
#client_maker pem user3
#client_maker der user4