diff --git a/reaction-mailcreate/createMails.py b/reaction-mailcreate/createMails.py
index c9358e1e04bce0d3e736e19c40d3e8fab840f75e..966ab9f1f4cf5db9d16b93e5e3453cb296131dba 100755
--- a/reaction-mailcreate/createMails.py
+++ b/reaction-mailcreate/createMails.py
@@ -28,10 +28,7 @@ import getpass
 import hashlib
 import jinja2
 import os
-import magic
-import requests
 import secrets
-import smtplib
 import sys
 
 parser = argparse.ArgumentParser()
@@ -58,13 +55,13 @@ parser.add_argument('-i', '--input',      dest='input',      default='{basedir}/
 parser.add_argument('-o', '--output',     dest='output',     default='{basedir}/{campaign}/{site}/Mail{infix}.eml', help='output file name template (default: "{basedir}/{campaign}/{site}/Mail{infix}.eml")')
 parser.add_argument('-R', '--reply-to',   dest='replyto',    default=None, help='reply-to mail address (default: None)')
 parser.add_argument(      '--salt',       dest='salt',       default=None, help='salt to use for hashing (default: random 8-byte hex string)')
-parser.add_argument(      '--sign',       dest='sign',       default=None, choices=[None, 'gpg', 'smime'], help='signature method (one of "", "gpg", "smime"; default: "")')
-parser.add_argument(      '--sign-as',    dest='sign-as',    default=None, help='signature key to use (default: autoselect)')
+parser.add_argument(      '--sign',       dest='sign',       default='', choices=['', 'gpg', 'gpgsm', 'openssl'], help='signature method (one of "", "gpg", "gpgsm", "openssl"; default: "")')
+parser.add_argument(      '--sign-as',    dest='signas',     default=None, help='signature key to use (default: None meaning autoselect)')
 parser.add_argument('-s', '--subject',    dest='subject',    default='Security Challenge for {site} -- {campaign}{infix}', help='mail subject (default: "Security Challenge Message -- {campaign}{infix}")')
 parser.add_argument('-S', '--smtpserver', dest='smtpserver', default='localhost', help='SMTP server to use (default: "localhost"); port can be specified with "<host>:<port>" notation and takes precedence over implied ports and port specification')
 parser.add_argument(      '--smtpport',   dest='smtpport',   default=0, type=int, help='SMTP port to use (default: 25); takes precedence over implied ports')
-parser.add_argument(      '--smtpuser',   dest='smtpuser',   default=None,        help='SMTP user to login with (default: none); implies TLS (port 465) unless STARTTLS is set as well')
-parser.add_argument(      '--smtppass',   dest='smtppass',   default=None,        help='SMTP password to login with (default: none); implies TLS (port 465) unless STARTTLS is set as well; will be queried interactively if set to "-"')
+parser.add_argument(      '--smtpuser',   dest='smtpuser',   default=None, help='SMTP user to login with (default: none); implies TLS (port 465) unless STARTTLS is set as well')
+parser.add_argument(      '--smtppass',   dest='smtppass',   default=None, help='SMTP password to login with (default: none); implies TLS (port 465) unless STARTTLS is set as well; will be queried interactively if set to "-"')
 parser.add_argument(      '--starttls',   dest='starttls',   default=False, action='store_true', help='login using STARTTLS (default: False); implies port 587')
 parser.add_argument('-t', '--template',   dest='template',   default='{basedir}/{campaign}/Mail.template', help='mail template file (default: "{basedir}/{campaign}/Mail.template")')
 parser.add_argument('-T', '--to',         dest='to',         default='{firstname} {lastname} <{email}>', help='recipient mail address (default: "{firstname} {lastname} <{email}>")')
@@ -83,6 +80,34 @@ if (args.sender == 'Nobody <nobody@example.com>') or \
    (args.webserver == 'https://challenge.example.com'):
     args.dryrun = True
 
+
+# Import further necessary dependencies
+# Import dependencies for actual SMTP interaction if necessary
+if not args.dryrun:
+    import smtplib
+
+    # Import dependencies for HTTTP interaction if necessary
+    if not args.webserver:
+        import requests
+
+# Import dependencies for attachment file-magic if necessary
+if args.attach:
+    import magic
+
+# Import dependencies for mail signing if necessary
+if args.sign:
+    import magic
+
+    match args.sign:
+        case 'gpg' | 'gpgsm':
+            # Import dependencies for GPG-based mail signing if necessary
+            from email.mime.multipart import MIMEMultipart
+            import gnupg
+        case 'openssl':
+            # Import dependencies for OpenSSL-based mail signing if necessary
+            from M2Crypto import BIO, Rand, SMIME
+
+
 if (len(args.smtpserver.split(':')) == 2) or \
    (']:' in args.smtpserver):
        args.smtpport = args.smtpserver.rsplit(':', 1)[1]
@@ -132,6 +157,14 @@ if args.verbose:
     print(f'Using "{args.input.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as input file.')
     print(f'Using "{args.output.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as output file name template.')
     print(f'Using "{args.salt.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as salt.')
+    if args.sign:
+        print(f'Using "{args.sign}" as signature method.')
+        if args.signas:
+            print(f'Using "{args.signas}" as signing key.')
+        else:
+            print(f'Using auto-selected signing key.')
+    else:
+        print(f'Using no signature method.')
     print(f'Using "{args.subject.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as mail subject.')
     print(f'Using "{args.template.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as template file.')
     print(f'Using "{args.to.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as recipient mail address.')
@@ -160,6 +193,7 @@ data = dict()
 template = jinja2.Template(open(args.template.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))).read())
 
 
