diff options
-rw-r--r-- | rpkid/rpki/irdb/models.py | 145 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 6 | ||||
-rw-r--r-- | scripts/convert-from-entitydb-to-sql.py | 38 |
3 files changed, 125 insertions, 64 deletions
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index 3a898d8d..1a4fdbd0 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -24,6 +24,7 @@ PERFORMANCE OF THIS SOFTWARE. import django.db.models import rpki.x509 +import rpki.sundial ### @@ -98,6 +99,10 @@ class RSAKeyField(DERField): description = "RSA keypair" rpki_type = rpki.x509.RSA +class CRLField(DERField): + description = "Certificate Revocation List" + rpki_type = rpki.x509.CRL + class PKCS10Field(DERField): description = "PKCS #10 certificate request" rpki_type = rpki.x509.PKCS10 @@ -145,6 +150,14 @@ class CA(django.db.models.Model): identity = django.db.models.ForeignKey(Identity, related_name = "bpki_certificates") purpose_map = ChoiceMap("resources", "servers") purpose = django.db.models.PositiveSmallIntegerField(choices = purpose_map.choices) + certificate = CertificateField() + private_key = RSAKeyField() + latest_crl = CRLField() + + # Might want to bring these into line with what rpkid does. Current + # variables here were chosen to map easily to what OpenSSL command + # line tool was keeping on disk. + next_serial = django.db.models.BigIntegerField(default = 1) next_crl_number = django.db.models.BigIntegerField(default = 1) last_crl_update = django.db.models.DateTimeField() @@ -153,55 +166,71 @@ class CA(django.db.models.Model): class Meta: unique_together = ("identity", "purpose") -class Certificate(django.db.models.Model): - - certificate = CertificateField() - - class Meta: - abstract = True - - def generate_certificate(self): - - # This is sort of vaguely the right idea, but most of it probably - # ought to be a method of the CA, not the object being certified, - # and the rest doesn't yet cover all the different kinds of - # certificates we need to support. - - # Perhaps something where each specialized class has its own - # generate_certificate() method which is mostly just a wrapper for - # a call to the CA object with the right parameters to get the CA - # to issue the certificate. Seems about right. Not awake enough - # to write it now. - - # This doesn't handle self-certification yet either. - # Self-certification may be different enough that we want to move - # the certificate and keypair back to the CA object, since we have - # to special-case it no matter what we do. - - cacert = self.issuer.keyed_certificates.filter(purpose = KeyedCertificate.purpose_map["ca"]) - subject_name, subject_key = self.get_certificate_subject() - cer = cacert.certificate - key = cacert.private_key - - result = cer.bpki_certify( - keypair = key, + # These should come from somewhere, but I don't yet know where + ca_certificate_lifetime = rpki.sundial.timedelta(days = 3652) + crl_interval = rpki.sundial.timedelta(days = 1) + + def self_certify(self): + subject_name = rpki.x509.X501DN("%s BPKI %s CA" % ( + self.identity.handle, self.get_purpose_display())) + now = rpki.sundial.now() + notAfter = now + self.ca_certificate_lifetime + self.certificate = rpki.x509.X509.bpki_self_certify( + keypair = self.private_key, + subject_name = subject_name, + serial = self.next_serial, + now = now, + notAfter = notAfter) + self.serial += 1 + return self.certificate + + def certify(self, subject_name, subject_key, validity_interval, is_ca, pathLenConstraint = None): + now = rpki.sundial.now() + notAfter = now + validity_interval + result = self.certificate.bpki_certify( + keypair = self.private_key, subject_name = subject_name, subject_key = subject_key, - serial = ca.next_serial, - - # This needs to be configurable - notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 60), + serial = self.next_serial, + now = now, + notAfter = notAfter, + is_ca = is_ca, + pathLenConstraint = pathLenConstraint) + self.serial += 1 + return result + + def revoke(self, cert): + Revocations.objects.create( + issuer = self, + revoked = rpki.sundial.now(), + serial = cert.certificate.getSerial(), + expires = cert.certificate.getNotAfter() + self.crl_interval) + cert.delete() + self.generate_crl() + + def generate_crl(self): + now = rpki.sundial.now() + self.revocations.filter(expires__lt = now).delete() + revoked_certificates = [(r.serial, rpki.sundial.datetime.fromdatetime(r.revoked).toASN1tuple(), ()) + for r in self.revocations] + self.latest_crl = rpki.x509.CRL.generate( + keypair = self.private_key, + issuer = self.certificate.getSubject(), + thisUpdate = now, + nextUpdate = now + self.crl_interval, + revoked_certificates = revoked_certificates) - # This is (at least) per-class, not universal - pathLenConstraint = 0, - # This is per-class too - is_ca = True) +class Certificate(django.db.models.Model): + certificate = CertificateField() - self.ca.next_serial += 1 + default_interval = rpki.sundial.timedelta(days = 60) - self.certificate = result + class Meta: + abstract = True + def revoke(self): + self.ca.revoke(self) class CrossCertification(Certificate): @@ -212,8 +241,14 @@ class CrossCertification(Certificate): abstract = True unique_together = ("issuer", "handle") - def get_certificate_subject(self): - return self.certificate.getSubject(), self.certificate.getPublicKey() + def generate_certificate(self): + self.certificate = self.issuer.certify( + subject_name = self.ta.getSubject(), + subject_key = self.ta.getPublicKey(), + interval = self.default_interval, + is_ca = True, + pathLenConstraint = 0) + class Revocation(django.db.models.Model): issuer = django.db.models.ForeignKey(CA, related_name = "revocations") @@ -224,15 +259,24 @@ class Revocation(django.db.models.Model): class Meta: unique_together = ("issuer", "serial") -class KeyedCertificate(Certificate): - issuer = django.db.models.ForeignKey(CA, related_name = "keyed_certificates") - purpose_map = ChoiceMap("ca", "rpkid", "pubd", "irdbd", "irbe", "rootd") +class EECertificate(Certificate): + issuer = django.db.models.ForeignKey(CA, related_name = "ee_certificates") + purpose_map = ChoiceMap("rpkid", "pubd", "irdbd", "irbe", "rootd") purpose = django.db.models.PositiveSmallIntegerField(choices = purpose_map.choices) private_key = RSAKeyField() class Meta: unique_together = ("issuer", "purpose") + def generate_certificate(self): + subject_name = rpki.x509.X501DN("%s BPKI %s EE" % ( + self.issuer.identity.handle, self.get_purpose_display())) + self.certificate = self.issuer.certify( + subject_name = subject_name, + subject_key = self.private_key.getPublicKey(), + interval = self.default_interval, + is_ca = False) + class BSC(Certificate): issuer = django.db.models.ForeignKey(CA, related_name = "bscs") handle = HandleField() @@ -241,6 +285,13 @@ class BSC(Certificate): class Meta: unique_together = ("issuer", "handle") + def generate_certificate(self): + self.certificate = self.issuer.certify( + subject_name = self.pkcs10.getSubject(), + subject_key = self.pkcs10.getPublicKey(), + interval = self.default_interval, + is_ca = False) + class Child(CrossCertification): issuer = django.db.models.ForeignKey(CA, related_name = "children") name = django.db.models.TextField(null = True, blank = True) diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 2011b416..2d5505d5 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -761,6 +761,12 @@ class PKCS10(DER_object): self.POWpkix = req return self.POWpkix + def getSubject(self): + """ + Extract the subject name from this certification request. + """ + return X501DN(self.get_POWpkix().certificationRequestInfo.subject.get()) + def getPublicKey(self): """ Extract the public key from this certification request. diff --git a/scripts/convert-from-entitydb-to-sql.py b/scripts/convert-from-entitydb-to-sql.py index 8885893b..1ab5201d 100644 --- a/scripts/convert-from-entitydb-to-sql.py +++ b/scripts/convert-from-entitydb-to-sql.py @@ -113,30 +113,35 @@ def read_openssl_serial(filename): return int(text.strip(), 16) def get_or_create_CA(purpose): + cer = rpki.x509.X509(Auto_file = os.path.join(bpki, purpose, "ca.cer")) + key = rpki.x509.RSA(Auto_file = os.path.join(bpki, purpose, "ca.key")) crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, purpose, "ca.crl")) serial = read_openssl_serial(os.path.join(bpki, purpose, "serial")) crl_number = read_openssl_serial(os.path.join(bpki, purpose, "crl_number")) - return rpki.irdb.CA.objects.get_or_create(identity = identity, - purpose = rpki.irdb.CA.purpose_map[purpose], - next_serial = serial, - next_crl_number = crl_number, - last_crl_update = crl.getThisUpdate().to_sql(), - next_crl_update = crl.getNextUpdate().to_sql())[0] - -def get_or_create_KeyedCertificate(issuer, purpose): + return rpki.irdb.CA.objects.get_or_create( + identity = identity, + purpose = rpki.irdb.CA.purpose_map[purpose], + certificate = cer, + private_key = key, + latest_crl = crl, + next_serial = serial, + next_crl_number = crl_number, + last_crl_update = crl.getThisUpdate().to_sql(), + next_crl_update = crl.getNextUpdate().to_sql())[0] + +def get_or_create_EECertificate(issuer, purpose): cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", purpose + ".cer")) key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", purpose + ".key")) - rpki.irdb.KeyedCertificate.objects.get_or_create( + rpki.irdb.EECertificate.objects.get_or_create( issuer = issuer, - purpose = rpki.irdb.KeyedCertificate.purpose_map[purpose], + purpose = rpki.irdb.EECertificate.purpose_map[purpose], certificate = cer, private_key = key) # Load BPKI CA data resource_ca = get_or_create_CA("resources") -get_or_create_KeyedCertificate(resource_ca, "ca") # Load BPKI server EE certificates and keys @@ -145,15 +150,14 @@ run_flags = dict((i, cfg.getboolean(i, section = "myrpki")) if any(run_flags.itervalues()): server_ca = get_or_create_CA("servers") - get_or_create_KeyedCertificate(server_ca, "ca") - get_or_create_KeyedCertificate(server_ca, "irbe") + get_or_create_EECertificate(server_ca, "irbe") if run_flags["run_rpkid"]: - get_or_create_KeyedCertificate(server_ca, "rpkid") - get_or_create_KeyedCertificate(server_ca, "irdbd") + get_or_create_EECertificate(server_ca, "rpkid") + get_or_create_EECertificate(server_ca, "irdbd") if run_flags["run_pubd"]: - get_or_create_KeyedCertificate(server_ca, "pubd") + get_or_create_EECertificate(server_ca, "pubd") if run_flags["run_rootd"]: - get_or_create_KeyedCertificate(server_ca, "rootd") + get_or_create_EECertificate(server_ca, "rootd") else: server_ca = None |