summaryrefslogtreecommitdiff
path: root/scripts/mkdkimkey.sh
diff options
context:
space:
mode:
authorJannis Hoffmann <jannis@fehcom.de>2024-07-03 15:48:04 +0200
committerJannis Hoffmann <jannis@fehcom.de>2024-07-03 15:48:04 +0200
commit89b7b67a13ebb7965cc7f13ad0595e2194a2d34c (patch)
tree25efd77a90ae87236e6730d8ea3846bbe0fd126f /scripts/mkdkimkey.sh
add sqmail-4.2.29asqmail-4.2
Diffstat (limited to 'scripts/mkdkimkey.sh')
-rw-r--r--scripts/mkdkimkey.sh213
1 files changed, 213 insertions, 0 deletions
diff --git a/scripts/mkdkimkey.sh b/scripts/mkdkimkey.sh
new file mode 100644
index 0000000..5714173
--- /dev/null
+++ b/scripts/mkdkimkey.sh
@@ -0,0 +1,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