aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--myrpki/Makefile7
-rw-r--r--myrpki/README54
-rw-r--r--myrpki/myrpki.conf54
-rw-r--r--myrpki/myrpki.py183
-rw-r--r--myrpki/relatives.conf19
5 files changed, 243 insertions, 74 deletions
diff --git a/myrpki/Makefile b/myrpki/Makefile
index f86c00c3..004b35df 100644
--- a/myrpki/Makefile
+++ b/myrpki/Makefile
@@ -1,5 +1,6 @@
# $Id$
+all:: relatives
all:: myrpki.xml
all:: lint
#all:: parse
@@ -21,12 +22,12 @@ load: myrpki.xml myrpki.rng
python myirbe.py
clean:
- rm -f *.xml *.pem
+ rm -rf *.xml *.pem bpki
relatives: mom.pem dad.pem bro.pem sis.pem
-mom.pem dad.pem bro.pem sis.pem:
- openssl req -new -sha256 -x509 -verbose -config myrpki.conf -extensions req_x509_ext -newkey rsa:2048 -nodes -keyout /dev/null -subj CN=$@ -out $@
+mom.pem dad.pem bro.pem sis.pem: relatives.conf
+ CN=$@ openssl req -new -sha256 -x509 -verbose -config relatives.conf -extensions req_x509_ext -newkey rsa:2048 -nodes -keyout /dev/null -out $@
format: myrpki.xml
xmllint --format myrpki.xml
diff --git a/myrpki/README b/myrpki/README
new file mode 100644
index 00000000..5161b376
--- /dev/null
+++ b/myrpki/README
@@ -0,0 +1,54 @@
+$Id$
+
+testbed.py creates so freaking many BPKI certificates that even I can't
+keep track of what they're all for anymore. So try starting over.
+
+Hosted (myrpki) entity needs:
+
+- self-signed bpki root (doesn't really need to be self-signed, nobody
+ else will care, but self-signed is simplest for our purposes). this
+ is what we've been calling the "self" cert in testbed.py.
+
+- BSC EE issued by self-signed root.
+
+- cross-certs of every foreign entity (parent, child, or pubd): these
+ are ca certs with pathLenConstraint 0. input for this cross-cert is
+ self-signed (or whatever) from foreign entity, output is
+ pathLenConstraint 0 ca cert issued by myrpki entity's own
+ self-signed root.
+
+Hosting rpkid needs:
+
+- self-signed bpki root
+
+- bsc ees for rpkid, irdbd, irbe_cli, etc
+
+- for each hosted entity (including self-hosting):
+
+ - cross-cert of hosted entity's root, issued by rpkid root, ca cert
+ perhaps with pathLenConstraint 1
+
+ In theory that's all that's required, everything else is handled
+ through the hosted entity's cert chain.
+
+pubd needs:
+
+- self signed root (might share with rpkid but let's keep it separate
+ conceptually)
+
+- bsc ees for pubd and irbe_cli
+
+- for each client entity of pubd:
+
+ - cross-cert of client entity's self cert (pathLenConstraint 0).
+
+ This should allow pubd to verify clients' bsc ee certs without
+ getting into transitive ca relationships.
+
+rootd (when applicable at all) needs:
+
+- self signed root
+
+- bsc ee for talking up-down (server) with one and only child
+
+- cross-cert (pathLenConstraint 0) of one and only child's self cert.
diff --git a/myrpki/myrpki.conf b/myrpki/myrpki.conf
index 54d63e04..4f68784b 100644
--- a/myrpki/myrpki.conf
+++ b/myrpki/myrpki.conf
@@ -13,38 +13,72 @@ children_csv = children.csv
parents_csv = parents.csv
prefix_csv = prefixes.csv
asn_csv = asns.csv
-bpki_ca_certificate = bpki-ca-cert.pem
-bpki_ca_key = bpki-ca-key.pem
-bpki_ee_certificate = bpki-ee-cert.pem
-bpki_ee_pkcs10 = bpki-ee-pkcs10.pem
-bpki_crl = bpki-crl.pem
-bpki_index = bpki-ca-index.idx
+
+bpki_ca_dir = bpki
+bpki_serial = bpki/serial.txt
+bpki_crl_number = bpki/crl_number.txt
+bpki_ca_certificate = bpki/ca-cert.pem
+bpki_ca_key = bpki/ca-key.pem
+bpki_ee_certificate = bpki/bsc-ee-cert.pem
+bpki_ee_pkcs10 = bpki/bsc-ee-pkcs10.pem
+bpki_crl = bpki/crl.pem
+bpki_index = bpki/index.txt
output_filename = myrpki.xml
relaxng_schema = myrpki.rng
+[constants]
+digest = sha256
+key_length = 2048
+
[req]
-default_bits = 2048
-default_md = sha256
+default_bits = ${constants::key_length}
+default_md = ${constants::digest}
distinguished_name = req_dn
x509_extensions = req_x509_ext
prompt = no
[req_dn]
-CN = wombat
+CN = ${myrpki::handle}
[req_x509_ext]
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
+[ca_ee_x509_ext]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always
+
+[ca_ca_x509_ext]
+basicConstraints = critical,CA:true,pathlen:0
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always
+
[ca]
default_ca = ca_default
[ca_default]
database = ${myrpki::bpki_index}
+new_certs_dir = ${myrpki::bpki_ca_dir}
certificate = ${myrpki::bpki_ca_certificate}
private_key = ${myrpki::bpki_ca_key}
+default_days = 365
default_crl_days = 365
-default_md = sha256
+default_md = ${constants::digest}
+policy = ca_dn_policy_allow_any_dn
+unique_subject = no
+serial = ${myrpki::bpki_serial}
+crlnumber = ${myrpki::bpki_crl_number}
+
+[ca_dn_policy_allow_any_dn]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+givenName = optional
+surname = optional
diff --git a/myrpki/myrpki.py b/myrpki/myrpki.py
index afded205..7a360fa3 100644
--- a/myrpki/myrpki.py
+++ b/myrpki/myrpki.py
@@ -27,7 +27,7 @@ import subprocess, csv, re, os, getopt, sys, ConfigParser
from xml.etree.ElementTree import Element, SubElement, ElementTree
-namespace = "http://www.hactrn.net/uris/rpki/myrpki/"
+namespace = "http://www.hactrn.net/uris/rpki/myrpki/"
class comma_set(set):
@@ -53,7 +53,7 @@ class roa_request(object):
elif self.v6re.match(prefix):
self.v6.add(prefix)
else:
- raise RuntimeError, 'Bad prefix syntax: "%s"' % prefix
+ raise RuntimeError, "Bad prefix syntax: %r" % (prefix,)
def xml(self, e):
return SubElement(e, "roa_request",
@@ -103,7 +103,7 @@ class child(object):
elif self.v6re.match(prefix):
self.v6.add(prefix)
else:
- raise RuntimeError, 'Bad prefix syntax: "%s"' % prefix
+ raise RuntimeError, "Bad prefix syntax: %r" % (prefix,)
if asn is not None:
self.asns.add(asn)
if validity is not None:
@@ -134,11 +134,11 @@ class children(dict):
c.xml(e)
@classmethod
- def from_csv(cls, children_csv_file, prefix_csv_file, asn_csv_file):
+ def from_csv(cls, children_csv_file, prefix_csv_file, asn_csv_file, cfg_file, bpki_dir):
self = cls()
# childname date pemfile
for handle, date, pemfile in csv_open(children_csv_file):
- self.add(handle = handle, validity = date, ta = pemfile)
+ self.add(handle = handle, validity = date, ta = xcert(pemfile, bpki_dir, cfg_file))
# childname p/n
for handle, pn in csv_open(prefix_csv_file):
self.add(handle = handle, prefix = pn)
@@ -183,27 +183,64 @@ class parents(dict):
c.xml(e)
@classmethod
- def from_csv(cls, parents_csv_file):
+ def from_csv(cls, parents_csv_file, cfg_file, bpki_dir):
self = cls()
# parentname uri pemfile
for handle, uri, pemfile in csv_open(parents_csv_file):
- self.add(handle = handle, uri = uri, ta = pemfile)
+ self.add(handle = handle, uri = uri, ta = xcert(pemfile, bpki_dir, cfg_file))
return self
def csv_open(filename, delimiter = "\t", dialect = None):
return csv.reader(open(filename, "rb"), dialect = dialect, delimiter = delimiter)
+def xcert(pemfile, bpki_dir, cfg_file):
+
+ if not pemfile:
+ return None
+ if not os.path.exists(pemfile):
+ raise RuntimeError, "PEM file %r does not exist" % (pemfile,)
+
+ # Extract public key and subject name from PEM file and hash it so
+ # we can use the result as a tag for cross-certifying this cert.
+
+ p1 = subprocess.Popen(("openssl", "x509", "-noout", "-pubkey", "-subject", "-in", pemfile), stdout = subprocess.PIPE)
+ p2 = subprocess.Popen(("openssl", "dgst", "-md5"), stdin = p1.stdout, stdout = subprocess.PIPE)
+ xcertfile = "%s/%s.xcert.pem" % (bpki_dir, p2.communicate()[0].strip())
+ if p1.wait() != 0 or p2.wait() != 0:
+ raise RuntimeError, "Couldn't generate cross-certification tag for %r" % pemfile
+
+ # Cross-certify the pemfile we were given, if we haven't already.
+ # This only works for self-signed certs, due to limitations of the
+ # OpenSSL command line tool.
+
+ if not os.path.exists(xcertfile):
+ subprocess.check_call(("openssl", "ca", "-verbose", "-notext", "-batch",
+ "-config", cfg_file,
+ "-ss_cert", pemfile,
+ "-out", xcertfile,
+ "-extensions", "ca_ca_x509_ext"))
+
+ # This should probably change to be the file content, coordinate with PEMElement()
+ return xcertfile
+
def PEMElement(e, tag, filename):
e = SubElement(e, tag)
e.text = "".join(p.strip() for p in open(filename).readlines()[1:-1])
-def bpki_ca(e, bpki_ca_key_file, bpki_ca_cert_file, bpki_crl_file, bpki_index_file, cfg_file):
+def bpki_setup(bpki_ca_key_file, bpki_ca_cert_file, bpki_crl_file, bpki_index_file, cfg_file,
+ bpki_dir, bpki_serial_file, bpki_crl_number_file, bpki_ee_req_file, bpki_ee_cert_file):
+
+ # Create our BPKI database directory
+ if not os.path.exists(bpki_dir):
+ os.makedirs(bpki_dir)
+ # Create our trust anchor key
if not os.path.exists(bpki_ca_key_file):
subprocess.check_call(("openssl", "genrsa",
"-out", bpki_ca_key_file,
"2048"))
+ # Create our self-signed trust anchor
if not os.path.exists(bpki_ca_cert_file):
subprocess.check_call(("openssl", "req", "-new", "-sha256", "-x509", "-verbose",
"-config", cfg_file,
@@ -211,32 +248,39 @@ def bpki_ca(e, bpki_ca_key_file, bpki_ca_cert_file, bpki_crl_file, bpki_index_fi
"-key", bpki_ca_key_file,
"-out", bpki_ca_cert_file))
- if not os.path.exists(bpki_crl_file):
+ # Create empty index file for "openssl ca"
+ if not os.path.exists(bpki_index_file):
+ f = open(bpki_index_file, "w")
+ f.close()
+
+ # Create serial number file for "openssl ca"
+ if not os.path.exists(bpki_serial_file):
+ f = open(bpki_serial_file, "w")
+ f.write("01\n")
+ f.close()
- if not os.path.exists(bpki_index_file):
- open(bpki_index_file, "w").close()
+ # Create CRL number file for "openssl ca"
+ if not os.path.exists(bpki_crl_number_file):
+ f = open(bpki_crl_number_file, "w")
+ f.write("01\n")
+ f.close()
- subprocess.check_call(("openssl", "ca", "-batch", "-verbose", "-gencrl",
+ # Create CRL
+ if not os.path.exists(bpki_crl_file):
+ subprocess.check_call(("openssl", "ca", "-batch", "-verbose", "-batch", "-notext",
+ "-gencrl",
+ "-config", cfg_file,
"-out", bpki_crl_file,
"-config", cfg_file))
- PEMElement(e, "bpki_ca_certificate", bpki_ca_cert_file)
- PEMElement(e, "bpki_crl", bpki_crl_file)
-
-def bpki_ee(e, bpki_ee_req_file, bpki_ee_cert_file, bpki_ca_cert_file, bpki_ca_key_file):
-
- if os.path.exists(bpki_ee_req_file):
-
- if not os.path.exists(bpki_ee_cert_file):
- subprocess.check_call(("openssl", "x509", "-req", "-sha256", "-days", "360",
- "-CA", bpki_ca_cert_file,
- "-CAkey", bpki_ca_key_file,
- "-in", bpki_ee_req_file,
- "-out", bpki_ee_cert_file,
- "-CAcreateserial"))
+ # Create BSC EE cert
+ if os.path.exists(bpki_ee_req_file) and not os.path.exists(bpki_ee_cert_file):
+ subprocess.check_call(("openssl", "ca", "-verbose", "-batch", "-notext",
+ "-config", cfg_file,
+ "-extensions", "ca_ee_x509_ext",
+ "-in", bpki_ee_req_file,
+ "-out", bpki_ee_cert_file))
- PEMElement(e, "bpki_ee_certificate", bpki_ee_cert_file)
-
def extract_resources():
pass
@@ -253,45 +297,62 @@ def main():
elif o in ("-c", "--config"):
cfg_file = a
if argv:
- raise RuntimeError, "Unexpected arguments %s" % argv
+ raise RuntimeError, "Unexpected arguments %r" % (argv,)
cfg = ConfigParser.RawConfigParser()
cfg.read(cfg_file)
- my_handle = cfg.get(myrpki_section, "handle")
- roa_csv_file = cfg.get(myrpki_section, "roa_csv")
- children_csv_file = cfg.get(myrpki_section, "children_csv")
- parents_csv_file = cfg.get(myrpki_section, "parents_csv")
- prefix_csv_file = cfg.get(myrpki_section, "prefix_csv")
- asn_csv_file = cfg.get(myrpki_section, "asn_csv")
- bpki_ca_cert_file = cfg.get(myrpki_section, "bpki_ca_certificate")
- bpki_ca_key_file = cfg.get(myrpki_section, "bpki_ca_key")
- bpki_ee_cert_file = cfg.get(myrpki_section, "bpki_ee_certificate")
- bpki_ee_req_file = cfg.get(myrpki_section, "bpki_ee_pkcs10")
- bpki_crl_file = cfg.get(myrpki_section, "bpki_crl")
- bpki_index_file = cfg.get(myrpki_section, "bpki_index")
- output_filename = cfg.get(myrpki_section, "output_filename")
- relaxng_schema = cfg.get(myrpki_section, "relaxng_schema")
-
- roas = roa_requests.from_csv(roa_csv_file)
- kids = children.from_csv(children_csv_file, prefix_csv_file, asn_csv_file)
- rents = parents.from_csv(parents_csv_file)
+ my_handle = cfg.get(myrpki_section, "handle")
+ roa_csv_file = cfg.get(myrpki_section, "roa_csv")
+ children_csv_file = cfg.get(myrpki_section, "children_csv")
+ parents_csv_file = cfg.get(myrpki_section, "parents_csv")
+ prefix_csv_file = cfg.get(myrpki_section, "prefix_csv")
+ asn_csv_file = cfg.get(myrpki_section, "asn_csv")
+ bpki_dir = cfg.get(myrpki_section, "bpki_ca_dir")
+ bpki_ca_cert_file = cfg.get(myrpki_section, "bpki_ca_certificate")
+ bpki_ca_key_file = cfg.get(myrpki_section, "bpki_ca_key")
+ bpki_ee_cert_file = cfg.get(myrpki_section, "bpki_ee_certificate")
+ bpki_ee_req_file = cfg.get(myrpki_section, "bpki_ee_pkcs10")
+ bpki_crl_file = cfg.get(myrpki_section, "bpki_crl")
+ bpki_index_file = cfg.get(myrpki_section, "bpki_index")
+ bpki_serial_file = cfg.get(myrpki_section, "bpki_serial")
+ bpki_crl_number_file = cfg.get(myrpki_section, "bpki_crl_number")
+ output_filename = cfg.get(myrpki_section, "output_filename")
+ relaxng_schema = cfg.get(myrpki_section, "relaxng_schema")
+
+ bpki_setup(
+ bpki_ca_cert_file = bpki_ca_cert_file,
+ bpki_ca_key_file = bpki_ca_key_file,
+ bpki_crl_file = bpki_crl_file,
+ bpki_dir = bpki_dir,
+ bpki_ee_cert_file = bpki_ee_cert_file,
+ bpki_ee_req_file = bpki_ee_req_file,
+ bpki_index_file = bpki_index_file,
+ bpki_serial_file = bpki_serial_file,
+ bpki_crl_number_file = bpki_crl_number_file,
+ cfg_file = cfg_file)
e = Element("myrpki", xmlns = namespace, version = "1", handle = my_handle)
- roas.xml(e)
- kids.xml(e)
- rents.xml(e)
- bpki_ca(e,
- bpki_ca_key_file = bpki_ca_key_file,
- bpki_ca_cert_file = bpki_ca_cert_file,
- bpki_crl_file = bpki_crl_file,
- bpki_index_file = bpki_index_file,
- cfg_file = cfg_file)
- bpki_ee(e,
- bpki_ee_req_file = bpki_ee_req_file,
- bpki_ee_cert_file = bpki_ee_cert_file,
- bpki_ca_cert_file = bpki_ca_cert_file,
- bpki_ca_key_file = bpki_ca_key_file)
+
+ roa_requests.from_csv(roa_csv_file).xml(e)
+
+ children.from_csv(
+ children_csv_file = children_csv_file,
+ prefix_csv_file = prefix_csv_file,
+ asn_csv_file = asn_csv_file,
+ cfg_file = cfg_file,
+ bpki_dir = bpki_dir).xml(e)
+
+ parents.from_csv(
+ parents_csv_file = parents_csv_file,
+ cfg_file = cfg_file,
+ bpki_dir = bpki_dir).xml(e)
+
+ PEMElement(e, "bpki_ca_certificate", bpki_ca_cert_file)
+ PEMElement(e, "bpki_crl", bpki_crl_file)
+
+ if os.path.exists(bpki_ee_cert_file):
+ PEMElement(e, "bpki_ee_certificate", bpki_ee_cert_file)
ElementTree(e).write(output_filename + ".tmp")
os.rename(output_filename + ".tmp", output_filename)
diff --git a/myrpki/relatives.conf b/myrpki/relatives.conf
new file mode 100644
index 00000000..8209a4ee
--- /dev/null
+++ b/myrpki/relatives.conf
@@ -0,0 +1,19 @@
+# $Id$
+#
+# Config file for self-signed test BPKI certificates.
+# Not for production use.
+
+[req]
+default_bits = 2048
+default_md = sha256
+distinguished_name = req_dn
+x509_extensions = req_x509_ext
+prompt = no
+
+[req_x509_ext]
+basicConstraints = critical,CA:true
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always
+
+[req_dn]
+CN = ${ENV::CN}