#!/usr/bin/perl use strict; use warnings; # create TLS certificates for qpsmtpd use File::Temp qw/ tempfile tempdir /; use Getopt::Long; use Sys::Hostname; my %opts = (); my $hostname = hostname(); print "Using hostname: $hostname\n"; my %defaults = ( C => 'XY', ST => 'unknown', L => 'unknown', O => 'QSMTPD', OU => 'Server', CN => $hostname, ); GetOptions(\%opts, 'C|Country:s', 'ST|State:s', 'L|Locality|City:s', 'O|Organization:s', 'OU|OrganizationalUnit|U:s', 'CN|CommonName|N:s', 'emailAddress|email|E:s', 'help|H', ); usage() if $opts{help}; # initialize defaults foreach my $key ( keys %defaults ) { $opts{$key} = $defaults{$key} unless $opts{$key} } $opts{emailAddress} = 'postmaster@'.$opts{CN}; mkdir 'ssl' if ! -d 'ssl'; my $CA_key = 'ssl/qpsmtpd-ca.key'; my $CA_crt = 'ssl/qpsmtpd-ca.crt'; my $CA_serial = 'ssl/.cert.serial'; my $template; my ($CA, $CAfilename) = tempfile( $template, DIR => "ssl", UNLINK => 1); print ${CA} return_cfg('CA'); close ${CA}; system('openssl', 'genrsa', '-out', $CA_key, 2048) == 0 or die "Cannot create CA key: $?"; system('openssl', 'req', '-config', $CAfilename, '-new', '-x509', '-days', (365*6), '-key', $CA_key, '-out', $CA_crt) == 0 or die "Cannot create CA cert: $?"; my $SERVER_key = 'ssl/qpsmtpd-server.key'; my $SERVER_csr = 'ssl/qpsmtpd-server.csr'; my $SERVER_crt = 'ssl/qpsmtpd-server.crt'; my $SERVER_dhparam = 'ssl/qpsmtpd-dhparam.pem'; my ($SERVER, $SERVERfilename) = tempfile( $template, DIR => "ssl", UNLINK => 1); print ${SERVER} return_cfg($opts{OU}); close ${SERVER}; system('openssl', 'genrsa', '-out', $SERVER_key, 1024) == 0 or die "Cannot create server key: $?"; system('openssl', 'req', '-config', $SERVERfilename, '-new', '-key', $SERVER_key, '-out', $SERVER_csr) == 0 or die "Cannot create server cert: $?"; my ($SIGN, $SIGNfilename) = tempfile( $template, DIR => "ssl", UNLINK => 1); print ${SIGN} <<"EOT"; extensions = x509v3 [ x509v3 ] subjectAltName = email:copy nsComment = tls certificate nsCertType = server EOT close ${SIGN}; open my $SERIAL, '>', $CA_serial; print ${SERIAL} "01\n"; close ${SERIAL}; system('openssl', 'x509', '-extfile', $SIGNfilename, '-days', (365*2), '-CAserial', $CA_serial, '-CA', $CA_crt, '-CAkey', $CA_key, '-in', $SERVER_csr, '-req', '-out', $SERVER_crt) == 0 or die "Cannot sign cert: $?"; system('openssl', 'dhparam', '-out', $SERVER_dhparam, 2048) == 0 or die "Cannot create server dhparam: $?"; exit(0); sub return_cfg { my $OU = shift; my $RANDOM = int(rand(1000)).'RAN'.int(rand(1000)).'DOM'; my $cfg = <<"EOT"; [ req ] default_bits = 1024 default_keyfile = keyfile.pem distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = mypass [ req_distinguished_name ] C = $opts{C} ST = $opts{ST} L = $opts{L} O = $opts{O} OU = $OU CN = $opts{CN} emailAddress = $opts{emailAddress} [ req_attributes ] challengePassword = $RANDOM challenge password EOT return $cfg; } sub usage { print STDERR <<"EOT"; $0 will generate a TLS certificate "the quick way", i.e. without interaction. You can change some defaults however. These options are recognized: Default: --C Country (two letters, e.g. DE) $defaults{C} --ST State (spelled out) $defaults{ST} --L City $defaults{L} --O Organization $defaults{O} --OU Organizational Unit $defaults{OU} --CN Common name $defaults{CN} --email Email address of postmaster postmaster\@CN --help Show usage EOT exit(1); }