+# Convert int to hex
 def toHex(serial):
     tmpString = hex(serial)[2:].upper()
     if ((len(tmpString) % 2) == 1):
@@ -167,6 +201,51 @@ def toHex(serial):
     return ':'.join([tmpString[i:i+2] for i in range(0, len(tmpString), 2)])
 
 
+# Sign message with GPG/GPGSM
+def signMailGPG(binary, message):
+    # Set up GPG context
+    gpg = gnupg.GPG(gpgbinary=binary)
+
+    # Sign mail
+    try:
+        signature = str(gpg.sign(message.as_string().replace('\n', '\r\n'), detach=True))
+    except:
+        print('ERROR signing mail!')
+
+    # Assemble signature message
+    signatureMessage = EmailMessage()
+    signatureMessage['Content-Type'] = 'application/pgp-signature; name="signature.asc"'
+    signatureMessage['Content-Description'] = 'OpenPGP digital signature'
+    signatureMessage.set_payload(signature)
+
+    # Assemble new message
+    newMessage = MIMEMultipart(_subtype="signed", protocol="application/pgp-signature")
+    newMessage.attach(message)
+    newMessage.attach(signatureMessage)
+
+    return newMessage
+
+
+# Sign message with OpenSSL
+def signMailOpenSSL(message):
+    return message
+
+
+# Sign message if requested
+def signMail(message):
+    if not args.sign:
+        return message
+
+    match args.sign:
+        case 'gpg':
+            return signMailGPG('gpg', message)
+        case 'gpgsm':
+            return signMailGPG('gpgsm', message)
+        case 'openssl':
+            return signMailOpenSSL(message)
+
+
+# Assemble and send the actual mail
 def createMail(data):
     output = args.output.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
     directory = os.path.dirname(output)
@@ -178,6 +257,9 @@ def createMail(data):
         with open(attach, 'rb') as attachment:
             message.add_attachment(attachment.read(), *magic.from_file(attach, mime=True).split('/'))
 
+    # Sign mail if requested
+    message = signMail(message)
+
     message['Date'] = formatdate(localtime=True)
     message['Subject'] = args.subject.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
     message['From'] = args.sender.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
@@ -225,12 +307,14 @@ def createMail(data):
             requests.get(f'{data["CreateURL"]}')
 
 
+# Generate hash, URL, and CreateURL
 def generateHashAndURL(data):
     data['hash'] = hashlib.sha256(args.hashstring.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'])).encode('utf-8')).hexdigest()
     data['URL'] = args.url.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash']))
     data['CreateURL'] = args.createurl.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash']))
 
 
+# Main loop
 with open(args.input.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt)), 'r') as inputfile:
     data = csv.DictReader(inputfile, fieldnames=['site', 'firstname', 'lastname', 'email'], delimiter=',')
     for row in data: