Puppet config erstellt und CA-Managment auf certtool umgestellt
Dieser Commit ist enthalten in:
Ursprung
8a89a0c56d
Commit
c62b8461c7
|
@ -0,0 +1 @@
|
||||||
|
../ca/ca.pem
|
|
@ -0,0 +1,151 @@
|
||||||
|
# This file is commonly accessed via passdb {} or userdb {} section in
|
||||||
|
# conf.d/auth-ldap.conf.ext
|
||||||
|
|
||||||
|
# This file is opened as root, so it should be owned by root and mode 0600.
|
||||||
|
#
|
||||||
|
# http://wiki2.dovecot.org/AuthDatabase/LDAP
|
||||||
|
#
|
||||||
|
# NOTE: If you're not using authentication binds, you'll need to give
|
||||||
|
# dovecot-auth read access to userPassword field in the LDAP server.
|
||||||
|
# With OpenLDAP this is done by modifying /etc/ldap/slapd.conf. There should
|
||||||
|
# already be something like this:
|
||||||
|
|
||||||
|
# access to attribute=userPassword
|
||||||
|
# by dn="<dovecot's dn>" read # add this
|
||||||
|
# by anonymous auth
|
||||||
|
# by self write
|
||||||
|
# by * none
|
||||||
|
|
||||||
|
# Space separated list of LDAP hosts to use. host:port is allowed too.
|
||||||
|
#hosts =
|
||||||
|
|
||||||
|
# LDAP URIs to use. You can use this instead of hosts list. Note that this
|
||||||
|
# setting isn't supported by all LDAP libraries.
|
||||||
|
#uris =
|
||||||
|
|
||||||
|
# Distinguished Name - the username used to login to the LDAP server.
|
||||||
|
# Leave it commented out to bind anonymously (useful with auth_bind=yes).
|
||||||
|
#dn =
|
||||||
|
|
||||||
|
# Password for LDAP server, if dn is specified.
|
||||||
|
#dnpass =
|
||||||
|
|
||||||
|
# Use SASL binding instead of the simple binding. Note that this changes
|
||||||
|
# ldap_version automatically to be 3 if it's lower.
|
||||||
|
#sasl_bind = no
|
||||||
|
# SASL mechanism name to use.
|
||||||
|
#sasl_mech =
|
||||||
|
# SASL realm to use.
|
||||||
|
#sasl_realm =
|
||||||
|
# SASL authorization ID, ie. the dnpass is for this "master user", but the
|
||||||
|
# dn is still the logged in user. Normally you want to keep this empty.
|
||||||
|
#sasl_authz_id =
|
||||||
|
|
||||||
|
# Use TLS to connect to the LDAP server.
|
||||||
|
#tls = no
|
||||||
|
# TLS options, currently supported only with OpenLDAP:
|
||||||
|
#tls_ca_cert_file =
|
||||||
|
#tls_ca_cert_dir =
|
||||||
|
#tls_cipher_suite =
|
||||||
|
# TLS cert/key is used only if LDAP server requires a client certificate.
|
||||||
|
#tls_cert_file =
|
||||||
|
#tls_key_file =
|
||||||
|
# Valid values: never, hard, demand, allow, try
|
||||||
|
#tls_require_cert =
|
||||||
|
|
||||||
|
# Use the given ldaprc path.
|
||||||
|
#ldaprc_path =
|
||||||
|
|
||||||
|
# LDAP library debug level as specified by LDAP_DEBUG_* in ldap_log.h.
|
||||||
|
# -1 = everything. You may need to recompile OpenLDAP with debugging enabled
|
||||||
|
# to get enough output.
|
||||||
|
#debug_level = 0
|
||||||
|
|
||||||
|
# Use authentication binding for verifying password's validity. This works by
|
||||||
|
# logging into LDAP server using the username and password given by client.
|
||||||
|
# The pass_filter is used to find the DN for the user. Note that the pass_attrs
|
||||||
|
# is still used, only the password field is ignored in it. Before doing any
|
||||||
|
# search, the binding is switched back to the default DN.
|
||||||
|
#auth_bind = no
|
||||||
|
|
||||||
|
# If authentication binding is used, you can save one LDAP request per login
|
||||||
|
# if users' DN can be specified with a common template. The template can use
|
||||||
|
# the standard %variables (see user_filter). Note that you can't
|
||||||
|
# use any pass_attrs if you use this setting.
|
||||||
|
#
|
||||||
|
# If you use this setting, it's a good idea to use a different
|
||||||
|
# dovecot-ldap.conf.ext for userdb (it can even be a symlink, just as long as
|
||||||
|
# the filename is different in userdb's args). That way one connection is used
|
||||||
|
# only for LDAP binds and another connection is used for user lookups.
|
||||||
|
# Otherwise the binding is changed to the default DN before each user lookup.
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
# auth_bind_userdn = cn=%u,ou=people,o=org
|
||||||
|
#
|
||||||
|
#auth_bind_userdn =
|
||||||
|
|
||||||
|
# LDAP protocol version to use. Likely 2 or 3.
|
||||||
|
#ldap_version = 3
|
||||||
|
|
||||||
|
# LDAP base. %variables can be used here.
|
||||||
|
# For example: dc=mail, dc=example, dc=org
|
||||||
|
base =
|
||||||
|
|
||||||
|
# Dereference: never, searching, finding, always
|
||||||
|
#deref = never
|
||||||
|
|
||||||
|
# Search scope: base, onelevel, subtree
|
||||||
|
#scope = subtree
|
||||||
|
|
||||||
|
# User attributes are given in LDAP-name=dovecot-internal-name list. The
|
||||||
|
# internal names are:
|
||||||
|
# uid - System UID
|
||||||
|
# gid - System GID
|
||||||
|
# home - Home directory
|
||||||
|
# mail - Mail location
|
||||||
|
#
|
||||||
|
# There are also other special fields which can be returned, see
|
||||||
|
# http://wiki2.dovecot.org/UserDatabase/ExtraFields
|
||||||
|
#user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid
|
||||||
|
|
||||||
|
# Filter for user lookup. Some variables can be used (see
|
||||||
|
# http://wiki2.dovecot.org/Variables for full list):
|
||||||
|
# %u - username
|
||||||
|
# %n - user part in user@domain, same as %u if there's no domain
|
||||||
|
# %d - domain part in user@domain, empty if user there's no domain
|
||||||
|
#user_filter = (&(objectClass=posixAccount)(uid=%u))
|
||||||
|
|
||||||
|
# Password checking attributes:
|
||||||
|
# user: Virtual user name (user@domain), if you wish to change the
|
||||||
|
# user-given username to something else
|
||||||
|
# password: Password, may optionally start with {type}, eg. {crypt}
|
||||||
|
# There are also other special fields which can be returned, see
|
||||||
|
# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
|
||||||
|
#pass_attrs = uid=user,userPassword=password
|
||||||
|
|
||||||
|
# If you wish to avoid two LDAP lookups (passdb + userdb), you can use
|
||||||
|
# userdb prefetch instead of userdb ldap in dovecot.conf. In that case you'll
|
||||||
|
# also have to include user_attrs in pass_attrs field prefixed with "userdb_"
|
||||||
|
# string. For example:
|
||||||
|
#pass_attrs = uid=user,userPassword=password,\
|
||||||
|
# homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid
|
||||||
|
|
||||||
|
# Filter for password lookups
|
||||||
|
#pass_filter = (&(objectClass=posixAccount)(uid=%u))
|
||||||
|
|
||||||
|
# Attributes and filter to get a list of all users
|
||||||
|
#iterate_attrs = uid=user
|
||||||
|
#iterate_filter = (objectClass=posixAccount)
|
||||||
|
|
||||||
|
# Default password scheme. "{scheme}" before password overrides this.
|
||||||
|
# List of supported schemes is in: http://wiki2.dovecot.org/Authentication
|
||||||
|
#default_pass_scheme = CRYPT
|
||||||
|
|
||||||
|
# By default all LDAP lookups are performed by the auth master process.
|
||||||
|
# If blocking=yes, auth worker processes are used to perform the lookups.
|
||||||
|
# Each auth worker process creates its own LDAP connection so this can
|
||||||
|
# increase parallelism. With blocking=no the auth master process can
|
||||||
|
# keep 8 requests pipelined for the LDAP connection, while with blocking=yes
|
||||||
|
# each connection has a maximum of 1 request running. For small systems the
|
||||||
|
# blocking=no is sufficient and uses less resources.
|
||||||
|
#blocking = no
|
|
@ -0,0 +1,20 @@
|
||||||
|
organization = "Berufsbildungswerk Südhessen gGmbH"
|
||||||
|
unit = "Kaufmännischer Bereich"
|
||||||
|
unit = "IT"
|
||||||
|
locality = "Karben"
|
||||||
|
state = "Hessen"
|
||||||
|
country = DE
|
||||||
|
cn = "BBW Root Zertifikat"
|
||||||
|
dc = "bbw"
|
||||||
|
dn_oid = "2.5.4.9 Am Heroldsrain 1" # Straße
|
||||||
|
dn_oid = "2.5.4.17 61184" # Postleitzahl
|
||||||
|
activation_date = "2018-03-01 00:00:00 UTC"
|
||||||
|
expiration_date = "2028-03-01 00:00:00 UTC"
|
||||||
|
ca
|
||||||
|
signing_key
|
||||||
|
crl_signing_key
|
||||||
|
ocsp_signing_key
|
||||||
|
policy1_txt = "Internes Zertifikat"
|
||||||
|
nc_permit_dns = bbw
|
||||||
|
honor_crq_extensions
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export typ=rsa
|
||||||
|
export laenge=4096
|
||||||
|
export crtdir=certs
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
source ./config
|
||||||
|
certtool -p --null-password --bits=$laenge --$typ --outfile=ca.key -8
|
||||||
|
certtool -s --null-password --load-privkey=ca.key --outfile=ca.pem --template=ca.txt
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
source ./config
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo -e "nicht genügend Argumente\nnutzung: $0 domain"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sed "s/DOMAIN/$1/g" normcert.txt >$crtdir/$1.txt
|
||||||
|
certtool -p --null-password --bits=$laenge --$typ --outfile=$crtdir/$1.key -8
|
||||||
|
certtool -c --null-password --load-ca-privkey=ca.key --load-ca-certificate=ca.pem --load-privkey=$crtdir/$1.key --outfile=$crtdir/$1.pem --template=$crtdir/$1.txt
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
source ./config
|
||||||
|
certtool -p --null-password --bits=$laenge --$typ --outfile=puppet.key -8
|
||||||
|
certtool -c --null-password --load-ca-privkey=ca.key --load-ca-certificate=ca.pem --load-privkey=puppet.key --outfile=puppet.pem --template=puppet.txt
|
|
@ -0,0 +1,16 @@
|
||||||
|
organization = "Berufsbildungswerk Südhessen gGmbH"
|
||||||
|
unit = "Kaufmännischer Bereich"
|
||||||
|
unit = "IT"
|
||||||
|
locality = "Karben"
|
||||||
|
state = "Hessen"
|
||||||
|
country = DE
|
||||||
|
dc = "bbw"
|
||||||
|
dn_oid = "2.5.4.9 Am Heroldsrain 1" # Straße
|
||||||
|
dn_oid = "2.5.4.17 61184" # Postleitzahl
|
||||||
|
activation_date = "2018-03-01 00:00:00 UTC"
|
||||||
|
expiration_date = "2028-03-01 00:00:00 UTC"
|
||||||
|
tls_www_server
|
||||||
|
encryption_key
|
||||||
|
cn = "DOMAIN"
|
||||||
|
dns_name = "DOMAIN"
|
||||||
|
dns_name = "www.DOMAIN"
|
|
@ -0,0 +1,14 @@
|
||||||
|
organization = "Berufsbildungswerk Südhessen gGmbH"
|
||||||
|
unit = "Kaufmännischer Bereich"
|
||||||
|
unit = "IT"
|
||||||
|
locality = "Karben"
|
||||||
|
state = "Hessen"
|
||||||
|
country = DE
|
||||||
|
cn = "Puppet Zertifikat"
|
||||||
|
dc = "bbw"
|
||||||
|
dn_oid = "2.5.4.9 Am Heroldsrain 1" # Straße
|
||||||
|
dn_oid = "2.5.4.17 61184" # Postleitzahl
|
||||||
|
activation_date = "2018-03-01 00:00:00 UTC"
|
||||||
|
expiration_date = "2028-03-01 00:00:00 UTC"
|
||||||
|
ca
|
||||||
|
honor_crq_extensions
|
|
@ -0,0 +1,134 @@
|
||||||
|
|
||||||
|
mod "oxc-dovecot", "1.0.1"
|
||||||
|
mod "pcfens-ca_cert", "1.7.1"
|
||||||
|
|
||||||
|
class { "ca_cert": }
|
||||||
|
|
||||||
|
ca_cert::ca { "GlobalSign-OrgSSL-Intermediate":
|
||||||
|
ensure => "trusted",
|
||||||
|
source => "puppet:///module/bbwmodul/bbwca.crt",
|
||||||
|
}
|
||||||
|
|
||||||
|
class {"apt":
|
||||||
|
update => {
|
||||||
|
frequency => "daily",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
class { "apt::backports":}
|
||||||
|
|
||||||
|
|
||||||
|
node "zeus", "hades" {
|
||||||
|
apt::source { "fusiondirectory":
|
||||||
|
location => "http://repos.fusiondirectory.org/fusiondirectory-current/debian-jessie/",
|
||||||
|
release => "stable",
|
||||||
|
repos => "main",
|
||||||
|
key => {
|
||||||
|
"id" => "D744D55EACDA69FF",
|
||||||
|
"server" => "pgp.mit.edu",
|
||||||
|
},
|
||||||
|
include => {
|
||||||
|
"deb" => true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node "zeus" {
|
||||||
|
class { "postgresql::globals":
|
||||||
|
encoding => "UTF-8",
|
||||||
|
locale => "de_DE.UTF-8",
|
||||||
|
}
|
||||||
|
class { "postgresql::server":
|
||||||
|
}
|
||||||
|
postgresql::server::db { "icinga":
|
||||||
|
user => "icinga",
|
||||||
|
password => postgresql_password("icinga", ""),
|
||||||
|
}
|
||||||
|
postgresql::server::db { "sogo":
|
||||||
|
user => "sogo",
|
||||||
|
password => postgresql_password("sogo", ""),
|
||||||
|
}
|
||||||
|
postgresql::server::role { "tobieseb":
|
||||||
|
password_hash => postgresql_password("tobieseb", "mypasswd"),
|
||||||
|
superuser => true,
|
||||||
|
}
|
||||||
|
postgresql::server::pg_hba_rule { "sogo access rule":
|
||||||
|
description => "Gibt sogo zugriff auf die datenbank",
|
||||||
|
type => "host",
|
||||||
|
database => "sogo",
|
||||||
|
user => "sogo",
|
||||||
|
address => "10.x.x.3",
|
||||||
|
auth_method => "md5",
|
||||||
|
}
|
||||||
|
postgresql::server::pg_hba_rule { "icinga access rule":
|
||||||
|
description => "Gibt Icinga zugriff auf die datenbank",
|
||||||
|
type => "host",
|
||||||
|
database => "icinga",
|
||||||
|
user => "icinga",
|
||||||
|
address => "10.x.x.3",
|
||||||
|
auth_method => "md5",
|
||||||
|
}
|
||||||
|
postgresql::server::pg_hba_rule { "tobieseb access rule":
|
||||||
|
description => "Gibt dem Superuser Zugriff auf die datenbank",
|
||||||
|
type => "host",
|
||||||
|
user => "tobieseb",
|
||||||
|
address => "172.16.34.0/23",
|
||||||
|
auth_method => "md5",
|
||||||
|
}
|
||||||
|
file { "/etc/dovecot/private/dovecot-ldap.conf":
|
||||||
|
mode => 000,
|
||||||
|
owner => "",
|
||||||
|
group => "",
|
||||||
|
source => "puppet:///modules/bbwmodul/dovecot-ldap.conf",
|
||||||
|
}
|
||||||
|
class { "dovecot":
|
||||||
|
"plugins" => [ "imap" ],
|
||||||
|
"config" => {
|
||||||
|
"protocols" => "imap",
|
||||||
|
"listen" => "*, ::",
|
||||||
|
},
|
||||||
|
"configs" => {
|
||||||
|
"auth-ldap" => {
|
||||||
|
"passdb" => {
|
||||||
|
"driver" => "ldap",
|
||||||
|
"args" => "/etc/dovecot/private/dovecot-ldap.conf",
|
||||||
|
},
|
||||||
|
"userdb" => {
|
||||||
|
"driver" => "ldap",
|
||||||
|
"args" => "/etc/dovecot/private/dovecot-ldap.conf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node "poseidon" {
|
||||||
|
}
|
||||||
|
node "hades" {
|
||||||
|
class { "apache": }
|
||||||
|
class { "apache::mod::ssl": }
|
||||||
|
apache2::vhost { "sogo.bbw http":
|
||||||
|
"port" => "80",
|
||||||
|
redirect_status => "permanent",
|
||||||
|
redirect_dest => "https://sogo.bbw/",
|
||||||
|
"servername" => "sogo.bbw",
|
||||||
|
"ssl" => false,
|
||||||
|
}
|
||||||
|
apache2::vhost { "sogo.bbw https":
|
||||||
|
"port" => "443",
|
||||||
|
"docroot" => "",
|
||||||
|
"servername" => "sogo.bbw",
|
||||||
|
"ssl" => true,
|
||||||
|
}
|
||||||
|
apache2::vhost { "management.bbw http":
|
||||||
|
"port" => "80",
|
||||||
|
redirect_status => "permanent",
|
||||||
|
redirect_dest => "https://management.bbw/",
|
||||||
|
"servername" => "management.bbw",
|
||||||
|
"ssl" => false,
|
||||||
|
}
|
||||||
|
apache2::vhost { "management.bbw https":
|
||||||
|
"port" => "80",
|
||||||
|
"docroot" => "",
|
||||||
|
"servername" => "management.bbw",
|
||||||
|
"ssl" => true,
|
||||||
|
}
|
||||||
|
}
|
Binäre Datei nicht angezeigt.
339
ca/myca.py
339
ca/myca.py
|
@ -1,339 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
import argparse
|
|
||||||
import sqlite3
|
|
||||||
from cryptography import x509
|
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
||||||
from cryptography.hazmat.backends import default_backend
|
|
||||||
from cryptography.hazmat.primitives import hashes
|
|
||||||
from cryptography.hazmat.primitives.serialization import PrivateFormat, NoEncryption, BestAvailableEncryption, load_pem_private_key
|
|
||||||
from cryptography.x509.oid import NameOID
|
|
||||||
from cryptography.hazmat.primitives.serialization import Encoding
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
def parser_factory():
|
|
||||||
parser = argparse.ArgumentParser(description="Programm zur Administration von Zertifikaten")
|
|
||||||
parser.add_argument("--db", type=str, help="Datenbank die benutzt wird.", default="certificates.sqlite3")
|
|
||||||
subparser = parser.add_subparsers(dest="aktion", title="Kommandos", )
|
|
||||||
newca = subparser.add_parser("newca", help="erstellt ein neues CA-Zertifikat")
|
|
||||||
request = subparser.add_parser("request", help="erstellt eine Zertifikatsanfrage")
|
|
||||||
sign = subparser.add_parser("sign", help="TBI Signiert eine Zertifikatsanfrage")
|
|
||||||
config = subparser.add_parser("config", help="TBI")
|
|
||||||
for kommando in [ newca, request]:
|
|
||||||
kommando.add_argument("--length", type=int, help="Schlüssellänge des Zertifikats, sollte ein Exponent von 2 und größer als 1024 sein", default=4096, dest="länge")
|
|
||||||
kommando.add_argument("--commonname", type=str, help="Name des Besitzers", required=True, dest="cn")
|
|
||||||
kommando.add_argument("--location", help="Stadt", dest="l")
|
|
||||||
kommando.add_argument("--state", help="Bundesland/Kanton", dest="st")
|
|
||||||
kommando.add_argument("--organization", help="Firma", dest="o")
|
|
||||||
kommando.add_argument("--ou", help="Abteilung", dest="ou")
|
|
||||||
kommando.add_argument("--country", help="Land", dest="c")
|
|
||||||
kommando.add_argument("--street", help="Straße", dest="street")
|
|
||||||
request.add_argument(
|
|
||||||
"--san",
|
|
||||||
help="Alternativer Name für die Resource, kann eine Emailadresse, URI oder eine Domain sein. sonderzeichen in Domains sollten bereits mittels IDNA umkodiert worden sein.",
|
|
||||||
action="append",
|
|
||||||
dest="san",
|
|
||||||
nargs="?",
|
|
||||||
)
|
|
||||||
request.add_argument("-s", dest="server", action="store_true", help="Serverzertifikat")
|
|
||||||
request.add_argument("-cl", dest="client", action="store_true", help="Clientzertifikat")
|
|
||||||
request.add_argument("-co", dest="code", action="store_true", help="Codesignaturzertifikat")
|
|
||||||
request.add_argument("-e", dest="email", action="store_true", help="Emailzertifikat")
|
|
||||||
request.add_argument("--ca", dest="ca", help="Certificate Authority, Zertifizierungstelle mit optionaler Pfadlänge", nargs="?", type=int)
|
|
||||||
newca.add_argument("tage", type=int, help="Tage die das Zertifikat gültig ist")
|
|
||||||
return parser
|
|
||||||
|
|
||||||
def initdb(cursor):
|
|
||||||
cursor.execute("CREATE TABLE IF NOT EXISTS \"certificates\" ( `commonname` TEXT NOT NULL, `serial` TEXT NOT NULL UNIQUE, `publickey` TEXT NOT NULL, `privatkey` TEXT NOT NULL UNIQUE, `parent` integer, `ca` INTEGER NOT NULL, PRIMARY KEY(`commonname`), FOREIGN KEY(`parent`) REFERENCES `certificates`(`serial`) ) WITHOUT ROWID")
|
|
||||||
cursor.execute("CREATE TABLE IF NOT EXISTS \"requests\" ( `commonname` TEXT NOT NULL, `csr` TEXT NOT NULL UNIQUE, `privatkey` TEXT NOT NULL, PRIMARY KEY(`commonname`) )")
|
|
||||||
|
|
||||||
def print_wrp(text):
|
|
||||||
def wrapper(*t):
|
|
||||||
print(text)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
def str_exttype(exttype):
|
|
||||||
if isinstance(exttype, x509.BasicConstraints):
|
|
||||||
if not exttype.ca:
|
|
||||||
return "Keine CA"
|
|
||||||
if exttype.path_length in [None, 0]:
|
|
||||||
return "CA, unendliche Pfadlänge"
|
|
||||||
return "CA, Pfadlänge {}".format(exttype.path_length)
|
|
||||||
elif isinstance(exttype, x509.ExtendedKeyUsage):
|
|
||||||
text = ""
|
|
||||||
for usage in exttype:
|
|
||||||
text += usage._name + ", "
|
|
||||||
return text[:-2]
|
|
||||||
elif isinstance(exttype, x509.SubjectAlternativeName):
|
|
||||||
text = ""
|
|
||||||
for an in exttype._general_names:
|
|
||||||
text += an._value + ", "
|
|
||||||
return text[:-2]
|
|
||||||
else:
|
|
||||||
return repr(exttype)
|
|
||||||
|
|
||||||
def print_info(cert):
|
|
||||||
for name in cert.subject.rdns:
|
|
||||||
for oid in name:
|
|
||||||
print("{}: {}".format(oid._oid._name, oid.value))
|
|
||||||
for extension in cert.extensions:
|
|
||||||
kritisch = "kritisch"
|
|
||||||
if not extension.critical:
|
|
||||||
kritisch = "nicht" + kritisch
|
|
||||||
print("{}({}): {}".format(extension.oid._name, kritisch, str_exttype(extension.value)))
|
|
||||||
|
|
||||||
class Certwrapper:
|
|
||||||
def __init__(self, backend):
|
|
||||||
self.threads = ThreadPoolExecutor(3, "cryptoops")
|
|
||||||
self.anfragetemplate = x509.CertificateSigningRequestBuilder()
|
|
||||||
self.backend = backend
|
|
||||||
|
|
||||||
def gencert(self, länge):
|
|
||||||
return self.threads.submit(rsa.generate_private_key, 65537, länge, self.backend)
|
|
||||||
|
|
||||||
def sign(self, subject, issuer, time_valid, public_key, privat_ca_key, extensions=None, ca_key=None):
|
|
||||||
if not extensions:
|
|
||||||
extensions = []
|
|
||||||
builder = x509.CertificateBuilder()
|
|
||||||
jetzt = datetime.datetime.now()
|
|
||||||
builder = builder.serial_number(x509.random_serial_number())
|
|
||||||
builder = builder.not_valid_before(jetzt)
|
|
||||||
builder = builder.not_valid_after(jetzt + time_valid)
|
|
||||||
builder = builder.subject_name(subject)
|
|
||||||
builder = builder.issuer_name(issuer)
|
|
||||||
builder = builder.public_key(public_key)
|
|
||||||
for extension in extensions:
|
|
||||||
builder = builder.add_extension(extension, True)
|
|
||||||
if ca_key:
|
|
||||||
builder = builder.add_extension(x509.AuthorityKeyIdentifier(ca_key.fingerprint(hashes.SHA512()), None, None), True)
|
|
||||||
return builder.sign(privat_ca_key, hashes.SHA512(), self.backend)
|
|
||||||
|
|
||||||
def request(self, subject, erweiterungen=[], certlänge=4096):
|
|
||||||
cert = self.gencert(certlänge)
|
|
||||||
anfrage = self.anfragetemplate.subject_name(subject)
|
|
||||||
for erweiterung in erweiterungen:
|
|
||||||
anfrage = anfrage.add_extension(erweiterung, True)
|
|
||||||
anfrage = anfrage.sign(cert.result(), hashes.SHA512(), self.backend)
|
|
||||||
return {
|
|
||||||
"privat": cert.result(),
|
|
||||||
"csr": anfrage
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DBbackend:
|
|
||||||
def __init__(self, connection):
|
|
||||||
self.connection = connection
|
|
||||||
|
|
||||||
def insert_certificate(self, commonname: str, serial, publickey, privatkey, ca: bool):
|
|
||||||
self.connection.execute(
|
|
||||||
"INSERT INTO `certificates`(commonname, serial, publickey, privatkey, ca) VALUES (?, ?, ?, ?, ?)",
|
|
||||||
(
|
|
||||||
commonname,
|
|
||||||
serial,
|
|
||||||
publickey,
|
|
||||||
privatkey,
|
|
||||||
ca
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
def insert_request(self, commonname, cst, privatkey):
|
|
||||||
self.connection.execute(
|
|
||||||
"INSERT INTO `requests`(commonname, csr, privatkey) VALUES (?, ?, ?)",
|
|
||||||
(
|
|
||||||
commonname,
|
|
||||||
csr,
|
|
||||||
privatkey,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
|
|
||||||
class Commands:
|
|
||||||
def __init__(self, backend, connection, args):
|
|
||||||
self.backend = backend
|
|
||||||
self.threads = ThreadPoolExecutor(3, "cryptoops")
|
|
||||||
self.connection = connection
|
|
||||||
self.connection.row_factory = sqlite3.Row
|
|
||||||
self.cursor = connection.cursor()
|
|
||||||
self.arguments = args
|
|
||||||
self.wrapper = Certwrapper(backend)
|
|
||||||
self.db = DBbackend(connection)
|
|
||||||
|
|
||||||
def get(self, name):
|
|
||||||
try:
|
|
||||||
return self.__getattribute__(name)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def fragen(self, fragen, typ=tuple):
|
|
||||||
antworten = []
|
|
||||||
for id in fragen:
|
|
||||||
antworten.append(typ(id, self.frage(fragen[id], False)))
|
|
||||||
return antworten
|
|
||||||
|
|
||||||
def frage(self, text, typ=str):
|
|
||||||
while True:
|
|
||||||
antwort = input("{}: ".format(text))
|
|
||||||
try:
|
|
||||||
return typ(antwort)
|
|
||||||
except TypeError:
|
|
||||||
print("Falsche Eingabe")
|
|
||||||
continue
|
|
||||||
|
|
||||||
def parsedn(self, dn):
|
|
||||||
namen = []
|
|
||||||
if dn.cn:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.COMMON_NAME, dn.cn))
|
|
||||||
if dn.st:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, dn.st))
|
|
||||||
if dn.l:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.LOCALITY_NAME, dn.l))
|
|
||||||
if dn.o:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, dn.o))
|
|
||||||
if dn.ou:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.ORGANIZATIONAL_UNIT_NAME, dn.ou))
|
|
||||||
if dn.c:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.COUNTRY_NAME, dn.c))
|
|
||||||
if dn.street:
|
|
||||||
namen.append(x509.NameAttribute(x509.NameOID.STREET_ADDRESS, dn.street))
|
|
||||||
return namen
|
|
||||||
|
|
||||||
def newca(self):
|
|
||||||
privater = self.wrapper.gencert(self.arguments.länge)
|
|
||||||
jetzt = datetime.datetime.now()
|
|
||||||
privater.add_done_callback(print_wrp("Privater Schlüssel generiert"))
|
|
||||||
subject = x509.Name(self.parsedn(self.arguments))
|
|
||||||
zertifikat = self.wrapper.sign(
|
|
||||||
subject=subject,
|
|
||||||
issuer=subject,
|
|
||||||
time_valid=datetime.timedelta(days=self.arguments.tage),
|
|
||||||
public_key=privater.result().public_key(),
|
|
||||||
privat_ca_key=privater.result()
|
|
||||||
)
|
|
||||||
passwort = self.frage("Passwort")
|
|
||||||
if passwort:
|
|
||||||
encryption = BestAvailableEncryption(passwort.encode())
|
|
||||||
else:
|
|
||||||
encryption = NoEncryption()
|
|
||||||
self.db.insert_certificate(
|
|
||||||
self.arguments.cn,
|
|
||||||
hex(zertifikat.serial_number),
|
|
||||||
zertifikat.public_bytes(Encoding.PEM),
|
|
||||||
privater.result().private_bytes(encoding=Encoding.PEM, format=PrivateFormat.PKCS8, encryption_algorithm=encryption),
|
|
||||||
True
|
|
||||||
)
|
|
||||||
|
|
||||||
def request(self):
|
|
||||||
erweiterungen = []
|
|
||||||
if self.arguments.ca:
|
|
||||||
erweiterungen.append(x509.BasicConstraints(True, self.arguments.ca))
|
|
||||||
else:
|
|
||||||
erweiterungen.append(x509.BasicConstraints(False, None))
|
|
||||||
if self.arguments.san:
|
|
||||||
alt = []
|
|
||||||
for san in self.arguments.san:
|
|
||||||
if ":" in san:
|
|
||||||
alt.append(x509.UniformResourceIdentifier(san))
|
|
||||||
elif "@" in san:
|
|
||||||
alt.append(x509.RFC822Name(san))
|
|
||||||
else:
|
|
||||||
alt.append(x509.DNSName(san))
|
|
||||||
erweiterungen.append(x509.SubjectAlternativeName(alt))
|
|
||||||
ziele = []
|
|
||||||
if self.arguments.server:
|
|
||||||
ziele.append(x509.oid.ExtendedKeyUsageOID.SERVER_AUTH)
|
|
||||||
if self.arguments.client:
|
|
||||||
ziele.append(x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH)
|
|
||||||
if self.arguments.code:
|
|
||||||
ziele.append(x509.oid.ExtendedKeyUsageOID.CODE_SIGNING)
|
|
||||||
if self.arguments.email:
|
|
||||||
ziele.append(x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION)
|
|
||||||
erweiterungen.append(x509.ExtendedKeyUsage(ziele))
|
|
||||||
anfrage = self.wrapper.request(
|
|
||||||
subject=x509.Name(self.parsedn(self.arguments)),
|
|
||||||
erweiterungen=erweiterungen,
|
|
||||||
certlänge=self.arguments.länge
|
|
||||||
)
|
|
||||||
passwort = self.frage("Passwort")
|
|
||||||
if passwort:
|
|
||||||
encryption = BestAvailableEncryption(passwort.encode())
|
|
||||||
else:
|
|
||||||
encryption = NoEncryption()
|
|
||||||
self.db.insert_request(
|
|
||||||
self.arguments.cn,
|
|
||||||
anfrage.public_bytes(Encoding.PEM),
|
|
||||||
privater.result().private_bytes(encoding=Encoding.PEM, format=PrivateFormat.PKCS8, encryption_algorithm=encryption),
|
|
||||||
)
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
def sign(self):
|
|
||||||
def delete_request(subject):
|
|
||||||
self.cursor.execute("DELETE FROM requests WHERE commonname=?", (subject, ))
|
|
||||||
self.cursor.execute("SELECT commonname, csr, privatkey FROM requests")
|
|
||||||
for reihe in self.cursor.fetchall():
|
|
||||||
try:
|
|
||||||
anfrage = x509.load_pem_x509_csr(reihe["csr"], self.backend)
|
|
||||||
if not anfrage.is_signature_valid:
|
|
||||||
raise TypeError()
|
|
||||||
except TypeError:
|
|
||||||
delete_request(reihe["commonname"])
|
|
||||||
continue
|
|
||||||
print_info(anfrage)
|
|
||||||
if not self.frage("Möchten Sie das Zertifikat signieren[01]" ,int):
|
|
||||||
delete_request(reihe["commonname"])
|
|
||||||
continue
|
|
||||||
cas = []
|
|
||||||
for row in self.cursor.execute("SELECT commonname, publickey, privatkey FROM certificates WHERE ca=1"):
|
|
||||||
cas.append((row["commonname"], x509.load_pem_x509_certificate(row["publickey"], self.backend), row["privatkey"]))
|
|
||||||
print("Wähle die CA:\n")
|
|
||||||
for counter in range(1, len(cas)+1):
|
|
||||||
print("{}: {}".format(counter, cas[counter-1][0]))
|
|
||||||
while True:
|
|
||||||
antwort = self.frage("CA", int)
|
|
||||||
antwort -= 1
|
|
||||||
if 0 <= antwort < len(cas):
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
privat = load_pem_private_key(cas[antwort][2], None, self.backend)
|
|
||||||
except TypeError:
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
pw = self.frage("Passwort")
|
|
||||||
privat = load_pem_private_key(cas[antwort][2], pw.encode("utf-8"), self.backend)
|
|
||||||
except TypeError:
|
|
||||||
print("Falsches Passwort")
|
|
||||||
ca = cas[antwort][1]
|
|
||||||
dn = cas[antwort][0]
|
|
||||||
zertifikat = self.sign_cert(
|
|
||||||
anfrage.subject,
|
|
||||||
ca.subject,
|
|
||||||
datetime.timedelta(days=self.frage("Laufzeit", int)),
|
|
||||||
anfrage.public_key(),
|
|
||||||
privat,
|
|
||||||
[ext.value for ext in anfrage.extensions],
|
|
||||||
ca
|
|
||||||
)
|
|
||||||
self.db.insert_certificate(
|
|
||||||
reihe["commonname"],
|
|
||||||
hex(zertifikat.serial_number),
|
|
||||||
zertifikat.public_bytes(Encoding.PEM),
|
|
||||||
reihe["privatkey"],
|
|
||||||
dn,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
delete_request(reihe["commonname"])
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = parser_factory()
|
|
||||||
argumente = parser.parse_args()
|
|
||||||
print(argumente.aktion)
|
|
||||||
datenbank = sqlite3.connect(argumente.db)
|
|
||||||
initdb(datenbank.cursor())
|
|
||||||
commandos = Commands(default_backend(), datenbank, argumente)
|
|
||||||
if commandos.get(argumente.aktion):
|
|
||||||
commandos.get(argumente.aktion)()
|
|
||||||
else:
|
|
||||||
parser.print_help()
|
|
In neuem Issue referenzieren