From 80a50f9bdb775c33e41d93258e00b397ea05598d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 3 Mar 2020 23:35:50 +0100 Subject: [PATCH] providers/saml: switch to new crypto --- passbook/providers/saml/api.py | 4 +- passbook/providers/saml/forms.py | 12 +-- .../migrations/0007_auto_20200303_2157.py | 29 +++++++ passbook/providers/saml/models.py | 11 ++- passbook/providers/saml/utils/cert.py | 84 ------------------- passbook/providers/saml/utils/xml_signing.py | 7 +- passbook/providers/saml/views.py | 6 +- 7 files changed, 47 insertions(+), 106 deletions(-) create mode 100644 passbook/providers/saml/migrations/0007_auto_20200303_2157.py delete mode 100644 passbook/providers/saml/utils/cert.py diff --git a/passbook/providers/saml/api.py b/passbook/providers/saml/api.py index 1348c85ca..1f8d4d6ec 100644 --- a/passbook/providers/saml/api.py +++ b/passbook/providers/saml/api.py @@ -24,9 +24,7 @@ class SAMLProviderSerializer(ModelSerializer): "property_mappings", "digest_algorithm", "signature_algorithm", - "signing", - "signing_cert", - "signing_key", + "singing_kp", ] diff --git a/passbook/providers/saml/forms.py b/passbook/providers/saml/forms.py index 08dc95f89..e2178f1f6 100644 --- a/passbook/providers/saml/forms.py +++ b/passbook/providers/saml/forms.py @@ -9,7 +9,6 @@ from passbook.providers.saml.models import ( SAMLProvider, get_provider_choices, ) -from passbook.providers.saml.utils.cert import CertificateBuilder class SAMLProviderForm(forms.ModelForm): @@ -19,13 +18,6 @@ class SAMLProviderForm(forms.ModelForm): choices=get_provider_choices(), label="Processor" ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - builder = CertificateBuilder() - builder.build() - self.fields["signing_cert"].initial = builder.certificate - self.fields["signing_key"].initial = builder.private_key - class Meta: model = SAMLProvider @@ -41,9 +33,7 @@ class SAMLProviderForm(forms.ModelForm): "property_mappings", "digest_algorithm", "signature_algorithm", - "signing", - "signing_cert", - "signing_key", + "singing_kp", ] widgets = { "name": forms.TextInput(), diff --git a/passbook/providers/saml/migrations/0007_auto_20200303_2157.py b/passbook/providers/saml/migrations/0007_auto_20200303_2157.py new file mode 100644 index 000000000..d9a3400d1 --- /dev/null +++ b/passbook/providers/saml/migrations/0007_auto_20200303_2157.py @@ -0,0 +1,29 @@ +# Generated by Django 3.0.3 on 2020-03-03 21:57 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_crypto", "0001_initial"), + ("passbook_providers_saml", "0006_auto_20200217_2031"), + ] + + operations = [ + migrations.RemoveField(model_name="samlprovider", name="signing",), + migrations.RemoveField(model_name="samlprovider", name="signing_cert",), + migrations.RemoveField(model_name="samlprovider", name="signing_key",), + migrations.AddField( + model_name="samlprovider", + name="singing_kp", + field=models.ForeignKey( + default=None, + help_text="Singing is enabled upon selection of a Key Pair.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="passbook_crypto.CertificateKeyPair", + ), + ), + ] diff --git a/passbook/providers/saml/models.py b/passbook/providers/saml/models.py index 42e97328e..d215f1a9b 100644 --- a/passbook/providers/saml/models.py +++ b/passbook/providers/saml/models.py @@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy as _ from structlog import get_logger from passbook.core.models import PropertyMapping, Provider +from passbook.crypto.models import CertificateKeyPair from passbook.lib.utils.reflection import class_to_path, path_to_class from passbook.lib.utils.template import render_to_string from passbook.providers.saml.processors.base import Processor @@ -74,9 +75,13 @@ class SAMLProvider(Provider): default="rsa-sha256", ) - signing = models.BooleanField(default=True) - signing_cert = models.TextField(verbose_name=_("Singing Certificate")) - signing_key = models.TextField() + singing_kp = models.ForeignKey( + CertificateKeyPair, + default=None, + null=True, + help_text=_("Singing is enabled upon selection of a Key Pair."), + on_delete=models.SET_NULL, + ) form = "passbook.providers.saml.forms.SAMLProviderForm" _processor = None diff --git a/passbook/providers/saml/utils/cert.py b/passbook/providers/saml/utils/cert.py deleted file mode 100644 index 9a0b6c56f..000000000 --- a/passbook/providers/saml/utils/cert.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Create self-signed certificates""" -import datetime -import uuid - -from cryptography import x509 -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID - - -class CertificateBuilder: - """Build self-signed certificates""" - - __public_key = None - __private_key = None - __builder = None - __certificate = None - - def __init__(self): - self.__public_key = None - self.__private_key = None - self.__builder = None - self.__certificate = None - - def build(self): - """Build self-signed certificate""" - one_day = datetime.timedelta(1, 0, 0) - self.__private_key = rsa.generate_private_key( - public_exponent=65537, key_size=2048, backend=default_backend() - ) - self.__public_key = self.__private_key.public_key() - self.__builder = ( - x509.CertificateBuilder() - .subject_name( - x509.Name( - [ - x509.NameAttribute( - NameOID.COMMON_NAME, - u"passbook Self-signed SAML Certificate", - ), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"passbook"), - x509.NameAttribute( - NameOID.ORGANIZATIONAL_UNIT_NAME, u"Self-signed" - ), - ] - ) - ) - .issuer_name( - x509.Name( - [ - x509.NameAttribute( - NameOID.COMMON_NAME, - u"passbook Self-signed SAML Certificate", - ), - ] - ) - ) - .not_valid_before(datetime.datetime.today() - one_day) - .not_valid_after(datetime.datetime.today() + datetime.timedelta(days=365)) - .serial_number(int(uuid.uuid4())) - .public_key(self.__public_key) - ) - self.__certificate = self.__builder.sign( - private_key=self.__private_key, - algorithm=hashes.SHA256(), - backend=default_backend(), - ) - - @property - def private_key(self): - """Return private key in PEM format""" - return self.__private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ).decode("utf-8") - - @property - def certificate(self): - """Return certificate in PEM format""" - return self.__certificate.public_bytes( - encoding=serialization.Encoding.PEM, - ).decode("utf-8") diff --git a/passbook/providers/saml/utils/xml_signing.py b/passbook/providers/saml/utils/xml_signing.py index 496e48f8a..3afa2bdff 100644 --- a/passbook/providers/saml/utils/xml_signing.py +++ b/passbook/providers/saml/utils/xml_signing.py @@ -31,9 +31,12 @@ def sign_with_signxml(data: str, provider: "SAMLProvider", reference_uri=None) - digest_algorithm=provider.digest_algorithm, ) signed = signer.sign( - root, key=key, cert=[provider.signing_cert], reference_uri=reference_uri + root, + key=key, + cert=[provider.singing_kp.certificate_data], + reference_uri=reference_uri, ) - XMLVerifier().verify(signed, x509_cert=provider.signing_cert) + XMLVerifier().verify(signed, x509_cert=provider.singing_kp.certificate_data) return etree.tostring(signed).decode("utf-8") # nosec diff --git a/passbook/providers/saml/views.py b/passbook/providers/saml/views.py index 793cfeab9..3a3dc0665 100644 --- a/passbook/providers/saml/views.py +++ b/passbook/providers/saml/views.py @@ -274,9 +274,9 @@ class DescriptorDownloadView(AccessRequiredView): kwargs={"application": provider.application.slug}, ) ) - pubkey = strip_pem_header(provider.signing_cert.replace("\r", "")).replace( - "\n", "" - ) + pubkey = strip_pem_header( + provider.singing_kp.certificate_data.replace("\r", "") + ).replace("\n", "") subject_format = provider.processor.subject_format ctx = { "entity_id": entity_id,