blob: 571417374f4355c6336f7d464cf23a2ef436e0ba (
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
|
#********************************************************************************
# Create/Handle domainkeys for openqmail/eQmail/(net)qmail and derivatives #
# #
# Author: Kai Peter (parts taken from Joerg Backschues), ©2014 #
# Version: 0.32 -> 0.46 #
# Licence: This program is Copyright(C) ©2015 Kai Peter. It can be copied and #
# modified according to the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 #
# or a later version. This software comes without any warranty. #
# #
# Description: Creation of domain keys and DNS TXT records for bind #
# #
# Addendum for s/qmail: #
# #
# a) This version is modified for s/qmail (etc/dkimkey -> ssl/domainkeys) #
# b) RSA and Ed25519 private/public keys are considered (-> RFC 8463) #
# c) tinydns supports DKIM records while just providing the public key; #
# beware of the 'selector'; it is set to 'default' #
# d) Unlike previous versions, the new private key is *not* automatically #
# linked to a file named 'default'; but rather to #
# rsa|ed25519.private_<selector> -> <selector> #
# e) The ed25519 public key is stripped from the ASN1 header information #
# This base64 encoded key is available as 'ed25519.basekey_<Selector> #
# f) RSA and Ed25519 private keys may share the same <Selector> name. #
# If identical, the Ed25519 private key is linked as '<Selector>_' #
# to be picked up automatically by qmail-dksign for simultaneous signing. #
# #
# Changelog: #
# #
# 0.46 Fix for RSA DKIM TXT file generation, compactified TXT data. #
#********************************************************************************
DKDIR="HOME/ssl/domainkeys"
USR="qmailq"
GRP="sqmail"
MODULUS=2048
CURVE=0
VERBOSE=0
SELECTOR="default"
DOMAIN=""
ASN1="MCowBQYDK2VwAyEA"
PRINT=0
OPENSSL=$(which openssl 2>/dev/null)
if [ $? -ne 0 ] ; then
echo "Couldn't find openssl! Aborting!" ; exit 0 ;
fi
showHelp() {
echo "Usage: $(basename $0) [-hpc] [-s selector] [-m modulus] domain"
echo
echo "-h (show this help and exit)"
echo "-v (verbose output)"
echo "-p (print TXT record for domain)"
echo "-s <selector> (set the selector)"
echo "-m <N> (set RSA modulo size; default: 2048 bits)"
echo "-c (generate Ed25519 keys)"
echo ""
echo "RSA key generation:"
echo "=================="
echo ""
echo " mkdkimkey -s rsa-selector domain"
echo ""
echo "Ed25519 key generation:"
echo "======================"
echo ""
echo " mkdkimkey -c -s ed-selector domain"
echo ""
echo "Key activation:"
echo "=============="
echo " The created private key 'rsa|ed25519.private_<selector>'"
echo " is automatically symlinked to '<selector>'."
echo " If included in 'control/dkimdomains', this file name"
echo " is picked up by qmail-dksign for signing outgoing mails."
echo " The default name for '<selector>' is 'default'."
echo " This is implicitely assumed if no particular '<selector>' is given."
echo ""
exit 1 ;
}
readInit() {
FLAG=""
PARAMS="vhpcs:m:"
if [ $# -gt 0 ]; then
while getopts ${PARAMS} FLAG
do
case ${FLAG} in
v) VERBOSE=1;;
p) PRINT=1;;
s) SELECTOR=${OPTARG};;
m) MODULUS=${OPTARG};;
c) CURVE=1;;
h) showHelp;;
*) showHelp;;
esac
done
shift $((OPTIND-1))
fi
# Validate the input a bit ...
DOMAIN=$1 ; if [ "x${DOMAIN}" = "x" ] ; then showHelp ; fi
# Only one argument is allowed ($1) and have to follow any other options
if [ $2 ] ; then echo "Syntax ERROR!;" showHelp ; fi
# Create main DKIM directory for keys if required
if [ ! -d ${DKDIR} ] ; then mkdir -p ${DKDIR} ; fi
}
showTXT() {
# backslashes MUST NOT be used in TXT records to quote semicolons !
cd ${DKDIR}/${DOMAIN}
echo "Domain's public key in '${DKDIR}/${DOMAIN}' used for TXT DNS record:"
echo -n "${SELECTOR}._domainkey.${DOMAIN}. IN TXT "
if [ -f rsa.public_${SELECTOR} ]; then
key=`grep -v -e '^-' rsa.public_${SELECTOR} | tr -d '\n'`
echo "\"v=DKIM1;k=rsa;t=y;p=${key}\""
elif [ -f ed25519.public_${SELECTOR} ]; then
key=`grep -v -e '^-' ed25519.public_${SELECTOR} | tr -d '\n'`
basekey=${key#${ASN1}}
if [ `echo -n ${basekey} | wc -c | awk '{print $1}'` -eq 44 ]; then
echo "\"v=DKIM1;k=ed25519;t=y;p=${basekey}\""
echo ${basekey} > ed25519.basekey_${SELECTOR}
else
(errString="error generating Ed25519 public key" && showError; echo ${key}; echo ${basekey}; exit 1)
fi
fi
echo -n "You need to publish this TXT record in the DNS before activating [rsa|ed25519].private_${SELECTOR} -> ${SELECTOR} for signing."
echo ; exit 0;
}
showError() {
echo "Domainkey for domain '${DOMAIN}' with selector '${SELECTOR}' [${errString}]!";
}
###############################################################################
# Main
###############################################################################
# Read input args; create dirs, do some validation
readInit ${@}
if [ ${PRINT} -eq 1 ]; then showTXT; fi
# Do some tests for existing keys
if [ ${VERBOSE} -eq 1 ] ; then
if [ ${CURVE} -eq 0 ] ; then
test -f ${DKDIR}/${DOMAIN}/rsa.private_${SELECTOR} && \
(errString="already exists" && showError)
else
test -f ${DKDIR}/${DOMAIN}/ed25519.private_${SELECTOR} && \
(errString="already exists" && showError)
fi
fi
# Create a directory for domain and populate it with new keys
# Existing old keys are safed as 'previous'
mkdir -p ${DKDIR}/${DOMAIN}
cd ${DKDIR}/${DOMAIN}
if [ ${CURVE} -eq 0 ] ; then
if [ -f rsa.public_${SELECTOR} ]; then
cp rsa.public_${SELECTOR} rsa.public_${SELECTOR}.previous
cp rsa.private_${SELECTOR} rsa.private_${SELECTOR}.previous
fi
${OPENSSL} genrsa -out rsa.private_${SELECTOR} ${MODULUS}
${OPENSSL} rsa -in rsa.private_${SELECTOR} \
-out rsa.public_${SELECTOR} -pubout -outform PEM
ln -sf rsa.private_${SELECTOR} ${SELECTOR}
else
if [ -f ed25519.public_${SELECTOR} ]; then
cp ed25519.public_${SELECTOR} ed25519.public_${SELECTOR}.previous
cp ed25519.private_${SELECTOR} ed25519.private_${SELECTOR}.previous
fi
${OPENSSL} genpkey -algorithm Ed25519 \
-out ed25519.private_${SELECTOR}
${OPENSSL} pkey -in ed25519.private_${SELECTOR} \
-out ed25519.public_${SELECTOR} -pubout
if [ -f ${SELECTOR} ]; then
ln -sf ed25519.private_${SELECTOR} "${SELECTOR}_"
else
ln -sf ed25519.private_${SELECTOR} ${SELECTOR}
fi
fi
# Set permissions
chmod 0711 ${DKDIR}
chmod 0700 ${DKDIR}/${DOMAIN}
chmod 0600 ${DKDIR}/${DOMAIN}/*
chown -R ${USR}:${GRP} ${DKDIR}
# Do some tests
if [ ${CURVE} -eq 0 ] ; then
test -f rsa.public_${SELECTOR} || \
(errString="does not exist" && showError)
else
test -f ed25519.public_${SELECTOR} || \
(errString="does not exist" && showError)
fi
# Done
[ "$?" = 0 ] && showTXT
exit 0
|